-- RLIdentityTests.lua
-- Purpose: Tests for Animal identity functions — getHash, getIdentifiers,
--          compareIdentifiers, computeCheckDigit
-- Author: Ritter

RLIdentityTests = {}

local Log = RmLogging.getLogger("RLRM")

--- Run all identity tests
---@param assert function Assert closure from RLTestRunner
function RLIdentityTests:runAll(assert)
    Log:info("--- Identity Tests ---")

    -- =========================================================================
    -- getHash
    -- =========================================================================

    local hashMock = { age = 10, health = 80, reproduction = 50, subTypeIndex = 3 }
    -- Expected: (100+10) + 1000*(100+80) + 1e6*(100+50) + 1e9*(100+3) = 103150180110
    local hash1 = RLAnimalUtil.getHash(hashMock)
    assert("getHash known values", hash1 == 103150180110, 103150180110, hash1)

    -- Deterministic
    local hash1b = RLAnimalUtil.getHash(hashMock)
    assert("getHash deterministic", hash1 == hash1b, hash1, hash1b)

    -- All zeros
    local hashZero = RLAnimalUtil.getHash({ age = 0, health = 0, reproduction = 0, subTypeIndex = 0 })
    assert("getHash all zeros", hashZero == 100100100100, 100100100100, hashZero)

    -- Different inputs produce different outputs
    local hashMock2 = { age = 20, health = 60, reproduction = 30, subTypeIndex = 1 }
    local hash2 = RLAnimalUtil.getHash(hashMock2)
    assert("getHash different inputs differ", hash1 ~= hash2, "different", hash1 == hash2 and "same" or "different")

    -- =========================================================================
    -- getIdentifiers
    -- =========================================================================

    local idMock1 = { birthday = { country = 1 }, farmId = "123456", uniqueId = "712345" }
    local id1 = Animal.getIdentifiers(idMock1)
    assert("getIdentifiers country=1", id1 == "UK 123456 712345", "UK 123456 712345", id1)

    local idMock2 = { birthday = { country = 2 }, farmId = "654321", uniqueId = "999999" }
    local id2 = Animal.getIdentifiers(idMock2)
    assert("getIdentifiers country=2 starts with US",
        string.sub(id2, 1, 2) == "US", "US", string.sub(id2, 1, 2))

    local idMock3 = { birthday = { country = 6 }, farmId = "111111", uniqueId = "222222" }
    local id3 = Animal.getIdentifiers(idMock3)
    assert("getIdentifiers country=6 starts with DE",
        string.sub(id3, 1, 2) == "DE", "DE", string.sub(id3, 1, 2))

    -- =========================================================================
    -- compareIdentifiers
    -- =========================================================================

    -- Add getIdentifiers to mock so self:getIdentifiers() works
    local cmpMock1 = { birthday = { country = 1 }, farmId = "123456", uniqueId = "712345",
        getIdentifiers = Animal.getIdentifiers }
    local cmpResult1 = Animal.compareIdentifiers(cmpMock1, "UK 123456 712345")
    assert("compareIdentifiers match", cmpResult1 == true, true, cmpResult1)

    local cmpMock2 = { birthday = { country = 1 }, farmId = "999999", uniqueId = "712345",
        getIdentifiers = Animal.getIdentifiers }
    local cmpResult2 = Animal.compareIdentifiers(cmpMock2, "UK 123456 712345")
    assert("compareIdentifiers mismatch (different farmId)", cmpResult2 == false, false, cmpResult2)

    -- =========================================================================
    -- computeCheckDigit
    -- =========================================================================

    -- Known value: tonumber("123456" .. "12345") % 7 + 1 = 12345612345 % 7 + 1 = 3 + 1 = 4
    local cd1 = RLAnimalUtil.computeCheckDigit("123456", "12345")
    assert("computeCheckDigit known value", cd1 == 4, 4, cd1)

    -- Range: result always in [1, 7]
    local allInRange = true
    local testCases = {
        { "100000", "10000" },
        { "999999", "99999" },
        { "123456", "11111" },
        { "111111", "12345" },
        { "500000", "50000" },
    }
    for _, tc in ipairs(testCases) do
        local cd = RLAnimalUtil.computeCheckDigit(tc[1], tc[2])
        if cd < 1 or cd > 7 then
            allInRange = false
        end
    end
    assert("computeCheckDigit range always [1, 7]", allInRange, true, allInRange)

    -- Deterministic
    local cd2a = RLAnimalUtil.computeCheckDigit("654321", "54321")
    local cd2b = RLAnimalUtil.computeCheckDigit("654321", "54321")
    assert("computeCheckDigit deterministic", cd2a == cd2b, cd2a, cd2b)
end
