-- RLDeathTests.lua
-- Purpose: Tests for death probability functions (low-health, old-age)
-- Author: Ritter
-- Validates: calculateLowHealthMonthlyAnimalDeaths(), calculateOldAgeMonthlyAnimalDeaths()
-- Pattern: Boundary condition testing (deterministic inputs) + one statistical smoke test

RLDeathTests = {}

local Log = RmLogging.getLogger("RLRM")

--- Run all death probability tests.
--- Tests guard conditions, boundary values, and one statistical smoke test.
--- @param assert function Assert closure from RLTestRunner
function RLDeathTests:runAll(assert)
    Log:info("--- Death Tests ---")

    self:testGuardConditions(assert)
    self:testLowHealthBoundaries(assert)
    self:testOldAgeBoundaries(assert)
    self:testLowHealthStatistical(assert)

    -- Cleanup
    RLTestUtils:cleanupTestAnimals()
end

-- =========================================================================
-- Guard Conditions
-- =========================================================================

--- Verify dead and zero-count animals are skipped by death functions.
--- @param assert function Assert closure from RLTestRunner
function RLDeathTests:testGuardConditions(assert)
    Log:info("--- Death Guard Conditions ---")

    -- Dead animal → calculateLowHealthMonthlyAnimalDeaths returns 0
    local dead = RLTestUtils:createTestAnimal("COW", { health = 10 })
    dead.isDead = true
    local result1 = dead:calculateLowHealthMonthlyAnimalDeaths()
    assert("dead animal skipped by low-health death", result1 == 0, 0, result1)

    -- numAnimals=0 → calculateLowHealthMonthlyAnimalDeaths returns 0
    local noCount = RLTestUtils:createTestAnimal("COW", { health = 10 })
    noCount.numAnimals = 0
    local result2 = noCount:calculateLowHealthMonthlyAnimalDeaths()
    assert("numAnimals=0 skipped by low-health death", result2 == 0, 0, result2)

    -- Dead animal → calculateOldAgeMonthlyAnimalDeaths returns 0
    local deadOld = RLTestUtils:createTestAnimal("COW", { age = 200 })
    deadOld.isDead = true
    local result3 = deadOld:calculateOldAgeMonthlyAnimalDeaths()
    assert("dead animal skipped by old-age death", result3 == 0, 0, result3)

    RLTestUtils:cleanupTestAnimals()
end

-- =========================================================================
-- Low-Health Death Boundaries
-- =========================================================================

--- Test deterministic boundaries of low-health death formula.
--- Formula: deathChance = (0.5 * (2 - healthGenetics)) - (health / 100)
--- Threshold: health >= 80 returns 0 immediately.
--- @param assert function Assert closure from RLTestRunner
function RLDeathTests:testLowHealthBoundaries(assert)
    Log:info("--- Low-Health Death Boundaries ---")

    -- health=100, genetics.health=1.0 → never dies (threshold: health >= 80)
    local healthy = RLTestUtils:createTestAnimal("COW", { health = 100 })
    local result1 = healthy:calculateLowHealthMonthlyAnimalDeaths()
    assert("health=100 never dies from low health", result1 == 0, 0, result1)

    -- health=80 → exact boundary, returns 0 (health >= 80)
    local boundary = RLTestUtils:createTestAnimal("COW", { health = 80 })
    local result2 = boundary:calculateLowHealthMonthlyAnimalDeaths()
    assert("health=80 at threshold never dies", result2 == 0, 0, result2)

    -- health=79, genetics.health=1.0 → passes threshold but formula gives negative
    -- deathChance = (0.5 * (2 - 1.0)) - (79/100) = 0.5 - 0.79 = -0.29
    -- math.random() is always > 0, so <= -0.29 never true → returns 0
    local belowThreshold = RLTestUtils:createTestAnimal("COW", { health = 79 })
    local result3 = belowThreshold:calculateLowHealthMonthlyAnimalDeaths()
    assert("health=79 genetics=1.0 formula negative, no death", result3 == 0, 0, result3)

    RLTestUtils:cleanupTestAnimals()
end

-- =========================================================================
-- Old-Age Death Boundaries
-- =========================================================================

--- Test per-species age boundaries for old-age death.
--- Animals below minAge always return 0.
--- @param assert function Assert closure from RLTestRunner
function RLDeathTests:testOldAgeBoundaries(assert)
    Log:info("--- Old-Age Death Boundaries ---")

    -- COW age=24 → never dies from old age (minAge=180)
    local youngCow = RLTestUtils:createTestAnimal("COW", { age = 24 })
    local result1 = youngCow:calculateOldAgeMonthlyAnimalDeaths()
    assert("COW age=24 below minAge=180 no old-age death", result1 == 0, 0, result1)

    -- COW age=179 → just below minAge, returns 0
    local almostOldCow = RLTestUtils:createTestAnimal("COW", { age = 179 })
    local result2 = almostOldCow:calculateOldAgeMonthlyAnimalDeaths()
    assert("COW age=179 just below minAge=180 no death", result2 == 0, 0, result2)

    -- CHICKEN age=59 → below minAge=60, returns 0
    local youngChicken = RLTestUtils:createTestAnimal("CHICKEN", { age = 59 })
    local result3 = youngChicken:calculateOldAgeMonthlyAnimalDeaths()
    assert("CHICKEN age=59 below minAge=60 no death", result3 == 0, 0, result3)

    -- HORSE age=299 → below minAge=300, returns 0
    local youngHorse = RLTestUtils:createTestAnimal("HORSE", { age = 299 })
    local result4 = youngHorse:calculateOldAgeMonthlyAnimalDeaths()
    assert("HORSE age=299 below minAge=300 no death", result4 == 0, 0, result4)

    RLTestUtils:cleanupTestAnimals()
end

-- =========================================================================
-- Statistical Smoke Test
-- =========================================================================

--- Statistical test: health=50, genetics.health=0.5 → ~25% death chance.
--- Formula: deathChance = (0.5 * (2 - 0.5)) - (50/100) = 0.75 - 0.5 = 0.25
--- Run 200 iterations, expect 25-75 deaths (generous range).
--- @param assert function Assert closure from RLTestRunner
function RLDeathTests:testLowHealthStatistical(assert)
    Log:info("--- Low-Health Statistical Smoke Test ---")

    local iterations = 200
    local deathCount = 0

    for i = 1, iterations do
        local cow = RLTestUtils:createTestAnimal("COW", {
            health = 50,
            genetics = { health = 0.5, fertility = 1.0, productivity = 1.0, quality = 1.0, metabolism = 1.0 }
        })
        local result = cow:calculateLowHealthMonthlyAnimalDeaths()
        deathCount = deathCount + result
        RLTestUtils:cleanupTestAnimals()
    end

    Log:debug("  Statistical: %d deaths in %d iterations (expected ~50, range 25-75)", deathCount, iterations)
    assert("low-health statistical: 25% death rate in range",
        deathCount >= 25 and deathCount <= 75, "25-75", deathCount)
end
