-- [ METATALENTS / DROPDOWNS ]-------------------------------------------------------------
-- Builds and owns the talent frame footer widgets: content picker, difficulty picker,
-- heatmap toggle, and the Apply Orbit Loadout button. RefreshMetaUI is the single
-- funnel that invalidates caches, re-runs the heatmap on every button, and nudges the
-- apply button's match state — every dropdown callback routes through it.
--
-- The apply button tooltip branches on _belowLevel (set by ApplyBuild when UnitLevel < 81)
-- so a low-level character sees "Level 81+" explicitly rather than a silent no-op click.

local _, Orbit = ...
local L = Orbit.L
local MT = Orbit.MetaTalents
local C = MT.Constants
local Data = MT.Data
local Build = MT.Build
local Overlay = MT.Overlay
local Apply = MT.Apply

local HIGHLIGHT_ALPHA = 0.2

local Dropdowns = {}
MT.Dropdowns = Dropdowns

-- [ CONTENT CATALOG ]--------------------------------------------------------------------
-- Prefers Orbit.Data.TalentMetaCatalog (auto-generated by .scripts/build_meta.py)
-- when present. Falls back to deriving the catalog from the TalentMeta data table
-- itself — every top-level key is a content name, and its second-level keys tell
-- us whether it's an M+ dungeon (contains "Mythic+") or a raid boss.
local _derivedCatalog = nil

local function DeriveCatalogFromData()
    local data = Orbit.Data and Orbit.Data.TalentMeta
    if not data then return nil end
    local mplus, raid = {}, {}
    local diffSet = {}
    for contentName, contentData in pairs(data) do
        if type(contentData) == "table" then
            if contentData["Mythic+"] then
                mplus[#mplus + 1] = contentName
            else
                raid[#raid + 1] = contentName
                for diffName in pairs(contentData) do
                    diffSet[diffName] = true
                end
            end
        end
    end
    table.sort(mplus)
    table.sort(raid)
    local diffOrder = { "Normal", "Heroic", "Mythic" }
    local diffs = {}
    for _, d in ipairs(diffOrder) do if diffSet[d] then diffs[#diffs + 1] = d end end
    if #diffs == 0 then diffs = diffOrder end
    return { mplusDungeons = mplus, raidBosses = raid, raidDifficulties = diffs }
end

local function GetCatalog()
    local emitted = Orbit.Data and Orbit.Data.TalentMetaCatalog
    if emitted then return emitted end
    if not _derivedCatalog then _derivedCatalog = DeriveCatalogFromData() end
    return _derivedCatalog
end

local function GetMPlusDungeons()
    local cat = GetCatalog()
    return cat and cat.mplusDungeons or {}
end

local function GetRaidBosses()
    local cat = GetCatalog()
    return cat and cat.raidBosses or {}
end

local function GetRaidDifficulties()
    local cat = GetCatalog()
    return cat and cat.raidDifficulties or { "Normal", "Heroic", "Mythic" }
end

local function IsMPlusDungeon(name)
    for _, dungeon in ipairs(GetMPlusDungeons()) do
        if dungeon == name then return true end
    end
    return false
end

-- [ SELECTION PERSISTENCE ]--------------------------------------------------------------
-- AccountSettings.MetaTalentsContent / MetaTalentsDifficulty round-trip the dropdown
-- choice across /reload. RestoreSelection validates the stored value against the live
-- catalog so a removed dungeon (post-data update) degrades to mplus[1] instead of
-- silently feeding a stale name into Data.UpdateActiveSpecData.
local function PersistSelection()
    local db = Orbit.db and Orbit.db.AccountSettings
    if not db then return end
    db.MetaTalentsContent = MT.SelectedContent
    db.MetaTalentsDifficulty = MT.SelectedDifficulty
end

local function RestoreSelection()
    local db = Orbit.db and Orbit.db.AccountSettings
    local savedContent = db and db.MetaTalentsContent
    local savedDifficulty = db and db.MetaTalentsDifficulty
    if savedContent then
        if IsMPlusDungeon(savedContent) then
            MT.SelectedContent = savedContent
            MT.SelectedDifficulty = "Mythic+"
            return
        end
        for _, boss in ipairs(GetRaidBosses()) do
            if boss == savedContent then
                MT.SelectedContent = savedContent
                for _, d in ipairs(GetRaidDifficulties()) do
                    if d == savedDifficulty then MT.SelectedDifficulty = d; return end
                end
                MT.SelectedDifficulty = "Heroic"
                return
            end
        end
    end
    local mplus = GetMPlusDungeons()
    if #mplus > 0 then
        MT.SelectedContent = mplus[1]
        MT.SelectedDifficulty = "Mythic+"
    else
        MT.SelectedContent = C.DEFAULT_CONTENT
        MT.SelectedDifficulty = C.DEFAULT_DIFFICULTY
    end
end

-- [ REFRESH FUNNEL ]---------------------------------------------------------------------
function Dropdowns.RefreshMetaUI()
    Data.MarkSpellCacheDirty()
    Build.Invalidate()
    if PlayerSpellsFrame and PlayerSpellsFrame.TalentsFrame then
        for talentButton in PlayerSpellsFrame.TalentsFrame:EnumerateAllTalentButtons() do
            Overlay.ApplyHeatmap(talentButton)
            if talentButton.trackPipArray then
                for _, pipFrame in ipairs(talentButton.trackPipArray) do
                    Overlay.ApplyHeatmap(pipFrame)
                end
            end
        end
    end
    if MT.UpdateApplyButtonState then MT.UpdateApplyButtonState() end
end

-- [ SEARCH BOX / LOAD SYSTEM PINS ]------------------------------------------------------
local function PinSearchBox()
    local searchBox = PlayerSpellsFrame.TalentsFrame.SearchBox
    hooksecurefunc(searchBox, "SetPoint", function(self)
        if self._orbitOverriding then return end
        self._orbitOverriding = true
        self:ClearAllPoints()
        self:SetPoint("LEFT", PlayerSpellsFrame.TalentsFrame.ApplyButton, "RIGHT", 150, 0)
        self._orbitOverriding = false
    end)
    searchBox:ClearAllPoints()
    searchBox:SetPoint("LEFT", PlayerSpellsFrame.TalentsFrame.ApplyButton, "RIGHT", 150, 0)
end

local function PinLoadSystem()
    local loadSys = PlayerSpellsFrame.TalentsFrame.LoadSystem
    hooksecurefunc(loadSys, "SetPoint", function(self)
        if self._orbitOverriding then return end
        self._orbitOverriding = true
        self:ClearAllPoints()
        self:SetPoint("LEFT", PlayerSpellsFrame.TalentsFrame.BottomBar, "LEFT", 33, 0)
        self:SetWidth(150)
        if self.Dropdown then self.Dropdown:SetWidth(150) end
        self._orbitOverriding = false
    end)
    loadSys._orbitOverriding = true
    loadSys:ClearAllPoints()
    loadSys:SetPoint("LEFT", PlayerSpellsFrame.TalentsFrame.BottomBar, "LEFT", 33, 0)
    loadSys:SetWidth(150)
    if loadSys.Dropdown then loadSys.Dropdown:SetWidth(150) end
    loadSys._orbitOverriding = false
end

-- [ CONTENT / DIFFICULTY DROPDOWNS ]-----------------------------------------------------
local function BuildContentDropdown(contentDropdown, diffDropdown)
    contentDropdown:SetupMenu(function(dropdown, rootDescription)
        local mplusDungeons = GetMPlusDungeons()
        if #mplusDungeons > 0 then
            rootDescription:CreateTitle("Mythic+ Dungeons")
            for _, dungeon in ipairs(mplusDungeons) do
                rootDescription:CreateRadio(dungeon,
                    function() return MT.SelectedContent == dungeon end,
                    function()
                        MT.SelectedContent = dungeon
                        MT.SelectedDifficulty = "Mythic+"
                        PersistSelection()
                        if diffDropdown.GenerateMenu then diffDropdown:GenerateMenu() end
                        Dropdowns.RefreshMetaUI()
                    end
                )
            end
        end
        local raidBosses = GetRaidBosses()
        if #raidBosses > 0 then
            if #mplusDungeons > 0 then rootDescription:CreateDivider() end
            rootDescription:CreateTitle("Raid Bosses")
            for _, boss in ipairs(raidBosses) do
                rootDescription:CreateRadio(boss,
                    function() return MT.SelectedContent == boss end,
                    function()
                        MT.SelectedContent = boss
                        MT.SelectedDifficulty = "Heroic"
                        PersistSelection()
                        if diffDropdown.GenerateMenu then diffDropdown:GenerateMenu() end
                        Dropdowns.RefreshMetaUI()
                    end
                )
            end
        end
    end)
end

local function BuildDifficultyDropdown(diffDropdown)
    diffDropdown:SetupMenu(function(dropdown, rootDescription)
        local options
        if IsMPlusDungeon(MT.SelectedContent) then
            options = { "Mythic+" }
        else
            options = GetRaidDifficulties()
        end
        for _, opt in ipairs(options) do
            rootDescription:CreateRadio(opt,
                function() return MT.SelectedDifficulty == opt end,
                function()
                    MT.SelectedDifficulty = opt
                    PersistSelection()
                    Dropdowns.RefreshMetaUI()
                end
            )
        end
    end)
end

-- [ TOGGLE BUTTON ]----------------------------------------------------------------------
local function CreateToggleButton()
    if MT._toggleBtn then return end
    local toggleBtn = CreateFrame("Button", nil, PlayerSpellsFrame.TalentsFrame)
    toggleBtn:SetSize(28, 28)
    toggleBtn:SetFrameLevel(9900)
    toggleBtn:SetPoint("BOTTOMLEFT", PlayerSpellsFrame.TalentsFrame.BottomBar, "TOPLEFT", 33, 20)

    local function UpdateToggleIcon()
        local enabled = Orbit.db and Orbit.db.AccountSettings and Orbit.db.AccountSettings.MetaTalentsTree
        toggleBtn:SetNormalAtlas(enabled and "common-icon-visual" or "common-icon-visual-disabled")
    end
    UpdateToggleIcon()

    toggleBtn:SetPushedAtlas("common-icon-visual-pressed")
    local thl = toggleBtn:CreateTexture(nil, "HIGHLIGHT")
    thl:SetAllPoints()
    thl:SetAtlas("common-icon-visual")
    thl:SetBlendMode("ADD")
    thl:SetAlpha(HIGHLIGHT_ALPHA)

    toggleBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(L.PLU_MT_TOGGLE_HEATMAP, 1, 1, 1)
        GameTooltip:Show()
    end)
    toggleBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
    toggleBtn:SetScript("OnClick", function()
        if Orbit.db and Orbit.db.AccountSettings then
            Orbit.db.AccountSettings.MetaTalentsTree = not Orbit.db.AccountSettings.MetaTalentsTree
            UpdateToggleIcon()
            Dropdowns.RefreshMetaUI()
        end
    end)

    MT._toggleBtn = toggleBtn
end

-- [ APPLY BUTTON ]-----------------------------------------------------------------------
local function CreateApplyButton(diffDropdown)
    if MT._applyBtn then return end
    local applyMetaBtn = CreateFrame("Button", nil, PlayerSpellsFrame.TalentsFrame)
    applyMetaBtn:SetSize(28, 28)
    applyMetaBtn:SetPoint("LEFT", diffDropdown, "RIGHT", 15, 0)

    applyMetaBtn:SetNormalAtlas("128-RedButton-Plus")
    applyMetaBtn:SetPushedAtlas("128-RedButton-Plus-Pressed")
    applyMetaBtn:SetDisabledAtlas("128-RedButton-Plus-Disabled")

    local hl = applyMetaBtn:CreateTexture(nil, "HIGHLIGHT")
    hl:SetAllPoints()
    hl:SetAtlas("128-RedButton-Plus")
    hl:SetBlendMode("ADD")
    hl:SetAlpha(HIGHLIGHT_ALPHA)

    applyMetaBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        if self._belowLevel then
            GameTooltip:SetText(L.PLU_MT_LOADOUT_LEVEL_F:format(C.MIN_APPLY_LEVEL), 0.5, 0.5, 0.5)
        elseif self._isApplied then
            GameTooltip:SetText(L.PLU_MT_LOADOUT_ACTIVE, 0.5, 0.5, 0.5)
        else
            GameTooltip:SetText(L.PLU_MT_APPLY_LOADOUT, 1, 1, 1)
        end
        GameTooltip:Show()
    end)
    applyMetaBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
    applyMetaBtn:SetScript("OnClick", function(self)
        if self._belowLevel or self._isApplied then return end
        Apply.ApplyMetaBuild()
    end)
    MT._applyBtn = applyMetaBtn
end

-- [ SETUP ]------------------------------------------------------------------------------
function Dropdowns.Setup()
    if MT._dropdownsSetup then return end
    if not (PlayerSpellsFrame and PlayerSpellsFrame.TalentsFrame and PlayerSpellsFrame.TalentsFrame.SearchBox) then return end
    MT._dropdownsSetup = true

    PinSearchBox()
    PinLoadSystem()

    -- Restore the player's last choice from saved variables; falls back to the first
    -- catalog entry (or Constants defaults if the catalog is empty) so a new tier/season
    -- never leaves users pointed at stale content.
    if not MT.SelectedContent then RestoreSelection() end
    if not MT.SelectedDifficulty then MT.SelectedDifficulty = C.DEFAULT_DIFFICULTY end

    local contentDropdown = CreateFrame("DropdownButton", "OrbitMetaTalentContentDropdown", PlayerSpellsFrame.TalentsFrame, "WowStyle1DropdownTemplate")
    contentDropdown:SetPoint("LEFT", PlayerSpellsFrame.TalentsFrame.LoadSystem, "RIGHT", 15, 0)
    contentDropdown:SetWidth(150)
    contentDropdown:SetMenuAnchor(AnchorUtil.CreateAnchor("BOTTOMLEFT", contentDropdown, "TOPLEFT", 0, 0))

    local diffDropdown = CreateFrame("DropdownButton", "OrbitMetaTalentDifficultyDropdown", PlayerSpellsFrame.TalentsFrame, "WowStyle1DropdownTemplate")
    diffDropdown:SetMenuAnchor(AnchorUtil.CreateAnchor("BOTTOMLEFT", diffDropdown, "TOPLEFT", 0, 0))
    diffDropdown:SetPoint("LEFT", contentDropdown, "RIGHT", 15, 0)
    diffDropdown:SetWidth(150)

    BuildContentDropdown(contentDropdown, diffDropdown)
    BuildDifficultyDropdown(diffDropdown)

    contentDropdown:Show()
    diffDropdown:Show()

    CreateToggleButton()
    CreateApplyButton(diffDropdown)

    Apply.UpdateApplyButtonState()
end
