--[[
    Commander 3.0 - Group Data Source

    Provides access to party and raid members.
    Commands: party, raid, group

    Supports both Retail and Classic
]]

local ADDON_NAME, C3 = ...

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

-------------------------------------------------------------------------------
-- GroupMember Type Registration
-------------------------------------------------------------------------------

C3.Types:Register("GroupMember", {
    description = "Party or raid member",
    source = "party, raid, group",
    properties = {
        { "name", "Character name" },
        { "unit", "Unit ID: party1, raid5, etc." },
        { "class", "Class file name: WARRIOR, MAGE, etc." },
        { "classID", "Class ID number" },
        { "level", "Character level" },
        { "role", "Role: TANK, HEALER, DAMAGER" },
        { "health", "Current health" },
        { "healthMax", "Maximum health" },
        { "healthPct", "Health percent (0-100)" },
        { "power", "Current power/mana" },
        { "powerMax", "Maximum power" },
        { "powerPct", "Power percent (0-100)" },
        { "isLeader", "true if party/raid leader" },
        { "isAssist", "true if raid assistant" },
        { "isOnline", "true if online" },
        { "isDead", "true if dead" },
        { "subgroup", "Raid subgroup (1-8)" },
        { "rank", "Raid rank: 0=member, 1=assist, 2=leader" },
    },
})

C3.Formatter:Register("GroupMember", {
    format = function(obj)
        local name = obj.name or "Unknown"

        -- Class color
        local classColors = {
            WARRIOR = "C79C6E",
            PALADIN = "F58CBA",
            HUNTER = "ABD473",
            ROGUE = "FFF569",
            PRIEST = "FFFFFF",
            DEATHKNIGHT = "C41F3B",
            SHAMAN = "0070DE",
            MAGE = "69CCF0",
            WARLOCK = "9482C9",
            MONK = "00FF96",
            DRUID = "FF7D0A",
            DEMONHUNTER = "A330C9",
            EVOKER = "33937F",
        }
        local color = classColors[obj.class] or "FFFFFF"
        name = C3.Formatter:Colorize(name, color)

        -- Role indicator
        local roleIcons = {
            TANK = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES:14:14:0:-12:64:64:0:19:22:41|t",
            HEALER = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES:14:14:0:-12:64:64:20:39:1:20|t",
            DAMAGER = "|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES:14:14:0:-12:64:64:20:39:22:41|t",
        }
        local roleStr = roleIcons[obj.role] or ""

        -- Status
        local statusStr = ""
        if not obj.isOnline then
            statusStr = " " .. Colorize("[Offline]", "status.dim")
        elseif obj.isDead then
            statusStr = " " .. Colorize("[Dead]", "accent.error")
        end

        -- Leader/Assist
        local rankStr = ""
        if obj.isLeader then
            rankStr = " |cffffd700[L]|r"
        elseif obj.isAssist then
            rankStr = " |cffffd700[A]|r"
        end

        -- Health (handles nil for WoW 12.0 secret values)
        local healthStr = ""
        if obj.isOnline and not obj.isDead then
            if obj.healthPct then
                local hpColor = obj.healthPct > 50 and "00FF00" or (obj.healthPct > 25 and "FFFF00" or "FF0000")
                healthStr = string.format(" |cff%s%d%%|r", hpColor, obj.healthPct)
            elseif C3.Secrets and C3.Secrets.hasSecretSystem then
                -- Secret value - show restricted indicator
                healthStr = " " .. Colorize("???", "status.dim")
            end
        end

        return roleStr .. name .. rankStr .. healthStr .. statusStr
    end,
    list = function(obj)
        local status
        if obj.isOnline then
            if obj.isDead then
                status = Colorize("X", "accent.error")
            else
                status = Colorize("+", "accent.success")
            end
        else
            status = Colorize("-", "status.dim")
        end
        return string.format("%s %s", status, obj.name or "?")
    end,
    compact = function(obj)
        return obj.name or "?"
    end
})

-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------

local function getUnitInfo(unit)
    if not UnitExists(unit) then
        return nil
    end

    local name, realm = UnitName(unit)
    if realm and realm ~= "" then
        name = name .. "-" .. realm
    end

    local _, class, classID = UnitClass(unit)
    local level = UnitLevel(unit)

    -- Use Secrets module for WoW 12.0+ secret value handling
    local health, healthMax, power, powerMax
    local healthPct, powerPct

    if C3.Secrets then
        health = C3.Secrets.UnitHealth(unit)
        healthMax = C3.Secrets.UnitHealthMax(unit)
        power = C3.Secrets.UnitPower(unit)
        powerMax = C3.Secrets.UnitPowerMax(unit)
        healthPct = C3.Secrets.GetHealthPercent(unit)
        powerPct = C3.Secrets.GetPowerPercent(unit)
    else
        health = UnitHealth(unit)
        healthMax = UnitHealthMax(unit)
        power = UnitPower(unit)
        powerMax = UnitPowerMax(unit)
        healthPct = healthMax and healthMax > 0 and math.floor(health / healthMax * 100) or 0
        powerPct = powerMax and powerMax > 0 and math.floor(power / powerMax * 100) or 0
    end

    local role = nil
    if UnitGroupRolesAssigned then
        role = UnitGroupRolesAssigned(unit)
        if role == "NONE" then role = nil end
    end

    local isLeader = UnitIsGroupLeader(unit)
    local isAssist = UnitIsGroupAssistant(unit)
    local isOnline = UnitIsConnected(unit)
    local isDead = UnitIsDead(unit) or UnitIsGhost(unit)

    return {
        name = name,
        unit = unit,
        class = class,
        classID = classID,
        level = level,
        role = role,
        health = health,
        healthMax = healthMax,
        healthPct = healthPct,
        power = power,
        powerMax = powerMax,
        powerPct = powerPct,
        isLeader = isLeader,
        isAssist = isAssist,
        isOnline = isOnline,
        isDead = isDead,
    }
end

-------------------------------------------------------------------------------
-- party - List party members
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "party",
    description = "List party members",
    category = "data",
    producesType = "Unit",
    acceptsInput = false,
    flags = {
        online = { short = "o", description = "Show only online members" },
        dead = { short = "d", description = "Show only dead members" },
    },
    execute = function(ctx)
        local onlineOnly = ctx:HasFlag("online") or ctx:HasFlag("o")
        local deadOnly = ctx:HasFlag("dead") or ctx:HasFlag("d")

        if not IsInGroup() then
            return ctx:Error("Not in a group")
        end

        return ctx:Produce(function(emit)
            -- Add player first
            local playerInfo = getUnitInfo("player")
            if playerInfo then
                playerInfo.subgroup = 1
                emit(C3.Types.Create("GroupMember", playerInfo))
            end

            -- Party members
            local numParty = GetNumGroupMembers() - 1
            for i = 1, numParty do
                local unit = "party" .. i
                local info = getUnitInfo(unit)

                if info then
                    info.subgroup = 1

                    local include = true
                    if onlineOnly and not info.isOnline then
                        include = false
                    end
                    if deadOnly and not info.isDead then
                        include = false
                    end

                    if include then
                        emit(C3.Types.Create("GroupMember", info))
                    end
                end
            end
        end)
    end
})

-------------------------------------------------------------------------------
-- raid - List raid members
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "raid",
    description = "List raid members",
    category = "data",
    producesType = "Unit",
    acceptsInput = false,
    flags = {
        online = { short = "o", description = "Show only online members" },
        dead = { short = "d", description = "Show only dead members" },
        tanks = { short = "t", description = "Show only tanks" },
        healers = { short = "h", description = "Show only healers" },
        dps = { description = "Show only DPS" },
    },
    args = {
        { name = "group", required = false, description = "Filter by group number (1-8)" }
    },
    execute = function(ctx)
        local onlineOnly = ctx:HasFlag("online") or ctx:HasFlag("o")
        local deadOnly = ctx:HasFlag("dead") or ctx:HasFlag("d")
        local tanksOnly = ctx:HasFlag("tanks") or ctx:HasFlag("t")
        local healersOnly = ctx:HasFlag("healers") or ctx:HasFlag("h")
        local dpsOnly = ctx:HasFlag("dps")
        local groupFilter = tonumber(ctx:GetArg(1))

        if not IsInRaid() then
            return ctx:Error("Not in a raid")
        end

        return ctx:Produce(function(emit)
            local numRaid = GetNumGroupMembers()

            for i = 1, numRaid do
                local unit = "raid" .. i
                local info = getUnitInfo(unit)

                if info then
                    -- Get subgroup
                    local name, rank, subgroup = GetRaidRosterInfo(i)
                    info.subgroup = subgroup or 1
                    info.rank = rank -- 0 = member, 1 = assist, 2 = leader

                    local include = true

                    if onlineOnly and not info.isOnline then
                        include = false
                    end
                    if deadOnly and not info.isDead then
                        include = false
                    end
                    if tanksOnly and info.role ~= "TANK" then
                        include = false
                    end
                    if healersOnly and info.role ~= "HEALER" then
                        include = false
                    end
                    if dpsOnly and info.role ~= "DAMAGER" then
                        include = false
                    end
                    if groupFilter and info.subgroup ~= groupFilter then
                        include = false
                    end

                    if include then
                        emit(C3.Types.Create("GroupMember", info))
                    end
                end
            end
        end)
    end
})

-------------------------------------------------------------------------------
-- group - List all group members (party or raid)
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "group",
    description = "List all group members",
    category = "data",
    acceptsInput = false,
    flags = {
        online = { short = "o", description = "Show only online members" },
        dead = { short = "d", description = "Show only dead members" },
    },
    execute = function(ctx)
        if IsInRaid() then
            -- Delegate to raid command
            return C3.Registry:Get("raid").execute(ctx)
        elseif IsInGroup() then
            -- Delegate to party command
            return C3.Registry:Get("party").execute(ctx)
        else
            return ctx:Error("Not in a group")
        end
    end
})

-------------------------------------------------------------------------------
-- target - Show current target info
-- NOTE: TargetUnit() is protected and cannot be called from addon code
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "target",
    description = "Show current target info",
    category = "data",
    acceptsInput = false,
    execute = function(ctx)
        if not UnitExists("target") then
            return C3.Types.Create("Text", {
                content = "No target"
            })
        end

        local info = getUnitInfo("target")
        if info then
            return C3.Types.Create("GroupMember", info)
        end

        return C3.Types.Create("Text", {
            content = "Target: " .. (UnitName("target") or "Unknown")
        })
    end
})

-------------------------------------------------------------------------------
-- focus - Show or clear focus target
-- NOTE: FocusUnit() is protected, but ClearFocus() works
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "focus",
    description = "Show focus or clear it",
    category = "action",
    acceptsInput = false,
    args = {
        { name = "action", required = false, description = "'clear' to clear focus, empty to show" }
    },
    execute = function(ctx)
        local action = ctx:GetArg(1)

        if action and action:lower() == "clear" then
            ClearFocus()
            return C3.Types.Create("Text", {
                content = "Focus cleared"
            })
        end

        -- Show current focus
        if not UnitExists("focus") then
            return C3.Types.Create("Text", {
                content = "No focus target"
            })
        end

        local info = getUnitInfo("focus")
        if info then
            return C3.Types.Create("GroupMember", info)
        end

        return C3.Types.Create("Text", {
            content = "Focus: " .. (UnitName("focus") or "Unknown")
        })
    end
})

return true
