-- ui.lua

local addonName = ...

local function withCombatGuard(fn)
    if InCombatLockdown() then
        print("|cffffff00CCS:|r Cannot change settings during combat. Changes will apply after combat ends.")
        return
    end
    fn()
end

-- Preview a sound by its value key (LSM name or "file:name" or CCS short name)
local function previewSound(value)
    if not value or value == "__default__" then return end
    local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
    local basePath = "Interface\\AddOns\\" .. addonName .. "\\sounds\\"

    local ccsShortNames = {
        "break","breath","burn","dot","marked","move","pools","soak","spread","targeted",
    }
    local ccsSet = {}
    for _, n in ipairs(ccsShortNames) do ccsSet[n] = true end

    local path
    if value:sub(1, 5) == "file:" then
        path = basePath .. value:sub(6) .. ".ogg"
    elseif ccsSet[value] then
        path = basePath .. value .. ".ogg"
    elseif LSM then
        path = LSM:Fetch("sound", value)
    end
    if path then PlaySoundFile(path, "Master") end
end

local CATEGORY_NAME = "Custom Countdown Sounds"
local CCS_CategoryID

------------------------------------------------------------
local CCS_DropdownPopup

local function CCS_GetOrCreatePopup()
    if CCS_DropdownPopup then return CCS_DropdownPopup end

    local MAX_VISIBLE = 12
    local ROW_H       = 16
    local PAD         = 6
    local SCROLL_W    = 14

    local popup = CreateFrame("Frame", "CCS_DropdownPopup", UIParent, "BackdropTemplate")
    popup:SetFrameStrata("FULLSCREEN_DIALOG")
    popup:SetFrameLevel(200)
    popup:SetBackdrop({
        bgFile   = "Interface/Tooltips/UI-Tooltip-Background",
        edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
        edgeSize = 12,
        insets   = { left=3, right=3, top=3, bottom=3 },
    })
    popup:SetBackdropColor(0.1, 0.1, 0.1, 0.97)
    popup:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
    popup:Hide()
    popup._buttons = {}
    popup._owner   = nil
    popup._offset  = 0

    local SEARCH_H = 22

    -- Search bar (only shown for warn dropdowns with _widePreview)
    local searchBox = CreateFrame("EditBox", nil, popup, "BackdropTemplate")
    searchBox:SetHeight(SEARCH_H)
    searchBox:SetPoint("TOPLEFT",  popup, "TOPLEFT",  PAD, -PAD)
    searchBox:SetPoint("TOPRIGHT", popup, "TOPRIGHT", -(PAD + SCROLL_W + 2), -PAD)
    searchBox:SetBackdrop({
        bgFile   = "Interface/Tooltips/UI-Tooltip-Background",
        edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
        edgeSize = 8,
        insets   = { left=3, right=3, top=3, bottom=3 },
    })
    searchBox:SetBackdropColor(0.05, 0.05, 0.05, 1)
    searchBox:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
    searchBox:SetFontObject("GameFontHighlightSmall")
    searchBox:SetTextInsets(6, 6, 0, 0)
    searchBox:SetAutoFocus(false)
    searchBox:SetMaxLetters(64)
    searchBox:Hide()
    popup._searchBox = searchBox

    local searchHint = searchBox:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    searchHint:SetPoint("LEFT",  searchBox, "LEFT",  6, 0)
    searchHint:SetPoint("RIGHT", searchBox, "RIGHT", -6, 0)
    searchHint:SetJustifyH("LEFT")
    searchHint:SetText("Type to search...")

    searchBox:SetScript("OnTextChanged", function(self)
        local txt = self:GetText()
        searchHint:SetShown(txt == "")
        -- Filter items and reset scroll
        local query = txt:lower()
        if query == "" then
            popup._items = popup._allItems
        else
            local filtered = {}
            for _, item in ipairs(popup._allItems) do
                if item.label:lower():find(query, 1, true) then
                    filtered[#filtered + 1] = item
                end
            end
            popup._items = filtered
        end
        popup._offset = 0
        popup._total  = #popup._items
        popup._refresh()
    end)

    searchBox:SetScript("OnEscapePressed", function()
        popup:Hide()
    end)

    local clipper = CreateFrame("Frame", nil, popup)
    clipper:SetPoint("TOPLEFT",     popup, "TOPLEFT",     PAD,               -PAD)
    clipper:SetPoint("BOTTOMRIGHT", popup, "BOTTOMRIGHT", -(PAD+SCROLL_W+2),  PAD)
    popup._clipper = clipper

    local track = CreateFrame("Frame", nil, popup, "BackdropTemplate")
    track:SetWidth(SCROLL_W)
    track:SetPoint("TOPRIGHT",    popup, "TOPRIGHT",    -PAD, -PAD)
    track:SetPoint("BOTTOMRIGHT", popup, "BOTTOMRIGHT", -PAD,  PAD)
    track:SetBackdrop({
        bgFile   = "Interface/Tooltips/UI-Tooltip-Background",
        edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
        edgeSize = 6,
        insets   = { left=2, right=2, top=2, bottom=2 },
    })
    track:SetBackdropColor(0.05, 0.05, 0.05, 1)
    track:SetBackdropBorderColor(0.3, 0.3, 0.3, 1)

    local thumb = track:CreateTexture(nil, "OVERLAY")
    thumb:SetColorTexture(0.5, 0.5, 0.5, 0.8)
    popup._thumb = thumb

    local function UpdateThumb()
        local total   = popup._total or 0
        local visible = math.min(total, MAX_VISIBLE)
        if total <= visible then thumb:Hide(); return end
        thumb:Show()
        local trackH = track:GetHeight()
        local ratio  = visible / total
        local thumbH = math.max(16, trackH * ratio)
        local travel = trackH - thumbH
        local frac   = (popup._offset or 0) / math.max(1, total - visible)
        thumb:SetHeight(thumbH)
        thumb:ClearAllPoints()
        thumb:SetPoint("TOPLEFT",  track, "TOPLEFT",  2, -(travel * frac))
        thumb:SetPoint("TOPRIGHT", track, "TOPRIGHT", -2, -(travel * frac))
    end
    popup._updateThumb = UpdateThumb

    popup:EnableMouseWheel(true)
    popup:SetScript("OnMouseWheel", function(_, delta)
        local total   = popup._total or 0
        local visible = math.min(total, MAX_VISIBLE)
        popup._offset = math.max(0, math.min(popup._offset - delta, total - visible))
        if popup._refresh then popup._refresh() end
    end)

    track:EnableMouse(true)
    track:SetScript("OnMouseDown", function(_, btn)
        if btn ~= "LeftButton" then return end
        local total   = popup._total or 0
        local visible = math.min(total, MAX_VISIBLE)
        if total <= visible then return end
        track:SetScript("OnUpdate", function()
            local _, my = GetCursorPosition()
            local scale = track:GetEffectiveScale()
            local ty    = track:GetTop()
            local th    = track:GetHeight()
            local frac  = math.max(0, math.min(1, (ty - my/scale) / th))
            popup._offset = math.floor(frac * (total - visible) + 0.5)
            if popup._refresh then popup._refresh() end
        end)
    end)
    track:SetScript("OnMouseUp", function() track:SetScript("OnUpdate", nil) end)

    local PREV_W = 22  -- width reserved for preview button

    for i = 1, MAX_VISIBLE do
        local row = CreateFrame("Button", nil, clipper)
        row:SetHeight(ROW_H)

        row._check = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        row._check:SetPoint("LEFT", row, "LEFT", 6, 0)
        row._check:SetWidth(14)
        row._check:SetJustifyH("LEFT")

        row._text = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        row._text:SetPoint("LEFT",  row._check, "RIGHT", 2, 0)
        row._text:SetJustifyH("LEFT")

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

        -- Preview button: plays the sound without selecting or closing
        local prev = CreateFrame("Button", nil, row)
        prev:SetSize(PREV_W, ROW_H)
        prev:SetPoint("RIGHT", row, "RIGHT", -2, 0)

        local prevBg = prev:CreateTexture(nil, "BACKGROUND")
        prevBg:SetAllPoints()
        prevBg:SetColorTexture(0.25, 0.25, 0.30, 0)  -- invisible until hover

        local prevHL = prev:CreateTexture(nil, "HIGHLIGHT")
        prevHL:SetAllPoints()
        prevHL:SetColorTexture(0.6, 0.8, 1, 0.15)

        local prevLbl = prev:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        prevLbl:SetPoint("CENTER", prev, "CENTER", 0, 0)
        prevLbl:SetText("|cffaaddff>|r")

        prev:SetScript("OnEnter", function() prevBg:SetColorTexture(0.25, 0.25, 0.30, 1) end)
        prev:SetScript("OnLeave", function() prevBg:SetColorTexture(0.25, 0.25, 0.30, 0) end)

        row._prev = prev
        row:Hide()
        popup._buttons[i] = row
    end

    local function Refresh()
        local items  = popup._items or {}
        local owner  = popup._owner
        local offset = popup._offset or 0
        for i = 1, MAX_VISIBLE do
            local row  = popup._buttons[i]
            local item = items[offset + i]
            if item then
                row._text:ClearAllPoints()
                row._text:SetPoint("LEFT", row._check, "RIGHT", 2, 0)
                if owner and owner._widePreview then
                    row._text:SetPoint("RIGHT", row, "RIGHT", -(PREV_W + 6), 0)
                    row._prev:Show()
                else
                    row._text:SetPoint("RIGHT", row, "RIGHT", -4, 0)
                    row._prev:Hide()
                end
                row._text:SetText(item.label)
                row._check:SetText(item.value == (owner and owner._value) and "|cff00ff00*|r" or "")
                row:ClearAllPoints()
                row:SetPoint("TOPLEFT",  clipper, "TOPLEFT",  0, -(i-1)*ROW_H)
                row:SetPoint("TOPRIGHT", clipper, "TOPRIGHT", 0, -(i-1)*ROW_H)
                row:Show()
                row:SetScript("OnClick", function()
                    if owner then
                        owner:SetValue(item.value)
                        if owner._onSelect then owner._onSelect(item.value, item.label) end
                    end
                    popup:Hide()
                end)
                -- Re-wire preview each Refresh so closure always has the correct item
                row._prev:SetScript("OnClick", function()
                    local val = item.value
                    if val == "__default__" then
                        val = popup._default
                    end
                    previewSound(val)
                end)
            else
                row:Hide()
            end
        end
        UpdateThumb()
    end
    popup._refresh = Refresh

    local catcher = CreateFrame("Frame", nil, UIParent)
    catcher:SetAllPoints(UIParent)
    catcher:SetFrameStrata("FULLSCREEN_DIALOG")
    catcher:SetFrameLevel(199)
    catcher:Hide()
    catcher:EnableMouse(true)
    catcher:SetScript("OnMouseDown", function() popup:Hide(); catcher:Hide() end)
    popup._catcher = catcher
    popup:SetScript("OnHide", function()
        catcher:Hide()
        popup._searchBox:SetText("")
        popup._searchBox:ClearFocus()
    end)

    CCS_DropdownPopup = popup
    return popup
end

local function CCS_CreateDropdown(parent, width, height)
    width  = width  or 120
    height = height or 22

    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetSize(width, height)
    btn:SetBackdrop({
        bgFile   = "Interface/Tooltips/UI-Tooltip-Background",
        edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
        edgeSize = 10,
        insets   = { left=3, right=3, top=3, bottom=3 },
    })
    btn:SetBackdropColor(0.15, 0.15, 0.15, 1)
    btn:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)

    btn._label = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    btn._label:SetPoint("LEFT",  btn, "LEFT",  8,   0)
    btn._label:SetPoint("RIGHT", btn, "RIGHT", -18, 0)
    btn._label:SetJustifyH("LEFT")
    btn._label:SetText("--")

    btn._arrow = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    btn._arrow:SetPoint("RIGHT", btn, "RIGHT", -6, 0)
    btn._arrow:SetText("v")
    btn._arrow:SetTextColor(0.8, 0.8, 0.8)

    btn._items    = {}
    btn._value    = nil
    btn._onSelect = nil
    btn._enabled  = true

    btn:SetScript("OnEnter", function(self)
        if self._enabled then self:SetBackdropBorderColor(0.8, 0.8, 0.8, 1) end
    end)
    btn:SetScript("OnLeave", function(self)
        if self._enabled then self:SetBackdropBorderColor(0.4, 0.4, 0.4, 1) end
    end)

    btn:SetScript("OnClick", function(self)
        if not self._enabled then return end
        local popup = CCS_GetOrCreatePopup()
        if popup:IsShown() and popup._owner == self then popup:Hide(); return end

        local MAX_VISIBLE = 12
        local ROW_H = 16
        local PAD   = 6
        local SCROLL_W = 14

        popup._owner   = self
        popup._allItems = self._items
        popup._items   = self._items
        popup._offset  = 0
        popup._total   = #self._items
        popup._default = self._defaultSound

        local SEARCH_H = 22
        local hasSearch = self._widePreview
        if hasSearch then
            popup._searchBox:Show()
            popup._searchBox:SetText("")
            popup._clipper:SetPoint("TOPLEFT", popup, "TOPLEFT", PAD, -(PAD + SEARCH_H + 4))
        else
            popup._searchBox:Hide()
            popup._clipper:SetPoint("TOPLEFT", popup, "TOPLEFT", PAD, -PAD)
        end

        local visible = math.min(#self._items, MAX_VISIBLE)
        local pw
        if self._widePreview then
            pw = math.max(width * 2, 260) + SCROLL_W + PAD
        else
            pw = width + SCROLL_W + PAD
        end
        local extraH = hasSearch and (SEARCH_H + 4) or 0
        popup:SetSize(pw, PAD*2 + visible*ROW_H + extraH)
        popup:ClearAllPoints()
        popup:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -2)
        popup._refresh()
        popup:Show()
        popup._catcher:Show()
        if hasSearch then
            popup._searchBox:SetFocus()
        end
    end)

    function btn:SetItems(items)  self._items = items or {} end
    function btn:GetValue()       return self._value        end
    function btn:SetOnSelect(fn)  self._onSelect = fn       end

    function btn:SetValue(value)
        self._value = value
        for _, item in ipairs(self._items) do
            if item.value == value then self._label:SetText(item.label); return end
        end
        self._label:SetText("--")
    end

    function btn:SetEnabled(enabled)
        self._enabled = enabled and true or false
        if self._enabled then
            self._label:SetTextColor(1,   1,   1)
            self._arrow:SetTextColor(0.8, 0.8, 0.8)
            self:SetBackdropColor(0.15, 0.15, 0.15, 1)
            self:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
        else
            self._label:SetTextColor(0.5, 0.5, 0.5)
            self._arrow:SetTextColor(0.4, 0.4, 0.4)
            self:SetBackdropColor(0.1, 0.1, 0.1, 1)
            self:SetBackdropBorderColor(0.25, 0.25, 0.25, 1)
        end
    end

    return btn
end

------------------------------------------------------------
-- Countdown helpers
------------------------------------------------------------

local COUNTDOWN_KEYS = { "file:ccs2s","file:ccs3s","file:ccs4s","file:ccs5s","file:ccs6s","file:ccs7s","file:ccs8s","file:ccs9s","file:ccs10s","file:ccs11s","file:ccs12s","file:ccs13s" }
local COUNTDOWN_LABELS = {
    ["file:ccs2s"]="2 Seconds",  ["file:ccs3s"]="3 Seconds",  ["file:ccs4s"]="4 Seconds",
    ["file:ccs5s"]="5 Seconds",  ["file:ccs6s"]="6 Seconds",  ["file:ccs7s"]="7 Seconds",
    ["file:ccs8s"]="8 Seconds",  ["file:ccs9s"]="9 Seconds",  ["file:ccs10s"]="10 Seconds",
    ["file:ccs11s"]="11 Seconds",["file:ccs12s"]="12 Seconds",["file:ccs13s"]="13 Seconds",
}

local function prettifyKey(key)
    if not key then return "none" end
    -- Strip file: prefix, replace underscores with spaces, title-case each word
    local s = key:gsub("^file:", ""):gsub("_", " ")
    return s:gsub("(%a)([%w]*)", function(first, rest)
        return first:upper() .. rest:lower()
    end)
end

local function getDefaultWarn(ability)
    local s = ability.soundH or ability.soundM
    if not s then return nil end
    return type(s) == "table" and s[1] or s
end

local cachedSoundItems = nil
local function getSoundItems()
    if cachedSoundItems then return cachedSoundItems end
    local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
    local items = {}
    if LSM then
        local list = LSM:List("sound") or {}
        table.sort(list)
        for _, name in ipairs(list) do
            items[#items + 1] = { label = name, value = name }
        end
    end
    cachedSoundItems = items
    return items
end

local function buildWarnItems(defaultKey)
    local items = {}
    local defLabel = prettifyKey(defaultKey) .. " (default)"
    items[#items + 1] = { label = defLabel, value = "__default__" }
    for _, item in ipairs(getSoundItems()) do
        items[#items + 1] = item
    end
    return items
end

local function getDefaultCountdown(ability, diff)
    local s = diff == "M" and ability.soundM or ability.soundH
    return type(s) == "table" and s[2] or nil
end

local function hasCountdown(ability)
    return getDefaultCountdown(ability,"H") ~= nil or getDefaultCountdown(ability,"M") ~= nil
end

local function hasHeroic(ability)
    return ability.soundH ~= nil
end

local function hasMythic(ability)
    return ability.soundM ~= nil
end

local function getWarnSound(ability)
    local s = ability.soundH or ability.soundM
    if not s then return nil end
    return type(s) == "table" and s[1] or s
end

local function buildCountdownItems(defaultKey)
    local items = {}
    -- Default entry first
    local defLabel = (defaultKey and (COUNTDOWN_LABELS[defaultKey] or prettifyKey(defaultKey)) or "off") .. " (default)"
    items[#items+1] = { label = defLabel, value = "__default__" }
    for _, k in ipairs(COUNTDOWN_KEYS) do
        items[#items+1] = { label = COUNTDOWN_LABELS[k], value = k }
    end
    return items
end

------------------------------------------------------------
-- Layout constants
------------------------------------------------------------

local ROW_HEIGHT  = 26
local HEADER_H    = 28
local INDENT      = 12
local CB_SIZE     = 16
local DD_W        = 110   -- dropdown button width
local LEFT_FRAC   = 0.48

------------------------------------------------------------
-- Ability List
------------------------------------------------------------

local function BuildAbilityList(scrollChild, totalWidth)
    local leftW  = math.floor(totalWidth * LEFT_FRAC)
    local rightW = totalWidth - leftW
    local rPad   = 10
    local colHCB = rPad + 12
    local colHDD = colHCB + CB_SIZE + 2
    local colMCB = colHDD + DD_W + 12 + 12
    local colMDD = colMCB + CB_SIZE + 2

    local y = -8
    local allWarnCBs, allHCBs, allMCBs = {}, {}, {}
    local allWarnDDs, allHDDs, allMDDs = {}, {}, {}
    local defaultHCBs, defaultMCBs = {}, {}   -- only entries that have a default countdown
    local defaultHDDs, defaultMDDs = {}, {}

    -- Vertical divider
    local divider = scrollChild:CreateTexture(nil, "BACKGROUND")
    divider:SetColorTexture(0.3, 0.3, 0.3, 0.8)
    divider:SetWidth(1)
    divider:SetPoint("TOPLEFT",    scrollChild, "TOPLEFT",    leftW, 0)
    divider:SetPoint("BOTTOMLEFT", scrollChild, "BOTTOMLEFT", leftW, 0)

    for _, entry in ipairs(CCS_Spells) do
        if entry.abilities then

            -- Thin separator above boss header (skip for first entry)
            if y < -8 then
                local sep = scrollChild:CreateTexture(nil, "BACKGROUND")
                sep:SetColorTexture(0.25, 0.25, 0.25, 0.6)
                sep:SetHeight(1)
                sep:SetPoint("TOPLEFT",  scrollChild, "TOPLEFT", 0,          y)
                sep:SetPoint("TOPRIGHT", scrollChild, "TOPLEFT", totalWidth, y)
                y = y - 6
            end

            -- Boss header
            local header = scrollChild:CreateFontString(nil, "ARTWORK", "GameFontHighlightLarge")
            header:SetPoint("TOPLEFT", INDENT, y)
            header:SetWidth(totalWidth - INDENT * 2)
            header:SetJustifyH("LEFT")
            header:SetText(entry.section or entry.boss)
            y = y - HEADER_H

            for _, ability in ipairs(entry.abilities) do

                -- Left cell
                local leftCell = CreateFrame("Frame", nil, scrollChild)
                leftCell:SetSize(leftW, ROW_HEIGHT)
                leftCell:SetPoint("TOPLEFT", 0, y)

                -- Right cell
                local rightCell = CreateFrame("Frame", nil, scrollChild)
                rightCell:SetSize(rightW, ROW_HEIGHT)
                rightCell:SetPoint("TOPLEFT", leftW, y)

                -- Warn sound dropdown + toggle (right-aligned in left cell)
                local warnDD = CCS_CreateDropdown(leftCell, DD_W, ROW_HEIGHT - 4)
                warnDD:SetPoint("RIGHT", leftCell, "RIGHT", -8, 0)
                local defaultWarn = getDefaultWarn(ability)
                warnDD:SetItems(buildWarnItems(defaultWarn))
                warnDD._defaultWarn  = defaultWarn
                warnDD._defaultSound = defaultWarn
                warnDD._widePreview  = true  -- wide popup with preview button
                local warnOverride = CCS.GetWarnOverride(ability.key)
                warnDD:SetValue(warnOverride or "__default__")
                warnDD:SetEnabled(CCS.isWarnEnabled(ability.key))
                warnDD:SetOnSelect(function(value)
                    withCombatGuard(function()
                        CCS.SetWarnOverride(ability.key, value ~= "__default__" and value or nil)
                        CCS.RefreshAbility(ability.key, ability)
                    end)
                end)

                local warnCB = CreateFrame("CheckButton", nil, leftCell, "UICheckButtonTemplate")
                warnCB:SetSize(CB_SIZE, CB_SIZE)
                warnCB:SetPoint("RIGHT", warnDD, "LEFT", -4, 0)
                warnCB:SetChecked(CCS.isWarnEnabled(ability.key))

                -- Spell icon
                local ICON_SIZE = 18
                local icon = leftCell:CreateTexture(nil, "ARTWORK")
                icon:SetSize(ICON_SIZE, ICON_SIZE)
                icon:SetPoint("LEFT", leftCell, "LEFT", INDENT, 0)
                local texID = C_Spell.GetSpellTexture(ability.privateID)
                if texID then
                    icon:SetTexture(texID)
                    icon:SetTexCoord(0.08, 0.92, 0.08, 0.92) -- trim default icon border
                else
                    icon:SetColorTexture(0.2, 0.2, 0.2, 0.5) -- grey placeholder if not found
                end

                -- Ability name — tooltip frame, truncates before warn checkbox
                local lblFrame = CreateFrame("Frame", nil, leftCell)
                lblFrame:SetPoint("LEFT",  leftCell, "LEFT", INDENT + ICON_SIZE + 4, 0)
                lblFrame:SetPoint("RIGHT", warnCB,   "LEFT", -6, 0)
                lblFrame:SetHeight(ROW_HEIGHT)

                local lbl = lblFrame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
                lbl:SetAllPoints()
                lbl:SetJustifyH("LEFT")
                lbl:SetJustifyV("MIDDLE")
                lbl:SetText(ability.label)

                lblFrame:EnableMouse(true)
                lblFrame:SetScript("OnEnter", function(self)
                    if not ability.privateID then return end
                    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
                    GameTooltip:SetSpellByID(ability.privateID)
                    GameTooltip:AddLine(" ")
                    GameTooltip:AddLine("|cffaaaaaa SpellID: " .. ability.privateID .. "|r")
                    GameTooltip:Show()
                end)
                lblFrame:SetScript("OnLeave", function()
                    GameTooltip:Hide()
                end)

                warnCB:SetScript("OnClick", function(self)
                    withCombatGuard(function()
                        local en = self:GetChecked()
                        CCS.SetWarnEnabled(ability.key, en)
                        CCS.RefreshAbility(ability.key, ability)
                        warnDD:SetEnabled(en)
                    end)
                end)
                allWarnCBs[#allWarnCBs + 1] = warnCB
                allWarnDDs[#allWarnDDs + 1] = warnDD

                -- Countdown controls in right cell — checkboxes always show if diff exists,
                -- dropdown shows if enabled OR a default sound exists
                if hasHeroic(ability) then
                    local hLbl = rightCell:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
                    hLbl:SetText("|cffaaaaaa H|r")
                    hLbl:SetPoint("LEFT", rightCell, "LEFT", colHCB - 12, 0)

                    local hCB = CreateFrame("CheckButton", nil, rightCell, "UICheckButtonTemplate")
                    hCB:SetSize(CB_SIZE, CB_SIZE)
                    hCB:SetPoint("LEFT", rightCell, "LEFT", colHCB, 0)
                    hCB:SetChecked(CCS.IsCDEnabled(ability.key, "H"))

                    local hDefaultCD = getDefaultCountdown(ability, "H")
                    local hDD = CCS_CreateDropdown(rightCell, DD_W, ROW_HEIGHT - 4)
                    hDD:SetPoint("LEFT", rightCell, "LEFT", colHDD, 0)
                    hDD:SetItems(buildCountdownItems(hDefaultCD))
                    hDD._defaultSound = hDefaultCD
                    local hOverride = CCS.GetCountdownOverride(ability.key, "H")
                    hDD:SetValue(hOverride or "__default__")
                    hDD:SetEnabled(CCS.IsCDEnabled(ability.key, "H"))

                    -- Show dropdown if checked or has a default; hide if unchecked and no default
                    hDD._noDefault = (hDefaultCD == nil)
                    local function refreshHDD()
                        local en = CCS.IsCDEnabled(ability.key, "H")
                        if en or hDefaultCD then
                            hDD:Show()
                            hDD:SetEnabled(en)
                        else
                            hDD:Hide()
                        end
                    end
                    refreshHDD()

                    hDD:SetOnSelect(function(value)
                        withCombatGuard(function()
                            CCS.SetCountdownOverride(ability.key, "H", value ~= "__default__" and value or nil)
                            CCS.RefreshAbility(ability.key, ability)
                        end)
                    end)
                    hCB:SetScript("OnClick", function(self)
                        withCombatGuard(function()
                            local en = self:GetChecked()
                            CCS.SetCDEnabled(ability.key, "H", en)
                            CCS.RefreshAbility(ability.key, ability)
                            refreshHDD()
                        end)
                    end)

                    allHCBs[#allHCBs + 1] = hCB
                    allHDDs[#allHDDs + 1] = hDD
                    if hDefaultCD then
                        defaultHCBs[#defaultHCBs + 1] = hCB
                        defaultHDDs[#defaultHDDs + 1] = hDD
                    end
                end

                if hasMythic(ability) then
                    local mLbl = rightCell:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
                    mLbl:SetText("|cffaaaaaa M|r")
                    mLbl:SetPoint("LEFT", rightCell, "LEFT", colMCB - 12, 0)

                    local mCB = CreateFrame("CheckButton", nil, rightCell, "UICheckButtonTemplate")
                    mCB:SetSize(CB_SIZE, CB_SIZE)
                    mCB:SetPoint("LEFT", rightCell, "LEFT", colMCB, 0)
                    mCB:SetChecked(CCS.IsCDEnabled(ability.key, "M"))

                    local mDefaultCD = getDefaultCountdown(ability, "M")
                    local mDD = CCS_CreateDropdown(rightCell, DD_W, ROW_HEIGHT - 4)
                    mDD:SetPoint("LEFT", rightCell, "LEFT", colMDD, 0)
                    mDD:SetItems(buildCountdownItems(mDefaultCD))
                    mDD._defaultSound = mDefaultCD
                    local mOverride = CCS.GetCountdownOverride(ability.key, "M")
                    mDD:SetValue(mOverride or "__default__")
                    mDD:SetEnabled(CCS.IsCDEnabled(ability.key, "M"))

                    mDD._noDefault = (mDefaultCD == nil)
                    local function refreshMDD()
                        local en = CCS.IsCDEnabled(ability.key, "M")
                        if en or mDefaultCD then
                            mDD:Show()
                            mDD:SetEnabled(en)
                        else
                            mDD:Hide()
                        end
                    end
                    refreshMDD()

                    mDD:SetOnSelect(function(value)
                        withCombatGuard(function()
                            CCS.SetCountdownOverride(ability.key, "M", value ~= "__default__" and value or nil)
                            CCS.RefreshAbility(ability.key, ability)
                        end)
                    end)
                    mCB:SetScript("OnClick", function(self)
                        withCombatGuard(function()
                            local en = self:GetChecked()
                            CCS.SetCDEnabled(ability.key, "M", en)
                            CCS.RefreshAbility(ability.key, ability)
                            refreshMDD()
                        end)
                    end)

                    allMCBs[#allMCBs + 1] = mCB
                    allMDDs[#allMDDs + 1] = mDD
                    if mDefaultCD then
                        defaultMCBs[#defaultMCBs + 1] = mCB
                        defaultMDDs[#defaultMDDs + 1] = mDD
                    end
                end

                y = y - ROW_HEIGHT - 2
            end
            y = y - 8
        end
    end

    divider:SetHeight(math.abs(y) + 16)
    scrollChild:SetHeight(math.abs(y) + 16)

    return allWarnCBs, allHCBs, allMCBs, allWarnDDs, allHDDs, allMDDs,
           defaultHCBs, defaultMCBs, defaultHDDs, defaultMDDs, {
        leftW  = leftW,
        colHCB = colHCB,
        colHDD = colHDD,
        colMCB = colMCB,
        colMDD = colMDD,
    }
end

------------------------------------------------------------
-- Options Panel
------------------------------------------------------------

local function CreateCCSOptionsPanel()
    local panel = CreateFrame("Frame")
    panel.name = CATEGORY_NAME

    local title = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
    title:SetPoint("TOPLEFT", 16, -16)
    title:SetText(CATEGORY_NAME)

    local inst = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
    inst:SetPoint("TOPLEFT",  title, "BOTTOMLEFT", 0, -8)
    inst:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -16, 0)
    inst:SetJustifyH("LEFT")
    inst:SetWordWrap(true)
    inst:SetText(
        "Choose sounds that play automatically when a private aura lands on you in Heroic or Mythic raid.\n" ..
        "Warning sounds (left side) and countdown sounds (right side) are toggled independently."
    )

    local warnLabel = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
    warnLabel:SetText("|cffaaaaaa All Spell Warnings|r")
    warnLabel:SetPoint("TOPLEFT", inst, "BOTTOMLEFT", 0, -14)

    local enableWarnBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
    enableWarnBtn:SetSize(70, 24)
    enableWarnBtn:SetPoint("LEFT", warnLabel, "RIGHT", 8, 0)
    enableWarnBtn:SetText("Enable")

    local disableWarnBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
    disableWarnBtn:SetSize(70, 24)
    disableWarnBtn:SetPoint("LEFT", enableWarnBtn, "RIGHT", 4, 0)
    disableWarnBtn:SetText("Disable")

    local cdLabel = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
    cdLabel:SetText("|cffaaaaaa All Countdowns|r")
    cdLabel:SetPoint("LEFT", disableWarnBtn, "RIGHT", 24, 0)

    local enableCDBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
    enableCDBtn:SetSize(70, 24)
    enableCDBtn:SetPoint("LEFT", cdLabel, "RIGHT", 8, 0)
    enableCDBtn:SetText("Enable")

    local disableCDBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
    disableCDBtn:SetSize(70, 24)
    disableCDBtn:SetPoint("LEFT", enableCDBtn, "RIGHT", 4, 0)
    disableCDBtn:SetText("Disable")

    -- Static two-column header
    local headerBar = CreateFrame("Frame", nil, panel, "BackdropTemplate")
    headerBar:SetHeight(42)
    headerBar:SetPoint("TOPLEFT",  warnLabel, "BOTTOMLEFT",  0,  -6)
    headerBar:SetPoint("TOPRIGHT", panel,     "TOPRIGHT",   -30,  0)
    headerBar:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile=true, tileSize=8, edgeSize=10,
        insets   = { left=3, right=3, top=3, bottom=3 },
    })
    headerBar:SetBackdropColor(0, 0, 0, 0)
    headerBar:SetBackdropBorderColor(0, 0, 0, 0)

    -- Right header: Countdown Timer only (no Boss Ability label)
    local rightHdrTitle = headerBar:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
    rightHdrTitle:SetText("|cffFFD100Countdown Timer|r")

    local rightHdrSub = headerBar:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
    rightHdrSub:SetText("|cffaaaaaa H: Heroic          M: Mythic|r")

    -- Vertical divider in header (x set on OnShow)
    local hdrDiv = headerBar:CreateTexture(nil, "ARTWORK")
    hdrDiv:SetColorTexture(0.3, 0.3, 0.3, 0.8)
    hdrDiv:SetWidth(1)
    hdrDiv:SetPoint("TOP",    headerBar, "TOPLEFT",    0, -4)
    hdrDiv:SetPoint("BOTTOM", headerBar, "BOTTOMLEFT", 0,  4)

    -- Scroll frame
    local scroll = CreateFrame("ScrollFrame", nil, panel, "UIPanelScrollFrameTemplate")
    scroll:SetPoint("TOPLEFT",     headerBar, "BOTTOMLEFT",  0,   -2)
    scroll:SetPoint("BOTTOMRIGHT", panel,     "BOTTOMRIGHT", -30, 10)

    if scroll.ScrollBar then
        scroll.ScrollBar:ClearAllPoints()
        scroll.ScrollBar:SetPoint("TOPRIGHT",    scroll, "TOPRIGHT",    0, -16)
        scroll.ScrollBar:SetPoint("BOTTOMRIGHT", scroll, "BOTTOMRIGHT", 0,  16)
    end

    scroll:EnableMouseWheel(true)
    scroll:SetScript("OnMouseWheel", function(self, delta)
        self:SetVerticalScroll(math.max(self:GetVerticalScroll() - delta * 30, 0))
    end)

    local scrollChild = CreateFrame("Frame")
    scrollChild:SetWidth(1)
    scrollChild:SetHeight(1)
    scroll:SetScrollChild(scrollChild)

    local built = false
    local warnDDsForRefresh = nil  -- populated after first build, refreshed on each show

    panel:SetScript("OnShow", function()
        cachedSoundItems = nil  -- invalidate LSM cache so new sounds appear

        if not built then
            built = true
            local w = scroll:GetWidth()
            scrollChild:SetWidth(w)

            local allWarnCBs, allHCBs, allMCBs, allWarnDDs, allHDDs, allMDDs,
                  defaultHCBs, defaultMCBs, defaultHDDs, defaultMDDs, cols = BuildAbilityList(scrollChild, w)
            local leftW = cols.leftW

            -- store warn dropdowns so we can refresh their items on subsequent opens
            warnDDsForRefresh = {}
            for i, dd in ipairs(allWarnDDs) do
                local ability = dd._ability  -- set below via BuildAbilityList (see note)
                warnDDsForRefresh[i] = dd
            end

            -- Position header elements
            hdrDiv:ClearAllPoints()
            hdrDiv:SetPoint("TOP",    headerBar, "TOPLEFT",    leftW, -4)
            hdrDiv:SetPoint("BOTTOM", headerBar, "BOTTOMLEFT", leftW,  4)

            local rightColCenter = leftW + (scroll:GetWidth() - leftW) / 2
            rightHdrSub:ClearAllPoints()
            rightHdrSub:SetPoint("CENTER", headerBar, "LEFT", rightColCenter, -14)
            rightHdrTitle:ClearAllPoints()
            rightHdrTitle:SetPoint("BOTTOM", rightHdrSub, "TOP", 0, 2)

            enableWarnBtn:SetScript("OnClick", function()
                withCombatGuard(function()
                    CCS.SetAllWarn(true)
                    CCS.RefreshAll()
                    for _, cb in ipairs(allWarnCBs) do cb:SetChecked(true) end
                    for _, dd in ipairs(allWarnDDs) do dd:SetEnabled(true) end
                end)
            end)
            disableWarnBtn:SetScript("OnClick", function()
                withCombatGuard(function()
                    CCS.SetAllWarn(false)
                    CCS.RefreshAll()
                    for _, cb in ipairs(allWarnCBs) do cb:SetChecked(false) end
                    for _, dd in ipairs(allWarnDDs) do dd:SetEnabled(false) end
                end)
            end)
            enableCDBtn:SetScript("OnClick", function()
                withCombatGuard(function()
                    CCS.SetAllCD(true)
                    CCS.RefreshAll()
                    for _, cb in ipairs(defaultHCBs) do cb:SetChecked(true) end
                    for _, cb in ipairs(defaultMCBs) do cb:SetChecked(true) end
                    for _, dd in ipairs(defaultHDDs) do dd:Show(); dd:SetEnabled(true) end
                    for _, dd in ipairs(defaultMDDs) do dd:Show(); dd:SetEnabled(true) end
                end)
            end)
            disableCDBtn:SetScript("OnClick", function()
                withCombatGuard(function()
                    CCS.SetAllCD(false)
                    CCS.RefreshAll()
                    for _, cb in ipairs(defaultHCBs) do cb:SetChecked(false) end
                    for _, cb in ipairs(defaultMCBs) do cb:SetChecked(false) end
                    for _, dd in ipairs(defaultHDDs) do dd:SetEnabled(false) end
                    for _, dd in ipairs(defaultMDDs) do dd:SetEnabled(false) end
                end)
            end)
        else
            -- Panel already built — just refresh the warn dropdown item lists
            -- so newly installed addon sounds appear without a /reload
            if warnDDsForRefresh then
                for _, dd in ipairs(warnDDsForRefresh) do
                    local currentVal = dd:GetValue()
                    dd:SetItems(buildWarnItems(dd._defaultWarn))
                    dd:SetValue(currentVal)
                end
            end
        end
    end)

    return panel
end

------------------------------------------------------------
-- Register, slash, init
------------------------------------------------------------

local function RegisterOptionsPanel(panel)
    local category = Settings.RegisterCanvasLayoutCategory(panel, CATEGORY_NAME)
    Settings.RegisterAddOnCategory(category)
    if type(category) == "table" then
        if type(category.GetID) == "function" then
            CCS_CategoryID = category:GetID()
        elseif type(category.ID) == "number" then
            CCS_CategoryID = category.ID
        end
    end
end

SLASH_CCS1 = "/ccs"
SlashCmdList["CCS"] = function()
    if not CCS_CategoryID then
        print("|cffff5555CCS:|r Options not registered yet.")
        return
    end
    Settings.OpenToCategory(CCS_CategoryID)
end

local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("ADDON_LOADED")
initFrame:SetScript("OnEvent", function(self, _, name)
    if name ~= addonName then return end
    RegisterOptionsPanel(CreateCCSOptionsPanel())
    self:UnregisterEvent("ADDON_LOADED")
end)