-- HousingDecorGuide/UI/Projects/HDG_RoomSidePanel.lua
-- Right-pane room detail. Shows shape, doors, and the room's crate list.

local _, privateDB = ...
local IDs   = privateDB.HDG_ProjectsIDs
local Store = privateDB.HDG_ProjectsStore

local M = {}

local PANE_WIDTH    = 280
local CRATE_ROW_H   = 22
local CRATE_LIST_MAX = 6  -- pre-pool size; overflow scrolls visually via SetHeight

function M:Create(parent)
    if self.frame then return self.frame end
    local f = CreateFrame("Frame", nil, parent)
    f:SetWidth(PANE_WIDTH)
    f:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, 0)
    f:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)
    -- HIGH strata so the panel draws above every room button (which are
    -- created later inside the canvas at MEDIUM with auto-incrementing
    -- frame levels). Without this the room atlases bleed through the panel.
    f:SetFrameStrata("HIGH")
    f:Hide()

    -- Translucent lighter sheet — reads as an overlay floating above the
    -- canvas. The light blue blends with whatever's behind (canvas, room
    -- tiles) instead of replacing it.
    local bg = f:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints()
    bg:SetColorTexture(0.30, 0.42, 0.58, 0.82)

    local edge = f:CreateTexture(nil, "BORDER")
    edge:SetWidth(1)
    edge:SetPoint("TOPLEFT")
    edge:SetPoint("BOTTOMLEFT")
    edge:SetColorTexture(0.96, 0.80, 0.36, 0.5)

    local header = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    header:SetPoint("TOPLEFT", f, "TOPLEFT", 12, -12)
    header:SetTextColor(0.96, 0.80, 0.36, 1)
    f.header = header

    local meta = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    meta:SetPoint("TOPLEFT", header, "BOTTOMLEFT", 0, -4)
    meta:SetTextColor(0.6, 0.7, 0.8, 1)
    f.meta = meta

    local doorsLabel = f:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    doorsLabel:SetPoint("TOPLEFT", meta, "BOTTOMLEFT", 0, -16)
    doorsLabel:SetText("Doors")
    f.doorsLabel = doorsLabel

    local doorsList = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    doorsList:SetPoint("TOPLEFT", doorsLabel, "BOTTOMLEFT", 0, -4)
    doorsList:SetWidth(PANE_WIDTH - 24)
    doorsList:SetJustifyH("LEFT")
    doorsList:SetJustifyV("TOP")
    f.doorsList = doorsList

    -- Crates section ------------------------------------------------------
    local crateLabel = f:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    crateLabel:SetPoint("TOPLEFT", doorsList, "BOTTOMLEFT", 0, -20)
    crateLabel:SetText("Crates")
    f.crateLabel = crateLabel

    local crateHost = CreateFrame("Frame", nil, f)
    crateHost:SetPoint("TOPLEFT",  crateLabel, "BOTTOMLEFT", 0, -4)
    crateHost:SetPoint("TOPRIGHT", f, "TOPRIGHT", -12, 0)
    crateHost:SetHeight(CRATE_ROW_H * CRATE_LIST_MAX)
    f.crateHost = crateHost

    local emptyLine = crateHost:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    emptyLine:SetPoint("TOPLEFT", crateHost, "TOPLEFT", 0, 0)
    emptyLine:SetText("(no crates yet)")
    f.crateEmpty = emptyLine

    f.crateRows = {}

    local newBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    newBtn:SetSize((PANE_WIDTH - 28) / 2, 22)
    newBtn:SetPoint("TOPLEFT", crateHost, "BOTTOMLEFT", 0, -8)
    newBtn:SetText("+ New crate")
    newBtn:SetScript("OnClick", function()
        if not self.currentRoomID then return end
        local HDG = _G.HousingDecorGuide
        local store = HDG and HDG.Store
        if not (store and store.Crate_Create) then return end
        local crate = store:Crate_Create(self.currentRoomID, "New crate")
        if crate and self.OnCrateOpen then self.OnCrateOpen(crate.id) end
    end)
    f.newBtn = newBtn

    -- Stamp template — pop a context menu of available library entries
    -- grouped by source. Click a row → Library_Stamp into the current room
    -- and open the new crate's detail.
    local stampBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    stampBtn:SetSize((PANE_WIDTH - 28) / 2, 22)
    stampBtn:SetPoint("LEFT", newBtn, "RIGHT", 4, 0)
    stampBtn:SetText("Stamp template")
    stampBtn:SetScript("OnClick", function()
        if not self.currentRoomID then return end
        local HDG = _G.HousingDecorGuide
        local store = HDG and HDG.Store
        if not (store and store.Library_ListAll) then return end
        if not (MenuUtil and MenuUtil.CreateContextMenu) then return end

        local libs = store:Library_ListAll() or {}
        local curated, mine = {}, {}
        for id, lib in pairs(libs) do
            local entry = { id = id, lib = lib }
            if lib.source == "curated" then
                curated[#curated + 1] = entry
            else
                mine[#mine + 1] = entry
            end
        end
        local function _byName(a, b)
            return (a.lib.name or "") < (b.lib.name or "")
        end
        table.sort(curated, _byName)
        table.sort(mine,    _byName)

        MenuUtil.CreateContextMenu(stampBtn, function(_, root)
            root:CreateTitle("Stamp template into this room")
            if #curated == 0 and #mine == 0 then
                local b = root:CreateButton("(no templates yet)", function() end)
                if b and b.SetEnabled then b:SetEnabled(false) end
                return
            end
            if #mine > 0 then
                local sub = root:CreateButton("My Templates")
                for _, e in ipairs(mine) do
                    local count = 0
                    for _ in pairs(e.lib.members or {}) do count = count + 1 end
                    sub:CreateButton(
                        string.format("%s  |cff8c8c8c· %d|r", e.lib.name or "?", count),
                        function()
                            local crate = store:Library_Stamp(e.id, self.currentRoomID)
                            if crate and self.OnCrateOpen then self.OnCrateOpen(crate.id) end
                        end)
                end
            end
            if #curated > 0 then
                local sub = root:CreateButton("Curated")
                for _, e in ipairs(curated) do
                    local count = 0
                    for _ in pairs(e.lib.members or {}) do count = count + 1 end
                    sub:CreateButton(
                        string.format("%s  |cff8c8c8c· %d|r", e.lib.name or "?", count),
                        function()
                            local crate = store:Library_Stamp(e.id, self.currentRoomID)
                            if crate and self.OnCrateOpen then self.OnCrateOpen(crate.id) end
                        end)
                end
            end
        end)
    end)
    f.stampBtn = stampBtn

    -- Unrooted crates section (only visible when orphans exist) -----------
    local orphanLabel = f:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    orphanLabel:SetPoint("TOPLEFT", newBtn, "BOTTOMLEFT", 0, -16)
    orphanLabel:SetText("Unrooted crates")
    orphanLabel:Hide()
    f.orphanLabel = orphanLabel

    local orphanHost = CreateFrame("Frame", nil, f)
    orphanHost:SetPoint("TOPLEFT",  orphanLabel, "BOTTOMLEFT", 0, -4)
    orphanHost:SetPoint("TOPRIGHT", f, "TOPRIGHT", -12, 0)
    orphanHost:SetHeight(CRATE_ROW_H * CRATE_LIST_MAX)
    orphanHost:Hide()
    f.orphanHost = orphanHost
    f.orphanRows = {}

    self.frame = f

    -- Refresh the list whenever a crate event lands. The room may not be
    -- visible — guard with currentRoomID and IsShown.
    local HDG = _G.HousingDecorGuide
    local bus = HDG and HDG.EventBus
    if bus and bus.Register then
        local function _onCrateEvent(payload)
            if not (payload and payload.type == "crate") then return end
            if not self.frame:IsShown() or not self.currentRoomID then return end
            self:_RefreshCrates()
        end
        bus:Register("HDG_COLLECTION_UPSERTED",       _onCrateEvent)
        bus:Register("HDG_COLLECTION_DELETED",        _onCrateEvent)
        bus:Register("HDG_COLLECTION_DECOR_ADDED",    _onCrateEvent)
        bus:Register("HDG_COLLECTION_DECOR_REMOVED",  _onCrateEvent)
        bus:Register("HDG_COLLECTION_FIELD_CHANGED",  _onCrateEvent)
    end

    return f
end

function M:SetOnCrateOpen(cb)
    self.OnCrateOpen = cb
end

local function _decorCount(coll)
    local n = 0
    for _ in pairs(coll.members or {}) do n = n + 1 end
    return n
end

local function _ensureRow(self, index)
    local row = self.frame.crateRows[index]
    if row then return row end

    local host = self.frame.crateHost
    row = CreateFrame("Button", nil, host)
    row:SetHeight(CRATE_ROW_H)
    row:SetPoint("TOPLEFT",  host, "TOPLEFT",  0, -(index - 1) * CRATE_ROW_H)
    row:SetPoint("TOPRIGHT", host, "TOPRIGHT", 0, -(index - 1) * CRATE_ROW_H)

    local hover = row:CreateTexture(nil, "BACKGROUND")
    hover:SetAllPoints()
    hover:SetColorTexture(0.96, 0.80, 0.36, 0.10)
    hover:Hide()
    row.hover = hover
    row:SetScript("OnEnter", function() hover:Show() end)
    row:SetScript("OnLeave", function() hover:Hide() end)

    local label = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    label:SetPoint("LEFT", row, "LEFT", 4, 0)
    label:SetJustifyH("LEFT")
    row.label = label

    local count = row:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    count:SetPoint("RIGHT", row, "RIGHT", -28, 0)
    row.count = count

    local del = CreateFrame("Button", nil, row)
    del:SetSize(16, 16)
    del:SetPoint("RIGHT", row, "RIGHT", -4, 0)
    local xTex = del:CreateTexture(nil, "ARTWORK")
    xTex:SetAtlas("common-icon-redx", false)
    xTex:SetAllPoints()
    row.del = del

    self.frame.crateRows[index] = row
    return row
end

function M:_RefreshCrates()
    local roomID = self.currentRoomID
    if not roomID then return end
    local HDG = _G.HousingDecorGuide
    local store = HDG and HDG.Store
    if not (store and store.Crate_ListForRoom) then return end

    local crates = store:Crate_ListForRoom(roomID)
    local sorted = {}
    for id, c in pairs(crates) do
        sorted[#sorted + 1] = { id = id, coll = c }
    end
    table.sort(sorted, function(a, b)
        return (a.coll.createdAt or 0) < (b.coll.createdAt or 0)
    end)

    -- Hide all rows first
    for _, row in ipairs(self.frame.crateRows) do row:Hide() end

    if #sorted == 0 then
        self.frame.crateEmpty:Show()
    else
        self.frame.crateEmpty:Hide()
    end

    for i, entry in ipairs(sorted) do
        local row = _ensureRow(self, i)
        row.label:SetText(entry.coll.name or "(unnamed)")
        row.count:SetText(string.format("%d decor", _decorCount(entry.coll)))
        row:SetScript("OnClick", function()
            if self.OnCrateOpen then self.OnCrateOpen(entry.id) end
        end)
        row.del:SetScript("OnClick", function()
            if store.Crate_Delete then store:Crate_Delete(entry.id) end
        end)
        row:Show()
    end

    self:_RefreshOrphans()
end

local function _ensureOrphanRow(self, index)
    local row = self.frame.orphanRows[index]
    if row then return row end

    local host = self.frame.orphanHost
    row = CreateFrame("Frame", nil, host)
    row:SetHeight(CRATE_ROW_H)
    row:SetPoint("TOPLEFT",  host, "TOPLEFT",  0, -(index - 1) * CRATE_ROW_H)
    row:SetPoint("TOPRIGHT", host, "TOPRIGHT", 0, -(index - 1) * CRATE_ROW_H)

    local label = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    label:SetPoint("LEFT",  row, "LEFT", 4, 0)
    label:SetPoint("RIGHT", row, "RIGHT", -90, 0)
    label:SetJustifyH("LEFT")
    label:SetWordWrap(false)
    row.label = label

    local move = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
    move:SetSize(80, 18)
    move:SetPoint("RIGHT", row, "RIGHT", -2, 0)
    move:SetText("Move here")
    row.move = move

    self.frame.orphanRows[index] = row
    return row
end

function M:_RefreshOrphans()
    local roomID = self.currentRoomID
    if not roomID then return end
    local HDG = _G.HousingDecorGuide
    local store = HDG and HDG.Store
    if not (store and store.Crate_ListOrphans) then return end

    local orphans = store:Crate_ListOrphans()
    local sorted = {}
    for id, c in pairs(orphans) do
        sorted[#sorted + 1] = { id = id, coll = c }
    end
    table.sort(sorted, function(a, b)
        return (a.coll.createdAt or 0) < (b.coll.createdAt or 0)
    end)

    for _, row in ipairs(self.frame.orphanRows) do row:Hide() end

    if #sorted == 0 then
        self.frame.orphanLabel:Hide()
        self.frame.orphanHost:Hide()
        return
    end

    self.frame.orphanLabel:Show()
    self.frame.orphanLabel:SetText(string.format("Unrooted crates (%d)", #sorted))
    self.frame.orphanHost:Show()
    self.frame.orphanHost:SetHeight(CRATE_ROW_H * #sorted)

    for i, entry in ipairs(sorted) do
        local row = _ensureOrphanRow(self, i)
        local nm = entry.coll.name or "(unnamed)"
        local cnt = _decorCount(entry.coll)
        row.label:SetText(string.format("%s |cff8c8c8c·|r %d decor", nm, cnt))
        row.move:SetScript("OnClick", function()
            if store.Crate_Reparent then store:Crate_Reparent(entry.id, roomID) end
        end)
        row:Show()
    end
end

function M:Show(roomID)
    if not self.frame then return end
    local room = Store.GetRoom(roomID)
    if not room then self.frame:Hide(); return end

    self.currentRoomID = roomID

    local parsed = IDs.parsePath(roomID) or {}
    self.frame.header:SetText(string.format("Room %s", parsed.roomShortID or "?"))
    self.frame.meta:SetText(string.format("F%d · shape=%s",
        parsed.floor or 0, tostring(room.shape)))

    local fp = room.fingerprint or {}
    local lines = {}

    -- Occupied (connected) doors
    for _, d in ipairs(fp.occupiedDoors or {}) do
        lines[#lines + 1] = string.format("|cff8ee0a3%s|r connected", d)
    end

    -- Open doors = doors not in occupiedDoors
    local seen = {}
    for _, d in ipairs(fp.occupiedDoors or {}) do seen[d] = (seen[d] or 0) + 1 end
    for _, d in ipairs(fp.doors or {}) do
        local count = seen[d] or 0
        if count == 0 then
            lines[#lines + 1] = string.format("|cfff5cb5c%s|r open", d)
        else
            seen[d] = count - 1
        end
    end

    self.frame.doorsList:SetText(table.concat(lines, "\n"))
    self:_RefreshCrates()
    self.frame:Show()
end

function M:Hide()
    if self.frame then self.frame:Hide() end
    self.currentRoomID = nil
end

privateDB.HDG_RoomSidePanel = M
