--[[
    Commander 3.0 - Guild Data Source

    Provides access to guild roster.
    Commands: guild, guildmembers

    Supports both Retail and Classic
]]

local ADDON_NAME, C3 = ...

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

-------------------------------------------------------------------------------
-- GuildMember Type Registration
-------------------------------------------------------------------------------

C3.Types:Register("GuildMember", {
    description = "Guild roster member",
    source = "guild",
    properties = {
        { "name", "Character name" },
        { "rank", "Guild rank name" },
        { "rankIndex", "Guild rank index (0=GM)" },
        { "level", "Character level" },
        { "class", "Class file name: WARRIOR, MAGE, etc." },
        { "classID", "Class ID number" },
        { "zone", "Current zone (if online)" },
        { "note", "Public note" },
        { "officerNote", "Officer note" },
        { "isOnline", "true if online" },
        { "status", "0=online, 1=AFK, 2=DND" },
        { "achievementPoints", "Achievement points" },
        { "lastOnline", "Last online time" },
    },
})

C3.Formatter:Register("GuildMember", {
    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 = obj.isOnline and (classColors[obj.class] or "FFFFFF") or nil
        if color then
            name = C3.Formatter:Colorize(name, color)
        else
            name = Colorize(name, "status.dim")
        end

        -- Level
        local levelStr = obj.level and (" " .. Colorize(string.format("(%d)", obj.level), "status.dim")) or ""

        -- Rank
        local rankStr = obj.rank and (" " .. Colorize(string.format("<%s>", obj.rank), "status.gold")) or ""

        -- Status
        local statusStr = ""
        if not obj.isOnline then
            statusStr = " " .. Colorize("[Offline]", "status.dim")
        elseif obj.status == 1 then
            statusStr = " " .. Colorize("[AFK]", "accent.warning")
        elseif obj.status == 2 then
            statusStr = " " .. Colorize("[DND]", "accent.error")
        end

        -- Zone
        local zoneStr = ""
        if obj.isOnline and obj.zone and obj.zone ~= "" then
            zoneStr = string.format(" |cff00ccff@ %s|r", obj.zone)
        end

        return name .. levelStr .. rankStr .. zoneStr .. statusStr
    end,
    list = function(obj)
        local status = obj.isOnline and Colorize("+", "accent.success") or Colorize("-", "status.dim")
        return string.format("%s %s (%d)", status, obj.name or "?", obj.level or 0)
    end,
    compact = function(obj)
        return obj.name or "?"
    end
})

-------------------------------------------------------------------------------
-- guild - List guild members
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "guild",
    description = "List guild members",
    category = "data",
    producesType = "GuildMember",
    acceptsInput = false,
    flags = {
        online = { short = "o", description = "Show only online members" },
        offline = { description = "Show only offline members" },
        maxlevel = { short = "m", description = "Show only max level characters" },
    },
    args = {
        { name = "rank", required = false, description = "Filter by rank name" }
    },
    execute = function(ctx)
        local onlineOnly = ctx:HasFlag("online") or ctx:HasFlag("o")
        local offlineOnly = ctx:HasFlag("offline")
        local maxLevelOnly = ctx:HasFlag("maxlevel") or ctx:HasFlag("m")
        local rankFilter = ctx:GetArg(1)

        if not IsInGuild() then
            return ctx:Error("Not in a guild")
        end

        -- Request guild roster update
        if C_GuildInfo and C_GuildInfo.GuildRoster then
            C_GuildInfo.GuildRoster()
        elseif GuildRoster then
            GuildRoster()
        end

        -- Build results array with yielding to prevent freezing
        -- We can't use ctx:Produce() because coroutine.wrap() creates a C-call
        -- boundary that prevents yielding during iteration
        local results = {}
        local processed = 0
        local YIELD_INTERVAL = 100  -- Guilds can have thousands of members

        local numTotal, numOnline = GetNumGuildMembers()
        local maxLevel = GetMaxPlayerLevel and GetMaxPlayerLevel() or 80

        for i = 1, numTotal do
            -- Yield periodically to prevent freezing
            processed = processed + 1
            if processed % YIELD_INTERVAL == 0 then
                coroutine.yield()
            end

            local name, rank, rankIndex, level, class, zone, note,
                  officerNote, isOnline, status, classFile, achievementPoints,
                  achievementRank, isMobile, isSoREligible, standingID

            if C_GuildInfo and C_GuildInfo.GetGuildRosterInfo then
                -- Retail
                local info = C_GuildInfo.GetGuildRosterInfo(i)
                if info then
                    name = info.fullName or info.name
                    rank = info.rankName
                    rankIndex = info.rankIndex
                    level = info.level
                    classFile = info.classFileName
                    zone = info.area
                    note = info.note
                    officerNote = info.officerNote
                    isOnline = info.isOnline
                    status = info.status
                    achievementPoints = info.achievementPoints
                    -- class derived from classFile
                    class = classFile
                end
            else
                -- Classic
                name, rank, rankIndex, level, class, zone, note,
                officerNote, isOnline, status, classFile, achievementPoints,
                achievementRank, isMobile, isSoREligible, standingID = GetGuildRosterInfo(i)
                class = classFile or class
            end

            if name then
                -- Apply filters
                local include = true

                if onlineOnly and not isOnline then
                    include = false
                end

                if offlineOnly and isOnline then
                    include = false
                end

                if maxLevelOnly and level ~= maxLevel then
                    include = false
                end

                if rankFilter then
                    local filterLower = rankFilter:lower()
                    if not rank or not rank:lower():find(filterLower, 1, true) then
                        include = false
                    end
                end

                if include then
                    table.insert(results, C3.Types.Create("GuildMember", {
                        name = name,
                        rank = rank,
                        rankIndex = rankIndex,
                        level = level,
                        class = class,
                        zone = zone,
                        note = note,
                        officerNote = officerNote,
                        isOnline = isOnline,
                        status = status,
                        achievementPoints = achievementPoints,
                    }))
                end
            end
        end

        return results
    end
})

-------------------------------------------------------------------------------
-- ginfo - Show guild info
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "ginfo",
    description = "Show guild information",
    category = "data",
    acceptsInput = false,
    execute = function(ctx)
        if not IsInGuild() then
            return ctx:Error("Not in a guild")
        end

        local guildName, guildRankName, guildRankIndex, realm = GetGuildInfo("player")
        local numTotal, numOnline, numOnlineAndMobile = GetNumGuildMembers()

        local motd = GetGuildRosterMOTD and GetGuildRosterMOTD() or ""

        local output = string.format(
            "%s\n" ..
            "Your Rank: %s\n" ..
            "Members: %d total, %s online\n",
            Colorize(guildName or "Unknown Guild", "status.gold"),
            Colorize(guildRankName or "Unknown", "accent.success"),
            numTotal or 0,
            Colorize(tostring(numOnline or 0), "accent.success")
        )

        if motd and motd ~= "" then
            output = output .. "MOTD: " .. Colorize(motd, "status.dim")
        end

        return C3.Types.Create("Text", {
            content = output
        })
    end
})

-------------------------------------------------------------------------------
-- ginvite - Invite player to guild
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "ginvite",
    description = "Invite player to guild",
    category = "action",
    acceptsInput = false,
    args = {
        { name = "player", required = true, description = "Player name to invite" }
    },
    execute = function(ctx)
        local player = ctx:GetArg(1)
        if not player or player == "" then
            return ctx:Error("Usage: ginvite <player>")
        end

        if not IsInGuild() then
            return ctx:Error("Not in a guild")
        end

        if not CanGuildInvite() then
            return ctx:Error("You don't have permission to invite")
        end

        GuildInvite(player)

        return C3.Types.Create("Text", {
            content = "Guild invite sent to: " .. player
        })
    end
})

return true
