local _, ns = ...

------------------------------------------------------------
-- Class colors
------------------------------------------------------------
local CLASS_COLORS = {
    WARRIOR     = { 0.78, 0.61, 0.43 },
    PALADIN     = { 0.96, 0.55, 0.73 },
    HUNTER      = { 0.67, 0.83, 0.45 },
    ROGUE       = { 1.00, 0.96, 0.41 },
    PRIEST      = { 1.00, 1.00, 1.00 },
    DEATHKNIGHT = { 0.77, 0.12, 0.23 },
    SHAMAN      = { 0.00, 0.44, 0.87 },
    MAGE        = { 0.25, 0.78, 0.92 },
    WARLOCK     = { 0.53, 0.53, 0.93 },
    MONK        = { 0.00, 1.00, 0.60 },
    DRUID       = { 1.00, 0.49, 0.04 },
    DEMONHUNTER = { 0.64, 0.19, 0.79 },
    EVOKER      = { 0.20, 0.58, 0.50 },
}

local TIER_COUNT_COLORS = {
    [0] = { 1.0, 0.3, 0.3 },   -- red: no tier
    [1] = { 1.0, 0.6, 0.3 },   -- orange: 1 piece
    [2] = { 1.0, 1.0, 0.3 },   -- yellow: 2-set
    [3] = { 0.7, 1.0, 0.3 },   -- lime: 3 pieces
    [4] = { 0.3, 1.0, 0.3 },   -- green: 4-set!
    [5] = { 0.3, 1.0, 0.8 },   -- teal: full 5
}

------------------------------------------------------------
-- Raid Inspect Window
------------------------------------------------------------
local RAID_WIDTH = 400
local RAID_HEIGHT = 500
local ROW_HEIGHT = 20

local raidFrame = CreateFrame("Frame", "YippYappRaidFrame", UIParent, "BackdropTemplate")
raidFrame:SetSize(RAID_WIDTH, RAID_HEIGHT)
raidFrame:SetPoint("CENTER", 200, 0)
raidFrame:SetMovable(true)
raidFrame:EnableMouse(true)
raidFrame:RegisterForDrag("LeftButton")
raidFrame:SetScript("OnDragStart", raidFrame.StartMoving)
raidFrame:SetScript("OnDragStop", raidFrame.StopMovingOrSizing)
raidFrame:SetClampedToScreen(true)
raidFrame:SetFrameStrata("HIGH")
raidFrame:SetBackdrop({
    bgFile = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    edgeSize = 16,
    insets = { left = 4, right = 4, top = 4, bottom = 4 },
})
raidFrame:SetBackdropColor(0.08, 0.08, 0.08, 0.95)
raidFrame:SetBackdropBorderColor(0.6, 0.6, 0.6, 1)
raidFrame:Hide()
ns.RaidFrame = raidFrame

-- Small quick-open button anchored to raid frames, only visible in raid
local quickBtn = CreateFrame("Button", "YippYappRaidQuickBtn", UIParent, "BackdropTemplate")
quickBtn:SetSize(70, 22)
quickBtn:SetFrameStrata("LOW")
quickBtn:SetBackdrop({
    bgFile = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    edgeSize = 10,
    insets = { left = 2, right = 2, top = 2, bottom = 2 },
})
quickBtn:SetBackdropColor(0.1, 0.1, 0.1, 0.95)
quickBtn:SetBackdropBorderColor(0.0, 0.5, 0.8, 0.7)
quickBtn:SetClampedToScreen(true)
quickBtn:Hide()

local quickBtnText = quickBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
quickBtnText:SetPoint("CENTER", 0, 0)
quickBtnText:SetText("|cff00aaffRaid|r")

local function AnchorRaidButton()
    quickBtn:ClearAllPoints()
    local anchor = CompactRaidFrameContainer or CompactPartyFrame
    if anchor then
        quickBtn:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -2)
    else
        quickBtn:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 10, -200)
    end
end

local function UpdateRaidButtonVisibility()
    if IsInRaid() then
        AnchorRaidButton()
        quickBtn:Show()
    else
        quickBtn:Hide()
        if raidFrame:IsShown() then
            raidFrame:Hide()
        end
    end
end

local raidVisEvents = CreateFrame("Frame")
raidVisEvents:RegisterEvent("GROUP_ROSTER_UPDATE")
raidVisEvents:RegisterEvent("PLAYER_ENTERING_WORLD")
raidVisEvents:SetScript("OnEvent", function()
    UpdateRaidButtonVisibility()
end)

quickBtn:SetScript("OnClick", function()
    if raidFrame:IsShown() then
        raidFrame:Hide()
    else
        if GetNumGroupMembers() > 0 then
            ns:ScanRaid()
        else
            ns:RefreshRaidTrinketDisplay()
            raidFrame:Show()
        end
    end
end)
quickBtn:SetScript("OnEnter", function(self)
    self:SetBackdropBorderColor(0.0, 0.67, 1.0, 1)
    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    GameTooltip:SetText("|cff00aaffRaid Tools|r")
    GameTooltip:AddLine("Click to toggle raid window", 1, 1, 1)
    GameTooltip:Show()
end)
quickBtn:SetScript("OnLeave", function(self)
    self:SetBackdropBorderColor(0.0, 0.5, 0.8, 0.7)
    GameTooltip_Hide()
end)

-- ESC to close
raidFrame:SetScript("OnShow", function()
    tinsert(UISpecialFrames, "YippYappRaidFrame")
    quickBtnText:SetText("|cff00aaffHide|r")
end)
raidFrame:SetScript("OnHide", function()
    for i = #UISpecialFrames, 1, -1 do
        if UISpecialFrames[i] == "YippYappRaidFrame" then
            table.remove(UISpecialFrames, i)
            break
        end
    end
    quickBtnText:SetText("|cff00aaffRaid|r")
end)

-- Close button
local close = CreateFrame("Button", nil, raidFrame, "UIPanelCloseButton")
close:SetPoint("TOPRIGHT", -2, -2)

------------------------------------------------------------
-- Tab system: Tier Tracker / Raid Trinkets
------------------------------------------------------------
local currentTab = "tier"
local rescanBtn, raidStyleBtn  -- forward declare

local tierContainer = CreateFrame("Frame", nil, raidFrame)
tierContainer:SetPoint("TOPLEFT", 0, -34)
tierContainer:SetPoint("BOTTOMRIGHT", 0, 0)

local trinketContainer = CreateFrame("Frame", nil, raidFrame)
trinketContainer:SetPoint("TOPLEFT", 0, -54)
trinketContainer:SetPoint("BOTTOMRIGHT", 0, 0)
trinketContainer:Hide()

local dungeonContainer = CreateFrame("Frame", nil, raidFrame)
dungeonContainer:SetPoint("TOPLEFT", 0, -54)
dungeonContainer:SetPoint("BOTTOMRIGHT", 0, 0)
dungeonContainer:Hide()

local function CreateTab(parent, text, xOffset)
    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetSize(110, 24)
    btn:SetPoint("TOPLEFT", xOffset, -8)
    btn:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        edgeSize = 10,
        insets = { left = 2, right = 2, top = 2, bottom = 2 },
    })
    btn.label = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    btn.label:SetPoint("CENTER", 0, 0)
    btn.label:SetText(text)
    return btn
end

local tierTab = CreateTab(raidFrame, "|cff00ff00Tier Tracker|r", 8)
local trinketTab = CreateTab(raidFrame, "|cffff8000Raid Trinkets|r", 122)
local dungeonTab = CreateTab(raidFrame, "|cff00aaffDungeon Trinkets|r", 236)

local function SetTabInactive(tab)
    tab:SetBackdropColor(0.1, 0.1, 0.1, 0.6)
    tab:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.5)
end

local function UpdateTabStyles()
    SetTabInactive(tierTab)
    SetTabInactive(trinketTab)
    SetTabInactive(dungeonTab)
    tierContainer:Hide()
    trinketContainer:Hide()
    dungeonContainer:Hide()

    if raidStyleBtn then raidStyleBtn:Hide() end
    if rescanBtn then rescanBtn:Hide() end

    if currentTab == "tier" then
        tierTab:SetBackdropColor(0.15, 0.25, 0.15, 1)
        tierTab:SetBackdropBorderColor(0.3, 1.0, 0.3, 0.8)
        tierContainer:Show()
        if rescanBtn then rescanBtn:Show() end
    elseif currentTab == "trinkets" then
        trinketTab:SetBackdropColor(0.2, 0.15, 0.05, 1)
        trinketTab:SetBackdropBorderColor(1.0, 0.5, 0.0, 0.8)
        trinketContainer:Show()
        if raidStyleBtn then raidStyleBtn:Show() end
    elseif currentTab == "dungeons" then
        dungeonTab:SetBackdropColor(0.05, 0.15, 0.25, 1)
        dungeonTab:SetBackdropBorderColor(0.0, 0.67, 1.0, 0.8)
        dungeonContainer:Show()
        if raidStyleBtn then raidStyleBtn:Show() end
    end
end

tierTab:SetScript("OnClick", function()
    currentTab = "tier"
    UpdateTabStyles()
end)
trinketTab:SetScript("OnClick", function()
    currentTab = "trinkets"
    UpdateTabStyles()
    ns:RefreshRaidTrinketDisplay()
end)
dungeonTab:SetScript("OnClick", function()
    currentTab = "dungeons"
    UpdateTabStyles()
    if ns.RefreshDungeonTrinketDisplay then
        ns:RefreshDungeonTrinketDisplay()
    end
end)

-- Expose tab switching for other modules
function ns.SetRaidTab(tabName)
    currentTab = tabName
    UpdateTabStyles()
    if tabName == "trinkets" then
        ns:RefreshRaidTrinketDisplay()
    elseif tabName == "dungeons" and ns.RefreshDungeonTrinketDisplay then
        ns:RefreshDungeonTrinketDisplay()
    end
end

------------------------------------------------------------
-- Tier Tracker tab content
------------------------------------------------------------
-- Rescan button (styled like the ST/AOE toggle, on the raidFrame second row)
rescanBtn = CreateFrame("Button", nil, raidFrame, "BackdropTemplate")
rescanBtn:SetSize(60, 18)
rescanBtn:SetPoint("TOPRIGHT", -36, -34)
rescanBtn:SetBackdrop({
    bgFile = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    edgeSize = 10,
    insets = { left = 2, right = 2, top = 2, bottom = 2 },
})
rescanBtn:SetBackdropColor(0.15, 0.15, 0.15, 1)
rescanBtn:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.8)
rescanBtn:SetFrameLevel(raidFrame:GetFrameLevel() + 10)
rescanBtn:Hide()

local rescanText = rescanBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
rescanText:SetAllPoints()
rescanText:SetText("|cff00ff00Rescan|r")

rescanBtn:SetScript("OnClick", function()
    ns:ScanRaid()
end)
rescanBtn:SetScript("OnEnter", function(self)
    self:SetBackdropBorderColor(0.3, 1.0, 0.3, 0.8)
    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    GameTooltip:SetText("Rescan raid for tier pieces")
    GameTooltip:Show()
end)
rescanBtn:SetScript("OnLeave", function(self)
    self:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.8)
    GameTooltip_Hide()
end)

-- Summary line
local summaryText = tierContainer:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
summaryText:SetPoint("TOPLEFT", 14, -6)
summaryText:SetWidth(RAID_WIDTH - 110)
summaryText:SetJustifyH("LEFT")

-- Divider
local divider = tierContainer:CreateTexture(nil, "ARTWORK")
divider:SetHeight(1)
divider:SetPoint("TOPLEFT", 12, -22)
divider:SetPoint("TOPRIGHT", -12, -22)
divider:SetColorTexture(0.5, 0.5, 0.5, 0.6)

-- Column headers (aligned with data columns)
local headerName = tierContainer:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
headerName:SetPoint("TOPLEFT", 14, -26)
headerName:SetText("|cffaaaaaaPlayer|r")

local headerTier = tierContainer:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
headerTier:SetPoint("TOPLEFT", 138, -26)
headerTier:SetText("|cffaaaaaaTier|r")

local headerPieces = tierContainer:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
headerPieces:SetPoint("TOPLEFT", 212, -26)
headerPieces:SetText("|cffaaaaaaPieces|r")

-- Scrollable content
local scrollFrame = CreateFrame("ScrollFrame", nil, tierContainer, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", 12, -40)
scrollFrame:SetPoint("BOTTOMRIGHT", -28, 12)

local scrollContent = CreateFrame("Frame", nil, scrollFrame)
scrollContent:SetSize(RAID_WIDTH - 44, 1)
scrollFrame:SetScrollChild(scrollContent)

ns.RaidRows = {}

------------------------------------------------------------
-- Raid Trinkets tab content
------------------------------------------------------------
local RAID_TRINKET_IDS = {
    -- The Voidspire
    249344, 249342, 249339, 249340, 249341, 249345, 249346, 249808, 249809,
    -- The Dreamrift
    249343, 249805,
    -- March on Quel'Danas
    260235, 249806,
}

local TIER_LABELS_RAID = {
    S = "|cffff8000S|r",
    A = "|cffa335eeA|r",
    B = "|cff0070ddB|r",
}

-- Readable spec names from ranking keys
local SPEC_DISPLAY = {
    Death_Knight         = "Frost DK",
    Death_Knight_Blood   = "Blood DK",
    Death_Knight_Frost   = "Frost DK",
    Death_Knight_Unholy  = "Unholy DK",
    Demon_Hunter_Havoc   = "Havoc DH",
    Demon_Hunter_Devourer = "Havoc DH (Devourer)",
    Demon_Hunter         = "Veng DH",
    Demon_Hunter_Vengeance = "Veng DH",
    Druid_Balance        = "Balance",
    Druid_Feral          = "Feral",
    Druid_Restoration    = "Resto Druid",
    Evoker_Devastation   = "Devastation",
    Evoker_Preservation  = "Preservation",
    Evoker_Augmentation  = "Augmentation",
    Hunter_Beast         = "BM Hunter",
    Hunter_Marksmanship  = "MM Hunter",
    Hunter_Survival      = "Surv Hunter",
    Mage_Arcane          = "Arcane",
    Mage_Fire            = "Fire",
    Mage_Frost           = "Frost Mage",
    Monk_Brewmaster      = "Brewmaster",
    Monk_Windwalker      = "Windwalker",
    Monk_Mistweaver      = "Mistweaver",
    Paladin_Holy         = "Holy Paladin",
    Paladin_Protection   = "Prot Paladin",
    Paladin_Retribution  = "Ret Paladin",
    Priest_Discipline    = "Discipline",
    Priest_Holy          = "Holy Priest",
    Priest_Shadow        = "Shadow",
    Rogue_Outlaw         = "Outlaw",
    Rogue_Subtlety       = "Subtlety",
    Shaman_Elemental     = "Elemental",
    Shaman_Enhancement   = "Enhancement",
    Shaman_Restoration   = "Resto Shaman",
    Warlock_Affliction   = "Affliction",
    Warlock_Demonology   = "Demonology",
    Warlock_Destruction  = "Destruction",
    Warrior_Arms         = "Arms",
    Warrior_Fury         = "Fury",
    Warrior_Protection   = "Prot Warrior",
}

-- Map spec keys to class tokens for coloring
local SPEC_TO_CLASS = {
    Death_Knight         = "DEATHKNIGHT",
    Death_Knight_Blood   = "DEATHKNIGHT",
    Death_Knight_Frost   = "DEATHKNIGHT",
    Death_Knight_Unholy  = "DEATHKNIGHT",
    Demon_Hunter         = "DEMONHUNTER",
    Demon_Hunter_Devourer = "DEMONHUNTER",
    Demon_Hunter_Havoc   = "DEMONHUNTER",
    Demon_Hunter_Vengeance = "DEMONHUNTER",
    Druid_Balance        = "DRUID",
    Druid_Feral          = "DRUID",
    Druid_Restoration    = "DRUID",
    Evoker_Devastation   = "EVOKER",
    Evoker_Preservation  = "EVOKER",
    Evoker_Augmentation  = "EVOKER",
    Hunter_Beast         = "HUNTER",
    Hunter_Beast_Mastery = "HUNTER",
    Hunter_Marksmanship  = "HUNTER",
    Hunter_Survival      = "HUNTER",
    Mage_Arcane          = "MAGE",
    Mage_Fire            = "MAGE",
    Mage_Frost           = "MAGE",
    Monk_Brewmaster      = "MONK",
    Monk_Windwalker      = "MONK",
    Monk_Mistweaver      = "MONK",
    Paladin_Holy         = "PALADIN",
    Paladin_Protection   = "PALADIN",
    Paladin_Retribution  = "PALADIN",
    Priest_Discipline    = "PRIEST",
    Priest_Holy          = "PRIEST",
    Priest_Shadow        = "PRIEST",
    Rogue_Outlaw         = "ROGUE",
    Rogue_Subtlety       = "ROGUE",
    Shaman_Elemental     = "SHAMAN",
    Shaman_Enhancement   = "SHAMAN",
    Shaman_Restoration   = "SHAMAN",
    Warlock_Affliction   = "WARLOCK",
    Warlock_Demonology   = "WARLOCK",
    Warlock_Destruction  = "WARLOCK",
    Warrior_Arms         = "WARRIOR",
    Warrior_Fury         = "WARRIOR",
    Warrior_Protection   = "WARRIOR",
}

local function GetSpecColoredName(specKey)
    local name = SPEC_DISPLAY[specKey] or specKey:gsub("_", " ")
    local classToken = SPEC_TO_CLASS[specKey]
    local cc = CLASS_COLORS[classToken]
    if cc then
        return string.format("|cff%02x%02x%02x%s|r",
            cc[1] * 255, cc[2] * 255, cc[3] * 255, name)
    end
    return name
end

-- Short spec name (just the spec part, no class)
local SPEC_SHORT = {
    Death_Knight         = "Frost",
    Death_Knight_Blood   = "Blood",
    Death_Knight_Frost   = "Frost",
    Death_Knight_Unholy  = "Unholy",
    Demon_Hunter         = "Havoc",
    Demon_Hunter_Devourer = "Devourer",
    Demon_Hunter_Havoc   = "Havoc",
    Demon_Hunter_Vengeance = "Vengeance",
    Druid_Balance        = "Balance",
    Druid_Feral          = "Feral",
    Evoker_Devastation   = "Devastation",
    Evoker_Augmentation  = "Augmentation",
    Hunter_Beast         = "BM",
    Hunter_Beast_Mastery = "BM",
    Hunter_Marksmanship  = "MM",
    Hunter_Survival      = "Survival",
    Mage_Arcane          = "Arcane",
    Mage_Fire            = "Fire",
    Mage_Frost           = "Frost",
    Monk_Brewmaster      = "Brew",
    Monk_Windwalker      = "WW",
    Paladin_Protection   = "Prot",
    Paladin_Retribution  = "Ret",
    Priest_Shadow        = "Shadow",
    Rogue_Outlaw         = "Outlaw",
    Rogue_Subtlety       = "Sub",
    Shaman_Elemental     = "Ele",
    Shaman_Enhancement   = "Enh",
    Warlock_Affliction   = "Aff",
    Warlock_Demonology   = "Demo",
    Warlock_Destruction  = "Destro",
    Warrior_Arms         = "Arms",
    Warrior_Fury         = "Fury",
    Warrior_Protection   = "Prot",
}

-- Class display order
local CLASS_ORDER = {
    "DEATHKNIGHT", "DEMONHUNTER", "DRUID", "EVOKER", "HUNTER",
    "MAGE", "MONK", "PALADIN", "PRIEST", "ROGUE",
    "SHAMAN", "WARLOCK", "WARRIOR",
}

local CLASS_SHORT = {
    DEATHKNIGHT = "DK", DEMONHUNTER = "DH", DRUID = "Druid", EVOKER = "Evoker",
    HUNTER = "Hunter", MAGE = "Mage", MONK = "Monk", PALADIN = "Paladin",
    PRIEST = "Priest", ROGUE = "Rogue", SHAMAN = "Shaman",
    WARLOCK = "Warlock", WARRIOR = "Warrior",
}

local function CountKeys(t)
    local n = 0
    for _ in pairs(t) do n = n + 1 end
    return n
end

-- Group spec keys by class, returns ordered list of { classToken, classColor, specs = {"Frost", "Unholy"} }
local function GroupSpecsByClass(specKeys)
    local byClass = {}
    for specKey in pairs(specKeys) do
        local classToken = SPEC_TO_CLASS[specKey] or "UNKNOWN"
        if not byClass[classToken] then
            byClass[classToken] = {}
        end
        table.insert(byClass[classToken], SPEC_SHORT[specKey] or specKey:gsub(".*_", ""))
    end
    local result = {}
    for _, classToken in ipairs(CLASS_ORDER) do
        if byClass[classToken] then
            table.sort(byClass[classToken])
            local cc = CLASS_COLORS[classToken] or { 1, 1, 1 }
            table.insert(result, {
                classToken = classToken,
                className = CLASS_SHORT[classToken] or classToken,
                color = cc,
                specs = byClass[classToken],
            })
        end
    end
    return result
end

-- Add grouped spec lines to a tooltip for a given tier
local function AddGroupedSpecsToTooltip(tierLabel, tierColor, specKeys)
    local groups = GroupSpecsByClass(specKeys)
    if #groups == 0 then return end
    GameTooltip:AddLine(tierLabel, tierColor[1], tierColor[2], tierColor[3])
    for _, group in ipairs(groups) do
        local line = "  " .. group.className .. ": " .. table.concat(group.specs, ", ")
        GameTooltip:AddLine(line, group.color[1], group.color[2], group.color[3])
    end
end

-- Build compact row text showing S and A tier counts + abbreviated class list
local function BuildCompactSpecText(sSpecs, aSpecs)
    local sCount = CountKeys(sSpecs)
    local aCount = CountKeys(aSpecs)

    local parts = {}
    if sCount > 0 then
        local sGroups = GroupSpecsByClass(sSpecs)
        local classNames = {}
        for _, g in ipairs(sGroups) do
            local cc = g.color
            table.insert(classNames, string.format("|cff%02x%02x%02x%s|r", cc[1]*255, cc[2]*255, cc[3]*255, g.className))
        end
        table.insert(parts, "|cffff8000S|r(" .. sCount .. "): " .. table.concat(classNames, " "))
    end
    if aCount > 0 then
        -- Only show A-tier class names if S-tier list is short, otherwise just count
        if sCount <= 3 then
            local aGroups = GroupSpecsByClass(aSpecs)
            local classNames = {}
            for _, g in ipairs(aGroups) do
                local cc = g.color
                table.insert(classNames, string.format("|cff%02x%02x%02x%s|r", cc[1]*255, cc[2]*255, cc[3]*255, g.className))
            end
            table.insert(parts, "|cffa335eeA|r(" .. aCount .. "): " .. table.concat(classNames, " "))
        else
            table.insert(parts, "|cffa335eeA|r(" .. aCount .. ")")
        end
    end

    if #parts == 0 then
        return "|cff666666No S/A-tier specs|r"
    end
    return table.concat(parts, "  ")
end

-- Current fight style for raid/dungeon trinket tabs
local raidTrinketStyle = "ST"

-- Build trinket -> spec ranking data from TRINKET_RANKINGS
local function BuildTrinketSpecData(trinketIDs, style)
    local data = {} -- [itemID] = { sSpecs = {}, aSpecs = {} }
    if not ns.TRINKET_RANKINGS then return data end

    for _, itemID in ipairs(trinketIDs) do
        data[itemID] = { sSpecs = {}, aSpecs = {} }
    end

    local byStyle = ns.TRINKET_RANKINGS[style]
    if byStyle then
        for specKey, rankings in pairs(byStyle) do
            for _, entry in ipairs(rankings) do
                if data[entry.id] then
                    if entry.tier == "S" then
                        data[entry.id].sSpecs[specKey] = true
                    elseif entry.tier == "A" then
                        data[entry.id].aSpecs[specKey] = true
                    end
                end
            end
        end
    end

    return data
end

local function SortTrinketsByTier(trinketIDs, data)
    local sorted = {}
    for _, id in ipairs(trinketIDs) do
        table.insert(sorted, id)
    end
    table.sort(sorted, function(a, b)
        local da, db = data[a], data[b]
        local sa, sb = CountKeys(da.sSpecs), CountKeys(db.sSpecs)
        if sa ~= sb then return sa > sb end
        local aa, ab = CountKeys(da.aSpecs), CountKeys(db.aSpecs)
        return aa > ab
    end)
    return sorted
end

-- ST/AOE toggle for raid/dungeon trinkets
raidStyleBtn = CreateFrame("Button", nil, raidFrame, "BackdropTemplate")
raidStyleBtn:SetSize(50, 18)
raidStyleBtn:SetPoint("TOPRIGHT", -36, -34)
raidStyleBtn:SetBackdrop({
    bgFile = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    edgeSize = 10,
    insets = { left = 2, right = 2, top = 2, bottom = 2 },
})
raidStyleBtn:SetBackdropColor(0.15, 0.15, 0.15, 1)
raidStyleBtn:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.8)
raidStyleBtn:SetFrameLevel(raidFrame:GetFrameLevel() + 10)
raidStyleBtn:Hide()

local raidStyleText = raidStyleBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
raidStyleText:SetAllPoints()
raidStyleText:SetText("|cff00ff00ST|r")

local function UpdateRaidStyleLabel()
    if raidTrinketStyle == "ST" then
        raidStyleText:SetText("|cff00ff00ST|r")
    else
        raidStyleText:SetText("|cffff8800AOE|r")
    end
end

raidStyleBtn:SetScript("OnClick", function()
    if raidTrinketStyle == "ST" then
        raidTrinketStyle = "AOE"
    else
        raidTrinketStyle = "ST"
    end
    UpdateRaidStyleLabel()
    if currentTab == "trinkets" then
        ns:RefreshRaidTrinketDisplay()
    elseif currentTab == "dungeons" and ns.RefreshDungeonTrinketDisplay then
        ns:RefreshDungeonTrinketDisplay()
    end
end)
raidStyleBtn:SetScript("OnEnter", function(self)
    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    GameTooltip:SetText("Toggle Single Target / AoE rankings")
    GameTooltip:Show()
end)
raidStyleBtn:SetScript("OnLeave", GameTooltip_Hide)

-- Now that both buttons exist, set initial tab styles
UpdateTabStyles()

-- Trinket scroll area
local trinketScroll = CreateFrame("ScrollFrame", nil, trinketContainer, "UIPanelScrollFrameTemplate")
trinketScroll:SetPoint("TOPLEFT", 12, -6)
trinketScroll:SetPoint("BOTTOMRIGHT", -28, 12)

local trinketScrollContent = CreateFrame("Frame", nil, trinketScroll)
trinketScrollContent:SetSize(RAID_WIDTH - 44, 1)
trinketScroll:SetScrollChild(trinketScrollContent)

local trinketRows = {}

function ns:RefreshRaidTrinketDisplay()
    for _, row in ipairs(trinketRows) do row:Hide() end
    wipe(trinketRows)

    local trinketData = BuildTrinketSpecData(RAID_TRINKET_IDS, raidTrinketStyle)
    local sortedIDs = SortTrinketsByTier(RAID_TRINKET_IDS, trinketData)
    local yOffset = 0

    for idx, itemID in ipairs(sortedIDs) do
        local info = trinketData[itemID]
        local srcInfo = ns.TRINKET_SOURCES and ns.TRINKET_SOURCES[itemID]
        local itemName = C_Item.GetItemNameByID(itemID)
        local itemIcon = C_Item.GetItemIconByID(itemID)

        if not itemName then
            C_Item.RequestLoadItemDataByID(itemID)
            itemName = "Loading..."
        end

        -- Filter A-tier specs that aren't also S-tier
        local filteredA = {}
        for specKey in pairs(info.aSpecs) do
            if not info.sSpecs[specKey] then
                filteredA[specKey] = true
            end
        end

        local sText = BuildCompactSpecText(info.sSpecs, filteredA)

        local row = CreateFrame("Button", nil, trinketScrollContent)
        row:SetSize(RAID_WIDTH - 44, 46)
        row:SetPoint("TOPLEFT", 0, -yOffset)

        if idx % 2 == 0 then
            local bg = row:CreateTexture(nil, "BACKGROUND")
            bg:SetAllPoints()
            bg:SetColorTexture(1, 1, 1, 0.025)
        end

        local hl = row:CreateTexture(nil, "HIGHLIGHT")
        hl:SetAllPoints()
        hl:SetColorTexture(1, 1, 1, 0.04)

        local iconBtn = CreateFrame("Button", nil, row)
        iconBtn:SetSize(24, 24)
        iconBtn:SetPoint("TOPLEFT", 4, -4)

        local iconTex = iconBtn:CreateTexture(nil, "ARTWORK")
        iconTex:SetAllPoints()
        iconTex:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        iconTex:SetTexture(itemIcon or 134400)

        local iconBorder = iconBtn:CreateTexture(nil, "OVERLAY")
        iconBorder:SetPoint("TOPLEFT", -2, 2)
        iconBorder:SetPoint("BOTTOMRIGHT", 2, -2)
        iconBorder:SetTexture("Interface\\Common\\WhiteIconFrame")
        local r, g, b = C_Item.GetItemQualityColor(4)
        iconBorder:SetVertexColor(r, g, b)

        local storedItemID = itemID
        iconBtn:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetItemByID(storedItemID)
            if ShoppingTooltip1 then ShoppingTooltip1:Hide() end
            if ShoppingTooltip2 then ShoppingTooltip2:Hide() end
            GameTooltip:Show()
        end)
        iconBtn:SetScript("OnLeave", function()
            GameTooltip_Hide()
            if ShoppingTooltip1 then ShoppingTooltip1:Hide() end
            if ShoppingTooltip2 then ShoppingTooltip2:Hide() end
        end)

        local nameStr = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        nameStr:SetPoint("TOPLEFT", iconBtn, "TOPRIGHT", 6, -1)
        nameStr:SetJustifyH("LEFT")
        nameStr:SetWordWrap(false)
        local srcSuffix = ""
        if srcInfo and srcInfo.boss and srcInfo.boss ~= "" then
            srcSuffix = "  |cff666666" .. srcInfo.boss .. "|r"
        end
        nameStr:SetText("|cffa335ee" .. (itemName or "Unknown") .. "|r" .. srcSuffix)

        local specLine = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        specLine:SetPoint("TOPLEFT", nameStr, "BOTTOMLEFT", 0, -2)
        specLine:SetPoint("RIGHT", row, "RIGHT", -4, 0)
        specLine:SetJustifyH("LEFT")
        specLine:SetWordWrap(false)
        specLine:SetText(sText)

        -- Tooltip: grouped by class
        local sSpecsCopy = info.sSpecs
        local aSpecsCopy = filteredA
        row:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetText("|cffa335ee" .. (itemName or "Unknown") .. "|r")
            if srcInfo then
                local src = srcInfo.source
                if srcInfo.boss and srcInfo.boss ~= "" then
                    src = srcInfo.boss .. " — " .. src
                end
                GameTooltip:AddLine(src, 0.5, 0.5, 0.5)
            end
            GameTooltip:AddLine(" ")
            AddGroupedSpecsToTooltip("S-tier", { 1.0, 0.5, 0.0 }, sSpecsCopy)
            if CountKeys(sSpecsCopy) > 0 and CountKeys(aSpecsCopy) > 0 then
                GameTooltip:AddLine(" ")
            end
            AddGroupedSpecsToTooltip("A-tier", { 0.64, 0.21, 0.93 }, aSpecsCopy)
            if CountKeys(sSpecsCopy) == 0 and CountKeys(aSpecsCopy) == 0 then
                GameTooltip:AddLine("No specs rank this as S or A tier", 0.5, 0.5, 0.5)
            end
            GameTooltip:Show()
        end)
        row:SetScript("OnLeave", GameTooltip_Hide)

        table.insert(trinketRows, row)
        yOffset = yOffset + 46
    end

    trinketScrollContent:SetHeight(math.max(yOffset, 1))
end

------------------------------------------------------------
-- Dungeon Trinkets tab content
------------------------------------------------------------
local DUNGEON_TRINKET_IDS = {
    -- Windrunner Spire
    250144, 250226, 250227, 250256,
    -- Maisara Caverns
    250223, 250258,
    -- Magisters' Terrace
    250242, 250257, 34470, 34471, 34472,
    -- Nexus-Point Xenas
    250253, 250241,
    -- Pit of Saron
    50259, 252421,
    -- Seat of the Triumvirate
    151307, 151310, 151312,
    -- Skyreach
    110006, 252411, 252418, 252420,
    -- Algeth'ar Academy
    193701, 193718, 193719,
}

local dungeonScroll = CreateFrame("ScrollFrame", nil, dungeonContainer, "UIPanelScrollFrameTemplate")
dungeonScroll:SetPoint("TOPLEFT", 12, -6)
dungeonScroll:SetPoint("BOTTOMRIGHT", -28, 12)

local dungeonScrollContent = CreateFrame("Frame", nil, dungeonScroll)
dungeonScrollContent:SetSize(RAID_WIDTH - 44, 1)
dungeonScroll:SetScrollChild(dungeonScrollContent)

local dungeonRows = {}

function ns:RefreshDungeonTrinketDisplay()
    for _, row in ipairs(dungeonRows) do row:Hide() end
    wipe(dungeonRows)

    local trinketData = BuildTrinketSpecData(DUNGEON_TRINKET_IDS, raidTrinketStyle)
    local sortedIDs = SortTrinketsByTier(DUNGEON_TRINKET_IDS, trinketData)
    local yOffset = 0

    for idx, itemID in ipairs(sortedIDs) do
        local info = trinketData[itemID]
        local srcInfo = ns.TRINKET_SOURCES and ns.TRINKET_SOURCES[itemID]
        local itemName = C_Item.GetItemNameByID(itemID)
        local itemIcon = C_Item.GetItemIconByID(itemID)

        if not itemName then
            C_Item.RequestLoadItemDataByID(itemID)
            itemName = "Loading..."
        end

        local filteredA = {}
        for specKey in pairs(info.aSpecs) do
            if not info.sSpecs[specKey] then
                filteredA[specKey] = true
            end
        end

        local sText = BuildCompactSpecText(info.sSpecs, filteredA)

        local row = CreateFrame("Button", nil, dungeonScrollContent)
        row:SetSize(RAID_WIDTH - 44, 46)
        row:SetPoint("TOPLEFT", 0, -yOffset)

        if idx % 2 == 0 then
            local bg = row:CreateTexture(nil, "BACKGROUND")
            bg:SetAllPoints()
            bg:SetColorTexture(1, 1, 1, 0.025)
        end

        local hl = row:CreateTexture(nil, "HIGHLIGHT")
        hl:SetAllPoints()
        hl:SetColorTexture(1, 1, 1, 0.04)

        local iconBtn = CreateFrame("Button", nil, row)
        iconBtn:SetSize(24, 24)
        iconBtn:SetPoint("TOPLEFT", 4, -4)

        local iconTex = iconBtn:CreateTexture(nil, "ARTWORK")
        iconTex:SetAllPoints()
        iconTex:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        iconTex:SetTexture(itemIcon or 134400)

        local iconBorder = iconBtn:CreateTexture(nil, "OVERLAY")
        iconBorder:SetPoint("TOPLEFT", -2, 2)
        iconBorder:SetPoint("BOTTOMRIGHT", 2, -2)
        iconBorder:SetTexture("Interface\\Common\\WhiteIconFrame")
        local r, g, b = C_Item.GetItemQualityColor(4)
        iconBorder:SetVertexColor(r, g, b)

        local storedItemID = itemID
        iconBtn:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetItemByID(storedItemID)
            if ShoppingTooltip1 then ShoppingTooltip1:Hide() end
            if ShoppingTooltip2 then ShoppingTooltip2:Hide() end
            GameTooltip:Show()
        end)
        iconBtn:SetScript("OnLeave", function()
            GameTooltip_Hide()
            if ShoppingTooltip1 then ShoppingTooltip1:Hide() end
            if ShoppingTooltip2 then ShoppingTooltip2:Hide() end
        end)

        local nameStr = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        nameStr:SetPoint("TOPLEFT", iconBtn, "TOPRIGHT", 6, -1)
        nameStr:SetJustifyH("LEFT")
        nameStr:SetWordWrap(false)
        local srcSuffix = ""
        if srcInfo then
            local parts = {}
            if srcInfo.source and srcInfo.source ~= "" then
                table.insert(parts, srcInfo.source)
            end
            if srcInfo.boss and srcInfo.boss ~= "" then
                table.insert(parts, srcInfo.boss)
            end
            if #parts > 0 then
                srcSuffix = "  |cff666666" .. table.concat(parts, " — ") .. "|r"
            end
        end
        nameStr:SetText("|cffa335ee" .. (itemName or "Unknown") .. "|r" .. srcSuffix)

        local specLine = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        specLine:SetPoint("TOPLEFT", nameStr, "BOTTOMLEFT", 0, -2)
        specLine:SetPoint("RIGHT", row, "RIGHT", -4, 0)
        specLine:SetJustifyH("LEFT")
        specLine:SetWordWrap(false)
        specLine:SetText(sText)

        local sSpecsCopy = info.sSpecs
        local aSpecsCopy = filteredA
        row:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetText("|cffa335ee" .. (itemName or "Unknown") .. "|r")
            if srcInfo then
                local src = srcInfo.source
                if srcInfo.boss and srcInfo.boss ~= "" then
                    src = srcInfo.boss .. " — " .. src
                end
                GameTooltip:AddLine(src, 0.5, 0.5, 0.5)
            end
            GameTooltip:AddLine(" ")
            AddGroupedSpecsToTooltip("S-tier", { 1.0, 0.5, 0.0 }, sSpecsCopy)
            if CountKeys(sSpecsCopy) > 0 and CountKeys(aSpecsCopy) > 0 then
                GameTooltip:AddLine(" ")
            end
            AddGroupedSpecsToTooltip("A-tier", { 0.64, 0.21, 0.93 }, aSpecsCopy)
            if CountKeys(sSpecsCopy) == 0 and CountKeys(aSpecsCopy) == 0 then
                GameTooltip:AddLine("No specs rank this as S or A tier", 0.5, 0.5, 0.5)
            end
            GameTooltip:Show()
        end)
        row:SetScript("OnLeave", GameTooltip_Hide)

        table.insert(dungeonRows, row)
        yOffset = yOffset + 46
    end

    dungeonScrollContent:SetHeight(math.max(yOffset, 1))
end

------------------------------------------------------------
-- Refresh display
------------------------------------------------------------
function ns:RefreshRaidDisplay()
    -- Clear old rows
    for _, row in ipairs(ns.RaidRows) do
        row:Hide()
    end
    wipe(ns.RaidRows)

    local sorted = ns:GetSortedTierData()
    local summary = ns:GetTierSummary()

    -- Update summary
    summaryText:SetText(string.format(
        "%d players scanned | 2-set: |cff00ff00%d|r | 4-set: |cff00ff00%d|r | No tier: |cffff4444%d|r",
        summary.total, summary.with2set, summary.with4set, summary.counts[0] or 0
    ))

    local yOffset = 0
    for i, data in ipairs(sorted) do
        local row = CreateFrame("Frame", nil, scrollContent)
        row:SetSize(RAID_WIDTH - 44, ROW_HEIGHT)
        row:SetPoint("TOPLEFT", 0, -yOffset)

        -- Alternating row background
        if i % 2 == 0 then
            local bg = row:CreateTexture(nil, "BACKGROUND")
            bg:SetAllPoints()
            bg:SetColorTexture(1, 1, 1, 0.03)
        end

        -- Player name (class colored)
        local nameStr = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        nameStr:SetPoint("LEFT", 2, 0)
        nameStr:SetWidth(120)
        nameStr:SetJustifyH("LEFT")
        nameStr:SetWordWrap(false)
        local cc = CLASS_COLORS[data.class] or { 1, 1, 1 }
        nameStr:SetTextColor(cc[1], cc[2], cc[3])

        -- Tag players who already have enough tier and should pass
        local displayName = data.name
        if data.tierCount >= 4 then
            displayName = displayName .. "  |cff888888PASS|r"
        end
        nameStr:SetText(displayName)

        -- Tier count with color
        local tierStr = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        tierStr:SetPoint("LEFT", 126, 0)
        tierStr:SetWidth(70)
        tierStr:SetJustifyH("CENTER")
        local tc = TIER_COUNT_COLORS[data.tierCount] or { 1, 1, 1 }
        tierStr:SetTextColor(tc[1], tc[2], tc[3])

        local tierLabel = data.tierCount .. "/5"
        if data.tierCount >= 4 then
            tierLabel = tierLabel .. " (4-set)"
        elseif data.tierCount >= 2 then
            tierLabel = tierLabel .. " (2-set)"
        end
        tierStr:SetText(tierLabel)

        -- Piece names (abbreviated to initials for compact display)
        local piecesStr = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        piecesStr:SetPoint("LEFT", row, "LEFT", 200, 0)
        piecesStr:SetWidth(140)
        piecesStr:SetJustifyH("LEFT")
        piecesStr:SetWordWrap(false)
        piecesStr:SetTextColor(0.6, 0.6, 0.6)
        if data.tierCount >= 4 then
            piecesStr:SetText("max")
        elseif #data.tierSlots > 0 then
            piecesStr:SetText(table.concat(data.tierSlots, ", "))
        else
            piecesStr:SetText("—")
        end

        table.insert(ns.RaidRows, row)
        yOffset = yOffset + ROW_HEIGHT
    end

    scrollContent:SetHeight(math.max(yOffset, 1))
end
