local ADDON_NAME, ns = ...

-- =============================================================
--  MT/OT TANK SYSTEM (EXTRACTED MODULE)
--  - Caches other tanks in group/raid
--  - Provides ns:GetTankModeState(unit) -> r,g,b,shouldFlash
--  - Mind-control / secret-safe
-- =============================================================

ns.TankUnits = ns.TankUnits or {}

-- -------------------------------------------------------------
-- DB helper (new schema)
-- -------------------------------------------------------------
local function GetThreatDB()
    if not PlateOMaticDB then return nil end
    PlateOMaticDB.threat = PlateOMaticDB.threat or {}
    PlateOMaticDB.threat.colors = PlateOMaticDB.threat.colors or {}
    return PlateOMaticDB.threat
end

local function IsSecretValue(v)
    local f = _G.issecretvalue
    if type(f) == "function" then
        local ok, ret = pcall(f, v)
        if ok and ret then
            return true
        end
    end
    return false
end

local function SafeUnitName(unit)
    local name = UnitName(unit)
    if not name then
        return nil, false
    end
    if IsSecretValue(name) then
        return nil, true
    end
    if type(name) ~= "string" then
        name = tostring(name)
    end
    return name, false
end

local function SortStringsSafe(t)
    table.sort(t, function(a, b)
        a = tostring(a or "")
        b = tostring(b or "")
        return a < b
    end)
end

-- NOTE: role assignment can be unreliable in some group contexts.
-- For reporting, we allow a spec-role fallback for the player.
local function PlayerIsTankRole_ForReport()
    if (IsInGroup() or IsInRaid()) and UnitGroupRolesAssigned("player") == "TANK" then
        return true
    end

    if GetSpecialization and GetSpecializationRole then
        local spec = GetSpecialization()
        if spec then
            return GetSpecializationRole(spec) == "TANK"
        end
    end

    return false
end

local function BuildTankSignature(inGroup, tankCount)
    local ids = {}

    if PlayerIsTankRole_ForReport() then
        ids[#ids + 1] = "player"
    end

    for _, u in ipairs(ns.TankUnits) do
        ids[#ids + 1] = tostring(u or "")
    end

    SortStringsSafe(ids)

    return (inGroup and "G" or "S") .. ":" .. tostring(tankCount) .. ":" .. table.concat(ids, ",")
end

local function BuildTankReportNames()
    local names = {}
    local hadSecret = false

    if PlayerIsTankRole_ForReport() then
        local pn, secret = SafeUnitName("player")
        if pn then
            names[#names + 1] = pn .. " (you)"
        elseif secret then
            hadSecret = true
            names[#names + 1] = "player (you)"
        else
            names[#names + 1] = "player (you)"
        end
    end

    for _, u in ipairs(ns.TankUnits) do
        local n, secret = SafeUnitName(u)
        if n then
            names[#names + 1] = n
        elseif secret then
            hadSecret = true
            names[#names + 1] = tostring(u)
        else
            names[#names + 1] = tostring(u)
        end
    end

    SortStringsSafe(names)
    return names, hadSecret
end

local function ReportTanks()
    local inGroup = IsInGroup() or IsInRaid()

    local playerIsTank = PlayerIsTankRole_ForReport()
    local tankCount = #ns.TankUnits + (playerIsTank and 1 or 0)

    -- IMPORTANT:
    -- Signature must never use names, because names can become secret during mind control.
    local sig = BuildTankSignature(inGroup, tankCount)

    if ns._lastTankReportSig == sig then
        return
    end
    ns._lastTankReportSig = sig

    if not inGroup then
        print("|cFF00FF00Plate-o-Matic:|r Tank cache: solo (0 other tanks).")
        return
    end

    if tankCount == 0 then
        print("|cFF00FF00Plate-o-Matic:|r Tank cache: 0 tanks detected (roles not set?).")
        return
    end

    local names, hadSecret = BuildTankReportNames()
    local label = table.concat(names, ", ")

    if hadSecret then
        print(string.format(
            "|cFF00FF00Plate-o-Matic:|r Tank cache: %d tank(s): %s |cffffff00(secret-safe fallback used)|r",
            tankCount,
            label
        ))
    else
        print(string.format("|cFF00FF00Plate-o-Matic:|r Tank cache: %d tank(s): %s", tankCount, label))
    end
end

function ns:UpdateTankCache()
    if table.wipe then
        table.wipe(ns.TankUnits)
    else
        for i = #ns.TankUnits, 1, -1 do
            ns.TankUnits[i] = nil
        end
    end

    if not IsInGroup() and not IsInRaid() then
        ReportTanks()
        return
    end

    local prefix = IsInRaid() and "raid" or "party"
    local count = GetNumGroupMembers()
    local limit = IsInRaid() and count or (count - 1)

    for i = 1, limit do
        local unit = prefix .. i
        if UnitExists(unit) and not UnitIsUnit(unit, "player") then
            if UnitGroupRolesAssigned(unit) == "TANK" then
                ns.TankUnits[#ns.TankUnits + 1] = unit
            end
        end
    end

    ReportTanks()
end

-- =============================================================
--  Role helpers
-- =============================================================
local function PlayerIsTankRole()
    if IsInGroup() or IsInRaid() then
        if UnitGroupRolesAssigned("player") == "TANK" then
            return true
        end
    end

    if GetSpecialization and GetSpecializationRole then
        local spec = GetSpecialization()
        if spec then
            return GetSpecializationRole(spec) == "TANK"
        end
    end

    return false
end

-- =============================================================
--  Main decision function:
--  Returns tank-safe color if I have aggro,
--  off-tank color if another tank has it,
--  danger color if no tank has it.
-- =============================================================
function ns:GetTankModeState(unit)
    local tdb = GetThreatDB()
    if not tdb then
        return nil, nil, nil, false
    end

    if not tdb.tankModeEnabled then
        return nil, nil, nil, false
    end

    if not PlayerIsTankRole() then
        return nil, nil, nil, false
    end

    -- HARD GATES: unit must be valid + attackable
    if not unit or not UnitExists(unit) then
        return nil, nil, nil, false
    end
    if UnitCanAttack and not UnitCanAttack("player", unit) then
        return nil, nil, nil, false
    end

    -- IMPORTANT FIX:
    -- Gate on THE UNIT being in combat, not the player.
    -- Prevents “random plates” turning red/flash just because you are in combat.
    if not UnitAffectingCombat(unit) then
        return nil, nil, nil, false
    end

    local c = tdb.colors or {}

    -- NEW color keys (match THREAT_DEFAULTS you pasted in pom.lua)
    local cSafe = c.safe or { r = 0.2, g = 0.8, b = 0.2 }  -- tank has aggro
    local cOT   = c.ot   or { r = 0.6, g = 0.2, b = 1.0 }  -- off-tank has aggro
    local cBad  = c.high or { r = 1.0, g = 0.0, b = 0.0 }  -- nobody tanking it

    local status = UnitThreatSituation("player", unit) or 0
    status = tonumber(status) or 0

    -- I have aggro
    if status >= 3 then
        return cSafe.r, cSafe.g, cSafe.b, false
    end

    -- Optional OT check toggle (from new DB)
    if tdb.checkOffTank then
        for _, tankUnit in ipairs(ns.TankUnits) do
            if UnitExists(tankUnit) then
                local otStatus = UnitThreatSituation(tankUnit, unit) or 0
                otStatus = tonumber(otStatus) or 0

                -- Accept 2 or 3 as “OT has it”
                if otStatus >= 2 then
                    return cOT.r, cOT.g, cOT.b, false
                end
            end
        end
    end

    -- No tank has it (THIS UNIT is in combat, so this is meaningful)
    return cBad.r, cBad.g, cBad.b, true
end

-- NOTE:
-- We intentionally do NOT register events here anymore.
-- pom.lua already updates tank cache on relevant events, and keeping both
-- causes duplicate work and inconsistent ordering in refresh cycles.