--[[
    Commander 3.0 - API Documentation Browser

    Browse WoW API documentation using the APIDocumentation global.
    Similar to WoW's built-in /api command but integrated with Commander3's
    pipeline system and typed objects.

    Usage:
        api                                     - Show help
        api system                              - List all API systems
        api search <pattern>                    - Search all API for pattern
        api <system>                            - Show system info
        api <system> list                       - List all API in system
        api <system> search <pattern>           - Search within system
        api <Namespace.Function> [-d]           - Lookup specific function
        api <EVENT_NAME> [-d]                   - Lookup specific event

    Features:
        - Rich type-annotated function signatures
        - Detailed documentation view with -d flag
        - Enum/structure member browsing
        - Event payload type information
        - Intellisense with function signatures
]]

local ADDON_NAME, C3 = ...

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

-- Constants
local YIELD_INTERVAL = C3.Constants.YIELD_INTERVAL
local MAX_SUGGESTIONS = C3.Constants.MAX_SUGGESTIONS

-------------------------------------------------------------------------------
-- Type/Signature Formatting Helpers
-------------------------------------------------------------------------------

---Format a type field (argument or return value) with type annotation
---@param field table Field with Name, Type, InnerType, Nilable
---@return string
local function formatType(field)
    local typeStr = field.Type or "any"
    -- Convert "bool" to "boolean" for clarity
    if typeStr == "bool" then
        typeStr = "boolean"
    end
    if field.InnerType then
        typeStr = typeStr .. "<" .. field.InnerType .. ">"
    end
    if field.Nilable then
        typeStr = typeStr .. "?"
    end
    return typeStr
end

---Build a rich type-annotated signature for a function
---@param func table API function object
---@return string signature Like "(mountID:number) -> name, spellID, icon"
local function buildRichSignature(func)
    local args = {}
    for _, arg in ipairs(func.Arguments or {}) do
        local argStr = arg.Name or "arg"
        if arg.Type then
            argStr = argStr .. ":" .. formatType(arg)
        elseif arg.Nilable then
            argStr = argStr .. "?"
        end
        args[#args + 1] = argStr
    end

    local returns = {}
    for _, ret in ipairs(func.Returns or {}) do
        local retStr = ret.Name or "value"
        if ret.Nilable then
            retStr = retStr .. "?"
        end
        returns[#returns + 1] = retStr
    end

    local sig = "(" .. table.concat(args, ", ") .. ")"
    if #returns > 0 then
        -- Truncate if too many returns (show first 3 + ...)
        if #returns > 3 then
            sig = sig .. " -> " .. table.concat({returns[1], returns[2], returns[3]}, ", ") .. ", ..."
        else
            sig = sig .. " -> " .. table.concat(returns, ", ")
        end
    end
    return sig
end

---Build payload string for an event
---@param payload table[] Event payload fields
---@return string
local function buildPayloadString(payload)
    if not payload or #payload == 0 then
        return ""
    end
    local parts = {}
    for _, arg in ipairs(payload) do
        local argStr = arg.Name or "arg"
        if arg.Type then
            argStr = argStr .. ":" .. formatType(arg)
        elseif arg.Nilable then
            argStr = argStr .. "?"
        end
        parts[#parts + 1] = argStr
    end
    -- Truncate if too many
    if #parts > 4 then
        return table.concat({parts[1], parts[2], parts[3], parts[4]}, ", ") .. ", ..."
    end
    return table.concat(parts, ", ")
end

-------------------------------------------------------------------------------
-- Type Definitions
-------------------------------------------------------------------------------

-- APISystem type - represents a WoW API system (e.g., C_MountJournal)
C3.Types:Register("APISystem", {
    description = "WoW API system documentation",
    source = "api systems",
    properties = {
        { "name", "System name (e.g., MountJournal)" },
        { "namespace", "Namespace for functions (e.g., C_MountJournal)" },
        { "functionCount", "Number of functions in this system" },
        { "eventCount", "Number of events in this system" },
        { "tableCount", "Number of tables/enums in this system" },
    },
    format = function(self)
        -- Use namespace if available, otherwise fall back to name
        local displayName = (self.namespace and self.namespace ~= "") and self.namespace or self.name
        local name = Colorize(displayName or "Unknown", "accent.primary")
        local counts = {}
        if self.functionCount and self.functionCount > 0 then
            counts[#counts + 1] = Colorize(self.functionCount .. " funcs", "accent.success")
        end
        if self.eventCount and self.eventCount > 0 then
            counts[#counts + 1] = Colorize(self.eventCount .. " events", "accent.warning")
        end
        if self.tableCount and self.tableCount > 0 then
            counts[#counts + 1] = Colorize(self.tableCount .. " tables", "accent.info")
        end
        local countsStr = #counts > 0 and (" [" .. table.concat(counts, ", ") .. "]") or ""
        return name .. countsStr
    end,
})

-- APIFunction type - represents a WoW API function
C3.Types:Register("APIFunction", {
    description = "WoW API function documentation",
    source = "api",
    properties = {
        { "name", "Function name" },
        { "system", "Parent system name" },
        { "fullName", "Full function name with namespace" },
        { "signature", "Function signature with arguments" },
        { "arguments", "Function arguments description" },
        { "returns", "Return values description" },
        { "type", "Always 'function'" },
    },
    format = function(self)
        local icon = Colorize("f", "accent.success")
        local name = Colorize(self.fullName or self.name or "Unknown", "accent.primary")
        local sig = ""
        if self.signature then
            sig = Colorize(" " .. self.signature, "status.dim")
        end
        return icon .. " " .. name .. sig
    end,
})

-- APIEvent type - represents a WoW API event
C3.Types:Register("APIEvent", {
    description = "WoW API event documentation",
    source = "api",
    properties = {
        { "name", "Event name" },
        { "literalName", "Actual event name fired" },
        { "system", "Parent system name" },
        { "payload", "Event payload with types" },
        { "payloadCount", "Number of payload arguments" },
        { "type", "Always 'event'" },
    },
    format = function(self)
        local icon = Colorize("e", "accent.warning")
        local name = Colorize(self.name or "Unknown", "accent.primary")
        local payload = ""
        if self.payload and self.payload ~= "" then
            payload = Colorize(" (" .. self.payload .. ")", "status.dim")
        end
        return icon .. " " .. name .. payload
    end,
})

-- APITable type - represents a WoW API table/enum
C3.Types:Register("APITable", {
    description = "WoW API table/enum documentation",
    source = "api",
    properties = {
        { "name", "Table/Enum name" },
        { "system", "Parent system name" },
        { "tableType", "Type: Enumeration, Structure, Constants" },
        { "memberCount", "Number of fields or values" },
        { "type", "Always 'table'" },
    },
    format = function(self)
        local icon = Colorize("t", "accent.info")
        local name = Colorize(self.name or "Unknown", "accent.primary")
        local meta = ""
        if self.tableType then
            meta = " [" .. self.tableType
            if self.memberCount and self.memberCount > 0 then
                meta = meta .. ", " .. self.memberCount .. " members"
            end
            meta = meta .. "]"
            meta = Colorize(meta, "status.dim")
        end
        return icon .. " " .. name .. meta
    end,
})

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

---Load APIDocumentation addon and generated data if not already loaded
---@return boolean success
local function loadAPIDocumentation()
    if APIDocumentation and APIDocumentation.systems and next(APIDocumentation.systems) then
        return true  -- Already loaded with data
    end

    -- Try to load the main addon
    local loadFn = (C_AddOns and C_AddOns.LoadAddOn) or LoadAddOn
    if loadFn then
        loadFn("Blizzard_APIDocumentation")
        -- Also load the generated documentation data
        loadFn("Blizzard_APIDocumentationGenerated")
    end

    -- Check if data was populated
    return APIDocumentation ~= nil and APIDocumentation.systems ~= nil and next(APIDocumentation.systems) ~= nil
end

---Check if APIDocumentation is available (attempts to load if not)
---@return boolean
local function hasAPIDocumentation()
    if APIDocumentation then
        return true
    end
    return loadAPIDocumentation()
end

---Get all API systems
---@return table[] systems
local function getAllSystems()
    if not hasAPIDocumentation() then return {} end

    local systems = {}

    -- Try different ways to access systems
    local systemsTable = APIDocumentation.systems

    -- If systems is nil, try to find it another way
    if not systemsTable then
        -- Check if there's a method to get systems
        if APIDocumentation.GetAllSystems then
            systemsTable = APIDocumentation:GetAllSystems()
        end
    end

    if systemsTable then
        for _, system in pairs(systemsTable) do
            systems[#systems + 1] = system
        end
    end

    -- Sort by name
    table.sort(systems, function(a, b)
        return (a.Name or "") < (b.Name or "")
    end)

    return systems
end

---Get system namespace for function calls
---@param system table API system object
---@return string namespace
local function getSystemNamespace(system)
    if system.Namespace then
        return system.Namespace
    end
    -- Try GetNamespaceName method if available
    if system.GetNamespaceName then
        local ok, name = pcall(system.GetNamespaceName, system)
        if ok and name then
            return name
        end
    end
    return system.Name or ""
end

---Convert an API function to typed object
---@param item table API function object
---@param system table? Parent system
---@return table
local function functionToObject(item, system)
    local namespace = system and getSystemNamespace(system) or ""
    local fullName = namespace ~= "" and (namespace .. "." .. item.Name) or item.Name

    -- Use rich signature with type annotations
    local signature = buildRichSignature(item)

    -- Build simple returns list for backwards compat
    local returnParts = {}
    if item.Returns then
        for _, ret in ipairs(item.Returns) do
            returnParts[#returnParts + 1] = ret.Name or "value"
        end
    end
    local returns = #returnParts > 0 and table.concat(returnParts, ", ") or ""

    return C3.Types.Create("APIFunction", {
        name = item.Name,
        system = system and system.Name,
        fullName = fullName,
        signature = signature,
        arguments = item.Arguments and #item.Arguments or 0,
        returns = returns,
        type = "function",
        -- Store raw item for detailed view
        _raw = item,
        _system = system,
    })
end

---Convert an API event to typed object
---@param item table API event object
---@param system table? Parent system
---@return table
local function eventToObject(item, system)
    -- Build rich payload string with types
    local payload = buildPayloadString(item.Payload)

    return C3.Types.Create("APIEvent", {
        name = item.Name,
        literalName = item.LiteralName,
        system = system and system.Name,
        payload = payload,
        payloadCount = item.Payload and #item.Payload or 0,
        type = "event",
        _raw = item,
        _system = system,
    })
end

---Convert an API table to typed object
---@param item table API table object
---@param system table? Parent system
---@return table
local function tableToObject(item, system)
    -- Count fields or values depending on type
    local memberCount = 0
    if item.Type == "Structure" and item.Fields then
        memberCount = #item.Fields
    elseif (item.Type == "Enumeration" or item.Type == "Constants") and item.Values then
        memberCount = #item.Values
    end

    return C3.Types.Create("APITable", {
        name = item.Name,
        system = system and system.Name,
        tableType = item.Type or "Table",
        memberCount = memberCount,
        type = "table",
        _raw = item,
        _system = system,
    })
end

---Convert a system to typed object
---@param system table API system object
---@return table
local function systemToObject(system)
    return C3.Types.Create("APISystem", {
        name = system.Name,
        namespace = getSystemNamespace(system),
        functionCount = system.Functions and #system.Functions or 0,
        eventCount = system.Events and #system.Events or 0,
        tableCount = system.Tables and #system.Tables or 0,
        _raw = system,
    })
end

-------------------------------------------------------------------------------
-- Detailed View Functions (for -d flag)
-------------------------------------------------------------------------------

---Show detailed view of a function with all documentation
---@param func table API function object
---@param system table? Parent system
---@return string
local function showDetailedFunction(func, system)
    local lines = {}
    local namespace = system and getSystemNamespace(system) or ""
    local fullName = namespace ~= "" and (namespace .. "." .. func.Name) or func.Name

    -- Header
    lines[#lines + 1] = Colorize(fullName, "accent.primary")
    lines[#lines + 1] = ""

    -- Documentation (if available)
    if func.Documentation and #func.Documentation > 0 then
        for _, doc in ipairs(func.Documentation) do
            lines[#lines + 1] = Colorize(doc, "status.dim")
        end
        lines[#lines + 1] = ""
    end

    -- Arguments
    if func.Arguments and #func.Arguments > 0 then
        lines[#lines + 1] = Colorize("Arguments:", "accent.warning")
        for _, arg in ipairs(func.Arguments) do
            local typeStr = formatType(arg)
            local defaultStr = ""
            if arg.Default ~= nil then
                defaultStr = Colorize(" = " .. tostring(arg.Default), "status.dim")
            end
            lines[#lines + 1] = "  " .. Colorize(arg.Name or "arg", "accent.success") .. " : " .. typeStr .. defaultStr
            if arg.Documentation and #arg.Documentation > 0 then
                for _, doc in ipairs(arg.Documentation) do
                    lines[#lines + 1] = "    " .. Colorize(doc, "status.dim")
                end
            end
        end
        lines[#lines + 1] = ""
    else
        lines[#lines + 1] = Colorize("Arguments:", "accent.warning") .. " " .. Colorize("(none)", "status.dim")
        lines[#lines + 1] = ""
    end

    -- Returns
    if func.Returns and #func.Returns > 0 then
        lines[#lines + 1] = Colorize("Returns:", "accent.warning")
        for _, ret in ipairs(func.Returns) do
            local typeStr = formatType(ret)
            lines[#lines + 1] = "  " .. Colorize(ret.Name or "value", "accent.success") .. " : " .. typeStr
            if ret.Documentation and #ret.Documentation > 0 then
                for _, doc in ipairs(ret.Documentation) do
                    lines[#lines + 1] = "    " .. Colorize(doc, "status.dim")
                end
            end
        end
    else
        lines[#lines + 1] = Colorize("Returns:", "accent.warning") .. " " .. Colorize("(none)", "status.dim")
    end

    return table.concat(lines, "\n")
end

---Show detailed view of an event with payload info
---@param event table API event object
---@param system table? Parent system
---@return string
local function showDetailedEvent(event, system)
    local lines = {}

    -- Header with literal name if different
    local displayName = event.Name
    if event.LiteralName and event.LiteralName ~= event.Name then
        displayName = event.Name .. " (" .. Colorize(event.LiteralName, "status.dim") .. ")"
    end
    lines[#lines + 1] = Colorize(displayName, "accent.primary") .. " " .. Colorize("(Event)", "accent.warning")
    lines[#lines + 1] = ""

    -- Documentation
    if event.Documentation and #event.Documentation > 0 then
        for _, doc in ipairs(event.Documentation) do
            lines[#lines + 1] = Colorize(doc, "status.dim")
        end
        lines[#lines + 1] = ""
    end

    -- Payload
    if event.Payload and #event.Payload > 0 then
        lines[#lines + 1] = Colorize("Payload:", "accent.warning")
        for _, arg in ipairs(event.Payload) do
            local typeStr = formatType(arg)
            lines[#lines + 1] = "  " .. Colorize(arg.Name or "arg", "accent.success") .. " : " .. typeStr
            if arg.Documentation and #arg.Documentation > 0 then
                for _, doc in ipairs(arg.Documentation) do
                    lines[#lines + 1] = "    " .. Colorize(doc, "status.dim")
                end
            end
        end
    else
        lines[#lines + 1] = Colorize("Payload:", "accent.warning") .. " " .. Colorize("(no payload)", "status.dim")
    end

    return table.concat(lines, "\n")
end

---Show detailed view of a table/enum/structure
---@param tbl table API table object
---@param system table? Parent system
---@return string
local function showDetailedTable(tbl, system)
    local lines = {}

    -- Header with type
    local typeLabel = tbl.Type or "Table"
    lines[#lines + 1] = Colorize(tbl.Name, "accent.primary") .. " " .. Colorize("(" .. typeLabel .. ")", "accent.info")
    lines[#lines + 1] = ""

    -- Documentation
    if tbl.Documentation and #tbl.Documentation > 0 then
        for _, doc in ipairs(tbl.Documentation) do
            lines[#lines + 1] = Colorize(doc, "status.dim")
        end
        lines[#lines + 1] = ""
    end

    -- Structure fields
    if tbl.Type == "Structure" and tbl.Fields and #tbl.Fields > 0 then
        lines[#lines + 1] = Colorize("Fields:", "accent.warning")
        for _, field in ipairs(tbl.Fields) do
            local typeStr = formatType(field)
            local defaultStr = ""
            if field.Default ~= nil then
                defaultStr = Colorize(" = " .. tostring(field.Default), "status.dim")
            end
            lines[#lines + 1] = "  " .. Colorize(field.Name or "field", "accent.success") .. " : " .. typeStr .. defaultStr
            if field.Documentation and #field.Documentation > 0 then
                for _, doc in ipairs(field.Documentation) do
                    lines[#lines + 1] = "    " .. Colorize(doc, "status.dim")
                end
            end
        end
    end

    -- Enum/Constants values
    if (tbl.Type == "Enumeration" or tbl.Type == "Constants") and tbl.Values and #tbl.Values > 0 then
        lines[#lines + 1] = Colorize("Values:", "accent.warning")
        for _, val in ipairs(tbl.Values) do
            local enumVal = ""
            if val.EnumValue ~= nil then
                enumVal = " = " .. Colorize(tostring(val.EnumValue), "accent.info")
            end
            lines[#lines + 1] = "  " .. Colorize(val.Name or "value", "accent.success") .. enumVal
            if val.Documentation and #val.Documentation > 0 then
                for _, doc in ipairs(val.Documentation) do
                    lines[#lines + 1] = "    " .. Colorize(doc, "status.dim")
                end
            end
        end

        -- Range info for enums
        if tbl.NumValues then
            lines[#lines + 1] = ""
            local minVal = tbl.MinValue or 0
            local maxVal = tbl.MaxValue or (tbl.NumValues - 1)
            lines[#lines + 1] = Colorize(
                string.format("Range: %d - %d (%d values)", minVal, maxVal, tbl.NumValues),
                "status.dim"
            )
        end
    end

    return table.concat(lines, "\n")
end

---Match using Lua pattern (like WoW's /api does)
---@param name string Name to match
---@param pattern string Lua pattern
---@return boolean
local function matchesPattern(name, pattern)
    if not pattern or pattern == "" then
        return true
    end
    -- Try pattern match first
    local ok, result = pcall(string.find, name:lower(), pattern:lower())
    if ok and result then
        return true
    end
    -- Fall back to plain text match
    return name:lower():find(pattern:lower(), 1, true) ~= nil
end

---Find system by name (case-insensitive, matches name or namespace)
---@param systemName string
---@return table?
local function findSystemByName(systemName)
    if not hasAPIDocumentation() then return nil end

    -- Try WoW's built-in function first
    if APIDocumentation.FindSystemByName then
        local system = APIDocumentation:FindSystemByName(systemName)
        if system then return system end
    end

    local nameLower = systemName:lower()
    -- Also try without C_ prefix if provided with it
    local nameWithoutPrefix = nameLower:gsub("^c_", "")

    for _, system in pairs(APIDocumentation.systems or {}) do
        if system.Name then
            local sysNameLower = system.Name:lower()
            local namespace = getSystemNamespace(system)
            local namespaceLower = namespace and namespace:lower() or ""

            -- Match against: name, namespace, or name without C_ prefix
            if sysNameLower == nameLower
                or sysNameLower == nameWithoutPrefix
                or namespaceLower == nameLower then
                return system
            end
        end
    end
    return nil
end

---Find a specific function, event, or table by name within a system
---@param system table API system
---@param itemName string Name of the item (case-insensitive)
---@return table? item The raw API item
---@return string? itemType "function", "event", or "table"
local function findItemInSystem(system, itemName)
    local nameLower = itemName:lower()

    -- Search functions
    if system.Functions then
        for _, func in ipairs(system.Functions) do
            if func.Name and func.Name:lower() == nameLower then
                return func, "function"
            end
        end
    end

    -- Search events
    if system.Events then
        for _, event in ipairs(system.Events) do
            if event.Name and event.Name:lower() == nameLower then
                return event, "event"
            end
            -- Also check LiteralName for events
            if event.LiteralName and event.LiteralName:lower() == nameLower then
                return event, "event"
            end
        end
    end

    -- Search tables
    if system.Tables then
        for _, tbl in ipairs(system.Tables) do
            if tbl.Name and tbl.Name:lower() == nameLower then
                return tbl, "table"
            end
        end
    end

    return nil, nil
end

---Find an API item by its full qualified name (e.g., "C_MountJournal.GetMountInfoByID")
---@param fullName string Full name with namespace
---@return table? item The raw API item
---@return string? itemType "function", "event", or "table"
---@return table? system The parent system
local function findAPIByFullName(fullName)
    if not hasAPIDocumentation() then return nil, nil, nil end

    -- Try to parse "Namespace.ItemName" format
    local namespace, itemName = fullName:match("^(.+)%.([^.]+)$")
    if not namespace then
        -- No dot found - might be searching for a top-level event name
        -- Search through all events for a match
        for _, system in pairs(APIDocumentation.systems or {}) do
            if system.Events then
                for _, event in ipairs(system.Events) do
                    if event.Name and event.Name:lower() == fullName:lower() then
                        return event, "event", system
                    end
                    if event.LiteralName and event.LiteralName:lower() == fullName:lower() then
                        return event, "event", system
                    end
                end
            end
        end
        return nil, nil, nil
    end

    -- Find the system
    local system = findSystemByName(namespace)
    if not system then
        return nil, nil, nil
    end

    -- Find the item within the system
    local item, itemType = findItemInSystem(system, itemName)
    return item, itemType, system
end

---Search all API for a pattern
---@param pattern string Search pattern
---@param typeFilter string? "function", "event", "table", or nil for all
---@return table[] results
local function searchAllAPI(pattern, typeFilter)
    if not APIDocumentation then return {} end

    local results = {}
    local processed = 0

    -- Try using WoW's built-in FindAllAPIMatches first (handles lazy loading)
    if APIDocumentation.FindAllAPIMatches then
        local matches = APIDocumentation:FindAllAPIMatches(pattern)
        if matches then
            -- matches = { systems, functions, events, tables, callbacks }
            if (not typeFilter or typeFilter == "function") and matches.functions then
                for _, func in ipairs(matches.functions) do
                    processed = processed + 1
                    if processed % YIELD_INTERVAL == 0 then
                        coroutine.yield()
                    end
                    results[#results + 1] = functionToObject(func, func.System)
                end
            end

            if (not typeFilter or typeFilter == "event") and matches.events then
                for _, event in ipairs(matches.events) do
                    processed = processed + 1
                    if processed % YIELD_INTERVAL == 0 then
                        coroutine.yield()
                    end
                    results[#results + 1] = eventToObject(event, event.System)
                end
            end

            if (not typeFilter or typeFilter == "table") and matches.tables then
                for _, tbl in ipairs(matches.tables) do
                    processed = processed + 1
                    if processed % YIELD_INTERVAL == 0 then
                        coroutine.yield()
                    end
                    results[#results + 1] = tableToObject(tbl, tbl.System)
                end
            end

            -- Sort results by name
            table.sort(results, function(a, b)
                return (a.name or "") < (b.name or "")
            end)

            return results
        end
    end

    -- Fallback: iterate systems directly
    for _, system in pairs(APIDocumentation.systems or {}) do
        -- Search functions
        if (not typeFilter or typeFilter == "function") and system.Functions then
            for _, func in ipairs(system.Functions) do
                processed = processed + 1
                if processed % YIELD_INTERVAL == 0 then
                    coroutine.yield()
                end
                if matchesPattern(func.Name, pattern) then
                    results[#results + 1] = functionToObject(func, system)
                end
            end
        end

        -- Search events
        if (not typeFilter or typeFilter == "event") and system.Events then
            for _, event in ipairs(system.Events) do
                processed = processed + 1
                if processed % YIELD_INTERVAL == 0 then
                    coroutine.yield()
                end
                if matchesPattern(event.Name, pattern) then
                    results[#results + 1] = eventToObject(event, system)
                end
            end
        end

        -- Search tables
        if (not typeFilter or typeFilter == "table") and system.Tables then
            for _, tbl in ipairs(system.Tables) do
                processed = processed + 1
                if processed % YIELD_INTERVAL == 0 then
                    coroutine.yield()
                end
                if matchesPattern(tbl.Name, pattern) then
                    results[#results + 1] = tableToObject(tbl, system)
                end
            end
        end
    end

    -- Sort results by name
    table.sort(results, function(a, b)
        return (a.name or "") < (b.name or "")
    end)

    return results
end

---Search within a system
---@param system table API system
---@param pattern string Search pattern
---@param typeFilter string? "function", "event", "table", or nil for all
---@return table[] results
local function searchInSystem(system, pattern, typeFilter)
    local results = {}

    -- Search functions
    if (not typeFilter or typeFilter == "function") and system.Functions then
        for _, func in ipairs(system.Functions) do
            if matchesPattern(func.Name, pattern) then
                results[#results + 1] = functionToObject(func, system)
            end
        end
    end

    -- Search events
    if (not typeFilter or typeFilter == "event") and system.Events then
        for _, event in ipairs(system.Events) do
            if matchesPattern(event.Name, pattern) then
                results[#results + 1] = eventToObject(event, system)
            end
        end
    end

    -- Search tables
    if (not typeFilter or typeFilter == "table") and system.Tables then
        for _, tbl in ipairs(system.Tables) do
            if matchesPattern(tbl.Name, pattern) then
                results[#results + 1] = tableToObject(tbl, system)
            end
        end
    end

    -- Sort results by type then name
    table.sort(results, function(a, b)
        if a.type ~= b.type then
            -- functions first, then events, then tables
            local order = { ["function"] = 1, event = 2, table = 3 }
            return (order[a.type] or 9) < (order[b.type] or 9)
        end
        return (a.name or "") < (b.name or "")
    end)

    return results
end

---List all API in a system
---@param system table API system
---@param typeFilter string? "function", "event", "table", or nil for all
---@return table[] results
local function listSystem(system, typeFilter)
    return searchInSystem(system, "", typeFilter)
end

-------------------------------------------------------------------------------
-- Intellisense Provider
-------------------------------------------------------------------------------

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

    local suggestions = {}

    -- Special actions (not system names)
    local specialActions = {
        { text = "system", description = "List all API systems" },
        { text = "search", description = "Search all API" },
    }

    local partialLower = partial and partial:lower() or ""

    -- If partial starts with a completed special action + space, no more suggestions
    -- e.g., "system " or "search mount" means user has moved past the first argument
    if partialLower:match("^system%s") or partialLower:match("^search%s") then
        return {}
    end

    -- Also check for completed system name + space (e.g., "C_MountJournal ")
    if partialLower:match("^c_%w+%s") or partialLower:match("^%w+%s") then
        -- Check if the first word is a valid system name
        local firstWord = partialLower:match("^(%S+)")
        if firstWord and findSystemByName(firstWord) then
            return {}
        end
    end

    -- Check if user is typing a fully qualified name (e.g., "C_MountJournal.Get")
    -- This is detected by the presence of a dot
    if partial and partial:find(".", 1, true) then
        local namespace, memberPartial = partial:match("^(.+)%.(.*)$")
        if namespace then
            local system = findSystemByName(namespace)
            if system then
                -- Show system members with rich signatures
                local memberItems = {}

                -- Functions
                if system.Functions then
                    for _, func in ipairs(system.Functions) do
                        local sig = buildRichSignature(func)
                        memberItems[#memberItems + 1] = {
                            text = namespace .. "." .. func.Name,
                            displayText = func.Name,
                            description = sig,
                            category = "function",
                        }
                    end
                end

                -- Events
                if system.Events then
                    for _, event in ipairs(system.Events) do
                        local payloadStr = buildPayloadString(event.Payload)
                        local desc = payloadStr ~= "" and ("payload: " .. payloadStr) or "no payload"
                        memberItems[#memberItems + 1] = {
                            text = namespace .. "." .. event.Name,
                            displayText = event.Name,
                            description = desc,
                            category = "event",
                        }
                    end
                end

                -- Tables/Enums
                if system.Tables then
                    for _, tbl in ipairs(system.Tables) do
                        memberItems[#memberItems + 1] = {
                            text = namespace .. "." .. tbl.Name,
                            displayText = tbl.Name,
                            description = tbl.Type or "table",
                            category = "table",
                        }
                    end
                end

                -- Filter by member partial
                if memberPartial and memberPartial ~= "" then
                    local fuzzyFilter = C3.Intellisense.fuzzyFilter
                    local filtered = fuzzyFilter(memberItems, memberPartial, function(item)
                        return item.displayText
                    end, MAX_SUGGESTIONS)

                    for _, item in ipairs(filtered) do
                        suggestions[#suggestions + 1] = {
                            text = item.text,
                            description = item.description,
                        }
                    end
                else
                    -- No member partial, show first MAX_SUGGESTIONS
                    for i = 1, math.min(#memberItems, MAX_SUGGESTIONS) do
                        suggestions[#suggestions + 1] = {
                            text = memberItems[i].text,
                            description = memberItems[i].description,
                        }
                    end
                end

                return suggestions
            end
        end
    end

    -- Add matching special actions
    for _, action in ipairs(specialActions) do
        if not partial or partial == "" or fuzzyMatch(partial, action.text) then
            suggestions[#suggestions + 1] = action
        end
    end

    -- Build list of system items for fuzzy filtering
    local systemItems = {}
    local systems = getAllSystems()
    for _, system in ipairs(systems) do
        if system.Name then
            local namespace = getSystemNamespace(system)
            local displayName = (namespace and namespace ~= "") and namespace or system.Name
            local funcCount = system.Functions and #system.Functions or 0
            local eventCount = system.Events and #system.Events or 0

            systemItems[#systemItems + 1] = {
                text = displayName,
                description = string.format("%d funcs, %d events", funcCount, eventCount),
            }
        end
    end

    -- Use fuzzy filter with scoring for proper ranking
    if partial and partial ~= "" then
        local fuzzyFilter = C3.Intellisense.fuzzyFilter
        local filtered = fuzzyFilter(systemItems, partial, function(item)
            return item.text
        end, MAX_SUGGESTIONS - #suggestions)

        for _, item in ipairs(filtered) do
            suggestions[#suggestions + 1] = {
                text = item.text,
                description = item.description,
            }
        end
    else
        -- No partial, return first MAX_SUGGESTIONS alphabetically
        local remaining = MAX_SUGGESTIONS - #suggestions
        for i = 1, math.min(#systemItems, remaining) do
            suggestions[#suggestions + 1] = {
                text = systemItems[i].text,
                description = systemItems[i].description,
            }
        end
    end

    return suggestions
end)

-- Provider for second argument (list, search, or search pattern)
C3.Intellisense:RegisterProvider("APIActions", function(partial, ctx)
    local suggestions = {
        { text = "list", description = "List all API in system" },
        { text = "search", description = "Search within system" },
    }

    if partial and partial ~= "" then
        local filtered = {}
        for _, s in ipairs(suggestions) do
            if fuzzyMatch(partial, s.text) then
                filtered[#filtered + 1] = s
            end
        end
        return filtered
    end

    return suggestions
end)

-------------------------------------------------------------------------------
-- Command Definition
-------------------------------------------------------------------------------

C3.Command:Define({
    name = "api",
    description = "Browse WoW API documentation",
    category = "utility",
    producesType = "APISystem",
    acceptsInput = false,
    args = {
        {
            name = "system_or_action",
            required = false,
            description = "System name, 'systems', or 'search'",
            provider = "APISystems"
        },
        {
            name = "action_or_query",
            required = false,
            description = "'list', 'search', or search pattern",
            provider = "APIActions"
        },
        {
            name = "query",
            required = false,
            vararg = true,
            description = "Search pattern (supports Lua patterns)"
        },
    },
    flags = {
        type = { short = "t", hasValue = true, description = "Filter by type: function, event, table" },
        details = { short = "d", description = "Show detailed output" },
    },
    execute = function(ctx)
        -- Guard: Check if APIDocumentation exists
        if not APIDocumentation then
            -- Try to load it
            loadAPIDocumentation()
        end

        if not APIDocumentation then
            return ctx:Error("APIDocumentation not available (Blizzard_APIDocumentation addon not loaded)")
        end

        -- Note: systems table may be empty but FindAllAPIMatches may still work

        local arg1 = ctx:GetArg(1)
        local arg2 = ctx:GetArg(2)
        local args = ctx:GetArgs()

        -- Get type filter from flag
        local typeFilter = ctx:GetFlag("type") or ctx:GetFlag("t")
        if typeFilter then
            typeFilter = typeFilter:lower()
            if typeFilter ~= "function" and typeFilter ~= "event" and typeFilter ~= "table" then
                return ctx:Error("Invalid type filter: " .. typeFilter .. " (use: function, event, table)")
            end
        end

        -- Check for details flag
        local showDetails = ctx:HasFlag("details") or ctx:HasFlag("d")

        -- No arguments: show usage
        if not arg1 or arg1 == "" then
            local lines = {
                Colorize("API Documentation Browser", "accent.primary"),
                Colorize("Browse WoW's API documentation with rich type information.", "status.dim"),
                "",
                Colorize("Browsing Systems:", "accent.warning"),
                "  api system                    " .. Colorize("List all API systems (C_*, Enum, etc.)", "status.dim"),
                "  api <system>                  " .. Colorize("Show system summary", "status.dim"),
                "  api <system> list             " .. Colorize("List all functions/events/tables in system", "status.dim"),
                "  api <system> <pattern>        " .. Colorize("Search within system", "status.dim"),
                "",
                Colorize("Direct Lookup:", "accent.warning"),
                "  api <System.Function>         " .. Colorize("Look up specific function", "status.dim"),
                "  api <System.Function> -d      " .. Colorize("Show detailed documentation", "status.dim"),
                "  api <EVENT_NAME>              " .. Colorize("Look up event by name", "status.dim"),
                "  api <System.EnumName> -d      " .. Colorize("Show enum values", "status.dim"),
                "",
                Colorize("Searching:", "accent.warning"),
                "  api search <pattern>          " .. Colorize("Search all API (functions, events, tables)", "status.dim"),
                "  api search <pattern> -t func  " .. Colorize("Search only functions", "status.dim"),
                "  api <pattern>                 " .. Colorize("Quick search (if not a system name)", "status.dim"),
                "",
                Colorize("Flags:", "accent.warning"),
                "  -d, --details                 " .. Colorize("Show full documentation with types", "status.dim"),
                "  -t, --type <type>             " .. Colorize("Filter results: function, event, table", "status.dim"),
                "",
                Colorize("Output Types:", "accent.warning"),
                "  " .. Colorize("f", "accent.success") .. " Function    " .. Colorize("(args:type) -> returns", "status.dim"),
                "  " .. Colorize("e", "accent.warning") .. " Event       " .. Colorize("(payload:type, ...)", "status.dim"),
                "  " .. Colorize("t", "accent.info") .. " Table       " .. Colorize("[Enumeration], [Structure], [Constants]", "status.dim"),
                "",
                Colorize("Detailed View (-d):", "accent.warning"),
                "  Functions: Arguments with types, return values, descriptions",
                "  Events:    Payload parameters with types and descriptions",
                "  Enums:     All values with numeric constants and ranges",
                "  Structs:   All fields with types and defaults",
                "",
                Colorize("Pipeline Examples:", "accent.warning"),
                "  api system | where functionCount > 20",
                "  api C_MountJournal list | where name:Get",
                "  api search Mount -t func | sort name",
                "",
                Colorize("Direct Lookup Examples:", "accent.warning"),
                "  api C_MountJournal.GetMountInfoByID -d",
                "  api C_Spell.GetSpellCooldown -d",
                "  api PLAYER_LOGIN -d",
                "  api Enum.ItemQuality -d",
                "",
                Colorize("Search Pattern Examples:", "accent.warning"),
                '  api search Mount              ' .. Colorize("Plain text search", "status.dim"),
                '  api search "^Get"             ' .. Colorize("Lua pattern: starts with Get", "status.dim"),
                '  api search "Info$"            ' .. Colorize("Lua pattern: ends with Info", "status.dim"),
                '  api search "Get.*Mount"       ' .. Colorize("Lua pattern: Get...Mount", "status.dim"),
                "",
                Colorize("Intellisense:", "accent.warning"),
                "  Type " .. Colorize("api C_MountJournal.", "accent.primary") .. " to see function signatures",
                "  Suggestions show type-annotated signatures for easy reference",
            }
            return ctx:Text(table.concat(lines, "\n"))
        end

        local arg1Lower = arg1:lower()

        -- Check for direct function lookup (arg contains a dot, e.g., "C_MountJournal.GetMountInfoByID")
        if arg1:find(".", 1, true) then
            local item, itemType, system = findAPIByFullName(arg1)
            if item then
                if showDetails then
                    -- Return detailed text view
                    if itemType == "function" then
                        return ctx:Text(showDetailedFunction(item, system))
                    elseif itemType == "event" then
                        return ctx:Text(showDetailedEvent(item, system))
                    elseif itemType == "table" then
                        return ctx:Text(showDetailedTable(item, system))
                    end
                else
                    -- Return typed object
                    if itemType == "function" then
                        return functionToObject(item, system)
                    elseif itemType == "event" then
                        return eventToObject(item, system)
                    elseif itemType == "table" then
                        return tableToObject(item, system)
                    end
                end
            end
            -- If not found as direct lookup, fall through to try as system name or search
        end

        -- Check for event name without namespace (e.g., "PLAYER_LOGIN")
        -- Events often have uppercase names with underscores
        if arg1:match("^[A-Z][A-Z_0-9]+$") and not findSystemByName(arg1) then
            local item, itemType, system = findAPIByFullName(arg1)
            if item and itemType == "event" then
                if showDetails then
                    return ctx:Text(showDetailedEvent(item, system))
                else
                    return eventToObject(item, system)
                end
            end
        end

        -- api system -> List all systems
        if arg1Lower == "system" or arg1Lower == "systems" or arg1Lower == "sys" then
            local systems = getAllSystems()
            if #systems == 0 then
                return ctx:Error("No API systems found. The documentation data may not be loaded.\nTry running WoW's built-in: /api system list")
            end
            local results = {}
            for i, system in ipairs(systems) do
                if i % YIELD_INTERVAL == 0 then
                    coroutine.yield()
                end
                results[#results + 1] = systemToObject(system)
            end
            return results
        end

        -- api search <pattern> -> Search all
        if arg1Lower == "search" or arg1Lower == "s" then
            -- Combine remaining args as pattern
            local pattern = ""
            if arg2 then
                local patternParts = {}
                for i = 2, #args do
                    patternParts[#patternParts + 1] = args[i]
                end
                pattern = table.concat(patternParts, " ")
            end

            if pattern == "" then
                return ctx:Error("Usage: api search <pattern>")
            end

            return searchAllAPI(pattern, typeFilter)
        end

        -- Try to find system by name
        local system = findSystemByName(arg1)

        if system then
            -- api <system> -> Show system info
            if not arg2 or arg2 == "" then
                return systemToObject(system)
            end

            local arg2Lower = arg2:lower()

            -- api <system> list -> List all in system
            if arg2Lower == "list" or arg2Lower == "l" then
                return listSystem(system, typeFilter)
            end

            -- api <system> search <pattern> -> Search in system
            if arg2Lower == "search" or arg2Lower == "s" then
                -- Combine remaining args as pattern
                local pattern = ""
                if #args >= 3 then
                    local patternParts = {}
                    for i = 3, #args do
                        patternParts[#patternParts + 1] = args[i]
                    end
                    pattern = table.concat(patternParts, " ")
                end

                if pattern == "" then
                    return ctx:Error("Usage: api " .. arg1 .. " search <pattern>")
                end

                return searchInSystem(system, pattern, typeFilter)
            end

            -- api <system> <pattern> -> Treat as search pattern
            local pattern = ""
            local patternParts = {}
            for i = 2, #args do
                patternParts[#patternParts + 1] = args[i]
            end
            pattern = table.concat(patternParts, " ")

            return searchInSystem(system, pattern, typeFilter)
        end

        -- Not a system name - try as global search pattern
        local pattern = table.concat(args, " ")

        -- If -d flag is used, check for exact match and show detailed view
        if showDetails then
            local results = searchAllAPI(pattern, typeFilter)

            -- Look for exact name match (case-insensitive)
            local patternLower = pattern:lower()
            local exactMatch = nil
            local exactMatchType = nil

            for _, result in ipairs(results) do
                if result.name and result.name:lower() == patternLower then
                    exactMatch = result
                    exactMatchType = result.type
                    break
                end
            end

            if exactMatch and exactMatch._raw then
                -- Show detailed view for exact match
                if exactMatchType == "function" then
                    return ctx:Text(showDetailedFunction(exactMatch._raw, exactMatch._system))
                elseif exactMatchType == "event" then
                    return ctx:Text(showDetailedEvent(exactMatch._raw, exactMatch._system))
                elseif exactMatchType == "table" then
                    return ctx:Text(showDetailedTable(exactMatch._raw, exactMatch._system))
                end
            end

            -- No exact match, return results normally
            return results
        end

        return searchAllAPI(pattern, typeFilter)
    end
})

return true
