local addonName, ns = ...

local W = ns.Widgets
local L = ns.L
local Categories = ns.BWV2Categories

local CYAN_R, CYAN_G, CYAN_B = 0, 207/255, 1.0
local DARK_R, DARK_G, DARK_B = 0.08, 0.08, 0.12

local function CC3()
    if ns.GetClassColor then return ns.GetClassColor() end
    return CYAN_R, CYAN_G, CYAN_B
end

-- Styled EditBox (mirrors MakeNumericBox style from BuffWatcher/Core.lua)
local function MakeStyledInput(parent, w, h, numeric, maxLetters)
    local eb = CreateFrame("EditBox", nil, parent, "BackdropTemplate")
    eb:SetSize(w or 120, h or 22)
    eb:SetBackdrop({
        bgFile   = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
    })
    eb:SetBackdropColor(DARK_R, DARK_G, DARK_B, 0.95)
    eb:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.6)
    eb:SetFontObject("GameFontHighlightSmall")
    eb:SetJustifyH("LEFT")
    eb:SetAutoFocus(false)
    eb:SetTextInsets(6, 4, 0, 0)
    eb:SetTextColor(1, 1, 1, 1)
    if numeric then eb:SetNumeric(true) end
    eb:SetMaxLetters(maxLetters or 50)
    eb:SetScript("OnEditFocusGained", function(self)
        local r, g, b = CC3()
        self:SetBackdropBorderColor(r, g, b, 1)
    end)
    eb:SetScript("OnEditFocusLost", function(self)
        self:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.6)
    end)
    eb:SetScript("OnEnterPressed", function(self) self:ClearFocus() end)
    eb:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    return eb
end

-- Custom radio button (styled like BrightQoL checkbox but circular feel via square with dot)
local function MakeRadioButton(parent, label, isChecked)
    local size = 16
    local btn = CreateFrame("CheckButton", nil, parent, "BackdropTemplate")
    btn:SetSize(size, size)
    btn:SetBackdrop({
        bgFile   = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
        insets   = { left = 1, right = 1, top = 1, bottom = 1 },
    })

    local dot = btn:CreateTexture(nil, "OVERLAY")
    dot:SetSize(8, 8)
    dot:SetPoint("CENTER", 0, 0)
    dot:SetColorTexture(1, 0.75, 0.1, 1)
    dot:Hide()
    btn.dot = dot

    local lbl = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    lbl:SetPoint("LEFT", btn, "RIGHT", 6, 0)
    lbl:SetText(label or "")
    lbl:SetTextColor(1, 1, 1, 1)
    btn.Text = lbl

    local function SetState(self)
        if self:GetChecked() then
            local r, g, b = CC3()
            self:SetBackdropColor(DARK_R, DARK_G, DARK_B, 0.95)
            self:SetBackdropBorderColor(r, g, b, 0.9)
            self.dot:Show()
            self.Text:SetTextColor(1, 1, 1, 1)
        else
            self:SetBackdropColor(DARK_R, DARK_G, DARK_B, 0.95)
            self:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.6)
            self.dot:Hide()
            self.Text:SetTextColor(0.7, 0.7, 0.7, 1)
        end
    end

    btn:SetChecked(isChecked and true or false)
    SetState(btn)

    btn:SetScript("OnEnter", function(self)
        local r, g, b = CC3()
        self:SetBackdropBorderColor(r, g, b, 0.8)
        self.Text:SetTextColor(1, 0.9, 0.8, 1)
    end)
    btn:SetScript("OnLeave", function(self) SetState(self) end)

    hooksecurefunc(btn, "SetChecked", function(self) SetState(self) end)

    btn.UpdateState = SetState
    return btn
end

-- Helper to get player talents
local function GetPlayerTalents()
    local talents = {}
    local configID = C_ClassTalents.GetActiveConfigID()
    if not configID then return talents end
    local specIndex = GetSpecialization()
    if not specIndex then return talents end
    local specID = GetSpecializationInfo(specIndex)
    if not specID then return talents end
    local treeID = C_ClassTalents.GetTraitTreeForSpec(specID)
    if not treeID then return talents end
    local nodeIDs = C_Traits.GetTreeNodes(treeID)
    if not nodeIDs then return talents end
    local seen = {}
    for _, nodeID in ipairs(nodeIDs) do
        local nodeInfo = C_Traits.GetNodeInfo(configID, nodeID)
        if nodeInfo and nodeInfo.entryIDs then
            for _, entryID in ipairs(nodeInfo.entryIDs) do
                local entryInfo = C_Traits.GetEntryInfo(configID, entryID)
                if entryInfo and entryInfo.definitionID then
                    local defInfo = C_Traits.GetDefinitionInfo(entryInfo.definitionID)
                    if defInfo and defInfo.spellID and not seen[defInfo.spellID] then
                        seen[defInfo.spellID] = true
                        local spellInfo = C_Spell.GetSpellInfo(defInfo.spellID)
                        if spellInfo then
                            table.insert(talents, {
                                spellID = defInfo.spellID,
                                name    = defInfo.overrideName or spellInfo.name,
                                icon    = defInfo.overrideIcon or spellInfo.iconID,
                            })
                        end
                    end
                end
            end
        end
    end
    table.sort(talents, function(a, b) return a.name < b.name end)
    return talents
end

---------------------------------------------------------------------------
-- Modal frame (reused)
---------------------------------------------------------------------------
local classBuffModal = nil

local function ShowClassBuffModal(initialClassName, groupData, onSave, onDelete, db)
    -- Build modal once
    if not classBuffModal then
        local m = CreateFrame("Frame", "BrightQoL_ClassBuffModal", UIParent, "BackdropTemplate")
        m:SetSize(540, 560)
        m:SetPoint("CENTER")
        m:SetFrameStrata("FULLSCREEN_DIALOG")
        m:SetBackdrop({
            bgFile   = [[Interface\Buttons\WHITE8x8]],
            edgeFile = [[Interface\Buttons\WHITE8x8]],
            edgeSize = 1,
            insets   = { left = 1, right = 1, top = 1, bottom = 1 },
        })
        m:SetBackdropColor(0.02, 0.02, 0.04, 0.97)
        m:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.8)
        m:EnableMouse(true)
        m:SetMovable(true)
        m:RegisterForDrag("LeftButton")
        m:SetScript("OnDragStart", m.StartMoving)
        m:SetScript("OnDragStop",  m.StopMovingOrSizing)
        m:Hide()

        -- Class-colour top accent
        local topAccent = m:CreateTexture(nil, "OVERLAY")
        topAccent:SetHeight(2)
        topAccent:SetPoint("TOPLEFT", 1, -1)
        topAccent:SetPoint("TOPRIGHT", -1, -1)
        m.topAccent = topAccent

        -- Title bar
        local titleBar = CreateFrame("Frame", nil, m, "BackdropTemplate")
        titleBar:SetHeight(30)
        titleBar:SetPoint("TOPLEFT",  m, "TOPLEFT",  1, -3)
        titleBar:SetPoint("TOPRIGHT", m, "TOPRIGHT", -1, -3)
        titleBar:SetBackdrop({ bgFile = [[Interface\Buttons\WHITE8x8]] })
        titleBar:SetBackdropColor(0.01, 0.01, 0.02, 0.95)

        local titleSep = titleBar:CreateTexture(nil, "OVERLAY")
        titleSep:SetHeight(1)
        titleSep:SetPoint("BOTTOMLEFT",  titleBar, "BOTTOMLEFT",  0, 0)
        titleSep:SetPoint("BOTTOMRIGHT", titleBar, "BOTTOMRIGHT", 0, 0)
        titleSep:SetColorTexture(CYAN_R, CYAN_G, CYAN_B, 0.4)

        m.titleText = titleBar:CreateFontString(nil, "OVERLAY", "BrightQoL_FontLarge")
        m.titleText:SetPoint("LEFT", titleBar, "LEFT", 12, 0)

        -- Close button
        local closeBtn = W:CreateButton(m, { text = "X", width = 26, height = 26 })
        closeBtn:SetPoint("TOPRIGHT", m, "TOPRIGHT", -4, -4)
        closeBtn:SetScript("OnClick", function() m:Hide() end)

        -- Separator between left and right panels
        local divider = m:CreateTexture(nil, "ARTWORK")
        divider:SetWidth(1)
        divider:SetPoint("TOPLEFT",    m, "TOPLEFT",  270, -35)
        divider:SetPoint("BOTTOMLEFT", m, "BOTTOMLEFT", 270, 50)
        divider:SetColorTexture(CYAN_R, CYAN_G, CYAN_B, 0.2)

        -- Bottom separator
        local bottomSep = m:CreateTexture(nil, "ARTWORK")
        bottomSep:SetHeight(1)
        bottomSep:SetPoint("BOTTOMLEFT",  m, "BOTTOMLEFT",  1, 48)
        bottomSep:SetPoint("BOTTOMRIGHT", m, "BOTTOMRIGHT", -1, 48)
        bottomSep:SetColorTexture(CYAN_R, CYAN_G, CYAN_B, 0.25)

        m:SetScript("OnHide", function(self)
            if self.talentDropdownPanel then
                self.talentDropdownPanel:Hide()
            end
        end)

        -- Apply class colour accent on show
        m:SetScript("OnShow", function(self)
            local r, g, b = CC3()
            self.topAccent:SetColorTexture(r, g, b, 1)
        end)

        classBuffModal = m
    end

    local modal = classBuffModal
    local isEdit = groupData ~= nil
    do
        local r, g, b = CC3()
        local titleStr = isEdit and L["BWV2_MODAL_EDIT_TITLE"] or L["BWV2_MODAL_ADD_TITLE"]
        modal.titleText:SetText(string.format("|cff%02x%02x%02x%s|r", r*255, g*255, b*255, titleStr))
    end

    -- Clear old content
    for _, key in ipairs({"leftContent", "rightContent", "saveBtn", "cancelBtn", "deleteBtn"}) do
        if modal[key] then
            modal[key]:Hide()
            modal[key]:SetParent(nil)
            modal[key] = nil
        end
    end
    if modal.talentDropdownPanel then
        modal.talentDropdownPanel:Hide()
        modal.talentDropdownPanel:SetParent(nil)
        modal.talentDropdownPanel = nil
    end

    -- Edit state
    local editState = {
        className      = initialClassName,
        name           = groupData and groupData.name or "",
        checkType      = groupData and groupData.checkType or "self",
        minRequired    = groupData and groupData.minRequired or 1,
        specFilter     = groupData and groupData.specFilter and {unpack(groupData.specFilter)} or {},
        spellIDs       = groupData and groupData.spellIDs and {unpack(groupData.spellIDs)} or {},
        enchantIDs     = groupData and groupData.enchantIDs and {unpack(groupData.enchantIDs)} or {},
        talentCondition = groupData and groupData.talentCondition and {
            talentID = groupData.talentCondition.talentID,
            mode     = groupData.talentCondition.mode,
        } or nil,
        thresholds = groupData and groupData.thresholds and {
            dungeon = groupData.thresholds.dungeon or 0,
            raid    = groupData.thresholds.raid    or 0,
            other   = groupData.thresholds.other   or 0,
        } or { dungeon = 0, raid = 0, other = 0 },
    }

    -- =========================================================
    -- LEFT PANEL
    -- =========================================================
    local LEFT_X = 12
    local leftContent = CreateFrame("Frame", nil, modal)
    leftContent:SetPoint("TOPLEFT", modal, "TOPLEFT", LEFT_X, -38)
    leftContent:SetSize(252, 510)
    modal.leftContent = leftContent

    local yOff = -4

    -- Section label helper
    local function SectionLabel(text, y)
        local fs = leftContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        fs:SetPoint("TOPLEFT", 0, y)
        fs:SetText(W.Colorize(text:upper(), "00cfff"))
        return fs
    end

    -- ── CLASS SELECTOR ──────────────────────────────────────
    SectionLabel(L["BWV2_CLASS"], yOff)
    yOff = yOff - 18

    -- Build class dropdown using W:CreateDropdown
    local classOptions = {}
    for _, cn in ipairs(Categories.CLASS_ORDER) do
        local ci = Categories.CLASS_INFO[cn]
        classOptions[#classOptions + 1] = { text = ci.name, value = cn }
    end

    -- Inline custom dropdown (simpler than W:CreateDropdown which needs db.key)
    local classDropBtn = CreateFrame("Button", nil, leftContent, "BackdropTemplate")
    classDropBtn:SetSize(200, 22)
    classDropBtn:SetPoint("TOPLEFT", 0, yOff)
    classDropBtn:SetBackdrop({
        bgFile   = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
    })
    classDropBtn:SetBackdropColor(DARK_R, DARK_G, DARK_B, 0.95)
    classDropBtn:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.6)

    local classDropText = classDropBtn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    classDropText:SetPoint("LEFT", 6, 0)
    classDropText:SetPoint("RIGHT", -18, 0)
    classDropText:SetJustifyH("LEFT")
    classDropText:SetTextColor(1, 1, 1, 1)

    local classDropArrow = classDropBtn:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    classDropArrow:SetPoint("RIGHT", -5, 0)
    classDropArrow:SetText(W.Colorize("▾", "00cfff"))

    local function UpdateClassDropText()
        local ci = Categories.CLASS_INFO[editState.className]
        classDropText:SetText(ci and ci.name or L["BWV2_SELECT_CLASS"])
    end
    UpdateClassDropText()

    -- Class dropdown menu
    local classMenu = CreateFrame("Frame", nil, classDropBtn, "BackdropTemplate")
    classMenu:SetWidth(200)
    classMenu:SetPoint("TOPLEFT", classDropBtn, "BOTTOMLEFT", 0, -2)
    classMenu:SetBackdrop({
        bgFile   = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
    })
    classMenu:SetBackdropColor(DARK_R, DARK_G, DARK_B, 0.98)
    classMenu:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.7)
    classMenu:SetFrameStrata("TOOLTIP")
    classMenu:SetFrameLevel(200)
    classMenu:Hide()

    local specElements  = {}
    local specContainer = nil
    local RebuildSpecCheckboxes  -- forward declare

    local classMenuItems = {}
    for i, opt in ipairs(classOptions) do
        local item = CreateFrame("Button", nil, classMenu, "BackdropTemplate")
        item:SetSize(198, 20)
        item:SetPoint("TOPLEFT", 1, -1 - (i-1)*20)
        item:SetBackdrop({ bgFile = [[Interface\Buttons\WHITE8x8]] })
        item:SetBackdropColor(0, 0, 0, 0)
        local itxt = item:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        itxt:SetPoint("LEFT", 6, 0)
        itxt:SetText(opt.text)
        itxt:SetTextColor(1, 1, 1, 1)
        item:SetScript("OnEnter", function(self)
            local r, g, b = CC3(); self:SetBackdropColor(r, g, b, 0.25)
        end)
        item:SetScript("OnLeave", function(self) self:SetBackdropColor(0, 0, 0, 0) end)
        local capVal = opt.value
        item:SetScript("OnClick", function()
            if not isEdit then
                editState.className = capVal
                wipe(editState.specFilter)
                UpdateClassDropText()
                classMenu:Hide()
                if RebuildSpecCheckboxes then RebuildSpecCheckboxes() end
            end
        end)
        classMenuItems[i] = item
    end
    classMenu:SetHeight(#classOptions * 20 + 2)

    classDropBtn:SetScript("OnClick", function()
        if isEdit then return end
        if classMenu:IsShown() then classMenu:Hide()
        else classMenu:Show() end
    end)
    classDropBtn:SetScript("OnEnter", function(self)
        if not isEdit then self:SetBackdropBorderColor(CC3()) end
    end)
    classDropBtn:SetScript("OnLeave", function(self)
        self:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.6)
    end)

    if isEdit then
        classDropBtn:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.5)
        classDropText:SetTextColor(0.5, 0.5, 0.5, 1)
        classDropArrow:SetText("")
    end

    yOff = yOff - 28

    -- ── GROUP NAME ───────────────────────────────────────────
    SectionLabel(L["BWV2_GROUP_NAME"], yOff)
    yOff = yOff - 18

    local nameInput = MakeStyledInput(leftContent, 200, 22, false, 40)
    nameInput:SetPoint("TOPLEFT", 0, yOff)
    nameInput:SetText(editState.name)
    nameInput:SetScript("OnTextChanged", function(self) editState.name = self:GetText() end)
    yOff = yOff - 28

    -- ── CHECK TYPE ───────────────────────────────────────────
    SectionLabel(L["BWV2_CHECK_TYPE"], yOff)
    yOff = yOff - 18

    local checkTypes = {
        { key = "self",         label = L["BWV2_TYPE_SELF"]    },
        { key = "targeted",     label = L["BWV2_TYPE_TARGETED"] },
        { key = "weaponEnchant",label = L["BWV2_TYPE_WEAPON"]  },
    }
    local checkTypeRadios = {}
    local function UpdateCheckTypeRadios()
        for _, r in ipairs(checkTypeRadios) do
            r:SetChecked(r.ctKey == editState.checkType)
        end
    end
    for _, ct in ipairs(checkTypes) do
        local radio = MakeRadioButton(leftContent, ct.label, editState.checkType == ct.key)
        radio:SetPoint("TOPLEFT", 4, yOff)
        radio.ctKey = ct.key
        radio:SetScript("OnClick", function(self)
            editState.checkType = self.ctKey
            UpdateCheckTypeRadios()
        end)
        checkTypeRadios[#checkTypeRadios + 1] = radio
        yOff = yOff - 20
    end
    yOff = yOff - 4

    -- ── MIN REQUIRED ─────────────────────────────────────────
    SectionLabel(L["BWV2_MIN_REQUIRED"], yOff)
    yOff = yOff - 18

    local minRow = CreateFrame("Frame", nil, leftContent)
    minRow:SetSize(200, 22)
    minRow:SetPoint("TOPLEFT", 0, yOff)

    local minInput = MakeStyledInput(minRow, 44, 22, true, 3)
    minInput:SetPoint("LEFT", 0, 0)
    minInput:SetText(tostring(editState.minRequired))
    minInput:SetScript("OnTextChanged", function(self)
        local v = tonumber(self:GetText())
        editState.minRequired = v and math.max(0, math.floor(v)) or 1
    end)

    local minHint = minRow:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    minHint:SetPoint("LEFT", minInput, "RIGHT", 6, 0)
    minHint:SetText(W.Colorize(L["BWV2_MIN_HINT"], "888888"))
    yOff = yOff - 28

    -- ── TALENT CONDITION ─────────────────────────────────────
    SectionLabel(L["BWV2_TALENT_CONDITION"], yOff)
    yOff = yOff - 18

    local cachedTalents = GetPlayerTalents()

    -- Talent selector button
    local talentBtn = CreateFrame("Button", nil, leftContent, "BackdropTemplate")
    talentBtn:SetSize(172, 22)
    talentBtn:SetPoint("TOPLEFT", 0, yOff)
    talentBtn:SetBackdrop({
        bgFile   = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
    })
    talentBtn:SetBackdropColor(DARK_R, DARK_G, DARK_B, 0.95)
    talentBtn:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.6)

    local talentIcon = talentBtn:CreateTexture(nil, "ARTWORK")
    talentIcon:SetSize(16, 16)
    talentIcon:SetPoint("LEFT", 4, 0)
    talentIcon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    talentIcon:Hide()

    local talentText = talentBtn:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    talentText:SetPoint("LEFT", 24, 0)
    talentText:SetPoint("RIGHT", -16, 0)
    talentText:SetJustifyH("LEFT")
    talentText:SetText(W.Colorize(L["BWV2_SELECT_TALENT"], "666666"))

    local talentArrow = talentBtn:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    talentArrow:SetPoint("RIGHT", -4, 0)
    talentArrow:SetText(W.Colorize("▾", "00cfff"))

    -- Clear talent button
    local clearTalentBtn = W:CreateButton(leftContent, { text = "X", width = 22, height = 22 })
    clearTalentBtn:SetPoint("LEFT", talentBtn, "RIGHT", 4, 0)

    -- Talent dropdown panel
    local talentPanel = CreateFrame("Frame", nil, modal, "BackdropTemplate")
    modal.talentDropdownPanel = talentPanel
    talentPanel:SetSize(190, 160)
    talentPanel:SetPoint("TOPLEFT", talentBtn, "BOTTOMLEFT", 0, -2)
    talentPanel:SetBackdrop({
        bgFile   = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
    })
    talentPanel:SetBackdropColor(DARK_R, DARK_G, DARK_B, 0.98)
    talentPanel:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.7)
    talentPanel:SetFrameStrata("TOOLTIP")
    talentPanel:SetFrameLevel(200)
    talentPanel:Hide()

    local talentSearch = MakeStyledInput(talentPanel, 178, 18, false, 50)
    talentSearch:SetPoint("TOPLEFT", 6, -4)

    local talentSearchPlaceholder = talentPanel:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    talentSearchPlaceholder:SetPoint("LEFT", talentSearch, "LEFT", 0, 0)
    talentSearchPlaceholder:SetText(L["BWV2_FILTER_TALENTS"])
    talentSearchPlaceholder:SetTextColor(0.4, 0.4, 0.4)
    talentSearch:SetScript("OnEditFocusGained", function() talentSearchPlaceholder:Hide() end)
    talentSearch:SetScript("OnEditFocusLost", function(self)
        if self:GetText() == "" then talentSearchPlaceholder:Show() end
    end)

    local talentListArea = CreateFrame("Frame", nil, talentPanel)
    talentListArea:SetPoint("TOPLEFT", 4, -26)
    talentListArea:SetPoint("BOTTOMRIGHT", -4, 4)
    talentListArea:EnableMouseWheel(true)

    local talentRows  = {}
    local talentFiltered = {}
    local talentScrollOff = 0
    local TALENT_ROW_H  = 20
    local TALENT_VISIBLE = 6

    local function UpdateTalentDisplay()
        if editState.talentCondition and editState.talentCondition.talentID then
            local info = C_Spell.GetSpellInfo(editState.talentCondition.talentID)
            if info then
                talentIcon:SetTexture(info.iconID)
                talentIcon:Show()
                talentText:SetText(info.name)
                talentText:SetTextColor(0.4, 0.9, 0.4, 1)
                return
            end
        end
        talentIcon:Hide()
        talentText:SetText(W.Colorize(L["BWV2_SELECT_TALENT"], "666666"))
    end

    local function RefreshTalentRows()
        for i = 1, TALENT_VISIBLE do
            if not talentRows[i] then
                local row = CreateFrame("Button", nil, talentListArea, "BackdropTemplate")
                row:SetSize(182, TALENT_ROW_H)
                row:SetPoint("TOPLEFT", 0, -(i-1)*TALENT_ROW_H)
                row:SetBackdrop({ bgFile = [[Interface\Buttons\WHITE8x8]] })
                row:SetBackdropColor(0, 0, 0, 0)
                row.bg = row:GetBackdropTexture("bg")
                row.iconTex = row:CreateTexture(nil, "ARTWORK")
                row.iconTex:SetSize(16, 16)
                row.iconTex:SetPoint("LEFT", 2, 0)
                row.iconTex:SetTexCoord(0.08, 0.92, 0.08, 0.92)
                row.nameTxt = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
                row.nameTxt:SetPoint("LEFT", row.iconTex, "RIGHT", 4, 0)
                row.nameTxt:SetPoint("RIGHT", -2, 0)
                row.nameTxt:SetJustifyH("LEFT")
                row:SetScript("OnEnter", function(self)
                    local r, g, b = CC3(); self:SetBackdropColor(r, g, b, 0.25)
                end)
                row:SetScript("OnLeave", function(self) self:SetBackdropColor(0, 0, 0, 0) end)
                talentRows[i] = row
            end
            local idx = talentFiltered[talentScrollOff + i]
            local row = talentRows[i]
            if idx then
                local t = cachedTalents[idx]
                row.iconTex:SetTexture(t.icon)
                row.nameTxt:SetText(t.name)
                row:SetScript("OnClick", function()
                    editState.talentCondition = editState.talentCondition or { mode = "activate" }
                    editState.talentCondition.talentID = t.spellID
                    UpdateTalentDisplay()
                    talentPanel:Hide()
                    talentSearch:SetText("")
                    talentSearchPlaceholder:Show()
                end)
                row:Show()
            else
                row:Hide()
            end
        end
    end

    local function FilterTalents(q)
        talentFiltered = {}
        local lower = q and q:lower() or ""
        for i, t in ipairs(cachedTalents) do
            if lower == "" or t.name:lower():find(lower, 1, true) then
                talentFiltered[#talentFiltered + 1] = i
            end
        end
        talentScrollOff = 0
        RefreshTalentRows()
    end

    talentSearch:SetScript("OnTextChanged", function(self) FilterTalents(self:GetText()) end)
    talentListArea:SetScript("OnMouseWheel", function(_, delta)
        talentScrollOff = math.max(0, math.min(talentScrollOff - delta, math.max(0, #talentFiltered - TALENT_VISIBLE)))
        RefreshTalentRows()
    end)

    talentBtn:SetScript("OnClick", function()
        if talentPanel:IsShown() then talentPanel:Hide()
        else
            FilterTalents("")
            talentPanel:Show()
            talentSearch:SetFocus()
        end
    end)
    talentBtn:SetScript("OnEnter", function(self)
        local r, g, b = CC3(); self:SetBackdropBorderColor(r, g, b, 0.9)
    end)
    talentBtn:SetScript("OnLeave", function(self)
        self:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.6)
    end)

    clearTalentBtn:SetScript("OnClick", function()
        editState.talentCondition = nil
        UpdateTalentDisplay()
        talentPanel:Hide()
        talentSearch:SetText("")
        talentSearchPlaceholder:Show()
    end)

    UpdateTalentDisplay()
    yOff = yOff - 26

    -- Talent mode radios
    local talentModes = {
        { key = "activate", label = L["BWV2_MODE_ACTIVATE"] },
        { key = "skip",     label = L["BWV2_MODE_SKIP"]     },
    }
    local talentModeRadios = {}
    for _, tm in ipairs(talentModes) do
        local isChecked = editState.talentCondition and editState.talentCondition.mode == tm.key
                          or (tm.key == "activate" and not editState.talentCondition)
        local radio = MakeRadioButton(leftContent, tm.label, isChecked)
        radio:SetPoint("TOPLEFT", 4, yOff)
        radio.tmKey = tm.key
        radio:SetScript("OnClick", function(self)
            for _, r in ipairs(talentModeRadios) do r:SetChecked(r.tmKey == self.tmKey) end
            if editState.talentCondition then
                editState.talentCondition.mode = self.tmKey
            else
                editState.talentCondition = { mode = self.tmKey }
            end
        end)
        talentModeRadios[#talentModeRadios + 1] = radio
        yOff = yOff - 20
    end
    yOff = yOff - 4

    -- ── SPEC FILTER ──────────────────────────────────────────
    SectionLabel(L["BWV2_SPECS"], yOff)
    yOff = yOff - 18

    specContainer = CreateFrame("Frame", nil, leftContent)
    specContainer:SetPoint("TOPLEFT", 0, yOff)
    specContainer:SetSize(250, 80)

    RebuildSpecCheckboxes = function()
        for _, e in ipairs(specElements) do e:Hide(); e:SetParent(nil) end
        wipe(specElements)
        local ci = Categories.CLASS_INFO[editState.className]
        if not ci or not ci.specs then return end
        local sy = 0
        -- "All Specs" checkbox
        local allCB = W:CreateCheckbox(specContainer, {
            label = L["BWV2_ALL_SPECS"],
            checked = (#editState.specFilter == 0),
            size = 16,
        })
        allCB:SetPoint("TOPLEFT", 0, sy)
        specElements[#specElements + 1] = allCB
        sy = sy - 20
        local specCBs = {}
        for _, specData in ipairs(ci.specs) do
            local specID, specName = specData[1], specData[2]
            local hasSpec = false
            for _, id in ipairs(editState.specFilter) do if id == specID then hasSpec = true; break end end
            local scb = W:CreateCheckbox(specContainer, {
                label   = specName,
                checked = hasSpec,
                size    = 16,
            })
            scb:SetPoint("TOPLEFT", 0, sy)
            scb.specID = specID
            scb:SetScript("OnClick", function(self)
                if self:GetChecked() then
                    table.insert(editState.specFilter, self.specID)
                    allCB:SetChecked(false)
                else
                    for i, id in ipairs(editState.specFilter) do
                        if id == self.specID then table.remove(editState.specFilter, i); break end
                    end
                    if #editState.specFilter == 0 then allCB:SetChecked(true) end
                end
            end)
            specElements[#specElements + 1] = scb
            specCBs[specID] = scb
            sy = sy - 20
        end
        allCB:SetScript("OnClick", function(self)
            if self:GetChecked() then
                wipe(editState.specFilter)
                for _, c in pairs(specCBs) do c:SetChecked(false) end
            else
                self:SetChecked(true)
            end
        end)
        specContainer:SetHeight(math.abs(sy) + 4)
    end
    RebuildSpecCheckboxes()

    -- ── DURATION THRESHOLDS ──────────────────────────────────
    -- (placed below specContainer dynamically after spec rebuild)
    local threshLabel = leftContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    threshLabel:SetPoint("TOPLEFT", specContainer, "BOTTOMLEFT", 0, -10)
    threshLabel:SetText(W.Colorize((L["BWV2_DURATION_THRESHOLDS"] or "Duration Thresholds"):upper(), "00cfff"))

    local threshHint = leftContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    threshHint:SetPoint("LEFT", threshLabel, "RIGHT", 6, 0)
    threshHint:SetText(W.Colorize(L["BWV2_THRESHOLD_HINT"] or "(min, 0=off)", "888888"))

    local threshRow = CreateFrame("Frame", nil, leftContent)
    threshRow:SetPoint("TOPLEFT", threshLabel, "BOTTOMLEFT", 0, -4)
    threshRow:SetSize(250, 22)

    local function ThreshField(parent, labelText, anchorLeft)
        local lbl = parent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        if anchorLeft then
            lbl:SetPoint("LEFT", anchorLeft, "RIGHT", 8, 0)
        else
            lbl:SetPoint("LEFT", 0, 0)
        end
        lbl:SetText(labelText)
        lbl:SetTextColor(0.7, 0.7, 0.7, 1)
        local inp = MakeStyledInput(parent, 34, 20, true, 4)
        inp:SetPoint("LEFT", lbl, "RIGHT", 4, 0)
        return inp, lbl
    end

    local thDungInput, thDungLbl = ThreshField(threshRow, L["BWV2_DUNGEON"] or "M+", nil)
    thDungInput:SetText(tostring(math.floor((editState.thresholds.dungeon or 0) / 60)))
    local thRaidInput, _ = ThreshField(threshRow, L["BWV2_RAID"] or "Raid", thDungInput)
    thRaidInput:SetText(tostring(math.floor((editState.thresholds.raid or 0) / 60)))
    local thOtherInput, _ = ThreshField(threshRow, L["BWV2_OTHER"] or "Other", thRaidInput)
    thOtherInput:SetText(tostring(math.floor((editState.thresholds.other or 0) / 60)))

    editState.threshInputs = { dungeon = thDungInput, raid = thRaidInput, other = thOtherInput }

    -- =========================================================
    -- RIGHT PANEL
    -- =========================================================
    local RIGHT_X = 278
    local rightContent = CreateFrame("Frame", nil, modal)
    rightContent:SetPoint("TOPLEFT", modal, "TOPLEFT", RIGHT_X, -38)
    rightContent:SetSize(248, 510)
    modal.rightContent = rightContent

    local ry = -4

    -- ID list label
    local idLabel = rightContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    idLabel:SetPoint("TOPLEFT", 0, ry)
    idLabel:SetText(W.Colorize((L["BWV2_SPELL_ENCHANT_IDS"] or "Spell / Enchant IDs"):upper(), "00cfff"))
    ry = ry - 18

    -- ID list frame
    local idListFrame = CreateFrame("Frame", nil, rightContent, "BackdropTemplate")
    idListFrame:SetPoint("TOPLEFT", 0, ry)
    idListFrame:SetSize(244, 200)
    idListFrame:SetBackdrop({
        bgFile   = [[Interface\Buttons\WHITE8x8]],
        edgeFile = [[Interface\Buttons\WHITE8x8]],
        edgeSize = 1,
    })
    idListFrame:SetBackdropColor(0.04, 0.04, 0.06, 0.9)
    idListFrame:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.3)

    local function RefreshIDList()
        for _, child in ipairs({idListFrame:GetChildren()}) do child:Hide(); child:SetParent(nil) end
        local ids = editState.checkType == "weaponEnchant" and editState.enchantIDs or editState.spellIDs
        local ly = -4
        for i, id in ipairs(ids) do
            local row = CreateFrame("Frame", nil, idListFrame, "BackdropTemplate")
            row:SetSize(234, 22)
            row:SetPoint("TOPLEFT", 5, ly)
            row:SetBackdrop({ bgFile = [[Interface\Buttons\WHITE8x8]] })
            row:SetBackdropColor(0.06, 0.06, 0.10, 0.7)
            row:SetBackdropBorderColor(CYAN_R, CYAN_G, CYAN_B, 0.12)

            local ico = row:CreateTexture(nil, "ARTWORK")
            ico:SetSize(16, 16)
            ico:SetPoint("LEFT", 4, 0)
            ico:SetTexCoord(0.08, 0.92, 0.08, 0.92)

            local displayName, displayIcon
            if editState.checkType == "weaponEnchant" then
                -- Enchant IDs are not spell IDs; resolve via ENCHANT_ICON_MAP first
                local map = Categories and Categories.ENCHANT_ICON_MAP
                local spellID = map and map[id]
                if spellID then
                    local spellInfo = C_Spell.GetSpellInfo(spellID)
                    displayName = spellInfo and spellInfo.name
                    displayIcon = spellInfo and spellInfo.iconID
                end
                -- Fallback: try talentCondition name
                if not displayName and editState.talentCondition and editState.talentCondition.talentID then
                    local talentInfo = C_Spell.GetSpellInfo(editState.talentCondition.talentID)
                    if talentInfo then displayName = talentInfo.name end
                end
                -- Last resort: try GetSpellInfo on the enchant ID directly
                if not displayName then
                    local info = C_Spell.GetSpellInfo(id)
                    displayName = info and info.name
                    displayIcon = info and info.iconID
                end
                displayName = displayName or (L["BWV2_ENCHANT_ID_LABEL"] or "Enchant ID")
            else
                local info = C_Spell.GetSpellInfo(id)
                displayName = info and info.name or (L["BWV2_UNKNOWN"] or "Unknown")
                displayIcon = info and info.iconID
            end

            if displayIcon then ico:SetTexture(displayIcon) end

            local txt = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
            txt:SetPoint("LEFT", ico, "RIGHT", 4, 0)
            txt:SetPoint("RIGHT", -26, 0)
            txt:SetJustifyH("LEFT")
            txt:SetText(W.Colorize(tostring(id) .. " - ", "00cfff") .. displayName)

            local capI = i
            local delBtn = W:CreateButton(row, { text = "X", width = 20, height = 18 })
            delBtn:SetPoint("RIGHT", -3, 0)
            delBtn:SetScript("OnClick", function()
                table.remove(ids, capI)
                RefreshIDList()
            end)

            ly = ly - 24
        end
        idListFrame:SetHeight(math.max(math.abs(ly) + 4, 30))
    end
    RefreshIDList()
    ry = ry - 210

    -- Add ID row
    local addIDRow = CreateFrame("Frame", nil, rightContent)
    addIDRow:SetSize(244, 24)
    addIDRow:SetPoint("TOPLEFT", 0, ry)

    local addIDInput = MakeStyledInput(addIDRow, 160, 22, true, 10)
    addIDInput:SetPoint("LEFT", 0, 0)

    local addIDBtn = W:CreateButton(addIDRow, {
        text = L["COMMON_ADD"] or "Add",
        width = 70,
        onClick = function()
            local id = tonumber(addIDInput:GetText())
            if id and id > 0 then
                local ids = editState.checkType == "weaponEnchant" and editState.enchantIDs or editState.spellIDs
                table.insert(ids, id)
                addIDInput:SetText("")
                RefreshIDList()
                -- Resize idListFrame parent if needed
            end
        end,
    })
    addIDBtn:SetPoint("LEFT", addIDInput, "RIGHT", 6, 0)

    -- Hook check type radios to update ID list label context
    for _, radio in ipairs(checkTypeRadios) do
        radio:HookScript("OnClick", function()
            RefreshIDList()
        end)
    end

    -- =========================================================
    -- BOTTOM BUTTONS
    -- =========================================================
    modal.saveBtn = W:CreateButton(modal, {
        text = L["COMMON_SAVE"] or "Save",
        width = 90,
        onClick = function()
            if not editState.className then
                print("|cffff0000[BuffWatcher]|r " .. (L["BWV2_ERR_SELECT_CLASS"] or "Select a class."))
                return
            end
            if editState.name == "" then
                print("|cffff0000[BuffWatcher]|r " .. (L["BWV2_ERR_NAME_REQUIRED"] or "Name required."))
                return
            end
            local ids = editState.checkType == "weaponEnchant" and editState.enchantIDs or editState.spellIDs
            if #ids == 0 then
                print("|cffff0000[BuffWatcher]|r " .. (L["BWV2_ERR_ID_REQUIRED"] or "Add at least one ID."))
                return
            end
            local key = editState.name:lower():gsub("%s+", "_"):gsub("[^%w_]", "")
            if groupData and groupData.key then key = groupData.key end

            local newGroup = {
                key             = key,
                name            = editState.name,
                checkType       = editState.checkType,
                minRequired     = editState.minRequired,
                specFilter      = editState.specFilter,
                spellIDs        = editState.checkType ~= "weaponEnchant" and editState.spellIDs or nil,
                enchantIDs      = editState.checkType == "weaponEnchant"  and editState.enchantIDs or nil,
                talentCondition = editState.talentCondition,
                thresholds      = {
                    dungeon = (tonumber(editState.threshInputs.dungeon:GetText()) or 0) * 60,
                    raid    = (tonumber(editState.threshInputs.raid:GetText())    or 0) * 60,
                    other   = (tonumber(editState.threshInputs.other:GetText())   or 0) * 60,
                },
            }

            if onSave then onSave(editState.className, newGroup) end
            modal:Hide()
        end,
    })
    modal.saveBtn:SetPoint("BOTTOMLEFT", 14, 14)

    modal.cancelBtn = W:CreateButton(modal, {
        text = L["COMMON_CANCEL"] or "Cancel",
        width = 90,
        onClick = function() modal:Hide() end,
    })
    modal.cancelBtn:SetPoint("LEFT", modal.saveBtn, "RIGHT", 8, 0)

    if isEdit and onDelete then
        modal.deleteBtn = W:CreateButton(modal, {
            text = L["BWV2_DELETE"] or "Delete",
            width = 90,
            onClick = function()
                if onDelete then onDelete() end
                modal:Hide()
            end,
        })
        modal.deleteBtn:SetPoint("BOTTOMRIGHT", -14, 14)
    end

    modal:Show()
end

-- Export
ns.ShowClassBuffModal = ShowClassBuffModal
