-- RLAgingTests.lua
-- Purpose: Tests for aging, health update, and weight update functions
-- Author: Ritter
-- Validates: increaseAge(), updateHealth(), updateWeight()
-- Pattern: Test direction of change (increased/decreased), not exact values

RLAgingTests = {}

local Log = RmLogging.getLogger("RLRM")

--- Run all aging and growth tests.
--- Tests age increment, health update direction, and weight update modifiers.
--- @param assert function Assert closure from RLTestRunner
function RLAgingTests:runAll(assert)
    Log:info("--- Aging Tests ---")

    self:testAgeIncrement(assert)
    self:testHealthUpdate(assert)
    self:testWeightUpdate(assert)

    -- Cleanup
    RLTestUtils:cleanupTestAnimals()
end

-- =========================================================================
-- Age Increment
-- =========================================================================

--- Verify increaseAge() increments age by 1.
--- @param assert function Assert closure from RLTestRunner
function RLAgingTests:testAgeIncrement(assert)
    Log:info("--- Age Increment ---")

    local cow = RLTestUtils:createTestAnimal("COW", { age = 10 })
    local ageBefore = cow.age
    cow:increaseAge()
    assert("increaseAge increments by 1", cow.age == ageBefore + 1, ageBefore + 1, cow.age)

    -- Verify specific value (age=10 → 11)
    assert("increaseAge from age=10 gives 11", cow.age == 11, 11, cow.age)

    RLTestUtils:cleanupTestAnimals()
end

-- =========================================================================
-- Health Update
-- =========================================================================

--- Test health changes in expected direction based on food factor.
--- updateHealth(1.0) = full food → health increases (or stays at max).
--- updateHealth(0.0) = no food → health decreases.
--- Uses subType fields (healthThresholdFactor, healthIncreaseHour, healthDecreaseHour).
--- @param assert function Assert closure from RLTestRunner
function RLAgingTests:testHealthUpdate(assert)
    Log:info("--- Health Update ---")

    -- Full food on cow with health=100 → stays at 100 (clamped)
    local maxHealth = RLTestUtils:createTestAnimal("COW", { health = 100 })
    maxHealth:updateHealth(1.0)
    assert("updateHealth(1.0) at health=100 stays at 100", maxHealth.health == 100, 100, maxHealth.health)

    -- No food on cow with health=100 → health decreases
    local starving = RLTestUtils:createTestAnimal("COW", { health = 100 })
    starving:updateHealth(0.0)
    assert("updateHealth(0.0) at health=100 decreases health",
        starving.health < 100, "<100", starving.health)

    -- No food on cow with health=50 → health decreases further
    local lowHealth = RLTestUtils:createTestAnimal("COW", { health = 50 })
    lowHealth:updateHealth(0.0)
    assert("updateHealth(0.0) at health=50 decreases health",
        lowHealth.health < 50, "<50", lowHealth.health)

    -- Full food on cow with health=50 → health increases (recovery)
    local recovering = RLTestUtils:createTestAnimal("COW", { health = 50 })
    recovering:updateHealth(1.0)
    assert("updateHealth(1.0) at health=50 increases health",
        recovering.health > 50, ">50", recovering.health)

    RLTestUtils:cleanupTestAnimals()
end

-- =========================================================================
-- Weight Update
-- =========================================================================

--- Test weight change modifiers: growth, gender, castration, lactation.
--- Uses public interface: updateWeight(foodFactor).
--- ASSUMES vanilla subType values for COW.
--- @param assert function Assert closure from RLTestRunner
function RLAgingTests:testWeightUpdate(assert)
    Log:info("--- Weight Update ---")

    -- Young cow + food → weight increases
    local young = RLTestUtils:createTestAnimal("COW", { age = 6 })
    local weightBefore = young.weight
    young:updateWeight(1.0)
    assert("updateWeight(1.0) young cow weight increases",
        young.weight > weightBefore, ">" .. string.format("%.3f", weightBefore),
        string.format("%.3f", young.weight))

    -- No food → weight should not increase (starvation path zeroes the food multiplier)
    local starved = RLTestUtils:createTestAnimal("COW", { age = 6 })
    local starveBefore = starved.weight
    starved:updateWeight(0.0)
    assert("updateWeight(0.0) weight does not increase",
        starved.weight <= starveBefore, "<=" .. string.format("%.3f", starveBefore),
        string.format("%.3f", starved.weight))

    -- Male gains weight faster than female (1.0 vs 0.6 multiplier)
    local female = RLTestUtils:createTestAnimal("COW", { age = 6, gender = "female" })
    local male = RLTestUtils:createTestAnimal("COW", { age = 6, gender = "male" })
    local femaleWeightBefore = female.weight
    local maleWeightBefore = male.weight
    female:updateWeight(1.0)
    male:updateWeight(1.0)
    local femaleDelta = female.weight - femaleWeightBefore
    local maleDelta = male.weight - maleWeightBefore
    Log:debug("  Weight deltas: male=%.3f female=%.3f", maleDelta, femaleDelta)
    assert("male weight gain > female weight gain",
        maleDelta > femaleDelta, "male > female",
        string.format("male=%.3f female=%.3f", maleDelta, femaleDelta))

    -- Castrated cow gains weight 15% faster than non-castrated
    local normal = RLTestUtils:createTestAnimal("COW", { age = 6, gender = "male" })
    local castrated = RLTestUtils:createTestAnimal("COW", { age = 6, gender = "male", isCastrated = true })
    local normalBefore = normal.weight
    local castratedBefore = castrated.weight
    normal:updateWeight(1.0)
    castrated:updateWeight(1.0)
    local normalDelta = normal.weight - normalBefore
    local castratedDelta = castrated.weight - castratedBefore
    Log:debug("  Weight deltas: castrated=%.3f normal=%.3f", castratedDelta, normalDelta)
    assert("castrated weight gain > non-castrated",
        castratedDelta > normalDelta, "castrated > normal",
        string.format("castrated=%.3f normal=%.3f", castratedDelta, normalDelta))

    -- Lactating cow gains weight slower (0.75 multiplier)
    -- SETUP: spec_husbandryMilk must be non-nil for lactation penalty guard
    local cs = RLTestUtils:getSharedMockClusterSystem()
    local savedMilkSpec = cs.owner.spec_husbandryMilk
    cs.owner.spec_husbandryMilk = {}  -- enable lactation penalty path

    local nonLactating = RLTestUtils:createTestAnimal("COW", { age = 6, gender = "female" })
    local lactating = RLTestUtils:createTestAnimal("COW", { age = 6, gender = "female", isLactating = true })
    local nonLactBefore = nonLactating.weight
    local lactBefore = lactating.weight
    nonLactating:updateWeight(1.0)
    lactating:updateWeight(1.0)
    local nonLactDelta = nonLactating.weight - nonLactBefore
    local lactDelta = lactating.weight - lactBefore
    Log:debug("  Weight deltas: nonLactating=%.3f lactating=%.3f", nonLactDelta, lactDelta)
    assert("lactating weight gain < non-lactating",
        lactDelta < nonLactDelta, "lactating < non-lactating",
        string.format("lactating=%.3f nonLactating=%.3f", lactDelta, nonLactDelta))

    -- Restore spec_husbandryMilk
    cs.owner.spec_husbandryMilk = savedMilkSpec

    RLTestUtils:cleanupTestAnimals()
end
