-- Enhanced Raid Browser — Boss Tooltip v3
-- Shows BiS items that drop from a boss + alternatives per slot
-- Persists through Blizzard tooltip refreshes via OnTooltipSetUnit hook
local ADDON_NAME, NS = ...
local ETB = NS.ETB
local ERB = ETB.ERB

local BT = {}
ERB.BossTooltip = BT
BT.enabled = true
BT.lastNPCID = nil
BT._lastNPCIsBoss = false
BT._pendingItems = {}

local MAX_ALTS = 3
local HEADER_COLOR = "|cff00ccff"
local SLOT_COLOR = "|cffffff00"
local ICON_OWNED = "|cff00ff00✓|r"
local ICON_NEEDED = "|cffff4444✗|r"
local RANK_COLORS = {
    [1] = "|cffff8000",
    [2] = "|cffa335ee",
    [3] = "|cff0070dd",
}

-- ============================================================================
-- HELPERS
-- ============================================================================

local function PlayerOwnsItem(itemID)
    if not C_TransmogCollection or not C_TransmogCollection.GetItemInfo then return false end
    local ok, appearanceID = pcall(C_TransmogCollection.GetItemInfo, itemID)
    if not ok or not appearanceID then return false end
    if C_TransmogCollection.GetAllAppearanceSources then
        local sOK, allSources = pcall(C_TransmogCollection.GetAllAppearanceSources, appearanceID)
        if sOK and allSources and C_TransmogCollection.PlayerHasTransmogItemModifiedAppearance then
            for _, sid in ipairs(allSources) do
                local hOK, has = pcall(C_TransmogCollection.PlayerHasTransmogItemModifiedAppearance, sid)
                if hOK and has then return true end
            end
        end
    end
    return false
end

local function GetItemDisplay(itemID)
    local name, _, quality, _, _, _, _, _, _, texture = GetItemInfo(itemID)
    if not name then
        GetItemInfo(itemID)
        name = "Loading..."
        quality = 4
    end
    local qColor = "|cffffffff"
    if quality and quality > 0 then
        local r, g, b = GetItemQualityColor(quality)
        qColor = string.format("|cff%02x%02x%02x", r*255, g*255, b*255)
    end
    return name, qColor, texture
end

-- ============================================================================
-- BOSS → PHASE MAPPING
-- Determines which BiS phase a boss belongs to based on instance.
-- PR = Dungeons, T14 raids (MSV, HoF, ToES), Sha of Anger, Galleon
-- P3 = Throne of Thunder, Nalak, Oondasta
-- ============================================================================

local INSTANCE_PHASE = {
    -- Dungeons
    [246]="PR", [302]="PR", [303]="PR", [311]="PR", [312]="PR",
    [313]="PR", [316]="PR", [321]="PR", [324]="PR",
    -- T14 Raids
    [317]="PR", [330]="PR", [320]="PR",
    -- World bosses (default PR, overridden per-encounter below)
    [322]="PR",
    -- T15
    [362]="P3",
}

-- World bosses by encounter ID (instance 322 has mixed phases)
local ENCOUNTER_PHASE = {
    [814]="P3",  -- Nalak
    [826]="P3",  -- Oondasta
}

local function GetBossPhase(npcID)
    if not ETB then return nil end
    if ETB._BossLootLoader then ETB._BossLootLoader(); ETB._BossLootLoader = nil end
    if not ETB.JNpcToEnc or not ETB.JEncounters then return nil end

    local encID = ETB.JNpcToEnc[npcID]
    if not encID then return nil end

    -- Check encounter-level override first (world bosses)
    if ENCOUNTER_PHASE[encID] then return ENCOUNTER_PHASE[encID] end

    -- Check instance-level mapping
    local encData = ETB.JEncounters[encID]
    if encData and encData[2] then
        return INSTANCE_PHASE[encData[2]]
    end

    return nil
end

function BT:AddBiSToTooltip(tooltip, npcID)
    if not tooltip or not npcID then return end
    if not ERB.DataBridge then return end

    local bisItems = ERB.DataBridge:GetBiSForNPC(npcID)
    if not bisItems or not next(bisItems) then return end

    local info = ERB:GetPlayerInfo()
    self._pendingItems = {}

    -- Determine which phase this boss belongs to
    local bossPhase = GetBossPhase(npcID)
    local phasesToSearch = {}
    if bossPhase then
        phasesToSearch = { bossPhase }
    else
        -- Unknown boss: search all phases
        phasesToSearch = ERB.DataBridge:GetPhases()
    end

    -- Collect boss drops from matching phases only
    local slotDrops = {}

    for itemID in pairs(bisItems) do
        for _, slot in ipairs(ERB.DataBridge.SLOT_ORDER) do
            for _, phase in ipairs(phasesToSearch) do
                local slotItems = ERB.DataBridge:GetItemsForSlot(slot, phase)
                for _, item in ipairs(slotItems) do
                    if item.itemID == itemID then
                        if not slotDrops[slot] then slotDrops[slot] = {} end
                        local found = false
                        for _, d in ipairs(slotDrops[slot]) do
                            if d.itemID == itemID then found = true; break end
                        end
                        if not found then
                            slotDrops[slot][#slotDrops[slot] + 1] = {
                                itemID = itemID,
                                rank = item.rank,
                                label = item.label,
                                isBossDrop = true,
                                phase = phase,
                            }
                        end
                    end
                end
            end
        end
    end

    if not next(slotDrops) then return end

    -- Add alternatives from matching phases only
    for slot, drops in pairs(slotDrops) do
        local dropIDs = {}
        for _, d in ipairs(drops) do dropIDs[d.itemID] = true end
        local altCount = 0
        for _, phase in ipairs(phasesToSearch) do
            local slotItems = ERB.DataBridge:GetItemsForSlot(slot, phase)
            for _, item in ipairs(slotItems) do
                if not dropIDs[item.itemID] and altCount < MAX_ALTS then
                    local already = false
                    for _, d in ipairs(drops) do
                        if d.itemID == item.itemID then already = true; break end
                    end
                    if not already then
                        drops[#drops + 1] = {
                            itemID = item.itemID,
                            rank = item.rank,
                            label = item.label,
                            isBossDrop = false,
                            phase = phase,
                        }
                        dropIDs[item.itemID] = true
                        altCount = altCount + 1
                    end
                end
            end
        end
        table.sort(drops, function(a, b)
            if a.isBossDrop ~= b.isBossDrop then return a.isBossDrop end
            return (a.rank or 99) < (b.rank or 99)
        end)
    end

    -- Count needed
    local totalNeeded = 0
    for _, drops in pairs(slotDrops) do
        for _, item in ipairs(drops) do
            if item.isBossDrop and not PlayerOwnsItem(item.itemID) then
                totalNeeded = totalNeeded + 1
            end
        end
    end

    -- Header
    tooltip:AddLine(" ")
    local classColor = info.classColor or "|cffffffff"
    local specName = ERB.DataBridge.playerSpecName or info.specName or "?"
    local srcLabel = ERB.DataBridge:GetSourceLabel(ERB.DataBridge.activeSource) or ""
    local phaseLabel = bossPhase and ERB.DataBridge:GetPhaseName(bossPhase) or ""

    tooltip:AddDoubleLine(
        HEADER_COLOR .. "BiS: |r" .. classColor .. (info.className or "?") .. "|r " .. specName,
        HEADER_COLOR .. totalNeeded .. " needed|r |cff888888(" .. srcLabel .. (phaseLabel ~= "" and (", " .. phaseLabel) or "") .. ")|r"
    )

    -- Render each slot

    for _, slot in ipairs(ERB.DataBridge.SLOT_ORDER) do
        local drops = slotDrops[slot]
        if drops then
            tooltip:AddLine(SLOT_COLOR .. slot .. ":|r")
            for _, item in ipairs(drops) do
                local name, qColor, texture = GetItemDisplay(item.itemID)
                if name == "Loading..." then
                    self._pendingItems[item.itemID] = npcID
                end
                local owned = PlayerOwnsItem(item.itemID)
                local icon = texture and ("|T" .. texture .. ":14:14:0:0|t ") or ""
                local rc = RANK_COLORS[item.rank] or "|cff888888"
                local rankTag = rc .. "#" .. item.rank .. " " .. item.label .. "|r"
                local status = owned and ICON_OWNED or ICON_NEEDED

                local prefix = item.isBossDrop and (" " .. icon) or ("   ")
                local nameStr = item.isBossDrop
                    and (qColor .. name .. "|r")
                    or ("|cff666666" .. name .. "|r")

                if not item.isBossDrop then
                    rankTag = "|cff555555#" .. item.rank .. " " .. item.label .. "|r"
                    local altBoss = ERB.DataBridge:GetBossName(item.itemID)
                    if altBoss then
                        nameStr = nameStr .. " |cff444444(" .. altBoss .. ")|r"
                    end
                end

                tooltip:AddDoubleLine(
                    prefix .. nameStr,
                    rankTag .. " " .. status
                )
            end
        end
    end

    tooltip:Show()
end

function BT:AddNoBiSMessage(tooltip, npcID)
    if not tooltip or not npcID then return end
    local info = ERB:GetPlayerInfo()
    local classColor = info.classColor or "|cffffffff"
    local specName = ERB.DataBridge.playerSpecName or info.specName or "?"
    tooltip:AddLine(" ")
    tooltip:AddLine(
        HEADER_COLOR .. "BiS: |r" .. classColor .. (info.className or "?") .. "|r " .. specName ..
        " — |cff888888No BiS loot from this boss|r"
    )
    tooltip:Show()
end

-- ============================================================================
-- BOSS DETECTION
-- ============================================================================

function BT:ShouldSuppress()
    if UnitAffectingCombat("player") then return true end
    if UnitIsDeadOrGhost("player") then return true end
    return false
end

function BT:IsBossNPC(npcID)
    if ETB then
        if ETB._BossLootLoader then ETB._BossLootLoader(); ETB._BossLootLoader = nil end
        if ETB.JNpcToEnc and ETB.JNpcToEnc[npcID] then
            return true
        end
    end
    local classification = UnitClassification("mouseover")
    return classification == "worldboss"
end

-- ============================================================================
-- EVENT HANDLING
-- ============================================================================

function BT:OnMouseoverUnit()
    if not self.enabled then return end
    if self:ShouldSuppress() then return end
    if not ERB.DataBridge or not ERB.BiSData then return end

    local guid = UnitGUID("mouseover")
    if not guid then return end

    local npcID = ERB:GetNPCIDFromGUID(guid)
    if not npcID then return end

    if npcID == self.lastNPCID then return end
    self.lastNPCID = npcID
    self._lastNPCIsBoss = false

    local bisItems = ERB.DataBridge:GetBiSForNPC(npcID)
    if bisItems and next(bisItems) then
        self._lastNPCIsBoss = true
        self:AddBiSToTooltip(GameTooltip, npcID)
    elseif self:IsBossNPC(npcID) then
        self._lastNPCIsBoss = true
        self:AddNoBiSMessage(GameTooltip, npcID)
    end
end

-- ============================================================================
-- INITIALIZATION
-- ============================================================================

function BT:Initialize()
    if self._initialized then return end

    local frame = CreateFrame("Frame")
    frame:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
    frame:RegisterEvent("PLAYER_REGEN_DISABLED")
    frame:RegisterEvent("GET_ITEM_INFO_RECEIVED")

    frame:SetScript("OnEvent", function(_, event, arg1)
        if event == "UPDATE_MOUSEOVER_UNIT" then
            local guid = UnitGUID("mouseover")
            local currentNPC = guid and ERB:GetNPCIDFromGUID(guid)
            if currentNPC ~= BT.lastNPCID then
                BT.lastNPCID = nil
                BT._lastNPCIsBoss = false
                BT:OnMouseoverUnit()
            end
        elseif event == "PLAYER_REGEN_DISABLED" then
            BT.lastNPCID = nil
            BT._lastNPCIsBoss = false
        elseif event == "GET_ITEM_INFO_RECEIVED" then
            local itemID = arg1
            if itemID and BT._pendingItems and BT._pendingItems[itemID] then
                BT._pendingItems[itemID] = nil
                if not BT._refreshThrottle then
                    BT._refreshThrottle = true
                    C_Timer.After(0.3, function()
                        BT._refreshThrottle = false
                        if UnitExists("mouseover") and GameTooltip:IsShown() and BT.lastNPCID then
                            BT:AddBiSToTooltip(GameTooltip, BT.lastNPCID)
                        end
                    end)
                end
            end
        end
    end)

    self._initialized = true

    -- Persist through Blizzard's internal tooltip refreshes
    GameTooltip:HookScript("OnTooltipSetUnit", function(tooltip)
        if not BT.lastNPCID or BT:ShouldSuppress() then return end
        if not ERB.DataBridge or not ERB.BiSData then return end

        local bisItems = ERB.DataBridge:GetBiSForNPC(BT.lastNPCID)
        if bisItems and next(bisItems) then
            BT:AddBiSToTooltip(tooltip, BT.lastNPCID)
        elseif BT._lastNPCIsBoss then
            BT:AddNoBiSMessage(tooltip, BT.lastNPCID)
        end
    end)
end
