-- HousingDecorGuide/tests/test_rotation_deducer.lua
---@diagnostic disable: undefined-global
-- Standalone Lua test file.
package.path = "./?.lua;../Core/?.lua;" .. package.path

-- Provide a minimal privateDB shim so both modules load cleanly
local sharedDB = {}
local function varargs_loader(path)
    local chunk = loadfile(path)
    if not chunk then error("Could not load " .. path) end
    -- Call with (nil, sharedDB) to mimic WoW varargs: addonName=nil, privateDB=sharedDB
    local result = chunk(nil, sharedDB)
    return result or sharedDB
end

-- Load DB first so RotationDeducer.deduce can access it via sharedDB
varargs_loader("../Core/HDG_RoomShapesDB.lua")
local RD = varargs_loader("../Core/HDG_RotationDeducer.lua")

local t = require("test_helpers")

local PI   = math.pi
local PI_2 = math.pi / 2
local PI32 = 3 * math.pi / 2

-- Convenience: convert math-convention facing to WoW compass convention.
-- compass = (π/2 - math) mod 2π
local function mathToCompass(mathRad)
    return (PI_2 - mathRad) % (2 * PI)
end

-- Convenience: wrap a set of math-facing angles into compass facings array
local function compassFacings(...)
    local out = {}
    for _, m in ipairs({...}) do
        out[#out + 1] = mathToCompass(m)
    end
    return out
end

t.describe("HDG_RotationDeducer.roundQuarter", function()
    t.it("rounds 0 to 0", function()
        t.assert_eq(RD.roundQuarter(0), 0)
    end)
    t.it("rounds ~π/2 to π/2", function()
        t.assert_eq(RD.roundQuarter(PI_2 + 0.01), PI_2)
    end)
    t.it("rounds 2π to 0 (wraps)", function()
        t.assert_eq(RD.roundQuarter(2 * PI), 0)
    end)
end)

t.describe("HDG_RotationDeducer.compassToMath", function()
    t.it("compass 0 (N) → math π/2 (N)", function()
        local r = RD.compassToMath(0)
        t.assert_eq(RD.roundQuarter(r), PI_2)
    end)
    t.it("compass π/2 (E) → math 0 (E)", function()
        local r = RD.compassToMath(PI_2)
        t.assert_eq(RD.roundQuarter(r), 0)
    end)
    t.it("compass π (S) → math 3π/2 (S)", function()
        local r = RD.compassToMath(PI)
        t.assert_eq(RD.roundQuarter(r), PI32)
    end)
    t.it("compass 3π/2 (W) → math π (W)", function()
        local r = RD.compassToMath(PI32)
        t.assert_eq(RD.roundQuarter(r), PI)
    end)
end)

-- ---------------------------------------------------------------------------
-- Core deduction cases
-- ---------------------------------------------------------------------------

t.describe("HDG_RotationDeducer.deduce — Octagon Medium (symmetric, 4 doors)", function()
    -- Octagon has all 4 cardinal doors (E,N,W,S in math). It is rotationally
    -- symmetric so all rotations match; the deducer returns the first candidate (r=0).
    t.it("all 4 compass cardinals → rotation 0 (first match for symmetric shape)", function()
        -- compass: N=0, E=π/2, S=π, W=3π/2
        local facings = { 0, PI_2, PI, PI32 }   -- N,E,S,W compass
        local r, err = RD.deduce("octagon_m", facings)
        t.assert_nil(err)
        t.assert_eq(r, 0)
    end)
end)

t.describe("HDG_RotationDeducer.deduce — L-Shape (asymmetric, 2 doors)", function()
    -- DB2 L-Shape local doors: E (0) + N (π/2) in math.
    -- rotation 0    → world E+N  → compass π/2 + 0
    -- rotation π/2  → world N+W  → compass 0 + 3π/2
    -- rotation π    → world W+S  → compass 3π/2 + π
    -- rotation 3π/2 → world S+E  → compass π + π/2

    t.it("rotation 0: doors at compass E(π/2) + N(0)", function()
        local facings = compassFacings(0, PI_2)   -- math E,N → compass π/2,0
        local r, err = RD.deduce("l_shape", facings)
        t.assert_nil(err)
        t.assert_eq(r, 0)
    end)

    t.it("rotation π/2: doors at compass N(0) + W(3π/2)", function()
        local facings = compassFacings(PI_2, PI)   -- math N,W → compass 0,3π/2
        local r, err = RD.deduce("l_shape", facings)
        t.assert_nil(err)
        t.assert_eq(r, PI_2)
    end)

    t.it("rotation π: doors at compass W(3π/2) + S(π)", function()
        local facings = compassFacings(PI, PI32)   -- math W,S → compass 3π/2,π
        local r, err = RD.deduce("l_shape", facings)
        t.assert_nil(err)
        t.assert_eq(r, PI)
    end)

    t.it("rotation 3π/2: doors at compass S(π) + E(π/2)", function()
        local facings = compassFacings(PI32, 2*PI)   -- math S,E → compass π,π/2
        local r, err = RD.deduce("l_shape", facings)
        t.assert_nil(err)
        t.assert_eq(r, PI32)
    end)

    -- Empirical: captured = {6.28, 4.71} ≈ {0, 3π/2} compass = N + W
    -- → rotation π/2 (matches test above since N+W = rotation π/2 case)
    t.it("empirical capture {6.28, 4.71} → rotation π/2", function()
        local r, err = RD.deduce("l_shape", { 6.28, 4.71 })
        t.assert_nil(err)
        t.assert_eq(r, PI_2)
    end)
end)

t.describe("HDG_RotationDeducer.deduce — Closet (N+S doors, rotationally symmetric)", function()
    -- DB2 closet_xs local doors: E(0), N(π/2), W(π), S(3π/2) — all 4.
    -- All rotations match; deducer returns 0.
    t.it("N+S compass (N=0, S=π) → rotation 0 (first match)", function()
        local r, err = RD.deduce("closet_xs", { 0, PI_2, PI, PI32 })
        t.assert_nil(err)
        t.assert_eq(r, 0)
    end)

    -- Empirical: Closet captured {6.28, 3.14} ≈ {0, π} compass = N + S
    t.it("empirical capture {6.28, 3.14} → rotation 0", function()
        local r, err = RD.deduce("closet_xs", { 6.28, 3.14, 4.71, 1.57 })
        t.assert_nil(err)
        t.assert_eq(r, 0)
    end)
end)

t.describe("HDG_RotationDeducer.deduce — error cases", function()
    t.it("wrong door count returns nil + doors_count_mismatch", function()
        local r, err = RD.deduce("l_shape", { 0, PI_2, PI })   -- 3 doors for 2-door shape
        t.assert_nil(r)
        t.assert_eq(err, "doors_count_mismatch")
    end)

    t.it("unknown shape returns nil + unknown_shape", function()
        local r, err = RD.deduce("totally_made_up", { 0 })
        t.assert_nil(r)
        t.assert_eq(err, "unknown_shape")
    end)
end)

t.describe("HDG_RotationDeducer.facingsToCardinals", function()
    t.it("L-shape rotation 0 → compass {E, N}", function()
        local cards = RD.facingsToCardinals("l_shape", 0, "compass")
        table.sort(cards)
        t.assert_table_eq(cards, {"E","N"})
    end)

    t.it("L-shape rotation π/2 → compass {N, W}", function()
        local cards = RD.facingsToCardinals("l_shape", PI_2, "compass")
        table.sort(cards)
        t.assert_table_eq(cards, {"N","W"})
    end)
end)

t.run_all()
