-- UI/WarBoard_MapOverlay.lua
-- Draws bounty targets on the world map (HereBeDragons-Pins), one icon per target.
-- World coordinates arrive from WB.BountyTracker; HBDPins places them on whatever
-- map/zoom the viewer is looking at. Icon art = the target's faction + bounty rank.

local addonName, WB = ...

WB.MapOverlay = WB.MapOverlay or {}
local Overlay = WB.MapOverlay

local HBDPins = LibStub and LibStub("HereBeDragons-Pins-2.0", true)

local targets     = {}    -- [targetGuid] = { worldX, worldY, iID, name, faction, rank, shard, rxAt }
local TARGET_STALE = 130   -- ~3 missed 45s broadcasts before the icon drops

-- Faction + rank → icon atlas (No rank = the faction crest; ranks 1-3 = Warfront waves).
local ICON_ATLAS = {
    Horde = {
        [0] = "HordeSymbol",
        [1] = "Warfront-HordeWave1", [2] = "Warfront-HordeWave2", [3] = "Warfront-HordeWave3",
    },
    Alliance = {
        [0] = "AllianceSymbol",
        [1] = "Warfront-AllianceWave1", [2] = "Warfront-AllianceWave2", [3] = "Warfront-AllianceWave3",
    },
}
local function IconAtlas(faction, rank)
    local set = ICON_ATLAS[faction] or ICON_ATLAS.Horde
    return set[rank or 0] or set[0]
end

-- Supply Runner / stolen-cargo map icon. Preferred art lives in the pvpscoreboard column-stat
-- atlas family (interface/pvpscoreboard/pvpscoreboardcolumnstats); until the exact region is
-- confirmed we fall back to the horn event icon, tinting stolen cargo red to set it apart.
local SUPPLY_ATLAS = "ColumnIcon-FlagCapture2"   -- interface/pvpscoreboard/pvpscoreboardcolumnstats
local function ApplyRoleIcon(tex, role)
    tex:SetVertexColor(1, 1, 1)
    if not pcall(tex.SetAtlas, tex, SUPPLY_ATLAS) or not tex:GetAtlas() then
        tex:SetTexture("Interface\\Minimap\\ObjectIcons")   -- fallback if the atlas is unavailable
    end
    if role == "stolen_cargo" then tex:SetVertexColor(1, 0.45, 0.35) end   -- red-tinted for stolen
end

-- ── Icon pool ──────────────────────────────────────────────────────────────────────
local pool = {}
local function GetIcon(idx)
    if pool[idx] then return pool[idx] end
    local icon = CreateFrame("Frame", nil, UIParent)
    icon:SetSize(22, 22)
    icon:EnableMouse(true)
    icon.tex = icon:CreateTexture(nil, "ARTWORK")
    icon.tex:SetAllPoints()
    icon.label = icon:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    icon.label:SetPoint("TOP", icon, "BOTTOM", 0, -1)
    icon.label:SetTextColor(1, 0.35, 0.30)
    icon:SetScript("OnEnter", function(self)
        if not self.tipTitle then return end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(self.tipTitle, 1, 0.82, 0.2)
        if self.tipBody then GameTooltip:AddLine(self.tipBody, 0.8, 0.8, 0.8) end
        GameTooltip:Show()
    end)
    icon:SetScript("OnLeave", function() GameTooltip:Hide() end)
    pool[idx] = icon
    return icon
end

-- ── Refresh (place all live target icons) ───────────────────────────────────────────
function Overlay:Refresh()
    if not HBDPins then return end
    if not WorldMapFrame or not WorldMapFrame:IsShown() then return end
    HBDPins:RemoveAllWorldMapIcons("WarBoard")

    local now, idx = time(), 0
    for guid, t in pairs(targets) do
        if t.worldX and t.worldY and t.iID and (now - (t.rxAt or 0)) < TARGET_STALE then
            idx = idx + 1
            local icon = GetIcon(idx)
            local short = t.name and (t.name:match("^([^%-]+)") or t.name) or "Bounty"
            short = (WB.SanitizeDisplay and WB.SanitizeDisplay(short, 24)) or short   -- wire name → sanitize
            icon.label:SetText(short)
            local ageS = now - (t.rxAt or now)
            -- Icon priority: stolen cargo > supply runner > bounty. The role comes from WB.Cargo
            -- (set when the viewer chose to TRACK a runner), independent of the bounty stream.
            local role = WB.Cargo and WB.Cargo.RoleFor and WB.Cargo.RoleFor(guid)
            if role then
                ApplyRoleIcon(icon.tex, role)
                local label = (role == "stolen_cargo") and "Stolen Cargo Carrier" or "Supply Runner"
                icon.tipTitle = "|cffffd100" .. label .. ":|r " .. short
                if t.rank and t.rank > 0 then icon.tipTitle = icon.tipTitle .. "  |cffff4040(Bounty)|r" end
            else
                icon.tex:SetVertexColor(1, 1, 1)
                icon.tex:SetAtlas(IconAtlas(t.faction, t.rank))
                icon.tipTitle = "|cffff4040Bounty:|r " .. short
            end
            local shard = t.shard and ((WB.SanitizeDisplay and WB.SanitizeDisplay(tostring(t.shard), 12)) or tostring(t.shard))
            icon.tipBody  = (shard and ("Shard " .. shard .. "  •  ") or "") .. "seen " .. math.floor(ageS) .. "s ago"
            HBDPins:AddWorldMapIconWorld("WarBoard", icon, t.iID, t.worldX, t.worldY,
                HBD_PINS_WORLDMAP_SHOW_CONTINENT)
        end
    end
end

-- ── Public API (called by WB.BountyTracker) ─────────────────────────────────────────
function Overlay:UpdateTarget(data)
    if type(data) ~= "table" or not data.targetGuid then return end
    targets[data.targetGuid] = {
        worldX = data.worldX, worldY = data.worldY, iID = data.iID,
        name = data.targetName, faction = data.faction, rank = data.rank,
        shard = data.shard, rxAt = time(),
    }
    if WorldMapFrame and WorldMapFrame:IsShown() then self:Refresh() end
end

function Overlay:RemoveTarget(guid)
    if guid then targets[guid] = nil else wipe(targets) end
    if WorldMapFrame and WorldMapFrame:IsShown() then self:Refresh() end
end

function Overlay:Purge()
    local now, dropped = time(), false
    for guid, t in pairs(targets) do
        if (now - (t.rxAt or 0)) >= TARGET_STALE then targets[guid] = nil; dropped = true end
    end
    -- If a stale target left, redraw once so its icon clears (only matters while the map is open).
    if dropped and WorldMapFrame and WorldMapFrame:IsShown() then self:Refresh() end
end

-- ── Hooks ──────────────────────────────────────────────────────────────────────────
if WorldMapFrame then
    WorldMapFrame:HookScript("OnShow", function() Overlay:Refresh() end)
    if WorldMapFrame.RegisterCallback then
        WorldMapFrame:RegisterCallback("MapChanged", function() Overlay:Refresh() end, Overlay)
    end
end

-- No per-second rebuild: live icons are redrawn the instant a position arrives (UpdateTarget) and
-- are repositioned as you pan/zoom by HBDPins itself. A single slow sweep just expires targets that
-- stopped broadcasting (Purge refreshes once, if the map is open, to clear their icon + freshen the
-- "seen Xs ago" text). This is what stops the map-open micro-stutter.
C_Timer.NewTicker(15, function() Overlay:Purge() end)
