EbonholdHub.ItemPicker = {}

local UI = EbonholdHub.UI
local C  = UI.Colors
local EL = EbonholdHub.EquipmentLoadout
local IC = EbonholdHub.ItemCatalog

local ROW_HEIGHT = 28
local FOOTER_HEIGHT = 36

local frame, searchBox, listScroll, scrollFrame, scrollChild, slotHint, statusLabel
local footerPanel
local filtered = {}
local rowPool = {}
local onConfirm, pendingSlot
local needsLookup = false
local lookupPending = false

local Render, PopulateRow, LookupFromSearch

local function CloneEntryForAssign(entry)
    return {
        itemId  = entry.itemId,
        name    = entry.name,
        link    = entry.link,
        quality = entry.quality,
        texture = entry.texture,
    }
end

local function Hide()
    lookupPending = false
    if frame then frame:Hide() end
    pendingSlot = nil
    onConfirm = nil
end

local function ConfirmEntry(entry)
    if not entry or not entry.itemId or not entry.name or entry.pendingLookup then return end

    local function Finish(full)
        IC.CacheEntry(full)
        if onConfirm then
            onConfirm(CloneEntryForAssign(full))
        end
        Hide()
    end

    IC.EnrichEntry(entry)
    if IC.IsClientLoaded(entry) then
        Finish(entry)
        return
    end

    lookupPending = true
    SetStatus("Loading item data...")
    IC.RequestItemById(entry.itemId, function(full)
        lookupPending = false
        if full then
            Finish(full)
        else
            IC.EnrichEntry(entry)
            Finish(entry)
        end
    end)
end

local function SetStatus(text)
    if statusLabel then
        statusLabel:SetText(text or "")
    end
end

local function RefreshEntries()
    IC.ImportBuildItems()
    filtered, needsLookup = IC.SearchItems(searchBox and searchBox:GetText() or "")
end

local function UpdateScrollWidth()
    if not scrollFrame or not scrollChild then return end
    local w = scrollFrame:GetWidth()
    if w and w > 0 then
        scrollChild:SetWidth(math.max(1, w))
    end
end

local function LayoutScroll()
    if not listScroll or not footerPanel then return end
    listScroll:ClearAllPoints()
    listScroll:SetPoint("TOPLEFT", frame._searchHolder, "BOTTOMLEFT", 0, -8)
    listScroll:SetPoint("TOPRIGHT", frame._searchHolder, "BOTTOMRIGHT", 0, -8)
    listScroll:SetPoint("BOTTOMLEFT", footerPanel, "TOPLEFT", 0, 6)
    listScroll:SetPoint("BOTTOMRIGHT", footerPanel, "TOPRIGHT", 0, 6)
end

local function CreateRow(parent)
    local row = CreateFrame("Button", nil, parent)
    row:SetHeight(ROW_HEIGHT)

    local bg = UI.Fill(row, C.elementBg)
    row._bg = bg
    row._entry = nil

    row:SetScript("OnEnter", function(self)
        self._bg:SetVertexColor(unpack(C.elementHov))
        local entry = self._entry
        if not entry then return end
        if entry.itemId and not entry.texture then
            IC.PrimeItemId(entry.itemId)
            IC.EnrichEntry(entry)
            if entry.texture and self._iconBtn then
                self._iconBtn.icon:SetTexture(entry.texture)
            end
        end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:ClearLines()
        local hyperlink = IC.GetItemHyperlink(entry)
        if hyperlink then
            GameTooltip:SetHyperlink(hyperlink)
        elseif entry.itemId then
            GameTooltip:SetHyperlink("item:" .. entry.itemId)
        elseif entry.name then
            GameTooltip:AddLine(entry.name, 1, 0.82, 0)
        end
        GameTooltip:Show()
    end)
    row:SetScript("OnLeave", function(self)
        self._bg:SetVertexColor(unpack(C.elementBg))
        GameTooltip:Hide()
    end)

    local iconBtn = UI.CreateIconButton(row, 22)
    iconBtn:SetPoint("LEFT", row, "LEFT", 4, 0)
    iconBtn:EnableMouse(false)
    row._iconBtn = iconBtn

    local label = row:CreateFontString(nil, "OVERLAY")
    UI.SetFont(label, UI.FONT_SIZE_NORMAL, C.text)
    label:SetPoint("LEFT", iconBtn, "RIGHT", 8, 0)
    label:SetPoint("RIGHT", row, "RIGHT", -6, 0)
    label:SetJustifyH("LEFT")
    row._label = label
    return row
end

PopulateRow = function(row, index, entry)
    IC.EnrichEntry(entry)
    row:ClearAllPoints()
    row:SetPoint("LEFT", scrollChild, "LEFT", 0, 0)
    row:SetPoint("RIGHT", scrollChild, "RIGHT", 0, 0)
    row:SetPoint("TOP", scrollChild, "TOP", 0, -(index - 1) * ROW_HEIGHT)

    row._entry = entry
    local tex = EL.GetItemIcon(entry) or "Interface\\Icons\\INV_Misc_QuestionMark"
    row._iconBtn.icon:SetTexture(tex)
    row._iconBtn.icon:SetVertexColor(1, 1, 1)
    if row._iconBtn._empty then row._iconBtn._empty:Hide() end

    local suffix = ""
    if entry.pendingLookup then
        suffix = "  |cff888888(press Enter to load from game)|r"
    elseif entry.source == "equipped" then
        suffix = "  |cff666666(equipped)|r"
    elseif entry.source == "bags" then
        suffix = "  |cff666666(bags)|r"
    end

    local hyperlink = IC.GetItemHyperlink(entry)
    if hyperlink then
        row._label:SetText(hyperlink .. suffix)
    else
        row._label:SetText((entry.name or "?") .. suffix)
    end

    if entry.pendingLookup then
        row:SetScript("OnClick", function()
            LookupFromSearch()
        end)
    else
        row:SetScript("OnClick", function()
            ConfirmEntry(entry)
        end)
    end
    row:Show()
end

Render = function()
    UpdateScrollWidth()
    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_HEIGHT))
    if listScroll and listScroll.UpdateThumb then
        listScroll.UpdateThumb()
    end

    if #filtered == 0 then
        local q = searchBox and strtrim(searchBox:GetText() or "") or ""
        local catalogCount = IC.GetCatalogCount()
        if q == "" then
            SetStatus(string.format(
                "Your items shown above. Type a name to search %d items, or paste an ID from Wowhead.",
                catalogCount))
        elseif needsLookup then
            SetStatus("Press Enter to load item from the game client.")
        elseif #q < 2 then
            SetStatus("Type at least 2 letters to search the item database.")
        else
            SetStatus("No matches. Try another name or paste an item ID from Wowhead.")
        end
    elseif needsLookup then
        SetStatus("Press Enter to load item #" .. tostring(filtered[1].itemId) .. " from the game.")
    else
        SetStatus(string.format("%d item(s) — click to assign", #filtered))
    end

    IC.EnrichList(filtered, function()
        if frame and frame:IsShown() then
            for i = 1, #filtered do
                if rowPool[i] and rowPool[i]:IsShown() then
                    PopulateRow(rowPool[i], i, filtered[i])
                end
            end
        end
    end)
end

LookupFromSearch = function()
    if lookupPending then return end
    local text = searchBox and searchBox:GetText() or ""
    local itemId = IC.ExtractItemId(text)
    if not itemId then
        local entry = IC.ParseInput(text)
        if entry then
            ConfirmEntry(entry)
        elseif EbonholdHub.Toast then
            EbonholdHub.Toast.Show("Enter an item ID, link, or name.")
        end
        return
    end

    lookupPending = true
    SetStatus("Loading item #" .. itemId .. "...")
    IC.RequestItemById(itemId, function(entry, failedId)
        lookupPending = false
        if entry then
            ConfirmEntry(entry)
            return
        end
        RefreshEntries()
        Render()
        if EbonholdHub.Toast then
            EbonholdHub.Toast.Show("Could not load item #" .. tostring(failedId or itemId) .. ". Open it in-game once, then retry.")
        end
    end)
end

local function BuildFrame()
    frame = UI.CreateWindow("EbonholdHubItemPicker", 420, 480, "Pick an Item", {
        strata = "FULLSCREEN_DIALOG",
        toplevel = true,
    })
    local content = frame.content

    slotHint = UI.CreateLabel(content, "", UI.FONT_SIZE_SMALL, C.textDim)
    slotHint:SetPoint("TOPLEFT", content, "TOPLEFT", 0, 0)
    slotHint:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, 0)
    slotHint:SetJustifyH("LEFT")

    local searchHint = UI.CreateLabel(content,
        string.format("Search by name (%d items), paste link, or enter ID from Wowhead",
            IC.GetCatalogCount()),
        UI.FONT_SIZE_SMALL, C.textDim)
    searchHint:SetPoint("TOPLEFT", slotHint, "BOTTOMLEFT", 0, -4)

    local searchHolder = UI.CreateEditBox(content, 380, 22, false)
    searchHolder:SetPoint("TOPLEFT", searchHint, "BOTTOMLEFT", 0, -4)
    searchHolder:SetPoint("TOPRIGHT", searchHint, "BOTTOMRIGHT", 0, -4)
    frame._searchHolder = searchHolder
    searchBox = searchHolder.editBox
    searchBox:SetScript("OnTextChanged", function(self)
        RefreshEntries()
        Render()
    end)
    searchBox:SetScript("OnEnterPressed", function()
        LookupFromSearch()
    end)

    footerPanel = CreateFrame("Frame", nil, content)
    footerPanel:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 0, 0)
    footerPanel:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", 0, 0)
    footerPanel:SetHeight(FOOTER_HEIGHT)

    statusLabel = UI.CreateLabel(footerPanel, "", UI.FONT_SIZE_SMALL, C.textDim)
    statusLabel:SetPoint("TOPLEFT", footerPanel, "TOPLEFT", 0, 0)
    statusLabel:SetPoint("TOPRIGHT", footerPanel, "TOPRIGHT", 0, 0)
    statusLabel:SetJustifyH("LEFT")

    local equippedBtn = UI.CreateButton(footerPanel, 110, 24, "Use equipped", { hoverBorder = C.accent })
    equippedBtn:SetPoint("BOTTOMLEFT", footerPanel, "BOTTOMLEFT", 0, 0)
    equippedBtn:SetScript("OnClick", function()
        if not pendingSlot then return end
        ConfirmEntry(EL.ReadPlayerSlot(pendingSlot))
    end)

    local lookupBtn = UI.CreateButton(footerPanel, 72, 24, "Lookup", { hoverBorder = C.accent })
    lookupBtn:SetPoint("BOTTOMRIGHT", footerPanel, "BOTTOMRIGHT", 0, 0)
    lookupBtn:SetScript("OnClick", LookupFromSearch)

    local cancelBtn = UI.CreateButton(footerPanel, 64, 24, "Cancel", { hoverBorder = C.accent })
    cancelBtn:SetPoint("RIGHT", lookupBtn, "LEFT", -6, 0)
    cancelBtn:SetScript("OnClick", Hide)

    listScroll = UI.CreateScrollFrame(content)
    scrollFrame = listScroll.scrollFrame
    scrollChild = listScroll.scrollChild

    scrollFrame:SetScript("OnSizeChanged", function()
        UpdateScrollWidth()
        Render()
    end)

    LayoutScroll()
    frame._layoutV2 = true
    frame:SetScript("OnHide", function()
        lookupPending = false
        pendingSlot = nil
        onConfirm = nil
    end)
    frame:Hide()
    return frame
end

function EbonholdHub.ItemPicker.Show(callback, slotDef)
    if frame and not frame._layoutV2 then
        frame:Hide()
        frame = nil
        footerPanel = nil
    end
    if not frame then BuildFrame() end
    onConfirm = callback
    pendingSlot = slotDef
    lookupPending = false
    slotHint:SetText("Slot: |cffcccccc" .. (slotDef and slotDef.label or "?") .. "|r")
    searchBox:SetText("")
    IC.ImportBuildItems()
    RefreshEntries()
    Render()
    frame:Show()
    searchBox:SetFocus()
end

function EbonholdHub.ItemPicker.Hide()
    Hide()
end

function EbonholdHub.ItemPicker.Init()
end
