-- WarBoard_ShardDetector.lua
-- Detects the current server shard ID by parsing creature unit GUIDs.
-- Shard detector.

local addonName, WB = ...

WB.ShardDetector = WB.ShardDetector or {}
local Detector = WB.ShardDetector

-- ── State ──────────────────────────────────────────────────────────────────────
Detector.shardID = nil   -- current shard (nil = unknown)
Detector.zoneID  = nil   -- current map ID

-- Hardcoded NPCs that retain a stale zone_uid across shard transitions
-- (player followers, summoned companions, etc.) — never trust these for the shard.
local BANNED_NPCS = {
    [154297]=true, [150202]=true, [154304]=true, [152108]=true, [151300]=true,
    [151310]=true, [142666]=true, [142668]=true, [69792]=true,  [62821]=true,
    [62822]=true,  [32639]=true,  [32638]=true,  [89715]=true,  [89713]=true,
    [180182]=true, [180181]=true, [180483]=true, [180208]=true, [183143]=true,
}

local VALID_UNIT_TYPES = { Creature = true, Vehicle = true }

-- ── Internal helpers ─────────────────────────────────────────────────────────────
local function IsBanned(npcID)
    return BANNED_NPCS[npcID]
end

local function NotifyPill(shardID)
    if WB.ShardPill and WB.ShardPill.SetShard then
        WB.ShardPill.SetShard(shardID)
    end
end

local function SetShard(zoneUID)
    if Detector.shardID == zoneUID then return end
    Detector.shardID = zoneUID
    NotifyPill(zoneUID)
    if WB.ShardMatcher and WB.ShardMatcher.OnLocalShardChanged then
        WB.ShardMatcher.OnLocalShardChanged(zoneUID)
    end
end

local function TrySetShard(zoneUID)
    if zoneUID and zoneUID ~= Detector.shardID then
        SetShard(zoneUID)
    end
end

-- Parse a creature GUID and attempt a shard update. Returns true if a change occurred.
local function CheckGUID(guid)
    if not guid or type(guid) ~= "string" then return false end
    -- Protect against secret (player) GUIDs in restricted PvP.
    local ok = pcall(function() return guid == "" end)
    if not ok then return false end

    -- Format: unittype-zero-serverID-instanceID-zone_uid-npcID-spawn_uid
    local unittype, _, _, _, zone_uid, npc_id_str = strsplit("-", guid)
    if not VALID_UNIT_TYPES[unittype] then return false end

    local npc_id = tonumber(npc_id_str)
    if not npc_id then return false end
    if IsBanned(npc_id) then return false end

    if zone_uid and zone_uid ~= Detector.shardID then
        TrySetShard(zone_uid)
        return true
    end
    return false
end

-- ── Event frame ──────────────────────────────────────────────────────────────────
local f = CreateFrame("Frame")

local function OnZoneTransition()
    local mapID = C_Map.GetBestMapForUnit("player")
    if not mapID then return end
    if mapID ~= Detector.zoneID then
        Detector.zoneID = mapID
        -- Reset shard on a genuine zone change; it re-resolves on the next creature seen.
        SetShard(nil)
    end
end

local function OnHealthDetection(unit)
    if not UnitExists(unit) then return end
    local guid = UnitGUID(unit)
    if not guid then return end
    if UnitPlayerControlled(unit) then return end
    CheckGUID(guid)
end

f:SetScript("OnEvent", function(_, event, ...)
    if event == "ZONE_CHANGED_NEW_AREA"
    or event == "PLAYER_ENTERING_WORLD"
    or event == "ZONE_CHANGED" then
        OnZoneTransition()

    elseif event == "PLAYER_TARGET_CHANGED" then
        OnHealthDetection("target")

    elseif event == "UPDATE_MOUSEOVER_UNIT" then
        OnHealthDetection("mouseover")

    elseif event == "NAME_PLATE_UNIT_ADDED" then
        local unitToken = ...
        if unitToken then OnHealthDetection(unitToken) end
    end
end)

-- Defer registration to PLAYER_LOGIN. UNIT_HEALTH / COMBAT_LOG_EVENT_UNFILTERED are
-- restricted from plain-frame RegisterEvent in modern WoW; the zone/target/mouseover/
-- nameplate vectors are sufficient to resolve the shard.
local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("PLAYER_LOGIN")
initFrame:SetScript("OnEvent", function()
    initFrame:UnregisterEvent("PLAYER_LOGIN")
    f:RegisterEvent("ZONE_CHANGED_NEW_AREA")
    f:RegisterEvent("PLAYER_ENTERING_WORLD")
    f:RegisterEvent("ZONE_CHANGED")
    f:RegisterEvent("PLAYER_TARGET_CHANGED")
    f:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
    f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
end)

-- ── Public API ───────────────────────────────────────────────────────────────────
-- Returns the current shard ID string, or nil if unknown.
function Detector:GetShardID()
    return self.shardID
end
