-- HousingDecorGuide/UI/Projects/HDG_CrateDetail.lua
-- Crate detail editor. Mounts on the Projects content host alongside the
-- room side panel and shows when a crate is selected.
--
-- MVP scope: rename, member decor list, "+ add by ID" text input, remove
-- button per row, "Back to room" close. No picker, no preview — those land
-- in P3b once the Styles widgets get extracted into shared components.

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

local M = {}

local PANE_WIDTH = 280
local ROW_HEIGHT = 22

local function _store()
    local HDG = _G.HousingDecorGuide
    return HDG and HDG.Store
end

-- Resolve a decorID to { name, itemID }. Icon is always pulled at render
-- time via C_Item.GetItemIconByID(itemID) — that's the canonical source.
local function _decorInfo(decorID)
    local id = tonumber(decorID) or decorID
    local out = { decorID = id }
    if HDG_Data and HDG_Data.GetCatalog then
        local row = HDG_Data.GetCatalog(id)
        if row then
            out.name   = row.name
            out.itemID = row.itemID
        end
    end
    if (not out.name or not out.itemID) and _G.HDG_AllDecorDB then
        for itemID, entry in pairs(_G.HDG_AllDecorDB) do
            if entry and entry[1] == id then
                out.itemID = out.itemID or itemID
                if not out.name then
                    out.name = entry.name
                        or (C_Item and C_Item.GetItemNameByID and C_Item.GetItemNameByID(itemID))
                end
                break
            end
        end
    end
    out.name = out.name or ("Decor #" .. tostring(decorID))
    return out
end

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 — see HDG_RoomSidePanel: room buttons inside the canvas
    -- inherit MEDIUM and would otherwise bleed through the panel.
    f:SetFrameStrata("HIGH")
    f:Hide()

    -- Translucent lighter sheet (matches RoomSidePanel).
    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)

    -- Back button --------------------------------------------------------
    local back = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    back:SetSize(80, 20)
    back:SetPoint("TOPLEFT", f, "TOPLEFT", 8, -8)
    back:SetText("|A:common-icon-backarrow:14:14|a Room")
    back:SetScript("OnClick", function()
        if self.OnBack then self.OnBack() end
    end)
    f.back = back

    -- Header (room context) ---------------------------------------------
    local crumb = f:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    crumb:SetPoint("TOPLEFT", back, "BOTTOMLEFT", 0, -6)
    f.crumb = crumb

    -- Editable name -----------------------------------------------------
    local nameBox = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
    nameBox:SetSize(PANE_WIDTH - 32, 22)
    nameBox:SetPoint("TOPLEFT", crumb, "BOTTOMLEFT", 6, -6)
    nameBox:SetAutoFocus(false)
    nameBox:SetScript("OnEnterPressed", function(eb)
        eb:ClearFocus()
        local store = _store()
        if not (store and store.Crate_Rename) or not self.currentCrateID then return end
        store:Crate_Rename(self.currentCrateID, eb:GetText())
    end)
    nameBox:SetScript("OnEscapePressed", function(eb) eb:ClearFocus() end)
    f.nameBox = nameBox

    -- Save-as-template — clones this crate into a new library entry that
    -- the Stamp UI can then drop into other rooms.
    local saveTplBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    saveTplBtn:SetSize(PANE_WIDTH - 24, 20)
    saveTplBtn:SetPoint("TOPLEFT", nameBox, "BOTTOMLEFT", -6, -4)
    saveTplBtn:SetText("Save as template")
    saveTplBtn:SetScript("OnClick", function()
        if not self.currentCrateID then return end
        local store = _store()
        if not (store and store.Collection_Get and store.Library_Create) then return end
        local coll = store:Collection_Get(self.currentCrateID)
        if not coll then return end
        local lib = store:Library_Create(coll.name, coll.members or {}, {
            source = "player_save",
        })
        if lib and DEFAULT_CHAT_FRAME then
            DEFAULT_CHAT_FRAME:AddMessage(string.format(
                "|cFFfcd34d[HDG]|r Saved |cFF14b8a6%s|r as a template",
                lib.name or "crate"))
        end
    end)
    f.saveTplBtn = saveTplBtn

    -- Decor list label --------------------------------------------------
    local listLabel = f:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    listLabel:SetPoint("TOPLEFT", saveTplBtn, "BOTTOMLEFT", 6, -10)
    listLabel:SetText("Decor")
    f.listLabel = listLabel

    -- "+ Add decor" anchored to the BOTTOM so the list above can stretch
    -- to fill the remaining height. Built before the list so listHost can
    -- anchor against it.
    local addBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    addBtn:SetSize(PANE_WIDTH - 32, 24)
    addBtn:SetPoint("BOTTOMLEFT",  f, "BOTTOMLEFT",  16, 12)
    addBtn:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -16, 12)
    addBtn:SetText("+ Add decor")
    addBtn:SetScript("OnClick", function()
        if not self.currentCrateID then return end
        local picker = privateDB.HDG_CratePicker
        if picker and picker.Show then picker:Show(self.currentCrateID) end
    end)
    f.addBtn = addBtn

    -- Scrollable list of members. WowScrollBoxList virtualizes rendering —
    -- only the viewport rows are realised, so a 100+ decor crate stays
    -- crisp.
    local listPanel = CreateFrame("Frame", nil, f)
    listPanel:SetPoint("TOPLEFT",     listLabel, "BOTTOMLEFT",  0, -4)
    listPanel:SetPoint("TOPRIGHT",    f,         "TOPRIGHT",   -8, 0)
    listPanel:SetPoint("BOTTOMLEFT",  addBtn,    "TOPLEFT",     0, 8)
    listPanel:SetPoint("BOTTOMRIGHT", addBtn,    "TOPRIGHT",    0, 8)
    f.listPanel = listPanel

    local scrollBox = CreateFrame("Frame", nil, listPanel, "WowScrollBoxList")
    scrollBox:SetPoint("TOPLEFT",     0,  -2)
    scrollBox:SetPoint("BOTTOMRIGHT", -16, 0)
    f.scrollBox = scrollBox

    local scrollBar = CreateFrame("EventFrame", nil, listPanel, "MinimalScrollBar")
    scrollBar:SetPoint("TOPLEFT",    scrollBox, "TOPRIGHT", 2, 0)
    scrollBar:SetPoint("BOTTOMLEFT", scrollBox, "BOTTOMRIGHT", 2, 0)
    f.scrollBar = scrollBar

    local view = CreateScrollBoxListLinearView(0, 0, 0, 0, 0)
    view:SetElementExtent(ROW_HEIGHT)
    view:SetElementInitializer("Frame", function(row, data)
        if not row._init then
            row._init = true
            row:SetHeight(ROW_HEIGHT)

            row.icon = row:CreateTexture(nil, "ARTWORK")
            row.icon:SetSize(ROW_HEIGHT - 4, ROW_HEIGHT - 4)
            row.icon:SetPoint("LEFT", row, "LEFT", 0, 0)

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

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

            row.hover = row:CreateTexture(nil, "HIGHLIGHT")
            row.hover:SetAllPoints()
            row.hover:SetColorTexture(0.96, 0.80, 0.36, 0.06)
        end

        local info = _decorInfo(data.decorID)
        local icon = info.itemID and C_Item and C_Item.GetItemIconByID
                     and C_Item.GetItemIconByID(info.itemID)
        row.icon:SetTexture(icon or 134400)  -- 134400 = ? fallback
        row.label:SetText(info.name)

        row:SetScript("OnEnter", function(rowFrame)
            if info.itemID then
                GameTooltip:SetOwner(rowFrame, "ANCHOR_RIGHT")
                GameTooltip:SetItemByID(info.itemID)
                GameTooltip:Show()
            end
        end)
        row:SetScript("OnLeave", function() GameTooltip:Hide() end)

        row.del:SetScript("OnClick", function()
            local store = _store()
            if not (store and store.Crate_RemoveDecor) then return end
            store:Crate_RemoveDecor(self.currentCrateID, data.decorID)
        end)
    end)
    ScrollUtil.InitScrollBoxListWithScrollBar(scrollBox, scrollBar, view)
    f.view = view

    local empty = listPanel:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    empty:SetPoint("TOPLEFT", listPanel, "TOPLEFT", 4, -4)
    empty:SetText("(empty crate)")
    f.empty = empty

    self.frame = f

    -- Refresh on any crate event for this crate
    local HDG = _G.HousingDecorGuide
    local bus = HDG and HDG.EventBus
    if bus and bus.Register then
        local function _onEvent(payload)
            if not (payload and payload.type == "crate") then return end
            if payload.collID ~= self.currentCrateID then return end
            if not self.frame:IsShown() then return end
            self:_Refresh()
        end
        bus:Register("HDG_COLLECTION_UPSERTED",       _onEvent)
        bus:Register("HDG_COLLECTION_DECOR_ADDED",    _onEvent)
        bus:Register("HDG_COLLECTION_DECOR_REMOVED",  _onEvent)
        bus:Register("HDG_COLLECTION_FIELD_CHANGED",  _onEvent)
        -- DELETED handled by ProjectsTab — it pops us back to the room.
    end

    return f
end

function M:SetOnBack(cb)
    self.OnBack = cb
end

function M:_Refresh()
    local crateID = self.currentCrateID
    if not crateID then return end
    local store = _store()
    if not (store and store.Collection_Get) then return end
    local coll = store:Collection_Get(crateID)
    if not coll then return end

    -- Header crumb (room context)
    local parsed = IDs.parsePath(coll.parent or "") or {}
    self.frame.crumb:SetText(string.format("Room %s · F%d",
        parsed.roomShortID or "?", parsed.floor or 0))

    -- Name (don't stomp focus while user is editing)
    if not self.frame.nameBox:HasFocus() then
        self.frame.nameBox:SetText(coll.name or "")
    end

    -- Members
    local sorted = {}
    for did in pairs(coll.members or {}) do
        sorted[#sorted + 1] = did
    end
    table.sort(sorted, function(a, b)
        local na, nb = tonumber(a), tonumber(b)
        if na and nb then return na < nb end
        return tostring(a) < tostring(b)
    end)

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

    -- Build a fresh provider; SetDataProvider once. Avoids per-row layout
    -- thrash on large crates.
    local provider = CreateDataProvider()
    for _, did in ipairs(sorted) do
        provider:Insert({ decorID = did })
    end
    self.frame.scrollBox:SetDataProvider(provider, ScrollBoxConstants.RetainScrollPosition)
end

function M:Show(crateID)
    if not self.frame then return end
    self.currentCrateID = crateID
    self:_Refresh()
    self.frame:Show()
end

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

function M:GetCurrentCrateID()
    return self.currentCrateID
end

privateDB.HDG_CrateDetail = M
