EbonholdHub.AffixPicker = {}

local ST = EbonholdHub.SiteTheme
local SW = EbonholdHub.SiteWidgets
local C  = ST.C
local L  = ST.Layout
local AD = EbonholdHub.AffixData

local FLAT = "Interface\\Buttons\\WHITE8X8"
local ROW_H      = 38
local ICON_SIZE  = 26
local PAD        = 14
local HEADER_H   = 52
local SEARCH_H   = 32
local RANK_H     = 70
local RANK_BTN_W = 34
local RANK_BTN_H = 26
local RANK_BTN_GAP = 4

-- ── State ─────────────────────────────────────────────────────────────────────
local frame, searchBox, scrollFrame, scrollChild
local rankPanel, rankLabel, rankBtns
local allEntries, filtered, rowPool = {}, {}, {}
local onConfirm, pendingAffix
local Render, PopulateRow, ShowRankPanel, HideRankPanel

-- ── Logic (unchanged) ─────────────────────────────────────────────────────────
local function BuildEntries()
    return AD.GetCatalog(true)
end

local function ApplySearch(text)
    filtered = {}
    text = (text or ""):lower()
    if text == "" then
        for i = 1, #allEntries do filtered[i] = allEntries[i] end
        return
    end
    for i = 1, #allEntries do
        local e = allEntries[i]
        if e.name:lower():find(text, 1, true) then
            filtered[#filtered + 1] = e
        end
    end
end

local function ConfirmRank(rank)
    if not pendingAffix or not onConfirm then return end
    local affixId = AD.GetAffixIdForRank(pendingAffix, rank)
    onConfirm(pendingAffix.id, pendingAffix.name, rank, affixId)
    frame:Hide()
end

-- ── Rank panel visibility ─────────────────────────────────────────────────────
HideRankPanel = function()
    pendingAffix = nil
    if rankPanel then rankPanel:Hide() end
    if scrollChild then
        scrollChild:ClearAllPoints()
        scrollChild:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
    end
    Render()
end

ShowRankPanel = function(affix)
    if affix.weapon then
        pendingAffix = affix
        ConfirmRank(nil)
        return
    end
    pendingAffix = affix
    rankLabel:SetText("Rank for |cff" .. string.format("%02x%02x%02x",
        math.floor(C.accent[1]*255), math.floor(C.accent[2]*255), math.floor(C.accent[3]*255))
        .. affix.name .. "|r")

    -- Show/dim rank buttons based on what IDs exist for this affix.
    for rank, btn in ipairs(rankBtns) do
        local hasId = AD.GetAffixIdForRank(affix, rank) ~= nil
        if hasId then
            btn._bg:SetVertexColor(unpack(C.elementBg))
            btn._label:SetTextColor(unpack(C.text))
            btn:EnableMouse(true)
        else
            btn._bg:SetVertexColor(C.bg[1], C.bg[2], C.bg[3])
            btn._label:SetTextColor(unpack(C.textDim))
            btn:EnableMouse(false)
        end
    end

    rankPanel:Show()
    rankPanel:Raise()
    Render()
end

-- ── Row factory ───────────────────────────────────────────────────────────────
local function CreateRow(parent)
    local row = CreateFrame("Button", nil, parent)
    row:SetHeight(ROW_H)

    -- Hover background
    local bg = row:CreateTexture(nil, "BACKGROUND")
    bg:SetTexture(FLAT)
    bg:SetVertexColor(C.elementBg[1], C.elementBg[2], C.elementBg[3], 0)
    bg:SetAllPoints(row)
    row._bg = bg

    -- Bottom separator
    local sep = row:CreateTexture(nil, "ARTWORK")
    sep:SetTexture(FLAT)
    sep:SetVertexColor(unpack(C.borderSoft))
    sep:SetHeight(1)
    sep:SetPoint("BOTTOMLEFT", 2, 0)
    sep:SetPoint("BOTTOMRIGHT", -2, 0)

    -- Icon container with quality border
    local iconHolder = CreateFrame("Frame", nil, row)
    iconHolder:SetSize(ICON_SIZE, ICON_SIZE)
    iconHolder:SetPoint("LEFT", row, "LEFT", PAD, 0)
    SW.ThinBorder(iconHolder, C.borderSoft, 1)

    local icon = iconHolder:CreateTexture(nil, "ARTWORK")
    icon:SetPoint("TOPLEFT", 1, -1)
    icon:SetPoint("BOTTOMRIGHT", -1, 1)
    row._icon = icon
    row._iconHolder = iconHolder

    -- Weapon indicator dot (shown for weapon affixes)
    local dot = row:CreateTexture(nil, "OVERLAY")
    dot:SetTexture(FLAT)
    dot:SetVertexColor(unpack(C.accent))
    dot:SetSize(4, 4)
    dot:SetPoint("BOTTOMRIGHT", iconHolder, "BOTTOMRIGHT", 0, 0)
    dot:Hide()
    row._dot = dot

    -- Name label
    local nameLabel = row:CreateFontString(nil, "OVERLAY")
    SW.SetFont(nameLabel, 12, "medium")
    nameLabel:SetTextColor(unpack(C.text))
    nameLabel:SetPoint("LEFT", iconHolder, "RIGHT", 10, 2)
    nameLabel:SetPoint("RIGHT", row, "RIGHT", -PAD, 0)
    nameLabel:SetJustifyH("LEFT")
    row._name = nameLabel

    -- Type label (weapon / I–VI)
    local typeLabel = row:CreateFontString(nil, "OVERLAY")
    SW.SetFont(typeLabel, 10)
    typeLabel:SetTextColor(unpack(C.textDim))
    typeLabel:SetPoint("LEFT", iconHolder, "RIGHT", 10, -8)
    row._type = typeLabel

    row:SetScript("OnEnter", function(self)
        bg:SetVertexColor(C.elementBg[1], C.elementBg[2], C.elementBg[3], 1)
        if self._entry then
            local AO = EbonholdHub.AffixOwnership
            local entry = self._entry
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:ClearLines()
            GameTooltip:AddLine(entry.name, unpack(C.accent))
            if entry.weapon then
                GameTooltip:AddLine("Weapon affix  •  rankless", unpack(C.textDim))
            else
                local maxR = AD.MAX_RANK
                -- find actual max rank for this entry
                if entry.rankAffixIds then
                    for r = maxR, 1, -1 do
                        if entry.rankAffixIds[r] then maxR = r; break end
                    end
                end
                GameTooltip:AddLine("Ranks I–" .. AD.RankToRoman(maxR), unpack(C.textDim))
            end
            if entry.description and entry.description ~= "" then
                GameTooltip:AddLine(" ")
                GameTooltip:AddLine(entry.description, 0.85, 0.85, 0.85, true)
            end
            if AO then AO.AddTooltipLines(entry) end
            GameTooltip:Show()
        end
    end)
    row:SetScript("OnLeave", function()
        bg:SetVertexColor(C.elementBg[1], C.elementBg[2], C.elementBg[3], 0)
        GameTooltip:Hide()
    end)
    row._entry = nil
    return row
end

-- ── Row population ────────────────────────────────────────────────────────────
PopulateRow = function(row, index, entry)
    row:ClearAllPoints()
    row:SetPoint("TOPLEFT",  scrollChild, "TOPLEFT",  0, -(index - 1) * ROW_H)
    row:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", 0, -(index - 1) * ROW_H)

    row._entry = entry
    row._icon:SetTexture(AD.GetIcon(entry))

    -- weapon dot
    if entry.weapon then
        row._dot:Show()
        row._type:SetText("Weapon")
        row._type:SetTextColor(C.accent[1], C.accent[2] * 0.7, C.accent[3] * 0.5)
    else
        row._dot:Hide()
        local maxR = AD.MAX_RANK
        if entry.rankAffixIds then
            for r = maxR, 1, -1 do
                if entry.rankAffixIds[r] then maxR = r; break end
            end
        end
        row._type:SetText("Ranks I–" .. AD.RankToRoman(maxR))
        row._type:SetTextColor(unpack(C.textDim))
    end

    -- name with optional learned badge
    if entry.learnedCount and entry.learnedCount > 0 then
        row._name:SetText(entry.name .. "  |cff19ff19×" .. entry.learnedCount .. "|r")
    else
        row._name:SetText(entry.name)
    end

    row:SetScript("OnClick", function() ShowRankPanel(entry) end)
    row:Show()
end

-- ── Render ────────────────────────────────────────────────────────────────────
Render = function()
    local w = scrollFrame:GetWidth()
    if w and w > 0 then scrollChild:SetWidth(math.max(1, w)) end

    for i = 1, #filtered do
        if not rowPool[i] then rowPool[i] = CreateRow(scrollChild) end
        PopulateRow(rowPool[i], i, filtered[i])
    end
    for i = #filtered + 1, #rowPool do rowPool[i]:Hide() end
    scrollChild:SetHeight(math.max(1, #filtered * ROW_H))
end

-- ── Search box helper ─────────────────────────────────────────────────────────
local function CreateSearchBox(parent)
    local holder = CreateFrame("Frame", nil, parent)
    holder:SetHeight(SEARCH_H)
    SW.Fill(holder, C.elementBg)
    SW.ThinBorder(holder, C.border, 1)

    local icon = holder:CreateFontString(nil, "OVERLAY")
    SW.SetFont(icon, 11)
    icon:SetTextColor(unpack(C.textDim))
    icon:SetText("⌕")
    icon:SetPoint("LEFT", holder, "LEFT", 10, 0)

    local edit = CreateFrame("EditBox", nil, holder)
    SW.SetFont(edit, 12)
    edit:SetTextColor(unpack(C.text))
    edit:SetAutoFocus(false)
    edit:SetMaxLetters(64)
    edit:SetPoint("LEFT",  holder, "LEFT",  30, 0)
    edit:SetPoint("RIGHT", holder, "RIGHT", -10, 0)
    edit:SetPoint("TOP",    holder, "TOP",    0, 0)
    edit:SetPoint("BOTTOM", holder, "BOTTOM", 0, 0)

    local hint = holder:CreateFontString(nil, "OVERLAY")
    SW.SetFont(hint, 11)
    hint:SetTextColor(unpack(C.textDim))
    hint:SetText("Search affixes by name…")
    hint:SetPoint("LEFT", edit, "LEFT", 0, 0)
    hint:SetJustifyH("LEFT")

    edit:SetScript("OnTextChanged", function(self)
        if self:GetText() == "" then hint:Show() else hint:Hide() end
        ApplySearch(self:GetText() or "")
        Render()
    end)
    edit:SetScript("OnEscapePressed", function(self)
        self:SetText("")
        self:ClearFocus()
    end)

    holder._edit = edit
    holder._hint = hint
    return holder
end

-- ── Main frame ────────────────────────────────────────────────────────────────
local function BuildFrame()
    frame = SW.CreateSiteWindow({
        name     = "EbonholdHubAffixPicker",
        width    = 360,
        height   = 480,
        strata   = "FULLSCREEN_DIALOG",
        toplevel = true,
        shadow   = true,
        closable = false,
    })
    local body = frame.body

    -- Custom close button (site-style icon instead of UIPanelCloseButton)
    local closeBtn = SW.CreateCloseButton(body, function() frame:Hide() end, 26)
    closeBtn:SetPoint("TOPRIGHT", body, "TOPRIGHT", -6, -6)
    closeBtn:SetFrameLevel(body:GetFrameLevel() + 10)

    -- ── Accent stripe ──────────────────────────────────────────────────────
    local stripe = body:CreateTexture(nil, "ARTWORK")
    stripe:SetTexture(FLAT)
    stripe:SetVertexColor(unpack(C.accent))
    stripe:SetHeight(2)
    stripe:SetPoint("TOPLEFT",  body, "TOPLEFT",  0, 0)
    stripe:SetPoint("TOPRIGHT", body, "TOPRIGHT", 0, 0)

    -- ── Header ─────────────────────────────────────────────────────────────
    local header = CreateFrame("Frame", nil, body)
    header:SetPoint("TOPLEFT",  body, "TOPLEFT",  0, -2)
    header:SetPoint("TOPRIGHT", body, "TOPRIGHT", 0, -2)
    header:SetHeight(HEADER_H)
    SW.Fill(header, C.sidebarBg)

    local title = SW.Label(header, "PICK AN AFFIX", 13, C.text, false, "bold")
    title:SetPoint("TOPLEFT", header, "TOPLEFT", PAD, -12)

    local slotHint = SW.Label(header, "", 10, C.textDim)
    slotHint:SetPoint("BOTTOMLEFT",  header, "BOTTOMLEFT",  PAD, 10)
    slotHint:SetPoint("BOTTOMRIGHT", header, "BOTTOMRIGHT", -36, 10)
    slotHint:SetJustifyH("LEFT")
    frame._slotHint = slotHint

    -- Header separator
    local hSep = body:CreateTexture(nil, "ARTWORK")
    hSep:SetTexture(FLAT)
    hSep:SetVertexColor(unpack(C.border))
    hSep:SetHeight(1)
    hSep:SetPoint("TOPLEFT",  header, "BOTTOMLEFT",  0, 0)
    hSep:SetPoint("TOPRIGHT", header, "BOTTOMRIGHT", 0, 0)

    -- ── Search ─────────────────────────────────────────────────────────────
    local searchHolder = CreateSearchBox(body)
    searchHolder:SetPoint("TOPLEFT",  header, "BOTTOMLEFT",  PAD, -10)
    searchHolder:SetPoint("TOPRIGHT", header, "BOTTOMRIGHT", -PAD, -10)
    searchBox = searchHolder._edit

    -- ── Scroll list ────────────────────────────────────────────────────────
    local listContainer = CreateFrame("Frame", nil, body)
    listContainer:SetPoint("TOPLEFT",  searchHolder, "BOTTOMLEFT",  0, -8)
    listContainer:SetPoint("TOPRIGHT", searchHolder, "BOTTOMRIGHT", 0, -8)
    listContainer:SetPoint("BOTTOMLEFT",  body, "BOTTOMLEFT",  0, RANK_H)
    listContainer:SetPoint("BOTTOMRIGHT", body, "BOTTOMRIGHT", 0, RANK_H)

    scrollFrame = CreateFrame("ScrollFrame", nil, listContainer)
    scrollFrame:SetAllPoints(listContainer)
    scrollFrame:EnableMouseWheel(true)

    scrollChild = CreateFrame("Frame", nil, scrollFrame)
    scrollChild:SetWidth(listContainer:GetWidth() or 300)
    scrollChild:SetHeight(1)
    scrollFrame:SetScrollChild(scrollChild)

    scrollFrame:SetScript("OnMouseWheel", function(self, delta)
        local range = scrollChild:GetHeight() - self:GetHeight()
        if range <= 0 then return end
        local pos = self:GetVerticalScroll()
        self:SetVerticalScroll(math.max(0, math.min(range, pos - delta * ROW_H * 2)))
    end)
    scrollFrame:SetScript("OnSizeChanged", function(self)
        local w = self:GetWidth()
        if w and w > 0 then scrollChild:SetWidth(math.max(1, w)) end
        Render()
    end)

    -- ── Rank panel ─────────────────────────────────────────────────────────
    rankPanel = CreateFrame("Frame", nil, body)
    rankPanel:SetPoint("BOTTOMLEFT",  body, "BOTTOMLEFT",  0, 0)
    rankPanel:SetPoint("BOTTOMRIGHT", body, "BOTTOMRIGHT", 0, 0)
    rankPanel:SetHeight(RANK_H)
    rankPanel:SetFrameLevel(body:GetFrameLevel() + 20)
    rankPanel:EnableMouse(true)
    rankPanel:Hide()
    SW.Fill(rankPanel, C.sidebarBg)

    -- Rank panel top border with accent glow
    local rankStripe = rankPanel:CreateTexture(nil, "ARTWORK")
    rankStripe:SetTexture(FLAT)
    rankStripe:SetVertexColor(unpack(C.accent))
    rankStripe:SetHeight(1)
    rankStripe:SetPoint("TOPLEFT",  rankPanel, "TOPLEFT",  0, 0)
    rankStripe:SetPoint("TOPRIGHT", rankPanel, "TOPRIGHT", 0, 0)

    local rankGlow = rankPanel:CreateTexture(nil, "BACKGROUND")
    rankGlow:SetTexture(FLAT)
    rankGlow:SetVertexColor(C.accent[1], C.accent[2], C.accent[3], 0.06)
    rankGlow:SetHeight(16)
    rankGlow:SetPoint("TOPLEFT",  rankPanel, "TOPLEFT",  0, -1)
    rankGlow:SetPoint("TOPRIGHT", rankPanel, "TOPRIGHT", 0, -1)

    rankLabel = SW.Label(rankPanel, "Select rank", 11, C.textDim)
    rankLabel:SetPoint("TOPLEFT", rankPanel, "TOPLEFT", PAD, -10)
    rankLabel:SetPoint("TOPRIGHT", rankPanel, "TOPRIGHT", -PAD, -10)
    rankLabel:SetJustifyH("LEFT")

    -- Rank buttons (I–VI)
    local btnRow = CreateFrame("Frame", nil, rankPanel)
    btnRow:SetHeight(RANK_BTN_H)
    btnRow:SetPoint("BOTTOMLEFT",  rankPanel, "BOTTOMLEFT",  PAD, 10)
    btnRow:SetPoint("BOTTOMRIGHT", rankPanel, "BOTTOMRIGHT", -(PAD + RANK_BTN_W + RANK_BTN_GAP + 6), 10)

    rankBtns = {}
    local prevBtn
    for rank = 1, AD.MAX_RANK do
        local btn = CreateFrame("Button", nil, btnRow)
        btn:SetSize(RANK_BTN_W, RANK_BTN_H)
        btn._bg     = SW.Fill(btn, C.elementBg)
        btn._border = SW.ThinBorder(btn, C.border, 1)
        btn._label  = SW.Label(btn, AD.RankToRoman(rank), 11, C.text, false, "medium")
        btn._label:SetPoint("CENTER")

        btn:SetScript("OnEnter", function(self)
            if self:IsMouseEnabled() then
                SW.SetBorderColor(self._border, C.accent)
                self._label:SetTextColor(unpack(C.accent))
            end
        end)
        btn:SetScript("OnLeave", function(self)
            SW.SetBorderColor(self._border, C.border)
            self._label:SetTextColor(
                self:IsMouseEnabled() and C.text[1] or C.textDim[1],
                self:IsMouseEnabled() and C.text[2] or C.textDim[2],
                self:IsMouseEnabled() and C.text[3] or C.textDim[3]
            )
        end)
        btn:SetScript("OnClick", function() ConfirmRank(rank) end)

        if prevBtn then
            btn:SetPoint("LEFT", prevBtn, "RIGHT", RANK_BTN_GAP, 0)
        else
            btn:SetPoint("LEFT", btnRow, "LEFT", 0, 0)
        end
        rankBtns[rank] = btn
        prevBtn = btn
    end

    -- Back button (right side of rank panel)
    local backBtn = SW.CreateOutlineButton(rankPanel, "Back", RANK_BTN_W + 8)
    backBtn:SetHeight(RANK_BTN_H)
    backBtn:SetPoint("BOTTOMRIGHT", rankPanel, "BOTTOMRIGHT", -PAD, 10)
    backBtn:SetScript("OnClick", HideRankPanel)

    frame:SetScript("OnHide", HideRankPanel)
    frame._v3 = true
    frame:Hide()
    return frame
end

-- ── Public API ────────────────────────────────────────────────────────────────
function EbonholdHub.AffixPicker.Show(callback, slotKey, slotDisplayName)
    if frame and not frame._v3 then
        frame:Hide()
        frame = nil
        rankPanel = nil
        rankBtns  = nil
    end
    if not frame then BuildFrame() end
    onConfirm  = callback
    allEntries = BuildEntries()
    ApplySearch("")
    searchBox:SetText("")
    HideRankPanel()
    frame._slotHint:SetText(
        "|cffcccccc" .. (slotDisplayName or slotKey or "?") .. "|r"
        .. "  —  pick affix, then rank"
    )
    Render()
    frame:Show()
    searchBox:SetFocus()
end

function EbonholdHub.AffixPicker.Hide()
    if frame then frame:Hide() end
end

function EbonholdHub.AffixPicker.Init()
end
