--[[
    Commander 3.0 - Intellisense Test Suite

    Tests for autocomplete system:
    - Context parsing
    - Command suggestions
    - Fuzzy matching
    - Provider system
]]

local ADDON_NAME, C3 = ...

local Framework = C3.TestFramework
local Assert = Framework.Assert
local suite = Framework:Suite("intellisense")

-------------------------------------------------------------------------------
-- Intellisense Module Tests
-------------------------------------------------------------------------------

Framework:Test(suite, "Intellisense module exists", function()
    Assert.notNil(C3.Intellisense, "Intellisense should exist")
end)

Framework:Test(suite, "Intellisense has GetContext method", function()
    Assert.notNil(C3.Intellisense.GetContext, "GetContext should exist")
end)

Framework:Test(suite, "Intellisense has GetSuggestions method", function()
    Assert.notNil(C3.Intellisense.GetSuggestions, "GetSuggestions should exist")
end)

-------------------------------------------------------------------------------
-- Context Parsing Tests
-------------------------------------------------------------------------------

Framework:Test(suite, "GetContext parses empty input", function()
    local ctx = C3.Intellisense:GetContext("", 0)
    Assert.notNil(ctx)
    Assert.isTrue(ctx.isFirstWord, "empty input should be first word")
end)

Framework:Test(suite, "GetContext parses command", function()
    local ctx = C3.Intellisense:GetContext("echo hello", 10)
    Assert.notNil(ctx)
    Assert.equals("echo", ctx.command)
    Assert.isFalse(ctx.isFirstWord)
end)

Framework:Test(suite, "GetContext handles partial command", function()
    local ctx = C3.Intellisense:GetContext("ec", 2)
    Assert.notNil(ctx)
    Assert.isTrue(ctx.isFirstWord)
    Assert.equals("ec", ctx.partial)
end)

Framework:Test(suite, "GetContext detects pipe context", function()
    local ctx = C3.Intellisense:GetContext("bags | ", 7)
    Assert.notNil(ctx)
    -- After pipe, should be ready for new command
end)

Framework:Test(suite, "GetContext detects argument position", function()
    local ctx = C3.Intellisense:GetContext("cast Fire", 9)
    Assert.notNil(ctx)
    Assert.equals("cast", ctx.command)
end)

-------------------------------------------------------------------------------
-- Command Suggestions Tests
-------------------------------------------------------------------------------

Framework:Test(suite, "GetSuggestions returns commands for partial", function()
    local suggestions = C3.Intellisense:GetSuggestions("ec", 2)
    local found = false
    for _, s in ipairs(suggestions) do
        if s.text == "echo" then
            found = true
            break
        end
    end
    Assert.isTrue(found, "should suggest 'echo' for 'ec'")
end)

Framework:Test(suite, "GetSuggestions returns empty for no match", function()
    local suggestions = C3.Intellisense:GetSuggestions("xyz123notacommand", 17)
    Assert.equals(0, #suggestions, "should have no suggestions for gibberish")
end)

Framework:Test(suite, "GetSuggestions limits results", function()
    -- Request suggestions with a very common prefix
    local suggestions = C3.Intellisense:GetSuggestions("", 0)
    -- Should be limited to reasonable number
    Assert.lessOrEqual(#suggestions, 50, "should limit suggestion count")
end)

-------------------------------------------------------------------------------
-- Fuzzy Matching Tests
-------------------------------------------------------------------------------

Framework:Test(suite, "hasMatch function exists", function()
    Assert.notNil(C3.Intellisense.hasMatch, "hasMatch should exist")
end)

Framework:Test(suite, "hasMatch detects subsequence", function()
    local result = C3.Intellisense.hasMatch("fw", "Firework")
    Assert.isTrue(result)
end)

Framework:Test(suite, "hasMatch is case insensitive", function()
    local result = C3.Intellisense.hasMatch("ECHO", "echo")
    Assert.isTrue(result)
end)

Framework:Test(suite, "hasMatch rejects non-match", function()
    local result = C3.Intellisense.hasMatch("xyz", "echo")
    Assert.isFalse(result)
end)

Framework:Test(suite, "computeScore function exists", function()
    Assert.notNil(C3.Intellisense.computeScore, "computeScore should exist")
end)

Framework:Test(suite, "computeScore ranks exact match highest", function()
    local exactScore = C3.Intellisense.computeScore("echo", "echo")
    local partialScore = C3.Intellisense.computeScore("echo", "echo_command")
    Assert.greaterThan(exactScore, partialScore)
end)

Framework:Test(suite, "computeScore ranks prefix match high", function()
    local prefixScore = C3.Intellisense.computeScore("ech", "echo")
    local middleScore = C3.Intellisense.computeScore("cho", "echo")
    Assert.greaterThan(prefixScore, middleScore)
end)

Framework:Test(suite, "fuzzyFilter function exists", function()
    Assert.notNil(C3.Intellisense.fuzzyFilter, "fuzzyFilter should exist")
end)

Framework:Test(suite, "fuzzyFilter returns limited results", function()
    local items = {}
    for i = 1, 100 do
        table.insert(items, { name = "item" .. i })
    end

    local filtered = C3.Intellisense.fuzzyFilter(
        items, "item", function(i) return i.name end, 10
    )

    Assert.lessOrEqual(#filtered, 10)
end)

-------------------------------------------------------------------------------
-- Provider Tests
-------------------------------------------------------------------------------

Framework:Test(suite, "Providers table exists", function()
    Assert.notNil(C3.Intellisense.Providers, "Providers should exist")
end)

Framework:Test(suite, "Spells provider exists", function()
    local provider = C3.Intellisense:GetProvider("Spells")
    Assert.notNil(provider, "Spells provider should exist")
end)

Framework:Test(suite, "Mounts provider exists", function()
    local provider = C3.Intellisense:GetProvider("Mounts")
    Assert.notNil(provider, "Mounts provider should exist")
end)

Framework:Test(suite, "Items provider exists", function()
    local provider = C3.Intellisense:GetProvider("Items")
    Assert.notNil(provider, "Items provider should exist")
end)

Framework:Test(suite, "Spells provider returns results", function()
    local provider = C3.Intellisense:GetProvider("Spells")
    if provider then
        local results = provider("", {})
        Assert.isType(results, "table")
    end
end)

Framework:Test(suite, "Mounts provider returns results", function()
    local provider = C3.Intellisense:GetProvider("Mounts")
    if provider then
        local results = provider("", {})
        Assert.isType(results, "table")
    end
end)

Framework:Test(suite, "QueryOperators provider exists", function()
    Assert.notNil(C3.Intellisense.Providers.QueryOperators,
        "QueryOperators provider should exist")
end)

Framework:Test(suite, "QueryOperators provider lists operators", function()
    local results = C3.Intellisense.Providers.QueryOperators("", {})
    Assert.greaterThan(#results, 0, "should have query operators (got " .. #results .. ")")

    -- Check that 'where' is present
    local found = false
    local names = {}
    for _, r in ipairs(results) do
        table.insert(names, r.text or "nil")
        if r.text == "where" then
            found = true
        end
    end
    Assert.isTrue(found, "should include 'where' operator. Got: " .. table.concat(names, ", "))
end)

-------------------------------------------------------------------------------
-- Command Registry Autocomplete Tests
-------------------------------------------------------------------------------

Framework:Test(suite, "Command:Autocomplete exists", function()
    Assert.notNil(C3.Command.Autocomplete, "Command:Autocomplete should exist")
end)

Framework:Test(suite, "Command:Autocomplete returns matches", function()
    local suggestions = C3.Command:Autocomplete("ec")
    local found = false
    for _, s in ipairs(suggestions) do
        if s.text == "echo" then
            found = true
            break
        end
    end
    Assert.isTrue(found)
end)

Framework:Test(suite, "Command:Autocomplete includes aliases", function()
    -- 'cls' is alias for 'clear'
    local suggestions = C3.Command:Autocomplete("cl")
    local hasClear = false
    local hasCls = false
    for _, s in ipairs(suggestions) do
        if s.text == "clear" then hasClear = true end
        if s.text == "cls" then hasCls = true end
    end
    Assert.isTrue(hasClear or hasCls, "should include clear or cls")
end)

Framework:Test(suite, "Command:Autocomplete respects category filter", function()
    -- This tests that filtering by category works if supported
    local suggestions = C3.Command:Autocomplete("", { category = "system" })
    -- Should return system commands
    Assert.notNil(suggestions)
end)

-------------------------------------------------------------------------------
-- Integration Tests
-------------------------------------------------------------------------------

Framework:Test(suite, "Suggestions for 'bags |' includes operators", function()
    local suggestions = C3.Intellisense:GetSuggestions("bags | ", 7)
    -- After pipe, should suggest pipeline operators
    local hasWhere = false
    for _, s in ipairs(suggestions) do
        if s.text == "where" then
            hasWhere = true
            break
        end
    end
    -- Depending on implementation, operators may or may not appear
end)

Framework:Test(suite, "Suggestions for 'cast ' includes spells", function()
    local suggestions = C3.Intellisense:GetSuggestions("cast ", 5)
    -- After 'cast ', should suggest spells
    Assert.notNil(suggestions)
end)

Framework:Test(suite, "Suggestions for 'mount ' includes mounts", function()
    local suggestions = C3.Intellisense:GetSuggestions("mount ", 6)
    -- After 'mount ', should suggest mounts
    Assert.notNil(suggestions)
end)

return suite
