--[[
    Commander 3.0 - Battle Pets Data Source

    Provides access to player's companion pets (battle pets).
    Commands: pets, pet, dismisspet

    Uses C_PetJournal API (Retail)
]]

local ADDON_NAME, C3 = ...

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

-------------------------------------------------------------------------------
-- Pet Type Registration
-------------------------------------------------------------------------------

C3.Types:Register("Pet", {
    description = "Companion pet (battle pet)",
    source = "pets",
    properties = {
        { "id", "Pet GUID" },
        { "speciesID", "Species ID number" },
        { "name", "Pet name (custom or default)" },
        { "speciesName", "Default species name" },
        { "icon", "Icon texture ID" },
        { "level", "Pet level (1-25)" },
        { "quality", "Pet quality (1-6)" },
        { "health", "Current health" },
        { "maxHealth", "Maximum health" },
        { "power", "Attack power" },
        { "speed", "Speed stat" },
        { "isFavorite", "true if marked as favorite" },
        { "isRevoked", "true if pet is revoked" },
        { "canBattle", "true if pet can battle" },
        { "petType", "Pet family type ID" },
        { "petTypeName", "Pet family name" },
        { "isSummoned", "true if currently summoned" },
    },
})

-- Pet type names for display
local PET_TYPE_NAMES = {
    [1] = "Humanoid",
    [2] = "Dragonkin",
    [3] = "Flying",
    [4] = "Undead",
    [5] = "Critter",
    [6] = "Magic",
    [7] = "Elemental",
    [8] = "Beast",
    [9] = "Aquatic",
    [10] = "Mechanical",
}

C3.Formatter:Register("Pet", {
    format = function(obj)
        local icon = obj.icon and (C3.Formatter:Icon(obj.icon) .. " ") or ""
        local name = obj.name or obj.speciesName or "Unknown Pet"

        -- Quality color
        local qualityColors = {
            [1] = "9d9d9d", -- Poor
            [2] = "ffffff", -- Common
            [3] = "1eff00", -- Uncommon
            [4] = "0070dd", -- Rare
            [5] = "a335ee", -- Epic
            [6] = "ff8000", -- Legendary
        }
        local qColor = qualityColors[obj.quality] or "ffffff"
        name = string.format("|cff%s%s|r", qColor, name)

        -- Status indicators
        local status = ""
        if obj.isSummoned then
            status = Colorize("*", "accent.success") .. " "
        end
        if obj.isFavorite then
            status = status .. Colorize("[Fav]", "status.gold") .. " "
        end

        -- Level and type
        local info = ""
        if obj.level then
            info = string.format(" Lv%d", obj.level)
        end
        if obj.petTypeName then
            info = info .. " " .. Colorize("(" .. obj.petTypeName .. ")", "status.dim")
        end

        return status .. icon .. name .. info
    end,
    list = function(obj)
        local fav = obj.isFavorite and "*" or " "
        local summoned = obj.isSummoned and "+" or " "
        return string.format("%s%s Lv%-2d %s", summoned, fav, obj.level or 0, obj.name or obj.speciesName or "?")
    end,
    compact = function(obj)
        return obj.name or obj.speciesName or "?"
    end
})

-------------------------------------------------------------------------------
-- Helper Functions
-------------------------------------------------------------------------------

local MAX_SUGGESTIONS = C3.Constants.MAX_SUGGESTIONS

---Get currently summoned pet GUID
---@return string|nil petGUID
local function GetSummonedPetGUID()
    if C_PetJournal and C_PetJournal.GetSummonedPetGUID then
        return C_PetJournal.GetSummonedPetGUID()
    end
    return nil
end

---Build a Pet object from pet GUID
---@param petGUID string
---@return table|nil
local function BuildPetObject(petGUID)
    if not C_PetJournal then return nil end

    local speciesID, customName, level, xp, maxXp, displayID, isFavorite,
          name, icon, petType, creatureID, sourceText, description,
          isWild, canBattle, tradable, unique, obtainable = C_PetJournal.GetPetInfoByPetID(petGUID)

    if not speciesID then return nil end

    local health, maxHealth, power, speed, quality = C_PetJournal.GetPetStats(petGUID)
    local isRevoked = C_PetJournal.PetIsRevoked and C_PetJournal.PetIsRevoked(petGUID) or false
    local summonedGUID = GetSummonedPetGUID()

    return C3.Types.Create("Pet", {
        id = petGUID,
        speciesID = speciesID,
        name = customName or name,
        speciesName = name,
        icon = icon,
        level = level,
        quality = quality,
        health = health,
        maxHealth = maxHealth,
        power = power,
        speed = speed,
        isFavorite = isFavorite,
        isRevoked = isRevoked,
        canBattle = canBattle,
        petType = petType,
        petTypeName = PET_TYPE_NAMES[petType],
        isSummoned = (summonedGUID == petGUID),
    })
end

-------------------------------------------------------------------------------
-- Pets Intellisense Provider
-------------------------------------------------------------------------------

C3.Intellisense:RegisterProvider("Pets", function(partial, ctx)
    if not C_PetJournal then
        return {}
    end

    -- Special options first
    local results = {}
    local partialLower = partial and partial:lower() or ""

    -- Only show special commands when user types a matching prefix
    if partialLower ~= "" and ("random"):find(partialLower, 1, true) == 1 then
        results[#results + 1] = {
            text = "random",
            description = "Summon random favorite pet",
            priority = 90,
        }
    end

    if partialLower ~= "" and ("dismiss"):find(partialLower, 1, true) == 1 then
        results[#results + 1] = {
            text = "dismiss",
            description = "Dismiss current pet",
            priority = 89,
        }
    end

    -- Search collected pets
    local numPets, numOwned = C_PetJournal.GetNumPets()
    local count = 0
    local summonedGUID = GetSummonedPetGUID()

    for i = 1, numPets do
        if count >= MAX_SUGGESTIONS then break end

        local petGUID, speciesID, owned, customName, level, favorite, isRevoked,
              speciesName, icon, petType = C_PetJournal.GetPetInfoByIndex(i)

        if petGUID and owned and not isRevoked then
            local displayName = customName or speciesName
            if displayName and fuzzyMatch(partial, displayName) then
                local desc = string.format("Lv%d %s", level or 0, PET_TYPE_NAMES[petType] or "Pet")
                if favorite then
                    desc = desc .. " " .. Colorize("[Fav]", "status.gold")
                end
                if petGUID == summonedGUID then
                    desc = desc .. " " .. Colorize("[Summoned]", "accent.success")
                end

                results[#results + 1] = {
                    text = displayName,
                    description = desc,
                    icon = icon,
                    priority = favorite and 80 or 50,
                }
                count = count + 1
            end
        end
    end

    -- Sort by priority
    table.sort(results, function(a, b)
        return (a.priority or 0) > (b.priority or 0)
    end)

    return results
end)

-------------------------------------------------------------------------------
-- pets - List companion pets
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "pets",
    description = "List companion pets",
    category = "data",
    producesType = "Pet",
    acceptsInput = false,
    flags = {
        favorites = { short = "f", description = "Show only favorites" },
        all = { short = "a", description = "Show all pets (including revoked)" },
        battle = { short = "b", description = "Show only battle-ready pets" },
    },
    execute = function(ctx)
        if not C_PetJournal then
            return ctx:Error("Pet journal not available in this game version")
        end

        local favoritesOnly = ctx:HasFlag("favorites") or ctx:HasFlag("f")
        local showAll = ctx:HasFlag("all") or ctx:HasFlag("a")
        local battleOnly = ctx:HasFlag("battle") or ctx:HasFlag("b")

        local results = {}
        local numPets = C_PetJournal.GetNumPets()
        local summonedGUID = GetSummonedPetGUID()

        for i = 1, numPets do
            local petGUID, speciesID, owned, customName, level, favorite, isRevoked,
                  speciesName, icon, petType, creatureID, sourceText, description,
                  isWild, canBattle = C_PetJournal.GetPetInfoByIndex(i)

            if petGUID and owned then
                -- Apply filters
                local include = true

                if not showAll and isRevoked then
                    include = false
                end
                if favoritesOnly and not favorite then
                    include = false
                end
                if battleOnly and not canBattle then
                    include = false
                end

                if include then
                    local health, maxHealth, power, speed, quality = C_PetJournal.GetPetStats(petGUID)

                    results[#results + 1] = C3.Types.Create("Pet", {
                        id = petGUID,
                        speciesID = speciesID,
                        name = customName or speciesName,
                        speciesName = speciesName,
                        icon = icon,
                        level = level,
                        quality = quality,
                        health = health,
                        maxHealth = maxHealth,
                        power = power,
                        speed = speed,
                        isFavorite = favorite,
                        isRevoked = isRevoked,
                        canBattle = canBattle,
                        petType = petType,
                        petTypeName = PET_TYPE_NAMES[petType],
                        isSummoned = (summonedGUID == petGUID),
                    })
                end
            end

            -- Yield periodically to kernel scheduler to prevent freezing
            if i % 100 == 0 then
                coroutine.yield()
            end
        end

        return results
    end
})

-------------------------------------------------------------------------------
-- pet - Summon a companion pet
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "pet",
    description = "Summon a companion pet",
    category = "action",
    args = {
        { name = "name", required = false, description = "Pet name or 'random'/'dismiss'",
          provider = "Pets" }
    },
    execute = function(ctx)
        if not C_PetJournal then
            return ctx:Error("Pet journal not available in this game version")
        end

        -- Check for pipeline input first
        if ctx:HasInput() then
            local input = ctx:GetInput()
            local items = {}

            if type(input) == "function" then
                for obj in input do
                    items[#items + 1] = obj
                end
            elseif type(input) == "table" then
                items = input._type and { input } or input
            end

            if #items > 0 then
                local petObj = items[1]
                if petObj._type == "Pet" and petObj.id then
                    C_PetJournal.SummonPetByGUID(petObj.id)
                    return C3.Types.Create("Text", {
                        content = "Summoning: " .. (petObj.name or petObj.speciesName or "pet")
                    })
                end
            end
        end

        -- Argument mode
        local query = ctx:GetArg(1)

        -- No argument: show current pet or summon random
        if not query or query == "" then
            local summonedGUID = GetSummonedPetGUID()
            if summonedGUID then
                return BuildPetObject(summonedGUID)
            else
                return ctx:Error("No pet summoned. Use: pet <name> or pet random")
            end
        end

        -- Special commands
        local queryLower = query:lower()

        if queryLower == "random" then
            C_PetJournal.SummonRandomPet(true) -- true = favorites only
            return C3.Types.Create("Text", {
                content = "Summoning random favorite pet..."
            })
        end

        if queryLower == "dismiss" then
            local summonedGUID = GetSummonedPetGUID()
            if summonedGUID then
                C_PetJournal.SummonPetByGUID(summonedGUID) -- Toggle dismisses
                return C3.Types.Create("Text", {
                    content = "Dismissed pet"
                })
            else
                return ctx:Error("No pet to dismiss")
            end
        end

        -- Find pet by exact name (case-insensitive)
        local numPets = C_PetJournal.GetNumPets()
        local bestMatch = nil
        local bestScore = 0

        for i = 1, numPets do
            local petGUID, speciesID, owned, customName, level, favorite, isRevoked,
                  speciesName = C_PetJournal.GetPetInfoByIndex(i)

            if petGUID and owned and not isRevoked then
                local displayName = customName or speciesName
                if displayName then
                    local nameLower = displayName:lower()

                    -- Strict: exact match only (case-insensitive)
                    if nameLower == queryLower then
                        -- If multiple exact matches, prefer favorites and higher level
                        local score = 100
                        if favorite then score = score + 5 end
                        if level then score = score + (level / 25) end

                        if score > bestScore then
                            bestScore = score
                            bestMatch = { guid = petGUID, name = displayName }
                        end
                    end
                end
            end
        end

        if bestMatch then
            C_PetJournal.SummonPetByGUID(bestMatch.guid)
            return C3.Types.Create("Text", {
                content = "Summoning: " .. bestMatch.name
            })
        end

        return ctx:Error("Pet not found: " .. query)
    end
})

-------------------------------------------------------------------------------
-- dismisspet - Dismiss current companion pet
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "dismisspet",
    description = "Dismiss current companion pet",
    category = "action",
    acceptsInput = false,
    execute = function(ctx)
        if not C_PetJournal then
            return ctx:Error("Pet journal not available in this game version")
        end

        local summonedGUID = GetSummonedPetGUID()
        if summonedGUID then
            local petObj = BuildPetObject(summonedGUID)
            local petName = petObj and (petObj.name or petObj.speciesName) or "pet"
            C_PetJournal.SummonPetByGUID(summonedGUID) -- Toggle dismisses
            return C3.Types.Create("Text", {
                content = "Dismissed: " .. petName
            })
        else
            return ctx:Error("No pet is currently summoned")
        end
    end
})

return true
