local addonName, ns = ...
local L = ns.L

-- Cached globals
local CreateFrame = CreateFrame
local hooksecurefunc = hooksecurefunc
local GameTooltip = GameTooltip
local C_Timer = C_Timer
local unpack = unpack or table.unpack
local _G = _G

ns.Integration = {}
local Integration = ns.Integration

-- Own sidebar-tab visuals (mirrors the Blizzard sidebar tab look).
-- Icon point matches PaperDollSidebarTabTemplate (BOTTOM, 1, 0).
-- TexCoords inset crops the logo's thick gold frame so it reads like the
-- built-in tab icons instead of overflowing the tab chrome.
local TAB_BG_TEX      = "Interface\\PaperDollInfoFrame\\PaperDollSidebarTabs"
local TAB_BG_ACTIVE   = { 0.01562500, 0.79687500, 0.78906250, 0.95703125 }
local TAB_BG_INACTIVE = { 0.01562500, 0.79687500, 0.61328125, 0.78125000 }
local ICON_TEX        = "Interface\\AddOns\\StatOrb\\media\\logo_s.tga"
local ICON_SIZE       = { 24, 26 }
local ICON_POINT      = { "BOTTOM", 1, 0 }
local ICON_TEXCOORD   = { 0.12, 0.88, 0.12, 0.88 }

-- Lazily created state.
local pane          -- our content pane (holds the embedded chart)
local tab           -- our standalone sidebar tab button
local hooked = false
local statsPaneStripped = false

-- True when embed should keep the ElvUI-skinned CharacterStatsPane as a
-- transparent backdrop (crest + glass), instead of hiding it.
local function WantsElvBackdrop()
    local t = ns.db and ns.db.theme
    if t == "elvui" then return true end
    if t == "classic" or t == "metal" then return false end
    -- "auto": follow flat-skin addons (same idea as Chart theme resolution).
    local IsAddOnLoaded = (C_AddOns and C_AddOns.IsAddOnLoaded) or _G.IsAddOnLoaded
    if type(IsAddOnLoaded) ~= "function" then return false end
    local skins = { "ElvUI", "Aurora", "AuroraClassic", "NDui", "Tukui" }
    for i = 1, #skins do
        local ok, loaded = pcall(IsAddOnLoaded, skins[i])
        if ok and loaded then return true end
    end
    return false
end

-- Hides CharacterStatsPane widgets but keeps ClassBackground (ElvUI glass + crest).
local function StripStatsPaneContent()
    local sp = CharacterStatsPane
    if not sp then return end
    local hideKeys = {
        "ItemLevelCategory",
        "ItemLevelFrame",
        "AttributesCategory",
        "EnhancementsCategory",
    }
    for i = 1, #hideKeys do
        local child = sp[hideKeys[i]]
        if child and child.Hide then
            child:Hide()
        end
    end
    if sp.statsFramePool and sp.statsFramePool.EnumerateActive then
        for frame in sp.statsFramePool:EnumerateActive() do
            if frame and frame.Hide then
                frame:Hide()
            end
        end
    end
    if sp.ClassBackground then
        sp.ClassBackground:Show()
    end
    statsPaneStripped = true
end

-- Re-shows CharacterStatsPane widgets and forces a Blizzard stats refresh.
local function RestoreStatsPaneIfNeeded()
    if not statsPaneStripped then return end
    statsPaneStripped = false

    local sp = CharacterStatsPane
    if not sp then return end

    -- Keep the embed overlay from covering the restored stats list.
    if pane then
        pane:Hide()
        if pane:GetParent() == sp then
            pane:SetParent(PaperDollFrame)
            pane:ClearAllPoints()
            pane:SetAllPoints(sp)
        end
    end

    local showKeys = {
        "ItemLevelCategory",
        "ItemLevelFrame",
        "AttributesCategory",
        "EnhancementsCategory",
    }
    for i = 1, #showKeys do
        local child = sp[showKeys[i]]
        if child and child.Show then
            child:Show()
        end
    end

    if type(PaperDollFrame_UpdateStats) == "function" then
        pcall(PaperDollFrame_UpdateStats)
    end
end

-- While our ElvUI backdrop keeps CharacterStatsPane shown, force Blizzard
-- sidebar tabs to look inactive so only the StatOrb tab reads as selected.
local function DeselectBlizzardSidebarTabs()
    for i = 1, 3 do
        local t = _G["PaperDollSidebarTab" .. i]
        if t then
            if t.TabBg then
                t.TabBg:SetTexCoord(
                    TAB_BG_INACTIVE[1], TAB_BG_INACTIVE[2],
                    TAB_BG_INACTIVE[3], TAB_BG_INACTIVE[4])
            end
            if t.Hider then
                t.Hider:Show()
            end
            if t.Icon then
                pcall(function() t.Icon:SetDesaturated(true) end)
            end
        end
    end
end
-- True when the live client exposes the character-frame sidebar-tab system.
local function CanEmbed()
    return CharacterFrame ~= nil
        and PaperDollFrame ~= nil
        and PaperDollSidebarTabs ~= nil
        and CharacterStatsPane ~= nil
        and _G["PaperDollSidebarTab1"] ~= nil
        and type(PaperDollFrame_SetSidebar) == "function"
        and type(PaperDollFrame_UpdateSidebarTabs) == "function"
end
Integration.CanEmbed = CanEmbed

-- Resolves a built-in sidebar pane (1=stats, 2=titles, 3=equipment).
local function GetBlizzardPane(i)
    if type(GetPaperDollSideBarFrame) == "function" then
        local ok, frame = pcall(GetPaperDollSideBarFrame, i)
        if ok and frame then return frame end
    end
    if i == 1 then return CharacterStatsPane end
    if i == 2 then return PaperDollFrame and PaperDollFrame.TitleManagerPane end
    if i == 3 then return PaperDollFrame and PaperDollFrame.EquipmentManagerPane end
    return nil
end

local function AnyBlizzardPaneShown()
    for i = 1, 3 do
        local f = GetBlizzardPane(i)
        if f and f:IsShown() then return true end
    end
    return false
end

-- Updates our tab's active/inactive appearance.
local function SetTabActive(active)
    if not tab then return end
    if active then
        tab.TabBg:SetTexCoord(TAB_BG_ACTIVE[1], TAB_BG_ACTIVE[2], TAB_BG_ACTIVE[3], TAB_BG_ACTIVE[4])
        tab.Hider:Hide()
        pcall(function() tab.Icon:SetDesaturated(false) end)
    else
        tab.TabBg:SetTexCoord(TAB_BG_INACTIVE[1], TAB_BG_INACTIVE[2], TAB_BG_INACTIVE[3], TAB_BG_INACTIVE[4])
        tab.Hider:Show()
        pcall(function() tab.Icon:SetDesaturated(true) end)
    end
end

-- Creates the pane the embedded chart fills (over the stats column).
local function EnsurePane()
    if pane then return pane end
    pane = CreateFrame("Frame", "StatOrbPane", PaperDollFrame)
    pane:Hide()
    pane:EnableMouse(true)
    pane:ClearAllPoints()
    if CharacterStatsPane then
        pane:SetAllPoints(CharacterStatsPane)
    else
        local anchor = CharacterFrameInsetRight or PaperDollFrame
        pane:SetPoint("TOPLEFT", anchor, "TOPLEFT", 0, 0)
        pane:SetPoint("BOTTOMRIGHT", anchor, "BOTTOMRIGHT", 0, 0)
    end
    pane:HookScript("OnShow", function()
        if ns.Chart and ns.Chart.Refresh and ns.Chart:IsEmbedded() then
            ns.Chart:Refresh()
        end
    end)
    return pane
end

-- Parents the embed pane for classic (covers stats) vs ElvUI (transparent over stats).
local function AnchorPaneForTheme()
    if not pane then return end
    if WantsElvBackdrop() and CharacterStatsPane then
        pane:SetParent(CharacterStatsPane)
        pane:ClearAllPoints()
        pane:SetAllPoints(CharacterStatsPane)
        pane:SetFrameLevel((CharacterStatsPane:GetFrameLevel() or 1) + 20)
    else
        pane:SetParent(PaperDollFrame)
        pane:ClearAllPoints()
        if CharacterStatsPane then
            pane:SetAllPoints(CharacterStatsPane)
        else
            local anchor = CharacterFrameInsetRight or PaperDollFrame
            pane:SetPoint("TOPLEFT", anchor, "TOPLEFT", 0, 0)
            pane:SetPoint("BOTTOMRIGHT", anchor, "BOTTOMRIGHT", 0, 0)
        end
    end
    -- Must stay mouse-enabled so child bar/legend rows receive hover tooltips.
    pane:EnableMouse(true)
end
-- Creates our standalone sidebar tab, left of the last Blizzard tab.
local function EnsureTab()
    if tab then return tab end
    tab = CreateFrame("Button", "StatOrbSidebarTab", PaperDollSidebarTabs)

    -- Sit immediately to the left of the first built-in tab (Character Stats),
    -- so we never collide with the close button on the right edge.
    local first = _G["PaperDollSidebarTab1"]
    if first then
        tab:SetSize(first:GetSize())
    else
        tab:SetSize(33, 35)
    end
    tab:ClearAllPoints()
    if first then
        tab:SetPoint("RIGHT", first, "LEFT", -4, 0)
    else
        tab:SetPoint("BOTTOMRIGHT", PaperDollSidebarTabs, "BOTTOMRIGHT", 0, 0)
    end

    tab.TabBg = tab:CreateTexture(nil, "BACKGROUND")
    tab.TabBg:SetTexture(TAB_BG_TEX)
    tab.TabBg:SetAllPoints(tab)
    tab.TabBg:SetTexCoord(TAB_BG_INACTIVE[1], TAB_BG_INACTIVE[2], TAB_BG_INACTIVE[3], TAB_BG_INACTIVE[4])

    -- Prefer live metrics from the first Blizzard tab so we stay aligned
    -- if Blizzard tweaks PaperDollSidebarTabTemplate.
    local iw, ih = ICON_SIZE[1], ICON_SIZE[2]
    local ix, iy = ICON_POINT[2], ICON_POINT[3]
    if first and first.Icon then
        local rw, rh = first.Icon:GetSize()
        if rw and rh and rw > 0 and rh > 0 then
            -- ~18% smaller than Blizzard: logo art has a heavy frame and
            -- reads oversized at the native 29x31 slot size.
            iw, ih = rw * 0.82, rh * 0.82
        end
        local _, _, _, ox, oy = first.Icon:GetPoint(1)
        if ox ~= nil then ix = ox end
        if oy ~= nil then iy = oy end
    end

    tab.Icon = tab:CreateTexture(nil, "ARTWORK")
    tab.Icon:SetTexture(ICON_TEX)
    tab.Icon:SetTexCoord(ICON_TEXCOORD[1], ICON_TEXCOORD[2], ICON_TEXCOORD[3], ICON_TEXCOORD[4])
    tab.Icon:SetSize(iw, ih)
    tab.Icon:SetPoint("BOTTOM", tab, "BOTTOM", ix, iy)

    tab.Hider = tab:CreateTexture(nil, "OVERLAY")
    tab.Hider:SetColorTexture(0, 0, 0, 0.4)
    tab.Hider:SetAllPoints(tab)

    tab:SetScript("OnClick", function()
        Integration:SelectEmbedTab()
    end)
    tab:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(L["EMBED_TAB_NAME"], 1, 1, 1)
        GameTooltip:Show()
    end)
    tab:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    tab:Hide()
    return tab
end

-- Installs taint-safe post-hooks that keep our pane/tab in sync with the
-- built-in sidebar. Selecting any Blizzard tab hides our pane.
local function SetupHooks()
    if hooked then return end
    if type(PaperDollFrame_SetSidebar) ~= "function" then return end
    if type(PaperDollFrame_UpdateSidebarTabs) ~= "function" then return end

    hooksecurefunc("PaperDollFrame_SetSidebar", function()
        if pane and pane:IsShown() then
            pane:Hide()
        end
        RestoreStatsPaneIfNeeded()
        SetTabActive(false)
        -- Blizzard just selected a sidebar; make sure stats are rebuilt if needed.
        if CharacterStatsPane and CharacterStatsPane:IsShown()
            and type(PaperDollFrame_UpdateStats) == "function" then
            pcall(PaperDollFrame_UpdateStats)
        end
    end)
    hooksecurefunc("PaperDollFrame_UpdateSidebarTabs", function()
        if pane and pane:IsShown() then
            SetTabActive(true)
            if WantsElvBackdrop() then
                DeselectBlizzardSidebarTabs()
            end
        end
    end)

    -- Keep ElvUI backdrop stripped if Blizzard/ElvUI rebuilds the stats list.
    if type(PaperDollFrame_UpdateStats) == "function" then
        hooksecurefunc("PaperDollFrame_UpdateStats", function()
            if pane and pane:IsShown() and WantsElvBackdrop() then
                StripStatsPaneContent()
            end
        end)
    end

    -- Make StatOrb the default sidebar when the character window opens.
    -- Blizzard re-shows the stats pane on show, so re-assert after its OnShow.
    if PaperDollFrame and PaperDollFrame.HookScript then
        PaperDollFrame:HookScript("OnShow", function()
            if not (ns.db and ns.db.embed and pane) then return end
            if C_Timer and C_Timer.After then
                C_Timer.After(0, function()
                    if ns.db and ns.db.embed and pane and ns.Chart and ns.Chart:IsEmbedded() then
                        Integration:SelectEmbedTab()
                    end
                end)
            else
                Integration:SelectEmbedTab()
            end
        end)
    end

    hooked = true
end

-- Activates our pane: classic hides Blizzard panes; ElvUI keeps CharacterStatsPane
-- as a transparent backdrop (crest + glass) and only strips its widgets.
function Integration:SelectEmbedTab()
    if not pane then return end
    AnchorPaneForTheme()

    local elvBackdrop = WantsElvBackdrop()
    for i = 1, 3 do
        local f = GetBlizzardPane(i)
        if f then
            if elvBackdrop and f == CharacterStatsPane then
                f:Show()
                StripStatsPaneContent()
            else
                f:Hide()
            end
        end
    end
    if not elvBackdrop then
        RestoreStatsPaneIfNeeded()
    end
    pane:Show()
    SetTabActive(true)
    if elvBackdrop then
        DeselectBlizzardSidebarTabs()
        if C_Timer and C_Timer.After then
            C_Timer.After(0, function()
                if pane and pane:IsShown() and WantsElvBackdrop() then
                    StripStatsPaneContent()
                    DeselectBlizzardSidebarTabs()
                end
            end)
        end
    end
    if type(PaperDollFrame_UpdateSidebarTabs) == "function" then
        pcall(PaperDollFrame_UpdateSidebarTabs)
    end
    if ns.Chart and ns.Chart.ApplyChrome then
        ns.Chart:ApplyChrome()
    end
end

-- Applies the current embed/float preference to the panel.
function Integration:ApplyMode()
    if not ns.Chart then return end

    if ns.db and ns.db.embed and CanEmbed() then
        EnsurePane()
        EnsureTab()
        SetupHooks()
        AnchorPaneForTheme()
        tab:Show()
        ns.Chart:EnterEmbed(pane)
        self:SelectEmbedTab()
    else
        if pane then
            pane:Hide()
            RestoreStatsPaneIfNeeded()
            if not AnyBlizzardPaneShown() then
                local stats = GetBlizzardPane(1)
                if stats then
                    stats:Show()
                    if type(PaperDollFrame_UpdateStats) == "function" then
                        pcall(PaperDollFrame_UpdateStats)
                    end
                end
            elseif CharacterStatsPane and CharacterStatsPane:IsShown()
                and type(PaperDollFrame_UpdateStats) == "function" then
                pcall(PaperDollFrame_UpdateStats)
            end
            if type(PaperDollFrame_UpdateSidebarTabs) == "function" then
                pcall(PaperDollFrame_UpdateSidebarTabs)
            end
        end
        if tab then
            SetTabActive(false)
            tab:Hide()
        end
        ns.Chart:ExitEmbed()
    end
end