local addonName, privateDB = ...

-- ============================================================================
-- ALTS TAB MODULE
-- Displays character profession data across all alts
-- Extracted from HDG_UI.lua in v2.0.3 for maintainability
-- ============================================================================

local CONFIG = privateDB.CONFIG
local L = HDG_L or {}  -- Localization
local HDG_AltsTab = {}
privateDB.HDG_AltsTab = HDG_AltsTab

local mainFrame = nil

-- ============================================================================
-- THEME HELPERS (delegating to ThemeEngine canonical 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

-- Color code helpers (delegates to ThemeEngine)
local function C_Text() return HDG.Theme.C_Text() end
local function C_Header() return HDG.Theme.C_Header() end
local function C_Dim() return HDG.Theme.C_Dim() end
local function C_Accent() return HDG.Theme.C_Accent() end
local function C_Success() return HDG.Theme.C_Success() end
local function C_Warning() return HDG.Theme.C_Warning() end
local function C_Error() return HDG.Theme.C_Error() end

-- Cache for recipe counts per profession/expansion
-- recipeCountCache[profName][expDisplayName] = { total = n, knownByChar = { [charKey] = count } }
local recipeCountCache = nil
local recipeCountCacheTime = 0
local RECIPE_CACHE_TTL = 60  -- Refresh every 60 seconds

-- Build recipe count cache from HDG_DecorDB
local function BuildRecipeCountCache()
    local now = GetTime()
    if recipeCountCache and (now - recipeCountCacheTime) < RECIPE_CACHE_TTL then
        return recipeCountCache
    end

    recipeCountCache = {}

    if not HDG_DecorDB then
        return recipeCountCache
    end

    -- Count total recipes per profession/expansion
    -- Note: DecorDB is keyed by recipeID, spellID is inside the recipe object
    for recipeID, recipe in pairs(HDG_DecorDB) do
        local profName = recipe.profession
        local expansion = recipe.expansion
        local spellID = recipe.spellID  -- Use the actual spellID from the recipe
        if profName and expansion and spellID then
            -- Normalize expansion name (e.g., "Outland Alchemy" -> "The Burning Crusade")
            local expDisplay = HDG_ExpansionData.NormalizeToDisplayName(expansion)

            if not recipeCountCache[profName] then
                recipeCountCache[profName] = {}
            end
            if not recipeCountCache[profName][expDisplay] then
                recipeCountCache[profName][expDisplay] = { total = 0, recipes = {} }
            end

            recipeCountCache[profName][expDisplay].total = recipeCountCache[profName][expDisplay].total + 1
            table.insert(recipeCountCache[profName][expDisplay].recipes, spellID)
        end
    end

    recipeCountCacheTime = now
    return recipeCountCache
end

-- Get known recipe count for any character (uses stored knownRecipes data)
-- Returns: known (number or nil if no data), total
local function GetKnownRecipeCountForChar(charKey, profName, expDisplayName)
    local cache = BuildRecipeCountCache()
    if not cache[profName] or not cache[profName][expDisplayName] then
        return nil, 0
    end

    local data = cache[profName][expDisplayName]
    local total = data.total

    -- Get stored known recipes for this character
    local charData = HDG_Settings.characters and HDG_Settings.characters[charKey]
    if not charData or not charData.professions or not charData.professions[profName] then
        return nil, total  -- nil = no data for this char/prof
    end

    local profData = charData.professions[profName]
    if not profData.knownRecipes then
        return nil, total  -- nil = no recipe data stored
    end

    -- Count how many of the decor recipes this character knows
    local known = 0
    for _, spellID in ipairs(data.recipes) do
        if profData.knownRecipes[spellID] then
            known = known + 1
        end
    end

    return known, total
end

-- Convenience: Get known recipe count for current character
local function GetKnownRecipeCount(profName, expDisplayName)
    local currentChar = HDG_Data.GetCharacterKey()
    return GetKnownRecipeCountForChar(currentChar, profName, expDisplayName)
end

-- Get best known recipe count across all characters
-- Returns: bestKnown (number or nil), total, bestCharKey
local function GetBestKnownRecipeCount(profName, expDisplayName)
    local cache = BuildRecipeCountCache()
    if not cache[profName] or not cache[profName][expDisplayName] then
        return nil, 0, nil
    end

    local data = cache[profName][expDisplayName]
    local total = data.total
    local bestKnown = nil
    local bestChar = nil

    -- Check all characters
    if HDG_Settings.characters then
        for charKey, charData in pairs(HDG_Settings.characters) do
            if charData.professions and charData.professions[profName] then
                local profData = charData.professions[profName]
                if profData.knownRecipes then
                    local known = 0
                    for _, spellID in ipairs(data.recipes) do
                        if profData.knownRecipes[spellID] then
                            known = known + 1
                        end
                    end
                    if bestKnown == nil or known > bestKnown then
                        bestKnown = known
                        bestChar = charKey
                    end
                end
            end
        end
    end

    return bestKnown, total, bestChar
end

-- Get total recipe count (no knowledge check)
local function GetTotalRecipeCount(profName, expDisplayName)
    local cache = BuildRecipeCountCache()
    if not cache[profName] or not cache[profName][expDisplayName] then
        return 0
    end
    return cache[profName][expDisplayName].total
end

-- Helper: Look up skill data from SavedVariables by display name
-- Handles legacy data where API names (Outland, Northrend, etc.) were stored
local function GetSkillDataByDisplayName(skillLevels, displayName)
    if not skillLevels then return nil end

    -- Try display name first (new format)
    if skillLevels[displayName] then
        return skillLevels[displayName]
    end

    -- Fall back to checking all stored keys and normalizing them
    for storedName, data in pairs(skillLevels) do
        local normalizedStored = HDG_ExpansionData.NormalizeApiName(storedName)
        if normalizedStored == displayName then
            return data
        end
    end

    return nil
end

-- ============================================================================
-- INITIALIZATION
-- ============================================================================

function HDG_AltsTab.Init(frame)
    mainFrame = frame
end

-- ============================================================================
-- CREATE PANEL
-- ============================================================================

function HDG_AltsTab.Create()
    if not mainFrame then return end

    -- Use factory to create panel with header and scroll
    local layout = HDG.UI:TabPanel(mainFrame, {
        title = "Your Characters",
        hasHeaderBar = true,
        hasScroll = true,
    })

    local charactersFrame = layout.frame
    charactersFrame:Hide()
    mainFrame.charactersFrame = charactersFrame
    mainFrame.charactersContent = layout.scrollChild

    -- View mode toggle buttons (Skills | Recipes) - in header bar
    local btnWidth = 60
    local btnHeight = 18
    local scheme = GetScheme()

    local recipesBtn = CreateFrame("Button", nil, layout.headerBg, "BackdropTemplate")
    recipesBtn:SetPoint("RIGHT", -8, 0)
    recipesBtn:SetSize(btnWidth, btnHeight)
    recipesBtn:SetBackdrop({
        bgFile = CONFIG.TEXTURES.WHITE_BG,
        edgeFile = CONFIG.TEXTURES.WHITE_BG,
        edgeSize = 1,
    })
    recipesBtn.text = recipesBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    recipesBtn.text:SetPoint("CENTER", 0, 0)
    ApplyFont(recipesBtn.text, scheme, "small")
    recipesBtn.text:SetText("Recipes")
    RegisterWidget(recipesBtn, "ToggleButton")

    local skillsBtn = CreateFrame("Button", nil, layout.headerBg, "BackdropTemplate")
    skillsBtn:SetPoint("RIGHT", recipesBtn, "LEFT", -2, 0)
    skillsBtn:SetSize(btnWidth, btnHeight)
    skillsBtn:SetBackdrop({
        bgFile = CONFIG.TEXTURES.WHITE_BG,
        edgeFile = CONFIG.TEXTURES.WHITE_BG,
        edgeSize = 1,
    })
    skillsBtn.text = skillsBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    skillsBtn.text:SetPoint("CENTER", 0, 0)
    ApplyFont(skillsBtn.text, scheme, "small")
    skillsBtn.text:SetText("Skills")
    RegisterWidget(skillsBtn, "ToggleButton")

    local function UpdateToggleAppearance()
        local viewMode = HDG_Settings.altsViewMode or "skills"
        local tScheme = GetScheme()
        if viewMode == "skills" then
            if tScheme then
                skillsBtn:SetBackdropColor(tScheme.accent.r, tScheme.accent.g, tScheme.accent.b, 1)
                skillsBtn:SetBackdropBorderColor(tScheme.accent.r, tScheme.accent.g, tScheme.accent.b, 1)
                recipesBtn:SetBackdropColor(tScheme.panel.r, tScheme.panel.g, tScheme.panel.b, 0.8)
                recipesBtn:SetBackdropBorderColor(tScheme.border.r, tScheme.border.g, tScheme.border.b, 1)
                skillsBtn.text:SetTextColor(tScheme.text.r, tScheme.text.g, tScheme.text.b)
                recipesBtn.text:SetTextColor(tScheme.text_dim.r, tScheme.text_dim.g, tScheme.text_dim.b)
            end
        else
            if tScheme then
                recipesBtn:SetBackdropColor(tScheme.accent.r, tScheme.accent.g, tScheme.accent.b, 1)
                recipesBtn:SetBackdropBorderColor(tScheme.accent.r, tScheme.accent.g, tScheme.accent.b, 1)
                skillsBtn:SetBackdropColor(tScheme.panel.r, tScheme.panel.g, tScheme.panel.b, 0.8)
                skillsBtn:SetBackdropBorderColor(tScheme.border.r, tScheme.border.g, tScheme.border.b, 1)
                recipesBtn.text:SetTextColor(tScheme.text.r, tScheme.text.g, tScheme.text.b)
                skillsBtn.text:SetTextColor(tScheme.text_dim.r, tScheme.text_dim.g, tScheme.text_dim.b)
            end
        end
    end

    skillsBtn:SetScript("OnClick", function()
        HDG_Settings.altsViewMode = "skills"
        UpdateToggleAppearance()
        HDG_AltsTab.Refresh()
    end)

    recipesBtn:SetScript("OnClick", function()
        HDG_Settings.altsViewMode = "recipes"
        UpdateToggleAppearance()
        HDG_AltsTab.Refresh()
    end)

    UpdateToggleAppearance()
    mainFrame.altsSkillsBtn = skillsBtn
    mainFrame.altsRecipesBtn = recipesBtn

    return charactersFrame
end

-- ============================================================================
-- REFRESH PANEL
-- ============================================================================

function HDG_AltsTab.Refresh()
    if not mainFrame or not mainFrame.charactersContent then return end

    local content = mainFrame.charactersContent
    -- Clear existing content
    for _, child in ipairs({ content:GetChildren() }) do
        child:Hide()
        child:SetParent(nil)
    end
    for _, region in ipairs({ content:GetRegions() }) do
        region:Hide()
        region:SetParent(nil)
    end

    local yOffset = 0
    local EXPANSION_COL_WIDTH = 60
    local PROF_COL_WIDTH = 100

    -- Get expansion order and abbreviations from HDG_Data
    -- Fallback uses display names (matching how skill data is now stored)
    local expansionOrder = HDG_Data.EXPANSION_ORDER or {
        "Classic", "The Burning Crusade", "Wrath of the Lich King", "Cataclysm",
        "Mists of Pandaria", "Warlords of Draenor", "Legion", "Battle for Azeroth",
        "Shadowlands", "Dragonflight", "The War Within"
    }
    local expansionAbbr = HDG_Data.EXPANSION_ABBR or {}

    -- Crafting professions first, then gathering at the bottom
    local professionOrder = { "Alchemy", "Blacksmithing", "Cooking", "Enchanting", "Engineering",
        "Inscription", "Jewelcrafting", "Leatherworking", "Tailoring", "Herbalism", "Mining", "Skinning" }
    local gatheringProfessions = { ["Herbalism"] = true, ["Mining"] = true, ["Skinning"] = true }
    local firstGatheringProf = "Herbalism"

    -- Get all characters from saved data
    local characters = HDG_Settings.characters or {}
    local currentChar = HDG_Data.GetCharacterKey()
    local charList = {}

    -- Helper: check if character has any professions
    local function hasProfessions(charKey)
        local charData = characters[charKey]
        if not charData or not charData.professions then return false end
        for _, _ in pairs(charData.professions) do
            return true
        end
        return false
    end

    -- Sort characters: current first, then those with professions, then alphabetically
    for charKey, _ in pairs(characters) do
        table.insert(charList, charKey)
    end
    table.sort(charList, function(a, b)
        -- Current character always first
        if a == currentChar then return true end
        if b == currentChar then return false end
        -- Characters with professions before those without
        local aHasProf = hasProfessions(a)
        local bHasProf = hasProfessions(b)
        if aHasProf ~= bHasProf then
            return aHasProf
        end
        -- Alphabetically within same category
        return a < b
    end)

    -- If no characters yet, show message
    if #charList == 0 then
        local noDataScheme = GetScheme()
        local noDataText = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        noDataText:SetPoint("TOPLEFT", 8, yOffset)
        noDataText:SetText(C_Dim() .. (L["STATUS_NO_CHARACTER_DATA"] or "No character data yet. Open a profession window to scan skill levels.") .. "|r")
        ApplyFont(noDataText, noDataScheme, "body")
        content:SetHeight(40)
        return
    end

    -- ========================================================================
    -- ACCOUNT SUMMARY GRID
    -- Shows best skill level for each profession/expansion with tooltip showing who has it
    -- ========================================================================

    -- Build account-wide best skills data
    -- accountBest[profName][expName] = { current, max, charKey }
    local accountBest = {}
    for _, profName in ipairs(professionOrder) do
        accountBest[profName] = {}
    end

    for charKey, charData in pairs(characters) do
        if charData.professions then
            for profName, profData in pairs(charData.professions) do
                if profData.skillLevels then
                    for expName, skillData in pairs(profData.skillLevels) do
                        -- Normalize expansion name to handle legacy data stored with API names
                        -- (e.g., "Outland" -> "The Burning Crusade", "Dragon Isles" -> "Dragonflight")
                        local normalizedExpName = HDG_ExpansionData.NormalizeApiName(expName)
                        local current = skillData.current or 0
                        local max = skillData.max or 0
                        if not accountBest[profName] then
                            accountBest[profName] = {}
                        end
                        local existing = accountBest[profName][normalizedExpName]
                        -- Track best (highest current skill) and all chars with this skill
                        if not existing then
                            accountBest[profName][normalizedExpName] = {
                                current = current,
                                max = max,
                                chars = { { name = charKey, current = current, max = max } }
                            }
                        else
                            -- Add to chars list
                            table.insert(existing.chars, { name = charKey, current = current, max = max })
                            -- Update best if this is higher
                            if current > existing.current then
                                existing.current = current
                                existing.max = max
                            end
                        end
                    end
                end
            end
        end
    end

    -- View mode (Skills vs Recipes) - toggle button is in title row
    local viewMode = HDG_Settings.altsViewMode or "skills"

    -- Summary header
    local summaryScheme = GetScheme()
    local summaryHeader = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    summaryHeader:SetPoint("TOPLEFT", 8, yOffset)
    ApplyFont(summaryHeader, summaryScheme, "body")
    if viewMode == "skills" then
        summaryHeader:SetText(C_Warning() .. "Account Summary|r " .. C_Dim() .. "(best skill per profession)|r")
    else
        summaryHeader:SetText(C_Warning() .. "Account Summary|r " .. C_Dim() .. "(known decor recipes)|r")
    end
    yOffset = yOffset - 20

    -- Expansion header row for summary
    local expHeader = CreateFrame("Frame", nil, content, "BackdropTemplate")
    expHeader:SetPoint("TOPLEFT", 0, yOffset)
    expHeader:SetSize(CONFIG.MAIN_FRAME_WIDTH - 60, 18)
    expHeader:SetBackdrop({ bgFile = CONFIG.TEXTURES.WHITE_BG })
    local expScheme = GetScheme()
    if expScheme then
        expHeader:SetBackdropColor(expScheme.panel.r, expScheme.panel.g, expScheme.panel.b, 0.9)
    else
        expHeader:SetBackdropColor(0.1, 0.15, 0.15, 0.9)
    end

    local profHeaderLabel = expHeader:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    profHeaderLabel:SetPoint("LEFT", 8, 0)
    profHeaderLabel:SetWidth(PROF_COL_WIDTH)
    profHeaderLabel:SetJustifyH("LEFT")
    ApplyFont(profHeaderLabel, expScheme, "small")
    profHeaderLabel:SetText(C_Text() .. "Profession|r")

    local xOffset = PROF_COL_WIDTH + 8
    for _, expName in ipairs(expansionOrder) do
        local expLabel = expHeader:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        expLabel:SetPoint("LEFT", xOffset, 0)
        expLabel:SetWidth(EXPANSION_COL_WIDTH)
        expLabel:SetJustifyH("CENTER")
        expLabel:SetText(C_Text() .. (expansionAbbr[expName] or expName) .. "|r")
        ApplyFont(expLabel, expScheme, "small")
        xOffset = xOffset + EXPANSION_COL_WIDTH
    end
    yOffset = yOffset - 18

    -- Summary profession rows
    for _, profName in ipairs(professionOrder) do
        local localizedProfName = HDG_ExpansionData.GetLocalizedProfessionName(profName)

        -- Add small divider line before gathering professions
        if profName == firstGatheringProf then
            local gatheringDivider = content:CreateTexture(nil, "ARTWORK")
            gatheringDivider:SetPoint("TOPLEFT", 8, yOffset - 2)
            gatheringDivider:SetSize(CONFIG.MAIN_FRAME_WIDTH - 76, 1)
            local gdScheme = GetScheme()
            if gdScheme then
                gatheringDivider:SetColorTexture(gdScheme.border.r, gdScheme.border.g, gdScheme.border.b, 0.8)
            else
                gatheringDivider:SetColorTexture(0.25, 0.35, 0.35, 0.8)
            end
            yOffset = yOffset - 6
        end

        local profRow = CreateFrame("Frame", nil, content, "BackdropTemplate")
        profRow:SetPoint("TOPLEFT", 0, yOffset)
        profRow:SetSize(CONFIG.MAIN_FRAME_WIDTH - 60, 18)
        profRow:SetBackdrop({ bgFile = CONFIG.TEXTURES.WHITE_BG })
        local profScheme = GetScheme()
        if profScheme then
            profRow:SetBackdropColor(profScheme.bg.r, profScheme.bg.g, profScheme.bg.b, 0.4)
        else
            profRow:SetBackdropColor(0.05, 0.05, 0.05, 0.4)
        end

        -- Determine profession name color based on view mode
        local profLabel = profRow:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        profLabel:SetPoint("LEFT", 8, 0)
        profLabel:SetWidth(PROF_COL_WIDTH)
        profLabel:SetJustifyH("LEFT")
        ApplyFont(profLabel, profScheme, "small")

        local profColor
        if viewMode == "recipes" then
            -- RECIPES MODE: Color based on recipe counts
            local totalKnown = 0
            local totalRecipes = 0
            local hasAnyRecipeData = false
            for _, expNameCheck in ipairs(expansionOrder) do
                local known, total = GetBestKnownRecipeCount(profName, expNameCheck)
                totalRecipes = totalRecipes + total
                if known then
                    hasAnyRecipeData = true
                    totalKnown = totalKnown + known
                end
            end

            if totalRecipes == 0 then
                profColor = C_Text()  -- White: no decor recipes for this profession
            elseif not hasAnyRecipeData then
                profColor = C_Text()  -- White: no recipe data scanned
            elseif totalKnown == 0 then
                profColor = C_Dim()  -- Grey: none known
            elseif totalKnown >= totalRecipes then
                profColor = C_Accent()  -- Teal: all known
            else
                profColor = C_Warning()  -- Yellow: partial
            end
        else
            -- SKILLS MODE: Color based on skill thresholds (original behavior)
            local anyBelowThreshold = false
            local hasAnyData = false
            for _, expNameCheck in ipairs(expansionOrder) do
                local bestData = accountBest[profName] and accountBest[profName][expNameCheck]
                if bestData then
                    hasAnyData = true
                    local current = bestData.current or 0
                    local decorThreshold = HDG_GetDecorThreshold and HDG_GetDecorThreshold(profName, expNameCheck) or 0
                    if decorThreshold > 0 and current < decorThreshold then
                        anyBelowThreshold = true
                        break
                    end
                end
            end

            if not hasAnyData then
                profColor = C_Text()  -- White: no data
            elseif anyBelowThreshold then
                profColor = C_Warning()  -- Yellow: below threshold
            else
                profColor = C_Accent()  -- Teal: all good
            end
        end
        profLabel:SetText(profColor .. localizedProfName .. "|r")

        xOffset = PROF_COL_WIDTH + 8
        for _, expName in ipairs(expansionOrder) do
            local bestData = accountBest[profName] and accountBest[profName][expName]

            -- Create a clickable button for tooltip
            local skillBtn = CreateFrame("Button", nil, profRow)
            skillBtn:SetPoint("LEFT", xOffset, 0)
            skillBtn:SetSize(EXPANSION_COL_WIDTH, 18)

            local skillBtnScheme = GetScheme()
            local skillLabel = skillBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            skillLabel:SetPoint("CENTER", 0, 0)
            skillLabel:SetWidth(EXPANSION_COL_WIDTH)
            skillLabel:SetJustifyH("CENTER")
            ApplyFont(skillLabel, skillBtnScheme, "small")

            if viewMode == "recipes" then
                -- RECIPES VIEW: Show best known/total decor recipes across all chars
                local known, total, bestChar = GetBestKnownRecipeCount(profName, expName)

                local color
                if total == 0 then
                    color = C_Dim()  -- Grey: no decor recipes
                    skillLabel:SetText(color .. "-|r")
                elseif known == nil then
                    -- No recipe data stored for any character
                    skillLabel:SetText(C_Dim() .. string.format("?/%d|r", total))
                elseif known == 0 then
                    color = C_Dim()  -- Grey: none known
                    skillLabel:SetText(string.format("%s0/%d|r", color, total))
                elseif known >= total then
                    color = C_Accent()  -- Teal: all known
                    skillLabel:SetText(string.format("%s%d/%d|r", color, known, total))
                else
                    color = C_Warning()  -- Yellow: partial
                    skillLabel:SetText(string.format("%s%d/%d|r", color, known, total))
                end

                -- Tooltip for recipes mode
                skillBtn:SetScript("OnEnter", function(self)
                    GameTooltip:SetOwner(self, HDG_UIHelpers.TOOLTIP_ANCHOR)
                    GameTooltip:AddLine(localizedProfName .. " - " .. (expansionAbbr[expName] or expName), 1, 0.82, 0.3)
                    if total > 0 then
                        if known == nil then
                            GameTooltip:AddLine(C_Dim() .. "Open profession window to scan recipes|r")
                        else
                            GameTooltip:AddLine(string.format("Best: %d / %d decor recipes", known, total))
                            if bestChar then
                                local charColor = (bestChar == currentChar) and C_Warning() or C_Text()
                                GameTooltip:AddLine(charColor .. bestChar .. "|r")
                            end
                            if known >= total then
                                GameTooltip:AddLine(C_Accent() .. "All recipes learned!|r")
                            elseif known > 0 then
                                GameTooltip:AddLine(C_Warning() .. string.format("%d recipes remaining|r", total - known))
                            end
                        end
                    else
                        GameTooltip:AddLine(C_Dim() .. "No decor recipes for this expansion|r")
                    end
                    GameTooltip:Show()
                end)
                skillBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
            elseif bestData then
                -- SKILLS VIEW: Show skill levels (original behavior)
                local current = bestData.current or 0
                local max = bestData.max or 0

                -- Get decor threshold for this profession/expansion (per-profession data)
                local decorThreshold = HDG_GetDecorThreshold and HDG_GetDecorThreshold(profName, expName) or nil
                decorThreshold = decorThreshold or 0

                -- Color coding (check "no recipes" first!):
                -- Grey = no decor recipes for this profession/expansion
                -- Teal = meets decor threshold (can learn recipes)
                -- Yellow = below decor threshold
                local color
                if decorThreshold == 0 then
                    color = C_Dim()  -- Grey: no decor recipes for this profession/expansion
                elseif current >= decorThreshold then
                    color = C_Accent()  -- Teal: can learn decor recipes
                else
                    color = C_Warning()  -- Yellow/orange: below threshold
                end
                skillLabel:SetText(string.format("%s%d|r", color, current))

                -- Tooltip showing which characters have this skill
                skillBtn:SetScript("OnEnter", function(self)
                    GameTooltip:SetOwner(self, HDG_UIHelpers.TOOLTIP_ANCHOR)
                    GameTooltip:AddLine(localizedProfName .. " - " .. (expansionAbbr[expName] or expName), 1, 0.82, 0.3)

                    -- Show decor threshold info
                    if decorThreshold and decorThreshold > 0 then
                        local thresholdMet = current >= decorThreshold
                        local statusColor = thresholdMet and C_Accent() or C_Warning()
                        local statusText = thresholdMet and "Can learn decor recipes" or "Below decor threshold"
                        GameTooltip:AddLine(statusColor .. statusText .. "|r")
                        GameTooltip:AddLine(C_Dim() .. string.format("Decor recipes require %d+ skill|r", decorThreshold))
                    else
                        GameTooltip:AddLine(C_Dim() .. "No decor recipes for this expansion|r")
                    end

                    GameTooltip:AddLine(" ")
                    -- Sort chars by skill level descending
                    table.sort(bestData.chars, function(a, b) return a.current > b.current end)
                    for _, charInfo in ipairs(bestData.chars) do
                        local charColor = (charInfo.name == currentChar) and C_Warning() or C_Text()
                        local skillColor = C_Dim()  -- Neutral grey for skill display
                        GameTooltip:AddDoubleLine(
                            charColor .. charInfo.name .. "|r",
                            skillColor .. charInfo.current .. "/" .. charInfo.max .. "|r",
                            1, 1, 1, 1, 1, 1
                        )
                    end
                    GameTooltip:Show()
                end)
                skillBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
            else
                skillLabel:SetText(C_Dim() .. "-|r")
            end

            xOffset = xOffset + EXPANSION_COL_WIDTH
        end

        yOffset = yOffset - 18
    end

    -- Color legend
    yOffset = yOffset - 12
    local legendScheme = GetScheme()
    local legendText = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    legendText:SetPoint("TOPLEFT", 8, yOffset)
    ApplyFont(legendText, legendScheme, "small")
    if viewMode == "recipes" then
        legendText:SetText("|cFF14b8a6Teal|r = All learned   |cFFFFAA00Yellow|r = Partial   |cFF888888Grey|r = None/No recipes")
    else
        legendText:SetText("|cFF14b8a6Teal|r = Can learn decor   |cFFFFAA00Yellow|r = Below threshold   |cFF888888Grey|r = No decor recipes")
    end
    yOffset = yOffset - 16

    -- Divider
    yOffset = yOffset - 8
    local divider = content:CreateTexture(nil, "ARTWORK")
    divider:SetPoint("TOPLEFT", 8, yOffset)
    divider:SetSize(CONFIG.MAIN_FRAME_WIDTH - 76, 1)
    local divScheme = GetScheme()
    if divScheme then
        divider:SetColorTexture(divScheme.border.r, divScheme.border.g, divScheme.border.b, 0.5)
    else
        divider:SetColorTexture(0.3, 0.3, 0.3, 0.5)
    end
    yOffset = yOffset - 10

    -- ========================================================================
    -- CHARACTER DETAILS (per-character breakdown)
    -- ========================================================================

    local detailsScheme = GetScheme()
    local detailsHeader = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    detailsHeader:SetPoint("TOPLEFT", 8, yOffset)
    ApplyFont(detailsHeader, detailsScheme, "body")
    detailsHeader:SetText(C_Warning() .. "Character Details|r")
    yOffset = yOffset - 24

    -- Process each character
    for _, charKey in ipairs(charList) do
        local charData = characters[charKey]
        local isCurrentChar = (charKey == currentChar)

        -- Character header with name
        local charHeader = CreateFrame("Frame", nil, content, "BackdropTemplate")
        charHeader:SetPoint("TOPLEFT", 0, yOffset)
        charHeader:SetSize(CONFIG.MAIN_FRAME_WIDTH - 60, 22)
        charHeader:SetBackdrop({ bgFile = CONFIG.TEXTURES.WHITE_BG })
        local chScheme = GetScheme()
        if chScheme then
            charHeader:SetBackdropColor(chScheme.panel.r, chScheme.panel.g, chScheme.panel.b, 0.9)
        else
            charHeader:SetBackdropColor(0.1, 0.15, 0.15, 0.9)
        end

        local charLabel = charHeader:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        charLabel:SetPoint("LEFT", 8, 0)
        ApplyFont(charLabel, chScheme, "body")
        local charColor = isCurrentChar and C_Warning() or C_Text()
        local currentIndicator = isCurrentChar and " (current)" or ""
        charLabel:SetText(charColor .. charKey .. currentIndicator .. "|r")

        yOffset = yOffset - 24

        -- Expansion header row (abbreviations) - matches Account Summary header style
        local charExpHeader = CreateFrame("Frame", nil, content, "BackdropTemplate")
        charExpHeader:SetPoint("TOPLEFT", 0, yOffset)
        charExpHeader:SetSize(CONFIG.MAIN_FRAME_WIDTH - 60, 18)
        charExpHeader:SetBackdrop({ bgFile = CONFIG.TEXTURES.WHITE_BG })
        local cehScheme = GetScheme()
        if cehScheme then
            charExpHeader:SetBackdropColor(cehScheme.panel.r, cehScheme.panel.g, cehScheme.panel.b, 0.9)
        else
            charExpHeader:SetBackdropColor(0.1, 0.15, 0.15, 0.9)
        end

        -- Profession column header
        local charProfHeaderLabel = charExpHeader:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        charProfHeaderLabel:SetPoint("LEFT", 8, 0)
        charProfHeaderLabel:SetWidth(PROF_COL_WIDTH)
        charProfHeaderLabel:SetJustifyH("LEFT")
        ApplyFont(charProfHeaderLabel, cehScheme, "small")
        charProfHeaderLabel:SetText(C_Text() .. "Profession|r")

        -- Expansion column headers
        xOffset = PROF_COL_WIDTH + 8
        for _, expName in ipairs(expansionOrder) do
            local expLabel = charExpHeader:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            expLabel:SetPoint("LEFT", xOffset, 0)
            expLabel:SetWidth(EXPANSION_COL_WIDTH)
            expLabel:SetJustifyH("CENTER")
            expLabel:SetText(C_Text() .. (expansionAbbr[expName] or expName) .. "|r")
            ApplyFont(expLabel, cehScheme, "small")
            xOffset = xOffset + EXPANSION_COL_WIDTH
        end

        yOffset = yOffset - 18

        -- Profession skill level rows for this character
        local charHasProfessions = false
        local gatheringDividerShown = false
        for _, profName in ipairs(professionOrder) do
            local localizedProfNameChar = HDG_ExpansionData.GetLocalizedProfessionName(profName)
            local profData = charData.professions and charData.professions[profName]
            if profData then
                -- Add small divider line before gathering professions (only once per character)
                if not gatheringDividerShown and gatheringProfessions[profName] then
                    local gatheringDivider = content:CreateTexture(nil, "ARTWORK")
                    gatheringDivider:SetPoint("TOPLEFT", 8, yOffset - 2)
                    gatheringDivider:SetSize(CONFIG.MAIN_FRAME_WIDTH - 76, 1)
                    local gd2Scheme = GetScheme()
                    if gd2Scheme then
                        gatheringDivider:SetColorTexture(gd2Scheme.border.r, gd2Scheme.border.g, gd2Scheme.border.b, 0.8)
                    else
                        gatheringDivider:SetColorTexture(0.25, 0.35, 0.35, 0.8)
                    end
                    yOffset = yOffset - 6
                    gatheringDividerShown = true
                end
                charHasProfessions = true
                local hasSkillData = profData.skillLevels and next(profData.skillLevels)

                -- Check if any expansion is below decor threshold for this character's profession
                local anyBelowThreshold = false
                if hasSkillData then
                    for _, expName in ipairs(expansionOrder) do
                        local skillData = GetSkillDataByDisplayName(profData.skillLevels, expName)
                        if skillData then
                            local current = skillData.current or 0
                            local decorThreshold = HDG_GetDecorThreshold and HDG_GetDecorThreshold(profName, expName) or 0
                            -- Only count as "below threshold" if there ARE decor recipes (threshold > 0)
                            if decorThreshold > 0 and current < decorThreshold then
                                anyBelowThreshold = true
                                break
                            end
                        end
                    end
                end

                -- Profession row
                local profRow = CreateFrame("Frame", nil, content, "BackdropTemplate")
                profRow:SetPoint("TOPLEFT", 0, yOffset)
                profRow:SetSize(CONFIG.MAIN_FRAME_WIDTH - 60, 18)
                profRow:SetBackdrop({ bgFile = CONFIG.TEXTURES.WHITE_BG })
                local prScheme = GetScheme()
                if prScheme then
                    profRow:SetBackdropColor(prScheme.bg.r, prScheme.bg.g, prScheme.bg.b, 0.4)
                else
                    profRow:SetBackdropColor(0.05, 0.05, 0.05, 0.4)
                end

                local profLabel = profRow:CreateFontString(nil, "OVERLAY", "GameFontNormal")
                profLabel:SetPoint("LEFT", 8, 0)
                profLabel:SetWidth(PROF_COL_WIDTH)
                profLabel:SetJustifyH("LEFT")
                ApplyFont(profLabel, prScheme, "small")
                -- White if no data, yellow if below threshold, teal if all good
                local profColor
                if not hasSkillData then
                    profColor = C_Text()  -- White: no data
                elseif anyBelowThreshold then
                    profColor = C_Warning()  -- Yellow: below threshold
                else
                    profColor = C_Accent()  -- Teal: all good
                end
                profLabel:SetText(profColor .. localizedProfNameChar .. "|r")

                -- Expansion skill columns
                xOffset = PROF_COL_WIDTH + 8

                if viewMode == "recipes" then
                    -- RECIPES VIEW for character details (uses stored knownRecipes data)
                    for _, expName in ipairs(expansionOrder) do
                        local skillLabel = profRow:CreateFontString(nil, "OVERLAY", "GameFontNormal")
                        skillLabel:SetPoint("LEFT", xOffset, 0)
                        ApplyFont(skillLabel, prScheme, "small")
                        skillLabel:SetWidth(EXPANSION_COL_WIDTH)
                        skillLabel:SetJustifyH("CENTER")

                        local known, total = GetKnownRecipeCountForChar(charKey, profName, expName)
                        if total == 0 then
                            skillLabel:SetText(C_Dim() .. "-|r")
                        elseif known == nil then
                            -- No recipe data stored for this char/prof
                            skillLabel:SetText(C_Dim() .. string.format("?/%d|r", total))
                        else
                            local color
                            if known == 0 then
                                color = C_Dim()
                            elseif known >= total then
                                color = C_Accent()
                            else
                                color = C_Warning()
                            end
                            skillLabel:SetText(string.format("%s%d/%d|r", color, known, total))
                        end

                        xOffset = xOffset + EXPANSION_COL_WIDTH
                    end
                elseif hasSkillData then
                    -- SKILLS VIEW: Show actual skill levels
                    for _, expName in ipairs(expansionOrder) do
                        local skillData = GetSkillDataByDisplayName(profData.skillLevels, expName)
                        local skillLabel = profRow:CreateFontString(nil, "OVERLAY", "GameFontNormal")
                        skillLabel:SetPoint("LEFT", xOffset, 0)
                        skillLabel:SetWidth(EXPANSION_COL_WIDTH)
                        skillLabel:SetJustifyH("CENTER")
                        ApplyFont(skillLabel, prScheme, "small")

                        if skillData then
                            local current = skillData.current or 0
                            local max = skillData.max or 0

                            -- Get decor threshold for this profession/expansion
                            local decorThreshold = HDG_GetDecorThreshold and HDG_GetDecorThreshold(profName, expName) or 0

                            -- Color coding (check "no recipes" first!):
                            -- grey=no recipes, teal=decor ready, yellow=below threshold
                            local color
                            if decorThreshold == 0 then
                                color = C_Dim()  -- Grey: no decor recipes
                            elseif current >= decorThreshold then
                                color = C_Accent()  -- Teal: can learn decor
                            else
                                color = C_Warning()  -- Yellow: below threshold
                            end
                            skillLabel:SetText(string.format("%s%d/%d|r", color, current, max))
                        else
                            skillLabel:SetText(C_Dim() .. "-|r")
                        end

                        xOffset = xOffset + EXPANSION_COL_WIDTH
                    end
                else
                    -- No skill data yet - show "Open profession to scan" message
                    local scanLabel = profRow:CreateFontString(nil, "OVERLAY", "GameFontNormal")
                    scanLabel:SetPoint("LEFT", xOffset, 0)
                    scanLabel:SetText(C_Dim() .. "(Open profession window to scan skill levels)|r")
                    ApplyFont(scanLabel, prScheme, "small")
                end

                yOffset = yOffset - 18
            end
        end

        -- If character has no professions at all
        if not charHasProfessions then
            local noProfScheme = GetScheme()
            local noProfText = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            noProfText:SetPoint("TOPLEFT", 16, yOffset)
            ApplyFont(noProfText, noProfScheme, "small")
            noProfText:SetText(C_Dim() .. "No professions detected for this character.|r")
            yOffset = yOffset - 18
        end

        yOffset = yOffset - 16  -- Gap between characters
    end

    -- Info text at bottom
    yOffset = yOffset - 10
    local infoScheme = GetScheme()
    local infoText = content:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    infoText:SetPoint("TOPLEFT", 8, yOffset)
    ApplyFont(infoText, infoScheme, "small")
    infoText:SetText(C_Dim() .. "Open a profession window on each character to scan their skill levels.|r")

    -- Update scroll content height
    content:SetHeight(math.abs(yOffset) + 20)
end

-- ============================================================================
-- EVENTBUS SUBSCRIPTIONS
-- Decouple UI refresh from direct function calls
-- ============================================================================

local function RegisterEventBusHandlers()
    local HDG = _G.HousingDecorGuide
    if not HDG or not HDG.EventBus then return end

    HDG.EventBus:Register("HDG_STATE_CHANGED", function(payload)
        if not payload or not payload.action then return end

        local action = payload.action

        -- Character data changes
        if action == "UPDATE_CHARACTER" then
            HDG_AltsTab.Refresh()
        end
    end)
end

-- Register handlers after a short delay to ensure EventBus is ready
C_Timer.After(0.1, RegisterEventBusHandlers)

-- ============================================================================
-- EXPORT TO GLOBAL
-- ============================================================================

_G.HDG_AltsTab = HDG_AltsTab

return HDG_AltsTab
