-- WarBoard_Sightings.lua
-- "Warm bucket" of nearby enemies.
--
-- Every War Board user passively records the OPPOSITE-faction players it sees (via
-- nameplate / target / mouseover) on its current shard — the same vectors JARVIS uses.
-- When you are a bounty TARGET, this list is shared with your hunters (the people who
-- acknowledged your bounty) so they can find friendly players near you to group up with
-- and converge on your shard. Those "enemies of the target" are, by definition, the
-- hunter's OWN faction, so the hunter can whisper them or request an invite.
--
-- Stored per sighting: name, realm, level, race, class token, guild (when readable),
-- shard, and the time last seen. Anything older than 15 minutes is pruned.

local addonName, WB = ...
WB.Sightings = WB.Sightings or {}
local S = WB.Sightings

local WINDOW    = 15 * 60   -- keep a sighting for 15 minutes
local MAX_SHARE = 30        -- cap the list we broadcast / cache per target

local function MyFaction() return WB.faction or UnitFactionGroup("player") or "Horde" end
local function MyShard()
    return WB.ShardDetector and WB.ShardDetector.GetShardID and WB.ShardDetector:GetShardID() or nil
end

-- ── My own sightings (collector) ───────────────────────────────────────────────
local seen = {}   -- [name-realm] = { name, realm, level, race, raceFile, classToken, guild, shard, t }

local function PruneSeen()
    local now = time()
    for k, e in pairs(seen) do
        if (now - (e.t or 0)) > WINDOW then seen[k] = nil end
    end
end

-- Record (or refresh) an enemy player from a unit token.
local function Record(unit)
    if WB.InInstance() then return end   -- don't collect enemies in instanced content
    if not UnitExists(unit) or not UnitIsPlayer(unit) then return end
    if UnitIsUnit(unit, "player") then return end
    if UnitInParty(unit) or UnitInRaid(unit) then return end
    -- Only opposite-faction players. UnitFactionGroup reports the TRUE faction (so most
    -- race-disguise toys don't fool it).
    local uf = UnitFactionGroup(unit)
    if not uf or uf == MyFaction() then return end
    local name, realm = UnitNameUnmodified(unit)
    if not name or name == "" or name == UNKNOWN then return end
    realm = (realm and realm ~= "") and realm or (GetNormalizedRealmName() or "")

    local key = name .. "-" .. realm
    local e   = seen[key] or { name = name, realm = realm }
    local level = UnitLevel(unit)
    if level and level > 0 then e.level = level end
    local race, raceFile = UnitRace(unit)
    if race     then e.race     = race     end
    if raceFile then e.raceFile = raceFile end
    local _, classToken = UnitClass(unit)
    if classToken then e.classToken = classToken end
    local guild = GetGuildInfo(unit)
    if guild and guild ~= "" then e.guild = guild end
    e.shard = MyShard() or e.shard
    e.t = time()
    seen[key] = e
end

-- A capped, recency-sorted snapshot of my recent enemy sightings (for broadcasting).
function S.GetRecent()
    PruneSeen()
    local out = {}
    for _, e in pairs(seen) do out[#out + 1] = e end
    table.sort(out, function(a, b) return (a.t or 0) > (b.t or 0) end)
    for i = #out, MAX_SHARE + 1, -1 do out[i] = nil end
    return out
end

function S.Count()
    PruneSeen()
    local n = 0
    for _ in pairs(seen) do n = n + 1 end
    return n
end

-- ── Remote caches (a target I'm hunting → the bucket it shared) ─────────────────
local remote = {}   -- [targetGuid] = { list = {...}, t }

function S.RememberRemote(targetGuid, list, t)
    if not targetGuid or type(list) ~= "table" then return end
    remote[targetGuid] = { list = list, t = t or time() }
    if WB.UI and WB.UI.RefreshSightings then WB.UI.RefreshSightings(targetGuid) end
end

-- Recency-sorted, freshness-filtered list of enemies the target reported.
function S.GetForTarget(targetGuid)
    local r = targetGuid and remote[targetGuid]
    if not r then return {} end
    local now, out = time(), {}
    for _, e in ipairs(r.list) do
        if (now - (e.t or 0)) <= WINDOW then out[#out + 1] = e end
    end
    table.sort(out, function(a, b) return (a.t or 0) > (b.t or 0) end)
    return out
end

function S.ForgetRemote(targetGuid)
    if targetGuid then remote[targetGuid] = nil end
end

-- ── Event frame ────────────────────────────────────────────────────────────────
-- Nameplate/target/mouseover events are restricted before login; defer registration.
do
    local f = CreateFrame("Frame")
    f:SetScript("OnEvent", function(_, event, unit)
        if     event == "PLAYER_TARGET_CHANGED"  then Record("target")
        elseif event == "UPDATE_MOUSEOVER_UNIT"  then Record("mouseover")
        elseif event == "NAME_PLATE_UNIT_ADDED" and unit then Record(unit) end
    end)
    local init = CreateFrame("Frame")
    init:RegisterEvent("PLAYER_LOGIN")
    init:SetScript("OnEvent", function()
        init:UnregisterEvent("PLAYER_LOGIN")
        f:RegisterEvent("PLAYER_TARGET_CHANGED")
        f:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
        f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
    end)
end
