---@diagnostic disable: undefined-global
-- Wheel resolver + engine-core cross-checks (real DB2 colours).
dofile("../Core/VDS_ColorMath.lua")
dofile("../Core/VDS_HarmonyWheel.lua")
dofile("../Core/VDS_Harmony.lua")
local HW = VDS.HarmonyWheel
local H  = VDS.Harmony

local function has(list, val)
    for _, v in ipairs(list) do if v == val then return true end end
    return false
end
local function one(list, val, msg)
    assert(#list == 1 and list[1] == val, msg .. " (got " .. table.concat(list, ",") .. ")")
end

-- ============================ wheel resolver ============================
one(HW.targets("Red", "Complement"),    "Green",  "Red complement = Green")
one(HW.targets("Blue", "Complement"),   "Orange", "Blue complement = Orange")
one(HW.targets("Yellow", "Complement"), "Purple", "Yellow complement = Purple")
print("  [OK] complements: Red/Blue/Yellow -> Green/Orange/Purple")

local tri = HW.targets("Red", "Triad")
assert(#tri == 2 and has(tri, "Blue") and has(tri, "Yellow"), "Red triad = Blue+Yellow")
print("  [OK] Red triad = Blue + Yellow")

assert(HW.ANGLE["Grey"] == nil and HW.NEUTRALS["Brown"] == true, "neutrals excluded from the wheel")
assert(#HW.targets("Grey", "Complement") == 0, "neutral anchor has no hue targets")
print("  [OK] neutrals excluded from hue math")

local clash = HW.targets("Red", "Clash")
assert(#clash == 1 and clash[1] ~= "Green", "Red clash must not be the complement (Green)")
print("  [OK] clash is discordant, not the complement")

-- ============================ engine core ============================
local function rgb(r, g, b) return { r = r / 255, g = g / 255, b = b / 255 } end
local HORDE, DUSTWALLOW, PINEWOOD, LUSH, SILVERSAGE = 1, 10, 11, 12, 13
local infos = {
    [HORDE]      = { colorGroup = "Red",   swatchRGB = rgb(177, 28, 30) },   -- anchor
    [DUSTWALLOW] = { colorGroup = "Green", swatchRGB = rgb(151, 155, 59) },  -- vivid, passes gap
    [PINEWOOD]   = { colorGroup = "Green", swatchRGB = rgb(167, 164, 124) }, -- washed, passes gap
    [LUSH]       = { colorGroup = "Green", swatchRGB = rgb(63, 160, 78) },   -- vivid but FAILS gap (dL .14)
    [SILVERSAGE] = { colorGroup = "Green", swatchRGB = rgb(160, 183, 140) }, -- washed, passes gap
    [2]          = { colorGroup = "Red",   swatchRGB = rgb(231, 107, 109) }, -- Rain Poppy (mono shade)
    [20]         = { colorGroup = "Grey",  swatchRGB = rgb(192, 209, 206) }, -- Stormsteel (light grey)
    [21]         = { colorGroup = "Brown", swatchRGB = rgb(198, 154, 102) }, -- Warm Teak
}

-- Complement of Horde Red picks the vivid green, never the washed Pinewood.
local res = H.forDye(HORDE, infos, { [DUSTWALLOW] = 2, [HORDE] = 9 })
local comp = res.byRelationship.Complement[1]
assert(comp, "complement pick exists")
assert(comp.dyeID ~= PINEWOOD and comp.dyeID ~= SILVERSAGE, "washed greens rejected as a pop")
assert(comp.chroma >= 0.08, "complement is a vivid green (chroma floor), not washed")
print("  [OK] complement picks vivid green, rejects washed Pinewood")

-- Chroma floor beats ownership: owning ONLY Pinewood still won't make it a pop.
local res2 = H.forDye(HORDE, infos, { [PINEWOOD] = 5 })
assert(res2.byRelationship.Complement[1].dyeID ~= PINEWOOD, "owned washed dye still rejected as a pop")
print("  [OK] chroma floor overrides ownership for pops")

-- Monochromatic = a contrasting same-family shade.
assert(#res.mono >= 1 and res.mono[1].dyeID == 2, "mono picks the contrasting red (Rain Poppy)")
print("  [OK] monochromatic picks a contrasting same-family shade")

-- Neutral pool grounds the colour (low-chroma neutrals).
assert(#res.neutral >= 1 and res.neutral[1].chroma < 0.08, "neutral pick is genuinely low-chroma")
print("  [OK] neutral grounding picks")

-- autoFill: a TRUE auto dresses every channel. Clash has no clash-hue dye here,
-- so picksFor is empty -- autoFill must still return 2 via the grounding pad
-- chain (neutral -> mono -> analogous), deduped and capped at n.
local af = H.autoFill(res, "Clash", 2)
assert(#af == 2, "autoFill pads a thin strategy to fill every channel (got " .. #af .. ")")
local afseen = {}
for _, p in ipairs(af) do
    assert(not afseen[p.dyeID], "autoFill never repeats a dye across channels")
    afseen[p.dyeID] = true
end
assert(#H.autoFill(res, "Complement", 1) == 1, "autoFill caps at n channels")
print("  [OK] autoFill fills every channel, dedups, caps at n")

-- Neutral anchor: value/temperature lens (Tones / Contrast / Accent), no hue.
infos[30] = { colorGroup = "Brown",  swatchRGB = rgb(147, 94, 53) }   -- Heartwood (warm anchor)
infos[31] = { colorGroup = "Grey",   swatchRGB = rgb(134, 141, 144) } -- Dark Iron (cool, low gap)
infos[32] = { colorGroup = "White",  swatchRGB = rgb(255, 249, 200) } -- Bone-White (warm)
infos[33] = { colorGroup = "Yellow", swatchRGB = rgb(223, 190, 32) }  -- Gold (vivid accent)
local nres = H.forDye(30, infos, {})
assert(next(nres.byRelationship) == nil, "neutral anchor has no hue relationships")
assert(nres.tones and #nres.tones >= 1, "neutral anchor surfaces tones")
for _, p in ipairs(nres.tones) do
    assert(VDS.ColorMath.warmth(p.swatchRGB.r, p.swatchRGB.g, p.swatchRGB.b) > 0,
        "tones share the warm undertone")
end
assert(nres.contrast and #nres.contrast == 1 and
    VDS.ColorMath.warmth(nres.contrast[1].swatchRGB.r, nres.contrast[1].swatchRGB.g, nres.contrast[1].swatchRGB.b) < 0,
    "contrast flips to a cool neutral")
assert(nres.accent and #nres.accent >= 1 and nres.accent[1].chroma >= 0.10 and nres.accent[1].dL >= 0.15,
    "accent is a vivid, value-contrasting pop")
print("  [OK] neutral anchor: warm tones, cool contrast, vivid accent")

print("ALL harmony tests passed")
