--[[
    Commander 3.0 - Alias Commands

    Provides command aliases with full intellisense support.
    Commands: alias, unalias, aliases
]]

local ADDON_NAME, C3 = ...

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

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

C3.Types:Register("Alias", {
    description = "Command alias mapping",
    source = "aliases",
    properties = {
        { "name", "Alias name" },
        { "command", "Command the alias expands to" },
    },
})

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

C3.Formatter:Register("Alias", {
    format = function(obj)
        local name = Colorize(obj.name or "?", "accent.primary")
        local arrow = Colorize("=", "status.dim")
        local command = obj.command or ""
        return string.format("%s %s %s", name, arrow, command)
    end,
    list = function(obj)
        return string.format("%s = %s", obj.name or "?", obj.command or "")
    end,
    compact = function(obj)
        return obj.name or "?"
    end
})

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

-- AliasBody Provider: Provides command + argument suggestions for alias definition
-- When user types "alias m mount Mim", this provider receives "mount Mim" and
-- delegates to the mount command's provider
C3.Intellisense:RegisterProvider("AliasBody", function(partial, ctx)
    -- Parse partial to extract words
    local words = {}
    for word in partial:gmatch("%S+") do
        words[#words + 1] = word
    end

    local isTypingWord = partial:match("%S$") ~= nil

    -- Case 1: No words yet or typing first word - suggest commands
    if #words == 0 or (#words == 1 and isTypingWord) then
        local cmdPartial = words[1] or ""
        -- Get command suggestions, excluding query operators (those only make sense after pipe)
        local allSuggestions = C3.Command:Autocomplete(cmdPartial)
        local suggestions = {}
        for _, s in ipairs(allSuggestions) do
            if s.category ~= "query" then
                suggestions[#suggestions + 1] = s
            end
        end
        return suggestions
    end

    -- Case 2: Have a complete first word - check if it's a valid command
    local cmdName = words[1]:lower()
    local cmdDef = C3.Command:Get(cmdName)

    if not cmdDef then
        -- Not a valid command yet, but user finished typing first word
        -- Still suggest commands that match
        return C3.Command:Autocomplete(words[1])
    end

    -- Case 3: Valid command - delegate to its argument provider
    -- Build the "partial arg" from words after command name
    local argWords = {}
    for i = 2, #words do
        argWords[#argWords + 1] = words[i]
    end
    local argPartial = table.concat(argWords, " ")

    -- Most Commander commands use a single provider for arg 1 that handles
    -- multi-word names (like "mount Mimiron's Head")
    local argDef = cmdDef.args and cmdDef.args[1]
    if argDef and argDef.provider then
        return argDef.provider(argPartial, ctx)
    end

    return {}
end)

-- Aliases Provider: Suggests existing aliases (for unalias command)
C3.Intellisense:RegisterProvider("Aliases", function(partial, ctx)
    local results = {}
    local session = C3:GetSession()

    if not session or not session.aliases then
        return results
    end

    for name, command in pairs(session.aliases) do
        if C3.Intellisense.hasMultiWordMatch(partial, name) then
            results[#results + 1] = {
                text = name,
                description = command,
            }
        end
    end

    return C3.Intellisense.fuzzyFilter(results, partial,
        function(r) return r.text end, 10)
end)

-------------------------------------------------------------------------------
-- alias - Create or list command aliases
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "alias",
    description = "Create or list command aliases",
    category = "utility",
    acceptsInput = false,
    args = {
        { name = "name", required = false, description = "Alias name" },
        { name = "command", required = false, varargs = true,
          description = "Command to alias", provider = "AliasBody" }
    },
    execute = function(ctx)
        local name = ctx:GetArg(1)
        local session = ctx.session

        -- No args: list all aliases
        if not name then
            local aliases = session:GetAliases()
            if not next(aliases) then
                return ctx:Text(Colorize("No aliases defined", "status.dim"))
            end
            local lines = {}
            for n, cmd in pairs(aliases) do
                local aliasName = Colorize(n, "accent.primary")
                local arrow = Colorize("=", "status.dim")
                lines[#lines + 1] = string.format("%s %s %s", aliasName, arrow, cmd)
            end
            table.sort(lines)
            return ctx:Text(table.concat(lines, "\n"))
        end

        -- One arg: show specific alias
        local args = ctx:GetArgs()
        if #args == 1 then
            local command = session:GetAlias(name)
            if command then
                local aliasName = Colorize(name, "accent.primary")
                local arrow = Colorize("=", "status.dim")
                return ctx:Text(string.format("%s %s %s", aliasName, arrow, command))
            end
            return ctx:Error("No such alias: " .. name)
        end

        -- Two+ args: create alias
        local cmdParts = {}
        for i = 2, #args do
            cmdParts[#cmdParts + 1] = args[i]
        end
        local command = table.concat(cmdParts, " ")

        session:SetAlias(name, command)

        local aliasName = Colorize(name, "accent.primary")
        local arrow = Colorize("=", "status.dim")
        return ctx:Text(string.format("Alias set: %s %s %s", aliasName, arrow, command))
    end
})

-------------------------------------------------------------------------------
-- unalias - Remove a command alias
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "unalias",
    description = "Remove a command alias",
    category = "utility",
    acceptsInput = false,
    args = {
        { name = "name", required = true, description = "Alias to remove",
          provider = "Aliases" }
    },
    execute = function(ctx)
        local name = ctx:GetArg(1)
        local session = ctx.session

        if session:GetAlias(name) then
            session:RemoveAlias(name)
            return ctx:Text("Removed alias: " .. Colorize(name, "accent.primary"))
        end
        return ctx:Error("No such alias: " .. name)
    end
})

-------------------------------------------------------------------------------
-- aliases - List all aliases with pipeline support
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "aliases",
    description = "List all command aliases",
    category = "utility",
    producesType = "Alias",
    acceptsInput = false,
    execute = function(ctx)
        return ctx:Produce(function(emit)
            local session = ctx.session
            if not session or not session.aliases then
                return
            end

            for name, command in pairs(session.aliases) do
                emit(C3.Types.Create("Alias", {
                    name = name,
                    command = command,
                }))
            end
        end)
    end
})

return true
