--[[
    Commander 3.0 - Transmog Data Source

    Provides access to transmog outfits and situations.
    Commands: outfits, outfit, situations, situation

    Uses C_TransmogCollection API (Retail)
    Uses C_TransmogOutfitInfo API (Midnight 12.0+)
]]

local ADDON_NAME, C3 = ...

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

-------------------------------------------------------------------------------
-- Type Registration
-------------------------------------------------------------------------------

C3.Types:Register("TransmogOutfit", {
    description = "Saved transmog outfit",
    source = "outfits",
    properties = {
        { "id", "Outfit ID number" },
        { "name", "Outfit name" },
        { "icon", "Icon texture ID" },
        { "isActive", "true if currently applied" },
    },
})

C3.Types:Register("TransmogSituation", {
    description = "Automatic outfit switching situation",
    source = "situations",
    properties = {
        { "category", "Situation category" },
        { "categoryID", "Category identifier" },
        { "option", "Specific situation option" },
        { "optionID", "Option identifier" },
        { "outfitID", "Linked outfit ID" },
        { "outfitName", "Linked outfit name" },
        { "isEnabled", "true if outfit assigned" },
    },
})

-------------------------------------------------------------------------------
-- Formatter Registration
-------------------------------------------------------------------------------

C3.Formatter:Register("TransmogOutfit", {
    format = function(obj)
        local prefix = ""
        if obj.isActive then
            prefix = Colorize("*", "accent.success") .. " "
        end

        local icon = obj.icon and (C3.Formatter:Icon(obj.icon) .. " ") or ""
        local name = obj.name or "Unknown Outfit"

        return prefix .. icon .. name
    end,
    list = function(obj)
        local active = obj.isActive and "*" or " "
        return string.format("%s %s", active, obj.name or "?")
    end,
    compact = function(obj)
        return obj.name or "?"
    end
})

C3.Formatter:Register("TransmogSituation", {
    format = function(obj)
        local status = obj.isEnabled
            and Colorize("+", "accent.success")
            or Colorize("-", "status.dim")

        local category = Colorize("[" .. (obj.category or "?") .. "]", "accent.info")
        local option = obj.option or "Unknown"

        local outfit = ""
        if obj.outfitName then
            outfit = " -> " .. Colorize(obj.outfitName, "accent.primary")
        end

        return status .. " " .. category .. " " .. option .. outfit
    end,
    list = function(obj)
        return string.format("%s %s: %s",
            obj.isEnabled and "+" or "-",
            obj.option or "?",
            obj.outfitName or "(none)")
    end,
    compact = function(obj)
        return obj.option or "?"
    end
})

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

local MAX_SUGGESTIONS = C3.Constants.MAX_SUGGESTIONS

---Check if transmog outfit API is available
---@return boolean
local function hasOutfitAPI()
    -- Midnight API (12.0+)
    if C_TransmogOutfitInfo and C_TransmogOutfitInfo.GetOutfitsInfo then
        return true
    end
    -- Legacy API
    if C_TransmogCollection and C_TransmogCollection.GetOutfits then
        return true
    end
    return false
end

---Get all saved outfits (abstracts API differences)
---@return table[] Array of {id, name, icon} tables
local function getAllOutfits()
    local outfits = {}

    -- Try Midnight API first (C_TransmogOutfitInfo.GetOutfitsInfo)
    if C_TransmogOutfitInfo and C_TransmogOutfitInfo.GetOutfitsInfo then
        local outfitInfos = C_TransmogOutfitInfo.GetOutfitsInfo()
        for _, info in ipairs(outfitInfos or {}) do
            outfits[#outfits + 1] = {
                id = info.outfitID or info.id,
                name = info.name,
                icon = info.icon,
            }
        end
        return outfits
    end

    -- Fall back to legacy API (C_TransmogCollection.GetOutfits)
    if C_TransmogCollection and C_TransmogCollection.GetOutfits then
        local outfitIDs = C_TransmogCollection.GetOutfits()
        for _, outfitID in ipairs(outfitIDs or {}) do
            local name, icon = C_TransmogCollection.GetOutfitInfo(outfitID)
            if name then
                outfits[#outfits + 1] = {
                    id = outfitID,
                    name = name,
                    icon = icon,
                }
            end
        end
        return outfits
    end

    return outfits
end

---Get outfit info by ID (abstracts API differences)
---@param outfitID number
---@return string|nil name
---@return number|nil icon
local function getOutfitInfo(outfitID)
    -- Try Midnight API
    if C_TransmogOutfitInfo and C_TransmogOutfitInfo.GetOutfitInfo then
        local info = C_TransmogOutfitInfo.GetOutfitInfo(outfitID)
        if info then
            return info.name, info.icon
        end
    end

    -- Fall back to legacy
    if C_TransmogCollection and C_TransmogCollection.GetOutfitInfo then
        return C_TransmogCollection.GetOutfitInfo(outfitID)
    end

    return nil, nil
end

---Find outfit by exact name (case-insensitive)
---@param query string Outfit name to find
---@return number|nil outfitID
---@return string|nil outfitName
---@return number|nil outfitIcon
local function findOutfitByName(query)
    if not hasOutfitAPI() then
        return nil, nil, nil
    end

    local queryLower = query:lower()
    local outfits = getAllOutfits()

    for _, outfit in ipairs(outfits) do
        local name = outfit.name
        if name and name:lower() == queryLower then
            return outfit.id, outfit.name, outfit.icon
        end
    end

    return nil, nil, nil
end

---Find an outfit on the player's action bars by scanning all slots
---@param outfitID number
---@return number|nil slotID Action bar slot ID
local function findOutfitOnActionBar(outfitID)
    for slot = 1, 180 do repeat
        if not HasAction(slot) then break end
        local actionType, actionID = GetActionInfo(slot)
        if actionType == "outfit" and actionID == outfitID then
            return slot
        end
    until true end
    return nil
end

---Apply a transmog outfit
---@param outfitID number
---@param outfitName string
---@param ctx table Context
---@return table Result
local function applyOutfit(outfitID, outfitName, ctx)
    local _, icon = getOutfitInfo(outfitID)
    local iconStr = icon and (C3.Formatter:Icon(icon) .. " ") or ""

    -- Check if this is the active outfit
    local isActive = false
    if C_TransmogOutfitInfo and C_TransmogOutfitInfo.GetActiveOutfitID then
        isActive = C_TransmogOutfitInfo.GetActiveOutfitID() == outfitID
    end

    if isActive then
        return C3.Types.Create("Text", {
            content = iconStr .. Colorize(outfitName, "accent.primary") ..
                " " .. Colorize("(currently active)", "accent.success")
        })
    end

    -- ChangeDisplayedOutfit is protected (AllowedWhenUntainted) - addons can't
    -- call it directly. The secure action system pre-arms outfit commands via
    -- UseAction on the action bar slot when the outfit is on a bar.
    -- If we reach here, the pre-arm didn't fire (outfit not on a bar).
    local slotID = findOutfitOnActionBar(outfitID)
    if slotID then
        -- Pre-arm should have handled this - give instructions
        return C3.Types.Create("Text", {
            content = "Type " .. Colorize("outfit " .. outfitName, "accent.primary") ..
                " and press Enter to apply"
        })
    end

    return C3.Types.Create("Text", {
        content = iconStr .. Colorize(outfitName, "accent.primary") ..
            "\n" .. Colorize("Place this outfit on an action bar to apply via Commander", "status.dim")
    })
end

---Check if the new situations API is available
---@return boolean
local function hasSituationsAPI()
    return C_TransmogOutfitInfo
        and C_TransmogOutfitInfo.GetUISituationCategoriesAndOptions ~= nil
end

-------------------------------------------------------------------------------
-- Intellisense Providers
-------------------------------------------------------------------------------

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

    local results = {}
    local outfits = getAllOutfits()

    for _, outfit in ipairs(outfits) do
        if outfit.name and fuzzyMatch(partial, outfit.name) then
            results[#results + 1] = {
                text = outfit.name,
                description = "Transmog Outfit",
                icon = outfit.icon,
            }
            if #results >= MAX_SUGGESTIONS then break end
        end
    end

    return results
end)

C3.Intellisense:RegisterProvider("TransmogSituations", function(partial, ctx)
    local results = {}
    local partialLower = partial and partial:lower() or ""

    -- Triage: special commands only when typing matches
    if partialLower ~= "" and ("enable"):find(partialLower, 1, true) == 1 then
        results[#results + 1] = {
            text = "enable",
            description = "Enable outfit situations",
            priority = 100,
        }
    end

    if partialLower ~= "" and ("disable"):find(partialLower, 1, true) == 1 then
        results[#results + 1] = {
            text = "disable",
            description = "Disable outfit situations",
            priority = 90,
        }
    end

    if partialLower ~= "" and ("reset"):find(partialLower, 1, true) == 1 then
        results[#results + 1] = {
            text = "reset",
            description = "Reset all situations to defaults",
            priority = 80,
        }
    end

    if partialLower ~= "" and ("commit"):find(partialLower, 1, true) == 1 then
        results[#results + 1] = {
            text = "commit",
            description = "Save pending situation changes",
            priority = 70,
        }
    end

    if partialLower ~= "" and ("status"):find(partialLower, 1, true) == 1 then
        results[#results + 1] = {
            text = "status",
            description = "Show situations enabled/disabled status",
            priority = 60,
        }
    end

    return results
end)

-------------------------------------------------------------------------------
-- Commands: outfits
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "outfits",
    description = "List saved transmog outfits",
    category = "data",
    producesType = "TransmogOutfit",
    acceptsInput = false,
    execute = function(ctx)
        if not hasOutfitAPI() then
            return ctx:Error("Transmog not available in this game version")
        end

        local outfits = getAllOutfits()

        if #outfits == 0 then
            return C3.Types.Create("Text", {
                content = Colorize("No transmog outfits saved", "status.dim")
            })
        end

        -- Get active outfit for comparison (Midnight API)
        local activeID = nil
        if C_TransmogOutfitInfo and C_TransmogOutfitInfo.GetActiveOutfitID then
            activeID = C_TransmogOutfitInfo.GetActiveOutfitID()
        end

        local results = {}
        for i, outfit in ipairs(outfits) do repeat
            if i % 50 == 0 then
                coroutine.yield()
            end

            if not outfit.name then break end

            results[#results + 1] = C3.Types.Create("TransmogOutfit", {
                id = outfit.id,
                name = outfit.name,
                icon = outfit.icon,
                isActive = (activeID and outfit.id == activeID),
            })
        until true end

        return results
    end
})

-------------------------------------------------------------------------------
-- Commands: outfit
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "outfit",
    description = "Show or apply a transmog outfit",
    category = "action",
    args = {
        { name = "name", required = false, description = "Outfit name",
          provider = "TransmogOutfits" }
    },
    execute = function(ctx)
        if not hasOutfitAPI() then
            return ctx:Error("Transmog not available")
        end

        -- Handle pipeline input
        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 outfitObj = items[1]
                if outfitObj._type == "TransmogOutfit" then
                    return applyOutfit(outfitObj.id, outfitObj.name, ctx)
                end
            end
        end

        local query = table.concat(ctx:GetArgs(), " ")
        if not query or query == "" then
            -- No argument: show current outfit info
            if C_TransmogOutfitInfo and C_TransmogOutfitInfo.GetActiveOutfitID then
                local activeID = C_TransmogOutfitInfo.GetActiveOutfitID()
                if activeID then
                    local name, icon = getOutfitInfo(activeID)
                    local iconStr = icon and (C3.Formatter:Icon(icon) .. " ") or ""
                    return C3.Types.Create("Text", {
                        content = "Current outfit: " .. iconStr ..
                            Colorize(name or "Unknown", "accent.primary")
                    })
                end
            end
            return C3.Types.Create("Text", {
                content = "Usage: " .. Colorize("outfit <name>", "accent.primary")
            })
        end

        -- Find outfit by name
        local outfitID, outfitName = findOutfitByName(query)
        if not outfitID then
            return ctx:Error("Outfit not found: " .. query)
        end

        return applyOutfit(outfitID, outfitName, ctx)
    end
})

-------------------------------------------------------------------------------
-- Commands: situations (Midnight only)
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "situations",
    description = "List transmog outfit situations (Midnight)",
    category = "data",
    producesType = "TransmogSituation",
    acceptsInput = false,
    flags = {
        enabled = { short = "e", description = "Show only enabled situations" },
    },
    execute = function(ctx)
        -- Check API availability
        if not hasSituationsAPI() then
            return ctx:Error("Transmog Situations require WoW 12.0 (Midnight) or later")
        end

        -- Check if feature is enabled
        local enabled = C_TransmogOutfitInfo.GetOutfitSituationsEnabled()
        if not enabled then
            return C3.Types.Create("Text", {
                content = "Outfit situations are " .. Colorize("disabled", "status.dim") ..
                    ". Use '" .. Colorize("situation enable", "accent.primary") .. "' to enable."
            })
        end

        local showEnabledOnly = ctx:HasFlag("enabled") or ctx:HasFlag("e")

        local categories = C_TransmogOutfitInfo.GetUISituationCategoriesAndOptions()
        local results = {}

        for _, category in ipairs(categories or {}) do
            for _, option in ipairs(category.options or {}) do repeat
                -- Get current situation state
                local situationData = {
                    categoryID = category.categoryID or category.id,
                    optionID = option.optionID or option.id
                }
                local state = C_TransmogOutfitInfo.GetOutfitSituation(situationData)

                local isEnabled = state and state.outfitID ~= nil

                -- Apply filter
                if showEnabledOnly and not isEnabled then break end

                local outfitName = nil
                if state and state.outfitID then
                    outfitName = getOutfitInfo(state.outfitID)
                end

                results[#results + 1] = C3.Types.Create("TransmogSituation", {
                    category = category.name or category.categoryID or "?",
                    categoryID = category.categoryID or category.id,
                    option = option.name or option.optionID or "?",
                    optionID = option.optionID or option.id,
                    outfitID = state and state.outfitID,
                    outfitName = outfitName,
                    isEnabled = isEnabled,
                })
            until true end
        end

        if #results == 0 then
            if showEnabledOnly then
                return C3.Types.Create("Text", {
                    content = Colorize("No situations have outfits assigned", "status.dim")
                })
            end
            return C3.Types.Create("Text", {
                content = Colorize("No situations available", "status.dim")
            })
        end

        return results
    end
})

-------------------------------------------------------------------------------
-- Commands: situation (Midnight only)
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "situation",
    description = "Manage transmog outfit situations (Midnight)",
    category = "action",
    args = {
        { name = "action", required = true, description = "enable, disable, reset, commit, or status",
          provider = "TransmogSituations" }
    },
    execute = function(ctx)
        if not C_TransmogOutfitInfo
           or not C_TransmogOutfitInfo.GetOutfitSituationsEnabled then
            return ctx:Error("Transmog Situations require WoW 12.0 (Midnight) or later")
        end

        local action = ctx:GetArg(1)
        if not action then
            return ctx:Error("Usage: situation <enable|disable|reset|commit|status>")
        end

        local actionLower = action:lower()

        if actionLower == "enable" then
            C_TransmogOutfitInfo.SetOutfitSituationsEnabled(true)
            return C3.Types.Create("Text", {
                content = Colorize("Enabled", "accent.success") .. " outfit situations"
            })

        elseif actionLower == "disable" then
            C_TransmogOutfitInfo.SetOutfitSituationsEnabled(false)
            return C3.Types.Create("Text", {
                content = Colorize("Disabled", "accent.warning") .. " outfit situations"
            })

        elseif actionLower == "reset" then
            C_TransmogOutfitInfo.ResetOutfitSituations()
            return C3.Types.Create("Text", {
                content = "Reset all outfit situations to defaults"
            })

        elseif actionLower == "commit" or actionLower == "save" then
            C_TransmogOutfitInfo.CommitPendingSituations()
            return C3.Types.Create("Text", {
                content = Colorize("Saved", "accent.success") .. " pending situation changes"
            })

        elseif actionLower == "status" then
            local enabled = C_TransmogOutfitInfo.GetOutfitSituationsEnabled()
            local status = enabled
                and Colorize("enabled", "accent.success")
                or Colorize("disabled", "status.dim")
            return C3.Types.Create("Text", {
                content = "Outfit situations: " .. status
            })

        else
            return ctx:Error("Unknown action: " .. action ..
                ". Use: enable, disable, reset, commit, or status")
        end
    end
})

-------------------------------------------------------------------------------
-- Exports for SecureAction system
-------------------------------------------------------------------------------

C3.Transmog = {
    FindOutfitByName = findOutfitByName,
    FindOutfitOnActionBar = findOutfitOnActionBar,
}

return true
