--[[
============================================================================
ETB - TooltipEnhancer.lua (Side Panel)
============================================================================
Shows ETB info (collection, source, crafting) in a separate panel
anchored next to the Blizzard tooltip. No longer modifies GameTooltip.
============================================================================
]]--

local ADDON_NAME, NS = ...
local ETB = NS.ETB

ETB.TooltipEnhancer = {
    initialized = false,
}

local TE = ETB.TooltipEnhancer
local PANEL_WIDTH = 260
local MAX_LINES = 20

-- =============================================================================
-- SIDE PANEL FRAME
-- =============================================================================

local sidePanel = nil
local sidePanelLines = {}
local sidePanelLineCount = 0
local lastItemID = nil

local function GetOrCreateSidePanel()
    if sidePanel then return sidePanel end

    sidePanel = CreateFrame("Frame", "ETBTooltipSidePanel", UIParent, "BackdropTemplate")
    sidePanel:SetSize(PANEL_WIDTH, 60)
    sidePanel:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Gold-Border",
        tile = true, tileSize = 32, edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 },
    })
    sidePanel:SetFrameStrata("TOOLTIP")
    sidePanel:Hide()

    -- Title
    sidePanel.title = sidePanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    sidePanel.title:SetPoint("TOPLEFT", 8, -8)
    sidePanel.title:SetText("|cffffd100Enhanced Transmog Browser|r")

    return sidePanel
end

local function AddPanelLine(text, r, g, b)
    sidePanelLineCount = sidePanelLineCount + 1
    local line = sidePanelLines[sidePanelLineCount]
    if not line then
        line = sidePanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        line:SetWidth(PANEL_WIDTH - 16)
        line:SetJustifyH("LEFT")
        line:SetWordWrap(true)
        sidePanelLines[sidePanelLineCount] = line
    end
    line:SetPoint("TOPLEFT", 8, -22 - (sidePanelLineCount - 1) * 14)
    line:SetText(text)
    if r then line:SetTextColor(r, g, b) end
    line:Show()
end

local function AddPanelDoubleLine(leftText, rightText)
    sidePanelLineCount = sidePanelLineCount + 1
    local line = sidePanelLines[sidePanelLineCount]
    if not line then
        line = sidePanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        line:SetWidth(PANEL_WIDTH - 16)
        line:SetJustifyH("LEFT")
        line:SetWordWrap(true)
        sidePanelLines[sidePanelLineCount] = line
    end
    line:SetPoint("TOPLEFT", 8, -22 - (sidePanelLineCount - 1) * 14)
    line:SetText(leftText .. "  " .. (rightText or ""))
    line:Show()
end

local function ClearPanel()
    for i = 1, sidePanelLineCount do
        sidePanelLines[i]:Hide()
    end
    sidePanelLineCount = 0
    lastItemID = nil
end

-- v15.8.11: Smart positioning that respects Blizzard's compare-tooltip
-- chain (ShoppingTooltip1/2/3). When the user holds Shift to compare an
-- item against currently-equipped pieces, Blizzard spawns 1-2 compare
-- tooltips beside GameTooltip. Previous versions of ETB anchored the
-- side panel to one side of GameTooltip, which competed for the same
-- space the compare tooltips were trying to use.
--
-- New behavior:
--   1. Find which ShoppingTooltips are currently visible
--   2. Determine which side (left or right of GameTooltip) they're on
--   3. Anchor the ETB panel to the OUTERMOST edge of the tooltip chain
--      — beyond the compare tooltips — so nothing overlaps.
--
-- Resulting layouts:
--   No compare:    [ETB panel] [GameTooltip]                              (panel LEFT)
--   Compare LEFT:  [ETB panel] [Shopping1] [Shopping2] [GameTooltip]      (panel beyond)
--   Compare RIGHT: [GameTooltip] [Shopping1] [Shopping2] [ETB panel]      (panel beyond)
--
-- If the chosen anchor side would clip off-screen (panel doesn't fit),
-- fall back to the opposite side.

local function getVisibleShoppingChain()
    -- Returns the list of visible ShoppingTooltips in frame-position order
    -- (leftmost first), or empty if none visible. Identifies which side
    -- of GameTooltip they sit on via their .Left coords.
    local visible = {}
    for _, name in ipairs({ "ShoppingTooltip1", "ShoppingTooltip2", "ShoppingTooltip3" }) do
        local t = _G[name]
        if t and t:IsShown() and t:GetLeft() then
            table.insert(visible, t)
        end
    end
    if #visible == 0 then return visible, nil end

    -- Sort by left edge so visible[1] is the leftmost frame
    table.sort(visible, function(a, b) return a:GetLeft() < b:GetLeft() end)

    -- Compare chain is on the LEFT if the leftmost shopping tooltip is to
    -- the left of GameTooltip's left edge, otherwise RIGHT.
    local gtLeft = GameTooltip:GetLeft()
    local side = (gtLeft and visible[1]:GetLeft() < gtLeft) and "LEFT" or "RIGHT"
    return visible, side
end

local function ShowPanel()
    if sidePanelLineCount == 0 then
        sidePanel:Hide()
        return
    end
    -- Size to content
    local height = 30 + sidePanelLineCount * 14
    sidePanel:SetHeight(height)

    sidePanel:ClearAllPoints()

    local chain, chainSide = getVisibleShoppingChain()
    local gtLeft = GameTooltip:GetLeft()
    local gtRight = GameTooltip:GetRight()
    local screenW = UIParent:GetRight() or 1920
    local PAD = 4

    -- Compute candidate anchors
    -- "ANCHOR_LEFT": panel.TOPRIGHT -> anchorFrame.TOPLEFT (panel on left)
    -- "ANCHOR_RIGHT": panel.TOPLEFT -> anchorFrame.TOPRIGHT (panel on right)
    local anchorFrame, mode

    if #chain == 0 then
        -- No compare tooltips: anchor to GameTooltip on the LEFT by default,
        -- with right fallback if not enough screen space.
        if gtLeft and gtLeft >= PANEL_WIDTH + PAD then
            anchorFrame, mode = GameTooltip, "LEFT"
        else
            anchorFrame, mode = GameTooltip, "RIGHT"
        end
    else
        -- Compare chain present. Anchor to the outermost shopping tooltip
        -- on the chain's side so the layout is contiguous and we never
        -- overlap any of them.
        if chainSide == "LEFT" then
            -- chain is left of GameTooltip; outermost = leftmost shopping = chain[1]
            local leftmost = chain[1]
            local leftmostLeft = leftmost:GetLeft() or 0
            if leftmostLeft >= PANEL_WIDTH + PAD then
                anchorFrame, mode = leftmost, "LEFT"  -- panel to the left of the chain
            else
                -- No room on the far left; the chain itself is hogging the
                -- left side. Anchor to GameTooltip on its RIGHT (the open
                -- side opposite the chain).
                anchorFrame, mode = GameTooltip, "RIGHT"
            end
        else
            -- chain is right of GameTooltip; outermost = rightmost shopping = chain[#chain]
            local rightmost = chain[#chain]
            local rightmostRight = rightmost:GetRight() or 0
            if (screenW - rightmostRight) >= PANEL_WIDTH + PAD then
                anchorFrame, mode = rightmost, "RIGHT"  -- panel to the right of the chain
            else
                -- No room on the far right; anchor to GameTooltip on its LEFT
                anchorFrame, mode = GameTooltip, "LEFT"
            end
        end
    end

    -- Apply the chosen anchor
    if mode == "LEFT" then
        sidePanel:SetPoint("TOPRIGHT", anchorFrame, "TOPLEFT", -PAD, 0)
    else
        sidePanel:SetPoint("TOPLEFT", anchorFrame, "TOPRIGHT", PAD, 0)
    end

    sidePanel:SetFrameLevel(GameTooltip:GetFrameLevel() + 1)
    sidePanel:Show()
end

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

function TE:Initialize()
    if self.initialized then return end

    if not ETB.Settings:Get("tooltips.enhance") then
        return
    end

    GetOrCreateSidePanel()

    -- Hook GameTooltip
    GameTooltip:HookScript("OnTooltipSetItem", function(tooltip)
        TE:OnTooltipSetItem(tooltip)
    end)

    -- Hide panel when tooltip hides
    GameTooltip:HookScript("OnHide", function()
        if sidePanel then sidePanel:Hide() end
        lastItemID = nil
    end)

    -- Hook ItemRefTooltip (linked items in chat)
    if ItemRefTooltip then
        ItemRefTooltip:HookScript("OnTooltipSetItem", function(tooltip)
            TE:OnTooltipSetItem(tooltip)
        end)
        ItemRefTooltip:HookScript("OnHide", function()
            if sidePanel then sidePanel:Hide() end
            lastItemID = nil
        end)
    end

    -- v15.5.0: Hook the secondary tooltips that have separate frames
    -- (shopping-compare tooltips show alongside the main GameTooltip when
    -- comparing equipment). MoP 5.4.8 exposes ShoppingTooltip1/2/3.
    -- v15.8.11: Also hook OnShow/OnHide so the side panel repositions
    -- itself when the compare chain appears or disappears (the chain
    -- spawns slightly after the main tooltip, so the OnTooltipSetItem
    -- pass on GameTooltip alone can't see them yet).
    for _, name in ipairs({ "ShoppingTooltip1", "ShoppingTooltip2", "ShoppingTooltip3" }) do
        local t = _G[name]
        if t then
            t:HookScript("OnTooltipSetItem", function(tooltip)
                TE:OnTooltipSetItem(tooltip)
            end)
            t:HookScript("OnShow", function()
                -- Compare tooltip just spawned — re-anchor the side panel
                -- so it sits at the outermost end of the chain.
                if sidePanel and sidePanel:IsShown() then
                    ShowPanel()
                end
            end)
            t:HookScript("OnHide", function()
                -- Compare tooltip dismissed — re-anchor in case the chain
                -- collapsed to just GameTooltip again.
                if sidePanel and sidePanel:IsShown() then
                    ShowPanel()
                end
            end)
        end
    end

    -- Note on AH / mail / vendor / merchant / loot / quest-reward tooltips:
    -- All of these are populated on the standard GameTooltip frame via
    -- SetAuctionItem / SetInboxItem / SetMerchantItem / etc. Since we hook
    -- GameTooltip:OnTooltipSetItem above, ALL of those surfaces are
    -- automatically covered — no per-surface hooks needed.

    -- Disable CollectionTooltip if it exists (we handle collection status now)
    if ETB.CollectionTooltip then
        ETB.CollectionTooltip.enabled = false
    end

    self.initialized = true
end

-- =============================================================================
-- TOOLTIP HANDLER — populate side panel
-- =============================================================================

function TE:OnTooltipSetItem(tooltip)
    if not tooltip or not sidePanel then return end

    local _, link = tooltip:GetItem()
    if not link then return end

    local itemID = tonumber(link:match("item:(%d+)"))
    if not itemID then return end

    -- Skip if same item (avoid flicker from repeated OnTooltipSetItem)
    if itemID == lastItemID then return end
    lastItemID = itemID

    ClearPanel()

    -- Collection status
    self:AddCollectionInfo(itemID)

    -- Source info
    if ETB.Settings:Get("tooltips.showSource") then
        self:AddSourceInfo(itemID)
    end

    -- Crafting info
    self:AddCraftingInfo(itemID)

    -- Favorite
    if ETB.FavoritesManager and ETB.FavoritesManager:IsFavorite(itemID) then
        AddPanelLine("|cffffd100★ Favorited|r")
    end

    -- Wishlist
    if ETB.Wishlist and ETB.Wishlist:HasItem(itemID) then
        AddPanelLine("|cffffcc00On your wishlist|r")
    end

    -- ERB BiS info
    local ERB = ETB.ERB
    if ERB and ERB.DataBridge then
        local bisInfo = ERB.DataBridge:GetItemBiSInfo(itemID)
        if bisInfo then
            AddPanelLine(" ")
            local rc = bisInfo.rank == 1 and "|cffff8000" or "|cffa335ee"
            AddPanelLine(rc .. "BiS Rank #" .. bisInfo.rank .. " — " .. bisInfo.label .. "|r")
            local bossName = ERB.DataBridge:GetBossName(itemID)
            if bossName then
                AddPanelLine("|cff888888Drops from: " .. bossName .. "|r")
            end
        end
    end

    -- Item IDs (debug)
    if ETB.Settings:Get("tooltips.showItemIDs") then
        AddPanelLine(" ")
        AddPanelLine("|cff888888ItemID: " .. itemID .. "|r")
    end

    ShowPanel()
end

-- =============================================================================
-- INFO SECTIONS (write to side panel, not tooltip)
-- =============================================================================

function TE:AddCollectionInfo(itemID)
    -- v15.5.0: delegates to Core/CollectionAPI for both the collection-state
    -- query AND the "can the player even learn this?" check. The 3-fallback
    -- resolution chain that lived here has moved into CollectionAPI:GetState.
    local CAPI = ETB.CollectionAPI
    if not CAPI then return end

    -- Pull both pieces of info from a single resolve (cached together)
    local state = CAPI:GetState(itemID)
    local canLearn, reason = CAPI:CanLearn(itemID)

    -- NO_APPEARANCE: silently skip — item isn't transmoggable. No useful info to show.
    -- nil:           item not loaded yet; OnTooltipSetItem will fire again when
    --                GET_ITEM_INFO_RECEIVED arrives. Skip rather than show a flashing line.
    if not state or state == "NO_APPEARANCE" then return end

    -- Show collection-status line (gated by tooltips.showCollected)
    if ETB.Settings:Get("tooltips.showCollected") then
        if state == "COLLECTED" then
            AddPanelLine("|cff00ff00✓ Appearance Collected|r")
        elseif state == "KNOWN_OTHER" then
            -- v15.5.0: distinguish "I own this exact source" from
            -- "I have the appearance via a different item"
            AddPanelLine("|cff66ccff✓ Known from another item|r")
        elseif state == "KNOWN_OTHER_DIFFERENT_ARMOR" then
            -- v15.8.12: the player has the visual collected, but only
            -- via a source they can't equip (e.g. mail when they're plate).
            -- In MoP 5.4.8 transmog is per-armor-type — the appearance is
            -- NOT actually usable on this item. Distinct color/message so
            -- the player understands why this isn't "collected".
            AddPanelLine("|cffff9900⚠ Appearance only from a different armor type|r")
        else
            AddPanelLine("|cffff6666✗ Not Collected|r")
        end
    end

    -- Show can't-learn reason if applicable (gated by tooltips.showCantLearn)
    -- Only meaningful when the appearance is NOT_COLLECTED OR
    -- KNOWN_OTHER_DIFFERENT_ARMOR — in both cases the queried item itself
    -- isn't usable for transmog, so showing WHY is informative. For pure
    -- KNOWN_OTHER the player obviously can use it (or they'd see the
    -- different-armor state instead), so we skip the reason there.
    if ETB.Settings:Get("tooltips.showCantLearn") and not canLearn and reason then
        if state == "NOT_COLLECTED" or state == "KNOWN_OTHER_DIFFERENT_ARMOR" then
            local msg
            if reason == "WRONG_ARMOR_TYPE" then
                msg = "|cffff6666Cannot learn — wrong armor type|r"
            elseif reason == "WRONG_WEAPON_TYPE" then
                msg = "|cffff6666Cannot learn — wrong weapon type|r"
            elseif reason == "NOT_EQUIPPABLE" then
                msg = nil  -- don't say this on rings/trinkets etc., it's noise
            elseif reason == "WRONG_ITEM_CLASS" then
                msg = nil  -- ditto, internal-only
            end
            if msg then AddPanelLine(msg) end
        end
    end
end

function TE:AddSourceInfo(itemID)
    if ETB.SourceMapper and ETB.SourceMapper.initialized then
        local tooltipLines = ETB.SourceMapper:FormatSourceTooltip(itemID)
        if tooltipLines and #tooltipLines > 0 then
            for _, line in ipairs(tooltipLines) do
                AddPanelLine(line)
            end
            return
        end
    end

    if ETB.SourceData_ATT then
        local source = ETB.SourceData_ATT:GetPrimarySource(itemID)
        if source and source.name ~= "Unknown" then
            AddPanelLine("|cffffd100Source:|r " .. source.name)
            if source.location and source.location ~= "" then
                AddPanelLine("|cffffd100Location:|r " .. source.location)
            end
            if ETB.TierPieceToToken then
                local tokenID = ETB.TierPieceToToken[itemID]
                if tokenID then
                    local tokenName = GetItemInfo(tokenID)
                    if tokenName then
                        AddPanelLine("|cffff8000Token:|r " .. tokenName)
                    end
                end
            end
        end
    end
end

local PROFESSION_NAMES = {
    [164]="Blacksmithing",[165]="Leatherworking",[171]="Alchemy",
    [182]="Herbalism",[186]="Mining",[197]="Tailoring",
    [202]="Engineering",[333]="Enchanting",[755]="Jewelcrafting",
    [773]="Inscription",[185]="Cooking",[129]="First Aid",
}

function TE:AddCraftingInfo(itemID)
    if not ETB.RecipeData then return end
    local recipe = ETB.RecipeData[itemID]
    if not recipe then return end

    local skillLine = recipe[2] or 0
    local spellID = recipe[4] or recipe[3] or 0
    local profName = PROFESSION_NAMES[skillLine] or "Crafted"

    local skillStr = ""
    if spellID > 0 then
        local spellName = GetSpellInfo(spellID)
        if spellName then skillStr = " |cff888888(" .. spellName .. ")|r" end
    end

    AddPanelLine("|cff66bbffCrafted: " .. profName .. "|r" .. skillStr)

    if ETB.ReagentData and spellID > 0 then
        local reags = ETB.ReagentData[spellID]
        if reags then
            AddPanelLine("|cff888888Reagents:|r")
            for _, entry in ipairs(reags) do
                local reagName = GetItemInfo(entry[1]) or ("Item #" .. entry[1])
                AddPanelLine("  " .. reagName .. " x" .. entry[2])
            end
        end
    end
end

-- =============================================================================
-- ENABLE/DISABLE
-- =============================================================================

function TE:Enable()
    ETB.Settings:Set("tooltips.enhance", true)
    if not self.initialized then self:Initialize() end
end

function TE:Disable()
    ETB.Settings:Set("tooltips.enhance", false)
end

function TE:Toggle()
    if ETB.Settings:Get("tooltips.enhance") then self:Disable() else self:Enable() end
end
