--[[
    Commander 3.0 - Advanced Features Test Suite

    Tests for advanced features:
    - Watch system (live updates)
    - Format modes
    - Expression parser
    - Kernel/Task system
    - Types system
]]

local ADDON_NAME, C3 = ...

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

-------------------------------------------------------------------------------
-- Cleanup helper
-------------------------------------------------------------------------------

suite.afterEach = function()
    -- Stop any watches created during tests
    if C3.WatchManager and C3.WatchManager.StopAll then
        C3.WatchManager:StopAll()
    end
end

-------------------------------------------------------------------------------
-- Watch System Tests
-------------------------------------------------------------------------------

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

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

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

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

Framework:Test(suite, "watch command starts a watch", function()
    -- Use very long interval to prevent watch from actually firing during test
    local result = C3:Query("watch gold --interval 9999")
    Assert.notNil(result)
    Assert.hasType(result, "Text")
    Assert.contains(result.content, "Watch #", "should confirm watch started")

    C3.WatchManager:StopAll()
end)

Framework:Test(suite, "watch with interval flag works", function()
    local result = C3:Query("watch gold --interval 9999")
    Assert.notNil(result)
    Assert.contains(result.content, "9999", "should show interval")

    C3.WatchManager:StopAll()
end)

Framework:Test(suite, "watches command lists active watches", function()
    C3:Query("watch gold --interval 9999")

    local result = C3:Query("watches")
    Assert.notNil(result)

    C3.WatchManager:StopAll()
end)

Framework:Test(suite, "unwatch all stops watches", function()
    C3:Query("watch gold --interval 9999")
    C3:Query("watch $health --interval 9999")

    local result = C3:Query("unwatch all")
    Assert.notNil(result)

    local count = C3.WatchManager:Count()
    Assert.equals(0, count, "all watches should be stopped")
end)

Framework:Test(suite, "unwatch specific ID works", function()
    -- Clear any existing watches first
    C3.WatchManager:StopAll()

    C3:Query("watch gold --interval 9999")
    local initialCount = C3.WatchManager:Count()
    Assert.greaterThan(initialCount, 0, "should have at least one watch")

    C3:Query("unwatch 1")
    local afterCount = C3.WatchManager:Count()

    Assert.lessOrEqual(afterCount, initialCount, "watch count should decrease or stay same")
    C3.WatchManager:StopAll()
end)

Framework:Test(suite, "watch blocks secure commands", function()
    local result = C3:Query("watch cast Fireball")
    Assert.hasType(result, "Error", "watch cast should be blocked")
end)

Framework:Test(suite, "watch blocks mount command", function()
    local result = C3:Query("watch mount")
    Assert.hasType(result, "Error", "watch mount should be blocked")
end)

-------------------------------------------------------------------------------
-- Format System Tests
-------------------------------------------------------------------------------

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

Framework:Test(suite, "format command shows current mode", function()
    local result = C3:Query("format")
    Assert.notNil(result)
end)

Framework:Test(suite, "format text sets mode", function()
    local original = C3.Formatter:GetMode()

    C3:Query("format text")
    Assert.equals("text", C3.Formatter:GetMode())

    C3.Formatter:SetMode(original)
end)

Framework:Test(suite, "format json sets mode", function()
    local original = C3.Formatter:GetMode()

    C3:Query("format json")
    Assert.equals("json", C3.Formatter:GetMode())

    C3.Formatter:SetMode(original)
end)

Framework:Test(suite, "format grid sets mode", function()
    local original = C3.Formatter:GetMode()

    C3:Query("format grid")
    Assert.equals("grid", C3.Formatter:GetMode())

    C3.Formatter:SetMode(original)
end)

Framework:Test(suite, "Formatter:ProgressBar generates output", function()
    local bar = C3.Formatter:ProgressBar(75, 100, 10)
    Assert.notNil(bar)
    Assert.isType(bar, "string")
    Assert.greaterThan(#bar, 0)
end)

Framework:Test(suite, "Formatter:ColorByQuality works", function()
    local colored = C3.Formatter:ColorByQuality("Test", 4)
    Assert.notNil(colored)
    Assert.contains(colored, "|c", "should have color codes")
end)

-------------------------------------------------------------------------------
-- Expression Parser Tests
-------------------------------------------------------------------------------

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

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

Framework:Test(suite, "ExpressionParser evaluates simple comparison", function()
    local data = { value = 10 }
    local result = C3.ExpressionParser:Evaluate("value > 5", data)
    Assert.isTrue(result)
end)

Framework:Test(suite, "ExpressionParser evaluates equality", function()
    local data = { name = "test" }
    local result = C3.ExpressionParser:Evaluate("name == 'test'", data)
    Assert.isTrue(result)
end)

Framework:Test(suite, "ExpressionParser handles boolean and", function()
    local data = { a = true, b = true }
    local result = C3.ExpressionParser:Evaluate("a and b", data)
    Assert.isTrue(result)
end)

Framework:Test(suite, "ExpressionParser handles boolean or", function()
    local data = { a = false, b = true }
    local result = C3.ExpressionParser:Evaluate("a or b", data)
    Assert.isTrue(result)
end)

Framework:Test(suite, "ExpressionParser handles not operator", function()
    local data = { flag = false }
    local result = C3.ExpressionParser:Evaluate("not flag", data)
    Assert.isTrue(result)
end)

Framework:Test(suite, "ExpressionParser handles >= comparison", function()
    local data = { value = 10 }
    local result = C3.ExpressionParser:Evaluate("value >= 10", data)
    Assert.isTrue(result)
end)

Framework:Test(suite, "ExpressionParser handles <= comparison", function()
    local data = { value = 10 }
    local result = C3.ExpressionParser:Evaluate("value <= 10", data)
    Assert.isTrue(result)
end)

Framework:Test(suite, "ExpressionParser handles ~= comparison", function()
    local data = { value = 10 }
    local result = C3.ExpressionParser:Evaluate("value ~= 5", data)
    Assert.isTrue(result)
end)

-------------------------------------------------------------------------------
-- Kernel Tests
-------------------------------------------------------------------------------

Framework:Test(suite, "Kernel exists and is initialized", function()
    Assert.notNil(C3.Kernel, "Kernel should exist")
end)

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

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

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

-------------------------------------------------------------------------------
-- Types System Tests
-------------------------------------------------------------------------------

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

Framework:Test(suite, "Types:Create works", function()
    local obj = C3.Types:Create("Text", { content = "test" })
    Assert.hasType(obj, "Text")
    Assert.equals("test", obj.content)
end)

Framework:Test(suite, "Types.Text shorthand works", function()
    local obj = C3.Types.Text("hello")
    Assert.hasType(obj, "Text")
    Assert.equals("hello", obj.content)
end)

Framework:Test(suite, "Types.Error shorthand works", function()
    local obj = C3.Types.Error("error message")
    Assert.hasType(obj, "Error")
end)

Framework:Test(suite, "Types Number creation works", function()
    local obj = C3.Types.Create("Number", { value = 42 })
    Assert.hasType(obj, "Number")
    Assert.equals(42, obj.value)
end)

Framework:Test(suite, "Created objects have _meta", function()
    local obj = C3.Types:Create("Text", { content = "test" })
    Assert.notNil(obj._meta, "object should have _meta")
end)

-------------------------------------------------------------------------------
-- Lexer Tests
-------------------------------------------------------------------------------

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

Framework:Test(suite, "Lexer.TokenizeString works", function()
    local tokens = C3.Lexer.TokenizeString("echo hello")
    Assert.notNil(tokens)
    Assert.greaterOrEqual(#tokens, 2, "should have at least 2 tokens")
end)

Framework:Test(suite, "Lexer handles pipe character", function()
    local tokens = C3.Lexer.TokenizeString("bags | count")
    local hasPipe = false
    for _, t in ipairs(tokens) do
        if t.type == "PIPE" then hasPipe = true end
    end
    Assert.isTrue(hasPipe, "should have PIPE token")
end)

Framework:Test(suite, "Lexer handles variables", function()
    local tokens = C3.Lexer.TokenizeString("echo $player")
    local hasVar = false
    for _, t in ipairs(tokens) do
        if t.type == "VARIABLE" then hasVar = true end
    end
    Assert.isTrue(hasVar, "should have VARIABLE token")
end)

-------------------------------------------------------------------------------
-- Parser Tests
-------------------------------------------------------------------------------

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

Framework:Test(suite, "Parser.ParseString works", function()
    local ast = C3.Parser.ParseString("echo hello")
    Assert.notNil(ast)
    -- Simple commands may be simplified or wrapped in Pipeline
    Assert.notNil(ast.type or ast.name, "should have type or name")
end)

Framework:Test(suite, "Parser handles pipelines", function()
    local ast = C3.Parser.ParseString("bags | first")
    Assert.notNil(ast)
    Assert.equals("Pipeline", ast.type, "should be Pipeline type")
    Assert.equals(2, #ast.stages, "should have 2 stages in pipeline")
end)

Framework:Test(suite, "Parser handles chains (&&)", function()
    local ast = C3.Parser.ParseString("echo a && echo b")
    Assert.notNil(ast)
    Assert.equals("Chain", ast.type, "should be Chain type")
end)

return suite
