-- RLTestRunner.lua
-- Purpose: Test framework entry point - sources test modules and runs tests
-- Author: Ritter
-- Usage: Run `rlTest` in console after savegame loads (singleplayer only)
-- Reference: Fresh/scripts/tests/RmTestRunner.lua (adapted for RLRM)

local modDirectory = g_currentModDirectory
local Log = RmLogging.getLogger("RLRM")

-- =============================================================================
-- SOURCE TEST MODULES
-- =============================================================================

source(modDirectory .. "scripts/tests/RLTestUtils.lua")
source(modDirectory .. "scripts/tests/RLAnimalFactoryTests.lua")
source(modDirectory .. "scripts/tests/RLHelloWorldTests.lua")
source(modDirectory .. "scripts/tests/RLFormulaTests.lua")
source(modDirectory .. "scripts/tests/RLBreedingMathTests.lua")
source(modDirectory .. "scripts/tests/RLIdentityTests.lua")
source(modDirectory .. "scripts/tests/RLAnimalUtilTests.lua")
source(modDirectory .. "scripts/tests/RLDiseaseTests.lua")
source(modDirectory .. "scripts/tests/RLGuiTests.lua")
source(modDirectory .. "scripts/tests/RLConsoleTests.lua")
source(modDirectory .. "scripts/tests/RLSettingsTests.lua")
source(modDirectory .. "scripts/tests/RLDeathTests.lua")
source(modDirectory .. "scripts/tests/RLAgingTests.lua")
source(modDirectory .. "scripts/tests/RLDiseaseTransitionTests.lua")
source(modDirectory .. "scripts/tests/RLReproductionTests.lua")
source(modDirectory .. "scripts/tests/RLAIAnimalManagerTests.lua")
source(modDirectory .. "scripts/tests/RLHorseTests.lua")

-- =============================================================================
-- TEST RUNNER
-- =============================================================================

RLTestRunner = {}

--- Run all automated tests
--- SERVER ONLY - tests mutate state
--- Uses pcall to ensure cleanup runs even if tests error
---@return string Summary message
function RLTestRunner:run()
    if g_server == nil then
        return "Tests must run on server"
    end

    Log:info("===========================================")
    Log:info("       RLRM AUTOMATED TESTS")
    Log:info("===========================================")

    local passed, failed = 0, 0
    local failures = {}

    -- Assert closure — named rlAssert to avoid shadowing Lua's built-in assert()
    -- Passed to test suites as the `assert` parameter for clean syntax
    local function rlAssert(name, condition, expected, actual)
        if condition then
            passed = passed + 1
            Log:debug("  PASS: %s", name)
        else
            failed = failed + 1
            table.insert(failures, { name = name, expected = expected, actual = actual })
            Log:warning("  FAIL: %s (expected: %s, got: %s)", name, tostring(expected), tostring(actual))
        end
    end

    -- CRITICAL: Enable test isolation - animal loops only process test animals
    RealisticLivestock.testAnimalPrefix = RLTestUtils.TEST_PREFIX
    Log:trace("TEST_SETUP: Set testAnimalPrefix = '%s'", RLTestUtils.TEST_PREFIX)

    -- Save state that test suites may mutate (restored post-pcall even on crash)
    local savedDiseasesEnabled = g_diseaseManager and g_diseaseManager.diseasesEnabled
    local savedMapAreaCode = RealisticLivestock.mapAreaCode

    -- Run test suites wrapped in pcall for safe cleanup on error
    local ok, err = pcall(function()
        RLAnimalFactoryTests:runAll(rlAssert)
        RLHelloWorldTests:runAll(rlAssert)
        RLFormulaTests:runAll(rlAssert)
        RLBreedingMathTests:runAll(rlAssert)
        RLIdentityTests:runAll(rlAssert)
        RLAnimalUtilTests:runAll(rlAssert)
        RLDiseaseTests:runAll(rlAssert)
        RLGuiTests:runAll(rlAssert)
        RLConsoleTests:runAll(rlAssert)
        RLSettingsTests:runAll(rlAssert)
        RLDeathTests:runAll(rlAssert)
        RLAgingTests:runAll(rlAssert)
        RLDiseaseTransitionTests:runAll(rlAssert)
        RLReproductionTests:runAll(rlAssert)
        RLAIAnimalManagerTests:runAll(rlAssert)
        RLHorseTests:runAll(rlAssert)
    end)

    -- ALWAYS cleanup (even on error)
    RLTestUtils:restoreAIManagerTestGlobals()
    RLTestUtils:cleanup()

    -- Restore state (even on crash)
    if g_diseaseManager then g_diseaseManager.diseasesEnabled = savedDiseasesEnabled end
    RealisticLivestock.mapAreaCode = savedMapAreaCode

    -- Report error if tests crashed
    if not ok then
        Log:error("TEST SUITE CRASHED: %s", tostring(err))
        Log:info("===========================================")
        Log:info("  RESULTS: %d passed, %d failed (CRASHED)", passed, failed)
        Log:info("===========================================")
        return string.format("%d passed, %d failed (CRASHED: %s)", passed, failed, tostring(err))
    end

    -- Summary
    Log:info("===========================================")
    if failed == 0 then
        Log:info("  RESULTS: %d passed, %d failed - ALL TESTS PASSED!", passed, failed)
    else
        Log:warning("  RESULTS: %d passed, %d failed", passed, failed)
        Log:warning("  FAILURES:")
        for _, f in ipairs(failures) do
            Log:warning("    - %s (expected: %s, got: %s)", f.name, tostring(f.expected), tostring(f.actual))
        end
    end
    Log:info("===========================================")

    return string.format("%d passed, %d failed", passed, failed)
end

--- Console command handler for rlTest
function RLTestRunner:consoleRunTests()
    return self:run()
end

-- =============================================================================
-- Self-contained Lifecycle Hooks (addModEventListener pattern)
-- Mirrors RmLogging.lua lines 414-425
-- g_currentMission is nil at source time — defer to loadMap()
-- =============================================================================

function RLTestRunner.loadMap()
    if RLTestRunner._commandRegistered then return end
    if g_currentMission:getIsServer()
        and not g_currentMission.missionDynamicInfo.isMultiplayer then
        addConsoleCommand("rlTest", "Run RLRM automated tests", "consoleRunTests", RLTestRunner)
        RLTestRunner._commandRegistered = true
        Log:debug("rlTest console command registered")
    end
end

function RLTestRunner.deleteMap()
    if RLTestRunner._commandRegistered then
        removeConsoleCommand("rlTest")
        RLTestRunner._commandRegistered = false
        Log:trace("rlTest console command unregistered")
    end
end

addModEventListener(RLTestRunner)

Log:info("TEST_FRAMEWORK: Test runner loaded (16 test modules)")
