local addonName, privateDB = ...

-- ============================================================================
-- HOUSE TAB MODULE
-- House level XP progress and decor collection stats by source and expansion
-- ============================================================================

local CONFIG = privateDB.CONFIG
local L = HDG_L or {}
local HDG_HouseTab = {}
privateDB.HDG_HouseTab = HDG_HouseTab

-- ============================================================================
-- THEME HELPERS
-- ============================================================================

local HDG = _G.HousingDecorGuide
local function GetScheme() return HDG.Theme.GetCurrentScheme() end
local function RegisterWidget(w, t) HDG.Theme.RegisterWidget(w, t) end
local ApplyFont = function(fs, s, ft) HDG.Theme.ApplyFont(fs, s, ft) end

-- ============================================================================
-- CONSTANTS
-- ============================================================================

local SOURCE_NAMES = {
    [0] = "Unknown", [1] = "Achievement", [2] = "Quest", [3] = "World Quest",
    [4] = "Drop", [5] = "Vendor", [6] = "Crafted", [7] = "Learned",
    [8] = "Gathering", [9] = "Treasure", [10] = "Promotion", [11] = "Profession",
}

local SOURCE_COLORS = {
    [0]  = { 0.55, 0.55, 0.55 }, [1]  = { 0.85, 0.70, 0.30 },
    [2]  = { 0.85, 0.85, 0.30 }, [3]  = { 0.30, 0.70, 0.90 },
    [4]  = { 0.70, 0.40, 0.70 }, [5]  = { 0.40, 0.75, 0.55 },
    [6]  = { 0.90, 0.55, 0.25 }, [7]  = { 0.50, 0.70, 0.85 },
    [8]  = { 0.45, 0.80, 0.35 }, [9]  = { 0.75, 0.60, 0.40 },
    [10] = { 0.65, 0.50, 0.80 }, [11] = { 0.60, 0.75, 0.85 },
}

local SOURCE_ORDER = { 5, 6, 2, 4, 3, 9, 1, 11, 8, 10, 7, 0 }

-- Expansion display order (newest first)
local EXPANSION_ORDER = {
    "Midnight", "The War Within", "Dragonflight", "Shadowlands",
    "Battle for Azeroth", "Legion", "Warlords of Draenor", "Mists of Pandaria",
    "Cataclysm", "Wrath of the Lich King", "The Burning Crusade", "Classic",
}

local EXPANSION_COLORS = {
    ["Midnight"]             = { 0.60, 0.30, 0.80 },
    ["The War Within"]       = { 0.40, 0.65, 0.85 },
    ["Dragonflight"]         = { 0.35, 0.75, 0.55 },
    ["Shadowlands"]          = { 0.50, 0.55, 0.70 },
    ["Battle for Azeroth"]   = { 0.70, 0.50, 0.30 },
    ["Legion"]               = { 0.45, 0.80, 0.30 },
    ["Warlords of Draenor"]  = { 0.75, 0.40, 0.25 },
    ["Mists of Pandaria"]    = { 0.35, 0.75, 0.70 },
    ["Cataclysm"]            = { 0.85, 0.40, 0.20 },
    ["Wrath of the Lich King"] = { 0.40, 0.60, 0.80 },
    ["The Burning Crusade"]  = { 0.55, 0.75, 0.30 },
    ["Classic"]              = { 0.75, 0.65, 0.45 },
}

local COLLECTION_CACHE_TTL = 60
local XP_PER_ITEM = 10
local XP_BAR_HEIGHT = 24
local XP_PANEL_PAD = 10
local TICK_WIDTH = 1
local ROW_HEIGHT = 22
local SOURCE_BAR_HEIGHT = 12
local ACCENT_BAR_WIDTH = 3
local SIDE_PAD = 12
local COL_GAP = 10
local COL_INNER_PAD = 4
local NAME_WIDTH = 90
local STAT_RIGHT_OFFSET = 125

-- ============================================================================
-- STATE
-- ============================================================================

local mainFrame = nil
local housePanel = nil
local xpBar = nil
local xpBarBorder = nil
local xpLabel = nil
local xpLevelLabel = nil
local tickFrames = {}
local sourceRows = {}
local expansionRows = {}
local xpSummaryLabel = nil
local collectionCache = nil
local collectionCacheTime = 0
local initialized = false

-- ============================================================================
-- COLLECTION STATS (builds both source + expansion breakdowns in one pass)
-- ============================================================================

local function BuildCollectionStats()
    local now = GetTime()
    if collectionCache and (now - collectionCacheTime) < COLLECTION_CACHE_TTL then
        return collectionCache
    end

    local bySource = {}
    local byExp = {}
    local totalAll, collectedAll = 0, 0

    if HDG_AllDecorDB then
        for itemID, entry in pairs(HDG_AllDecorDB) do
            local sourceType = entry[2] or 0
            local expansion = entry.exp or "Unknown"
            local collected = HDG_Data and HDG_Data.IsItemCollected and HDG_Data.IsItemCollected(itemID)

            -- Source stats
            if not bySource[sourceType] then bySource[sourceType] = { total = 0, collected = 0 } end
            bySource[sourceType].total = bySource[sourceType].total + 1

            -- Expansion stats
            if not byExp[expansion] then byExp[expansion] = { total = 0, collected = 0 } end
            byExp[expansion].total = byExp[expansion].total + 1

            totalAll = totalAll + 1
            if collected then
                bySource[sourceType].collected = bySource[sourceType].collected + 1
                byExp[expansion].collected = byExp[expansion].collected + 1
                collectedAll = collectedAll + 1
            end
        end
    end

    collectionCache = {
        bySource = bySource, byExp = byExp,
        totalAll = totalAll, collectedAll = collectedAll,
    }
    collectionCacheTime = now
    return collectionCache
end

-- ============================================================================
-- ROW FACTORY (shared between source and expansion sections)
-- ============================================================================

local function CreateStatRow(parent, yOffset, index)
    local scheme = GetScheme()

    local rowFrame = CreateFrame("Frame", nil, parent)
    rowFrame:SetPoint("TOPLEFT", COL_INNER_PAD, yOffset)
    rowFrame:SetPoint("TOPRIGHT", -COL_INNER_PAD, yOffset)
    rowFrame:SetHeight(ROW_HEIGHT)

    if index % 2 == 0 then
        local rowBg = rowFrame:CreateTexture(nil, "BACKGROUND")
        rowBg:SetAllPoints()
        rowBg:SetColorTexture(1, 1, 1, 0.03)
    end

    local hoverOverlay = rowFrame:CreateTexture(nil, "BACKGROUND", nil, 1)
    hoverOverlay:SetAllPoints()
    hoverOverlay:SetColorTexture(1, 1, 1, 0.05)
    hoverOverlay:Hide()

    local completeTint = rowFrame:CreateTexture(nil, "BACKGROUND", nil, 2)
    completeTint:SetAllPoints()
    completeTint:SetColorTexture(0.3, 0.85, 0.3, 0.05)
    completeTint:Hide()

    local nameText = rowFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    nameText:SetPoint("LEFT", 0, 0)
    nameText:SetWidth(NAME_WIDTH)
    nameText:SetJustifyH("LEFT")
    nameText:SetWordWrap(false)
    if scheme then ApplyFont(nameText, scheme, "small") end
    RegisterWidget(nameText, "Text")

    local barBg = CreateFrame("Frame", nil, rowFrame, "BackdropTemplate")
    barBg:SetPoint("LEFT", nameText, "RIGHT", 4, 0)
    barBg:SetPoint("RIGHT", -STAT_RIGHT_OFFSET, 0)
    barBg:SetHeight(SOURCE_BAR_HEIGHT)
    barBg:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8" })
    barBg:SetBackdropColor(0.1, 0.1, 0.1, 0.6)

    local barFill = barBg:CreateTexture(nil, "ARTWORK")
    barFill:SetPoint("TOPLEFT", 0, 0)
    barFill:SetPoint("BOTTOMLEFT", 0, 0)
    barFill:SetWidth(1)
    barFill:SetTexture("Interface\\TargetingFrame\\UI-StatusBar")

    local countText = rowFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    countText:SetPoint("LEFT", barBg, "RIGHT", 4, 0)
    countText:SetWidth(68)
    countText:SetJustifyH("RIGHT")
    countText:SetWordWrap(false)
    if scheme then ApplyFont(countText, scheme, "small") end
    RegisterWidget(countText, "Text")

    local pctText = rowFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    pctText:SetPoint("LEFT", countText, "RIGHT", 2, 0)
    pctText:SetWidth(35)
    pctText:SetJustifyH("RIGHT")
    pctText:SetWordWrap(false)
    if scheme then ApplyFont(pctText, scheme, "small") end
    RegisterWidget(pctText, "Text")

    local checkmark = rowFrame:CreateTexture(nil, "OVERLAY")
    checkmark:SetSize(14, 14)
    checkmark:SetPoint("LEFT", pctText, "RIGHT", 2, 0)
    checkmark:SetAtlas("common-icon-checkmark")
    checkmark:SetVertexColor(0.3, 0.85, 0.3)
    checkmark:Hide()

    rowFrame:SetScript("OnEnter", function() hoverOverlay:Show() end)
    rowFrame:SetScript("OnLeave", function() hoverOverlay:Hide() end)

    rowFrame:Hide()
    return {
        frame = rowFrame, nameText = nameText, barBg = barBg, barFill = barFill,
        countText = countText, pctText = pctText, checkmark = checkmark,
        hoverOverlay = hoverOverlay, completeTint = completeTint,
    }
end

local function UpdateRow(row, name, collected, total, color)
    local scheme = GetScheme()
    local pct = total > 0 and math.floor(collected / total * 100) or 0
    local complete = collected >= total
    local clr = color or { 0.6, 0.6, 0.6 }

    row.nameText:SetText(name)
    row.countText:SetText(string.format("%d / %d", collected, total))
    row.pctText:SetText(string.format("%d%%", pct))

    local fillPct = total > 0 and (collected / total) or 0
    local barWidth = row.barBg:GetWidth()
    if barWidth < 1 then barWidth = 200 end
    row.barFill:SetWidth(math.max(1, barWidth * fillPct))

    if complete then
        row.barFill:SetVertexColor(clr[1], clr[2], clr[3], 1.0)
        if row.completeTint then row.completeTint:Show() end
    else
        row.barFill:SetVertexColor(clr[1], clr[2], clr[3], 0.8)
        if row.completeTint then row.completeTint:Hide() end
    end

    if row.xpText then
        local remaining = total - collected
        if remaining > 0 then
            row.xpText:SetText(string.format("|cffffd700%d XP|r", remaining * XP_PER_ITEM))
        else
            row.xpText:SetText("")
        end
    end

    if scheme then
        row.nameText:SetTextColor(scheme.text.r, scheme.text.g, scheme.text.b)
        if complete then
            row.countText:SetTextColor(0.3, 0.85, 0.3)
            row.pctText:SetTextColor(0.3, 0.85, 0.3)
            row.checkmark:Show()
        else
            row.countText:SetTextColor(scheme.text_dim.r, scheme.text_dim.g, scheme.text_dim.b)
            row.pctText:SetTextColor(scheme.text_dim.r, scheme.text_dim.g, scheme.text_dim.b)
            row.checkmark:Hide()
        end
    end
    row.frame:Show()
end

-- ============================================================================
-- XP BAR
-- ============================================================================

local function UpdateXPBar()
    if not xpBar then return end
    local houseData = HDG_UI.GetCachedHouseData()
    if not houseData then
        xpBar:Hide()
        if xpBarBorder then xpBarBorder:Hide() end
        xpLabel:SetText(L["HOUSE_NO_DATA"])
        xpLevelLabel:SetText("")
        return
    end

    local level = houseData.level
    local xp = houseData.xp
    local maxLevel = houseData.maxLevel
    local thresholds = houseData.thresholds
    local maxXP = thresholds[maxLevel] or thresholds[#thresholds] or 1

    xpBar:SetMinMaxValues(0, maxXP)
    xpBar:SetValue(xp)
    xpBar:Show()
    if xpBarBorder then xpBarBorder:Show() end

    if level >= maxLevel then
        xpLevelLabel:SetText(string.format("|cffffd700Level %d|r  |cff888888(Max)|r", level))
    else
        xpLevelLabel:SetText(string.format("|cffffd700Level %d|r", level))
    end

    if level >= maxLevel then
        xpLabel:SetText(string.format("%s XP", BreakUpLargeNumbers(xp)))
    else
        xpLabel:SetText(string.format("%s / %s", BreakUpLargeNumbers(xp), BreakUpLargeNumbers(maxXP)))
    end

    local barWidth = xpBar:GetWidth()
    if barWidth < 1 then barWidth = 600 end

    for _, tick in pairs(tickFrames) do tick:Hide() end

    for i = 1, maxLevel do
        local threshold = thresholds[i] or 0
        if threshold > 0 and threshold < maxXP then
            local pct = threshold / maxXP
            local xPos = barWidth * pct

            local tick = tickFrames[i]
            if not tick then
                tick = xpBar:CreateTexture(nil, "OVERLAY")
                tick:SetSize(TICK_WIDTH, XP_BAR_HEIGHT)
                tickFrames[i] = tick
            end
            tick:SetColorTexture(0, 0, 0, 0.5)
            tick:ClearAllPoints()
            tick:SetPoint("LEFT", xpBar, "LEFT", xPos, 0)
            tick:Show()
        end
    end
end

-- ============================================================================
-- UPDATE STATS
-- ============================================================================

local function UpdateCollectionStats()
    local data = BuildCollectionStats()
    if not data then return end

    local scheme = GetScheme()
    local totalAvailXP = (data.totalAll - data.collectedAll) * XP_PER_ITEM

    -- Hero panel summary line
    if xpSummaryLabel then
        local pct = data.totalAll > 0 and math.floor(data.collectedAll / data.totalAll * 100) or 0
        if totalAvailXP > 0 then
            xpSummaryLabel:SetText(string.format("%d / %d collected (%d%%)  --  |cffffd700%s XP available|r",
                data.collectedAll, data.totalAll, pct, BreakUpLargeNumbers(totalAvailXP)))
        else
            xpSummaryLabel:SetText(string.format("%d / %d collected (%d%%)", data.collectedAll, data.totalAll, pct))
        end
    end

    -- Source rows
    local rowIndex = 0
    for _, sourceType in ipairs(SOURCE_ORDER) do
        local s = data.bySource[sourceType]
        if s and s.total > 0 then
            rowIndex = rowIndex + 1
            if sourceRows[rowIndex] then
                UpdateRow(sourceRows[rowIndex], SOURCE_NAMES[sourceType] or ("Source " .. sourceType),
                    s.collected, s.total, SOURCE_COLORS[sourceType])
            end
        end
    end
    for i = rowIndex + 1, #sourceRows do sourceRows[i].frame:Hide() end

    -- Expansion rows
    local expIndex = 0
    for _, expName in ipairs(EXPANSION_ORDER) do
        local e = data.byExp[expName]
        if e and e.total > 0 then
            expIndex = expIndex + 1
            if expansionRows[expIndex] then
                expansionRows[expIndex].expKey = expName
                local shortName = HDG_ExpansionData.GetAbbreviation(expName)
                UpdateRow(expansionRows[expIndex], shortName or expName,
                    e.collected, e.total, EXPANSION_COLORS[expName] or { 0.6, 0.6, 0.6 })
            end
        end
    end
    -- Handle any unlisted expansion names
    for expName, e in pairs(data.byExp) do
        local found = false
        for _, n in ipairs(EXPANSION_ORDER) do
            if n == expName then found = true; break end
        end
        if not found and e.total > 0 then
            expIndex = expIndex + 1
            if expansionRows[expIndex] then
                expansionRows[expIndex].expKey = expName
                UpdateRow(expansionRows[expIndex], expName, e.collected, e.total, { 0.6, 0.6, 0.6 })
            end
        end
    end
    for i = expIndex + 1, #expansionRows do expansionRows[i].frame:Hide() end
end

-- ============================================================================
-- INIT / SHOW / HIDE
-- ============================================================================

function HDG_HouseTab.Init(frame)
    mainFrame = frame
end

local function CreateHouseTab()
    if initialized then return end
    if not mainFrame then return end
    initialized = true

    local tabResult = HDG.UI:TabPanel(mainFrame, { hasHeaderBar = false })
    housePanel = tabResult.frame

    local parent = housePanel
    local scheme = GetScheme()
    local yOff = 0

    -- ========================================================================
    -- SECTION 1: House Level + XP Bar (Hero Panel)
    -- ========================================================================
    local xpSection = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    xpSection:SetPoint("TOPLEFT", SIDE_PAD, yOff - 4)
    xpSection:SetPoint("TOPRIGHT", -SIDE_PAD, yOff - 4)
    xpSection:SetHeight(XP_BAR_HEIGHT + 48)
    xpSection:SetBackdrop(HDG.UI.BACKDROP_FLAT)
    if scheme then
        xpSection:SetBackdropColor(scheme.panel.r, scheme.panel.g, scheme.panel.b, 0.5)
        xpSection:SetBackdropBorderColor(scheme.border.r, scheme.border.g, scheme.border.b, 0.3)
    end
    RegisterWidget(xpSection, "Panel")

    xpLevelLabel = xpSection:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    xpLevelLabel:SetPoint("TOPLEFT", XP_PANEL_PAD, -XP_PANEL_PAD)
    if scheme then ApplyFont(xpLevelLabel, scheme, "header") end
    RegisterWidget(xpLevelLabel, "SectionHeader")

    xpBar = CreateFrame("StatusBar", nil, xpSection)
    xpBar:SetPoint("TOPLEFT", XP_PANEL_PAD, -(XP_PANEL_PAD + 20))
    xpBar:SetPoint("TOPRIGHT", -XP_PANEL_PAD, -(XP_PANEL_PAD + 20))
    xpBar:SetHeight(XP_BAR_HEIGHT)
    xpBar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
    xpBar:SetStatusBarColor(0.85, 0.65, 0.13, 0.9)

    local xpBg = xpBar:CreateTexture(nil, "BACKGROUND")
    xpBg:SetAllPoints()
    xpBg:SetColorTexture(0.08, 0.08, 0.08, 0.8)

    xpBarBorder = CreateFrame("Frame", nil, xpBar, "BackdropTemplate")
    xpBarBorder:SetPoint("TOPLEFT", -1, 1)
    xpBarBorder:SetPoint("BOTTOMRIGHT", 1, -1)
    xpBarBorder:SetBackdrop({ edgeFile = "Interface\\Buttons\\WHITE8x8", edgeSize = 1 })
    xpBarBorder:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.6)

    xpLabel = xpBar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    xpLabel:SetPoint("CENTER", 0, 0)
    xpLabel:SetTextColor(1, 1, 1, 0.9)
    if scheme then ApplyFont(xpLabel, scheme, "small") end
    RegisterWidget(xpBar, "StatusBar")

    -- Collection summary line below XP bar
    xpSummaryLabel = xpSection:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    xpSummaryLabel:SetPoint("TOPLEFT", xpBar, "BOTTOMLEFT", 0, -4)
    xpSummaryLabel:SetPoint("TOPRIGHT", xpBar, "BOTTOMRIGHT", 0, -4)
    xpSummaryLabel:SetJustifyH("CENTER")
    if scheme then
        ApplyFont(xpSummaryLabel, scheme, "small")
        xpSummaryLabel:SetTextColor(scheme.text_dim.r, scheme.text_dim.g, scheme.text_dim.b)
    end
    RegisterWidget(xpSummaryLabel, "Text")

    yOff = yOff - 4 - (XP_BAR_HEIGHT + 48) - 12

    -- ========================================================================
    -- TWO-COLUMN LAYOUT: Source (left) | Expansion (right)
    -- ========================================================================
    local leftCol = CreateFrame("Frame", nil, parent)
    leftCol:SetPoint("TOPLEFT", SIDE_PAD, yOff)
    leftCol:SetPoint("TOPRIGHT", parent, "TOP", -COL_GAP / 2, yOff)
    leftCol:SetHeight(600)

    local rightCol = CreateFrame("Frame", nil, parent)
    rightCol:SetPoint("TOPLEFT", parent, "TOP", COL_GAP / 2, yOff)
    rightCol:SetPoint("TOPRIGHT", -SIDE_PAD, yOff)
    rightCol:SetHeight(600)

    -- -- LEFT COLUMN: Collection by Source -- --
    local srcTitle = HDG.UI:SectionTitle(leftCol, "Collection by Source", 0)

    local srcAccent = leftCol:CreateTexture(nil, "ARTWORK")
    srcAccent:SetSize(ACCENT_BAR_WIDTH, 14)
    srcAccent:SetPoint("RIGHT", srcTitle, "LEFT", -4, 0)
    if scheme then
        srcAccent:SetColorTexture(scheme.accent.r, scheme.accent.g, scheme.accent.b, 0.8)
    else
        srcAccent:SetColorTexture(0.85, 0.65, 0.13, 0.8)
    end

    local divider1 = leftCol:CreateTexture(nil, "ARTWORK")
    divider1:SetHeight(1)
    divider1:SetPoint("TOPLEFT", 0, -16)
    divider1:SetPoint("TOPRIGHT", 0, -16)
    divider1:SetColorTexture(0.4, 0.4, 0.4, 0.3)

    local sourceTop = -20
    for i = 1, #SOURCE_ORDER do
        sourceRows[i] = CreateStatRow(leftCol, sourceTop - (i - 1) * ROW_HEIGHT, i)
    end

    -- -- RIGHT COLUMN: Collection by Expansion -- --
    local expTitle = HDG.UI:SectionTitle(rightCol, "Collection by Expansion", 0)

    local expAccent = rightCol:CreateTexture(nil, "ARTWORK")
    expAccent:SetSize(ACCENT_BAR_WIDTH, 14)
    expAccent:SetPoint("RIGHT", expTitle, "LEFT", -4, 0)
    if scheme then
        expAccent:SetColorTexture(scheme.accent.r, scheme.accent.g, scheme.accent.b, 0.8)
    else
        expAccent:SetColorTexture(0.85, 0.65, 0.13, 0.8)
    end

    local divider2 = rightCol:CreateTexture(nil, "ARTWORK")
    divider2:SetHeight(1)
    divider2:SetPoint("TOPLEFT", 0, -16)
    divider2:SetPoint("TOPRIGHT", 0, -16)
    divider2:SetColorTexture(0.4, 0.4, 0.4, 0.3)

    local maxExpRows = #EXPANSION_ORDER + 4
    local expTop = -20
    for i = 1, maxExpRows do
        expansionRows[i] = CreateStatRow(rightCol, expTop - (i - 1) * ROW_HEIGHT, i)
    end

end

function HDG_HouseTab.Show()
    if not initialized then CreateHouseTab() end
    if housePanel then
        housePanel:Show()
        C_Timer.After(0, function()
            UpdateXPBar()
            UpdateCollectionStats()
        end)
    end
end

function HDG_HouseTab.Hide()
    if housePanel then housePanel:Hide() end
end

function HDG_HouseTab.Refresh()
    collectionCache = nil
    collectionCacheTime = 0
    if housePanel and housePanel:IsShown() then
        UpdateXPBar()
        UpdateCollectionStats()
    end
end
