--[[
    Commander 3.0 - Reputation Data Source

    Provides access to faction reputations.
    Commands: rep, reputation, factions

    Supports Retail API (C_Reputation)
    Handles: Classic standings, Major Factions (Renown), Friendship factions
]]

local ADDON_NAME, C3 = ...

-- Use shared utilities
local Colorize = function(...) return C3.Colors.Colorize(...) end

-------------------------------------------------------------------------------
-- Standing Names & Colors (Classic reputation system)
-------------------------------------------------------------------------------

local STANDING_NAMES = {
    [0] = "Unknown",
    [1] = "Hated",
    [2] = "Hostile",
    [3] = "Unfriendly",
    [4] = "Neutral",
    [5] = "Friendly",
    [6] = "Honored",
    [7] = "Revered",
    [8] = "Exalted",
}

local STANDING_COLORS = {
    [0] = "808080", -- Unknown - gray
    [1] = "cc2222", -- Hated - dark red
    [2] = "ff0000", -- Hostile - red
    [3] = "ee6622", -- Unfriendly - orange
    [4] = "ffff00", -- Neutral - yellow
    [5] = "00ff00", -- Friendly - green
    [6] = "00ff88", -- Honored - teal
    [7] = "00ffcc", -- Revered - cyan
    [8] = "00ffff", -- Exalted - bright cyan
}

-------------------------------------------------------------------------------
-- Reputation Type Detection Helpers
-------------------------------------------------------------------------------

-- Cache for major faction IDs (built once per session)
local majorFactionCache = nil

local function getMajorFactionIDs()
    if majorFactionCache then return majorFactionCache end
    majorFactionCache = {}
    if C_MajorFactions and C_MajorFactions.GetMajorFactionIDs then
        local ids = C_MajorFactions.GetMajorFactionIDs()
        for _, id in ipairs(ids or {}) do
            majorFactionCache[id] = true
        end
    end
    return majorFactionCache
end

-- Check if faction is a Major Faction (Renown system)
local function isMajorFaction(factionID)
    if not factionID then return false end
    local cache = getMajorFactionIDs()
    return cache[factionID] == true
end

-- Get Major Faction (Renown) data
local function getMajorFactionData(factionID)
    if not C_MajorFactions or not C_MajorFactions.GetMajorFactionData then
        return nil
    end
    return C_MajorFactions.GetMajorFactionData(factionID)
end

-- Get Friendship reputation data
local function getFriendshipData(factionID)
    if not C_GossipInfo or not C_GossipInfo.GetFriendshipReputation then
        return nil
    end
    local info = C_GossipInfo.GetFriendshipReputation(factionID)
    -- Only return if this is actually a friendship faction
    if info and info.friendshipFactionID and info.friendshipFactionID > 0 then
        return info
    end
    return nil
end

-- Determine reputation type and get appropriate data
-- Returns: repType ("renown", "friendship", "classic"), standingName, current, max, level
local function getReputationDetails(factionID, classicData)
    -- Check Major Faction (Renown) first
    if isMajorFaction(factionID) then
        local majorData = getMajorFactionData(factionID)
        if majorData then
            return "renown",
                   "Renown " .. (majorData.renownLevel or 1),
                   majorData.renownReputationEarned or 0,
                   majorData.renownLevelThreshold or 0,
                   majorData.renownLevel or 1
        end
    end

    -- Check Friendship faction
    local friendshipData = getFriendshipData(factionID)
    if friendshipData then
        local rankName = friendshipData.reaction or friendshipData.text or "Friend"
        local current = friendshipData.standing or 0
        local max = friendshipData.nextThreshold or friendshipData.maxRep or 0
        -- Calculate progress within current rank
        local rankStart = friendshipData.reactionThreshold or 0
        local progress = current - rankStart
        local rankMax = max > 0 and (max - rankStart) or 0
        return "friendship",
               rankName,
               progress,
               rankMax,
               nil
    end

    -- Classic standing system
    local standing = classicData.reaction or 4
    local standingName = STANDING_NAMES[standing] or "Unknown"
    local current = 0
    local max = 0

    if classicData.currentStanding then
        current = classicData.currentStanding - (classicData.currentReactionThreshold or 0)
        if classicData.nextReactionThreshold and classicData.currentReactionThreshold then
            max = classicData.nextReactionThreshold - classicData.currentReactionThreshold
        end
    end

    return "classic", standingName, current, max, standing
end

-------------------------------------------------------------------------------
-- Reputation Type Registration
-------------------------------------------------------------------------------

-- Color for reputation type
local function getStandingColor(repType, standing, standingName)
    if repType == "renown" then
        return "00ccff"  -- Cyan for renown
    elseif repType == "friendship" then
        return "ff8000"  -- Orange for friendship ranks
    else
        return STANDING_COLORS[standing] or "ffffff"
    end
end

C3.Types:Register("Reputation", {
    description = "Faction reputation standing",
    source = "rep, reputation, factions",
    properties = {
        { "id", "Faction ID number" },
        { "name", "Faction name" },
        { "repType", "Reputation type: classic, renown, or friendship" },
        { "standing", "Standing level: 1=Hated to 8=Exalted (classic only)" },
        { "standingName", "Standing/rank name: Exalted, Renown 25, Mastermind, etc." },
        { "renownLevel", "Renown level (renown factions only)" },
        { "current", "Current reputation within standing/level" },
        { "max", "Max reputation for current standing/level" },
        { "percent", "Percent of current standing (0-100)" },
        { "isParagon", "true if at paragon level" },
        { "paragonLevel", "Paragon level count" },
        { "isWatched", "true if being tracked" },
        { "isHeader", "true if category header" },
        { "isTopLevel", "true if top-level expansion header" },
        { "hasRewardPending", "true if paragon reward available" },
        { "expansion", "Expansion name this faction belongs to" },
        { "category", "Sub-category within expansion" },
    },
    format = function(self)
        if self.isHeader then
            -- Top-level headers (expansions) get prominent styling
            if self.isTopLevel then
                return Colorize(string.format("━━ %s ━━", self.name or "Category"), "status.gold")
            else
                -- Sub-headers get indented, dimmer styling
                return "  " .. Colorize(string.format("── %s ──", self.name or "Category"), "status.dim")
            end
        end

        local color = getStandingColor(self.repType, self.standing, self.standingName)
        local standingName = self.standingName or "Unknown"
        local name = self.name or "Unknown Faction"

        -- Indent factions under their headers
        local indent = "  "

        -- Progress bar
        local progress = ""
        if self.max and self.max > 0 then
            local pct = math.floor((self.current or 0) / self.max * 100)
            progress = " " .. Colorize(string.format("(%d/%d)", self.current or 0, self.max), "status.dim")
        end

        -- Paragon indicator
        local paragon = ""
        if self.isParagon then
            if self.hasRewardPending then
                paragon = " |cffff00ff[P" .. (self.paragonLevel or 1) .. " REWARD!]|r"
            else
                paragon = " " .. Colorize("[P" .. (self.paragonLevel or 1) .. "]", "accent.special")
            end
        end

        -- Watched indicator
        local watched = self.isWatched and (Colorize("*", "accent.success") .. " ") or ""

        return string.format("%s%s|cff%s%s|r - |cff%s%s|r%s%s",
            indent, watched, color, standingName, "ffffff", name, progress, paragon)
    end
})

-- Register formatter
C3.Formatter:Register("Reputation", {
    format = function(obj)
        if obj.isHeader then
            if obj.isTopLevel then
                return Colorize(string.format("━━ %s ━━", obj.name or "Category"), "status.gold")
            else
                return "  " .. Colorize(string.format("── %s ──", obj.name or "Category"), "status.dim")
            end
        end

        local color = getStandingColor(obj.repType, obj.standing, obj.standingName)
        local standingName = obj.standingName or "Unknown"
        local indent = "  "

        local progress = ""
        if obj.max and obj.max > 0 then
            progress = string.format(" (%d/%d)", obj.current or 0, obj.max)
        end

        local paragon = ""
        if obj.isParagon then
            paragon = obj.hasRewardPending and " [REWARD!]" or (" " .. Colorize("[P" .. (obj.paragonLevel or 1) .. "]", "accent.special"))
        end

        local watched = obj.isWatched and (Colorize("*", "accent.success") .. " ") or ""

        return string.format("%s%s|cff%s%s|r - %s%s%s",
            indent, watched, color, standingName, obj.name or "?", progress, paragon)
    end,
    list = function(obj)
        if obj.isHeader then
            if obj.isTopLevel then
                return string.format("━━ %s ━━", obj.name or "Category")
            else
                return string.format("  ── %s ──", obj.name or "Category")
            end
        end
        return string.format("  %s: %s", obj.name or "?", obj.standingName or "?")
    end,
    compact = function(obj)
        return string.format("%s: %s", obj.name or "?", obj.standingName or "?")
    end
})

-------------------------------------------------------------------------------
-- API Compatibility
-------------------------------------------------------------------------------

local function isRetailReputation()
    return C_Reputation and C_Reputation.GetNumFactions
end

-------------------------------------------------------------------------------
-- rep / reputation - List all factions
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "rep",
    description = "List faction reputations",
    category = "data",
    producesType = "Reputation",
    acceptsInput = false,
    flags = {
        flat = { short = "f", description = "Flat list without expansion headers" },
        paragon = { short = "p", description = "Show only paragon factions" },
        exalted = { short = "e", description = "Show only exalted factions" },
        watched = { short = "w", description = "Show only watched/tracked factions" },
        renown = { short = "r", description = "Show only renown (major) factions" },
    },
    execute = function(ctx)
        local flatMode = ctx:HasFlag("flat") or ctx:HasFlag("f")
        local paragonOnly = ctx:HasFlag("paragon") or ctx:HasFlag("p")
        local exaltedOnly = ctx:HasFlag("exalted") or ctx:HasFlag("e")
        local watchedOnly = ctx:HasFlag("watched") or ctx:HasFlag("w")
        local renownOnly = ctx:HasFlag("renown") or ctx:HasFlag("r")

        local results = {}
        local processed = 0
        local YIELD_INTERVAL = C3.Constants.YIELD_INTERVAL

        if not isRetailReputation() then
            return ctx:Error("Reputation API not available (Classic not supported)")
        end

        -- First expand all headers to see all factions
        local function expandAllHeaders()
            local changed = true
            local iterations = 0
            local MAX_ITERATIONS = 100
            while changed and iterations < MAX_ITERATIONS do
                iterations = iterations + 1
                changed = false
                local numFactions = C_Reputation.GetNumFactions()
                for i = 1, numFactions do
                    local data = C_Reputation.GetFactionDataByIndex(i)
                    if data and data.isHeader and data.isCollapsed then
                        C_Reputation.ExpandFactionHeader(i)
                        changed = true
                        break -- List indices change after expand, restart
                    end
                end
            end
        end

        expandAllHeaders()

        -- Iterate through all factions, tracking hierarchy
        local numFactions = C_Reputation.GetNumFactions()
        local currentExpansion = nil  -- Top-level header (expansion)
        local currentCategory = nil   -- Sub-header within expansion
        local pendingExpansion = nil  -- Expansion header waiting for factions
        local pendingCategory = nil   -- Sub-category header waiting for factions

        for i = 1, numFactions do repeat
            -- Yield periodically
            processed = processed + 1
            if processed % YIELD_INTERVAL == 0 then
                coroutine.yield()
            end

            local data = C_Reputation.GetFactionDataByIndex(i)

            -- Guard: Invalid data
            if not data then break end

            -- Handle headers - track hierarchy
            if data.isHeader then
                -- Determine header level by isChild flag
                if data.isChild then
                    -- Sub-category within an expansion
                    currentCategory = data.name
                    if not flatMode then
                        pendingCategory = {
                            id = data.factionID or 0,
                            name = data.name,
                            expansion = currentExpansion,
                            category = currentCategory,
                            isTopLevel = false,
                        }
                    end
                else
                    -- Top-level expansion header - reset category
                    currentExpansion = data.name
                    currentCategory = nil
                    pendingCategory = nil  -- New expansion clears pending sub-category
                    if not flatMode then
                        pendingExpansion = {
                            id = data.factionID or 0,
                            name = data.name,
                            expansion = currentExpansion,
                            category = nil,
                            isTopLevel = true,
                        }
                    end
                end
                break
            end

            -- Get reputation details (handles renown, friendship, and classic)
            local repType, standingName, current, max, level = getReputationDetails(data.factionID, data)
            local standing = level or data.reaction or 4
            local renownLevel = (repType == "renown") and level or nil

            -- Get paragon info
            local isParagon = false
            local paragonLevel = 0
            local hasRewardPending = false

            if data.factionID and C_Reputation.IsFactionParagon(data.factionID) then
                isParagon = true
                local currentValue, threshold, _, hasReward = C_Reputation.GetFactionParagonInfo(data.factionID)
                if threshold and threshold > 0 then
                    paragonLevel = math.floor(currentValue / threshold)
                end
                hasRewardPending = hasReward
            end

            -- Guard: Paragon filter
            if paragonOnly and not isParagon then break end

            -- Guard: Exalted filter (only applies to classic rep)
            if exaltedOnly and repType == "classic" and data.reaction ~= 8 then break end

            -- Guard: Watched filter
            if watchedOnly and not data.isWatched then break end

            -- Guard: Renown filter
            if renownOnly and repType ~= "renown" then break end

            -- Calculate percent
            local percent = 0
            if max and max > 0 then
                percent = math.floor((current or 0) / max * 100)
            elseif repType == "classic" and standing == 8 then
                percent = 100 -- Exalted shows as 100%
            end

            -- Faction passed filters - output any pending headers (expansion first, then category)
            if pendingExpansion then
                table.insert(results, C3.Types.Create("Reputation", {
                    id = pendingExpansion.id,
                    name = pendingExpansion.name,
                    repType = "header",
                    standing = 0,
                    standingName = "Header",
                    current = 0,
                    max = 0,
                    percent = 0,
                    isHeader = true,
                    isTopLevel = true,
                    expansion = pendingExpansion.expansion,
                    category = nil,
                }))
                pendingExpansion = nil
            end

            if pendingCategory then
                table.insert(results, C3.Types.Create("Reputation", {
                    id = pendingCategory.id,
                    name = pendingCategory.name,
                    repType = "header",
                    standing = 0,
                    standingName = "Header",
                    current = 0,
                    max = 0,
                    percent = 0,
                    isHeader = true,
                    isTopLevel = false,
                    expansion = pendingCategory.expansion,
                    category = pendingCategory.category,
                }))
                pendingCategory = nil
            end

            -- Add the faction
            table.insert(results, C3.Types.Create("Reputation", {
                id = data.factionID or 0,
                name = data.name,
                repType = repType,
                standing = standing,
                standingName = standingName,
                renownLevel = renownLevel,
                current = current or 0,
                max = max or 0,
                percent = percent,
                isParagon = isParagon,
                paragonLevel = paragonLevel,
                isWatched = data.isWatched,
                isHeader = false,
                hasRewardPending = hasRewardPending,
                expansion = currentExpansion,
                category = currentCategory,
                isChild = data.isChild,
            }))
        until true end

        return results
    end
})

return true
