local addonName, ns = ...

local COLORS = {
    BLUE = "018ee7",
    ORANGE = "00cfff",
    SUCCESS = "00ff00",
    ERROR = "ff0000",
}

local function ColorizeText(text, color)
    return "|cff" .. color .. text .. "|r"
end

BrightQoL = BrightQoL or {}

-- Session-only suppression flag (resets on reload)
ns.notificationsSuppressed = false
-- ---------------------------------------------------------------------------
-- Felguard family names across all WoW client locales.
-- UnitCreatureFamily("pet") returns a localised string, so we need every
-- known translation to correctly detect Felguard on non-English clients.
-- Last verified: WoW 11.x / 10.x
-- ---------------------------------------------------------------------------
ns.FELGUARD_FAMILY_NAMES = {
    -- enUS / enGB
    "felguard",
    -- deDE
    "teufelswache",
    -- frFR
    "garde-démon",
    "garde-demon",
    -- esES / esMX
    "guardián del mal",
    "guardian del mal",
    -- ptBR / ptPT
    "guarda-infernal",
    -- itIT
    "guardia infernale",
    -- ruRU
    "страж скверны",
    -- koKR
    "악마 경비병",
    -- zhCN
    "恶魔卫兵",
    -- zhTW
    "惡魔衛兵",
}

ns.DB = ns.DB or {}
ns.DefaultConfig = {
    config = {
        posX = 0,
        posY = 0,
        autoRepair = false,
        autoSell = false,
        skinChar = true,
        optimized = false,
        combatNotify = true,
        minimapButtonPos = 225,
    }
}

-- Apply default values to a settings table
local function ApplyDefaults(target, defaults)
    for k, v in pairs(defaults) do
        if target[k] == nil then target[k] = v end
    end
end

-- Module default settings tables
local BRIGHT_FONT = "Interface\\AddOns\\BrightQoL\\Assets\\Fonts\\Bright.ttf"

local BRIGHT_DATAPANELS_DEFAULTS = {
    enabled         = false,
    -- Global BDT settings
    locked          = false,
    showPanels      = true,
    showLoginMessage= false,
    debugMode       = false,
    useClassColor   = false,
    useCustomColor  = false,
    customColorHex  = "#ffffff",
    hideModuleTitle = false,
    use24HourClock  = false,
    font            = "Friz Quadrata TT",
    fontOutline     = "NONE",
    fontSize        = 12,
    fontShadowEnabled   = true,
    tooltipFont         = "Friz Quadrata TT",
    tooltipFontOutline  = "NONE",
    tooltipHeaderFontSize = 14,
    tooltipLineFontSize   = 12,
    tooltipShadowEnabled  = true,
    bars = {},
    moduleSettings = {},
    minimap = { hide = false },
}

local COMBAT_TIMER_DEFAULTS = {
    enabled = false, unlock = false, font = BRIGHT_FONT,
    colorR = 1, colorG = 1, colorB = 1, useClassColor = false, point = "TOP",
    x = 0, y = -50, width = 200, slotHeight = 30, hidePrefix = false,
    instanceOnly = false, chatReport = true, stickyTimer = false,
    showBackdrop = true, showBorder = true,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
}

local COMBAT_ALERT_DEFAULTS = {
    enabled = false, unlock = false, font = BRIGHT_FONT,
    enterR = 0, enterG = 1, enterB = 0, leaveR = 1, leaveG = 0, leaveB = 0,
    point = "CENTER", x = 0, y = 100, width = 200, height = 50,
    enterText = "+Combat", leaveText = "-Combat",
    -- Enter combat audio (audioMode: "none", "sound", "tts")
    enterAudioMode = "none", enterSoundID = 8959,
    enterTtsMessage = "Combat", enterTtsVolume = 50, enterTtsRate = 0, enterTtsVoiceID = 0,
    -- Leave combat audio
    leaveAudioMode = "none", leaveSoundID = 8959,
    leaveTtsMessage = "Safe", leaveTtsVolume = 50, leaveTtsRate = 0, leaveTtsVoiceID = 0,
}

local CROSSHAIR_DEFAULTS = {
    enabled = false, size = 20, thickness = 2, gap = 6,
    colorR = 0, colorG = 1, colorB = 0, useClassColor = false, opacity = 0.8,
    offsetX = 0, offsetY = 0, combatOnly = false,
    dotEnabled = false, dotSize = 2,
    outlineEnabled = true, outlineWeight = 1,
    outlineR = 0, outlineG = 0, outlineB = 0, rotation = 0,
    showTop = true, showRight = true, showBottom = true, showLeft = true,
    dualColor = false, color2R = 1, color2G = 0, color2B = 0,
    circleEnabled = false, circleSize = 30, circleR = 0, circleG = 1, circleB = 0,
    hideWhileMounted = false, meleeRecolor = false,
    meleeRecolorBorder = true, meleeRecolorArms = false,
    meleeRecolorDot = false, meleeRecolorCircle = false,
    meleeOutColorR = 1, meleeOutColorG = 0, meleeOutColorB = 0,
    meleeSoundEnabled = false, meleeSoundID = 8959, meleeSoundInterval = 3,
}

local COMBAT_LOGGER_DEFAULTS = {
    enabled = false,
}



local BUFF_TRACKER_DEFAULTS = {
    enabled = false, iconSize = 40, spacing = 4, textSize = 14,
    font = BRIGHT_FONT, showMissingOnly = false, combatOnly = false,
    showCooldown = true, showStacks = true, unlocked = false,
    showAllRaidBuffs = false, showRaidBuffs = true, showPersonalAuras = true,
    showStances = true, growDirection = "RIGHT", maxIconsPerRow = 10,
    point = "TOP", posX = 0, posY = -100, width = 450, height = 60,
}

local GCD_TRACKER_DEFAULTS = {
    enabled = false, unlock = false, duration = 5, iconSize = 32,
    direction = "RIGHT", spacing = 4, fadeStart = 0.5, stackOverlapping = true,
    point = "CENTER", x = 0, y = -100, combatOnly = false,
    showInDungeon = true, showInRaid = true, showInArena = true,
    showInBattleground = true, showInWorld = true,
    timelineColorR = 0.01, timelineColorG = 0.56, timelineColorB = 0.91, timelineHeight = 4,
    showDowntimeSummary = true,
}

local STEALTH_REMINDER_DEFAULTS = {
    enabled = false, unlock = false, font = BRIGHT_FONT,
    stealthR = 0, stealthG = 1, stealthB = 0, warningR = 1, warningG = 0, warningB = 0,
    showStealthed = true, showNotStealthed = true, disableWhenRested = false,
    stealthText = "STEALTH", warningText = "RESTEALTH",
    point = "CENTER", x = 0, y = 150, width = 200, height = 40,
    stanceEnabled = false, stanceUnlock = false, stanceWarnR = 1, stanceWarnG = 0.4, stanceWarnB = 0,
    stancePoint = "CENTER", stanceX = 0, stanceY = 100, stanceWidth = 200, stanceHeight = 40,
    stanceCombatOnly = false, stanceDisableWhenRested = false,
    stanceSoundEnabled = false, stanceSoundID = 8959, stanceSoundInterval = 3,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
}

local MOVEMENT_ALERT_DEFAULTS = {
    -- Movement Cooldown sub-feature
    enabled = false, unlock = false, font = BRIGHT_FONT,
    displayMode = "text",  -- "text", "icon", "bar"
    textFormat = "No %a - %ts",  -- %a = ability name, %t = time
    barShowIcon = true,
    textColorR = 1, textColorG = 1, textColorB = 1,
    precision = 1,
    pollRate = 100,  -- ms between countdown updates
    point = "CENTER", x = 0, y = 50, width = 200, height = 40,
    combatOnly = false,
    showBackdrop = true, showBorder = true,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
    disabledClasses = {},
    spellOverrides = {},  -- Per-spell config: [spellId] = {enabled, customText, class}
    -- Time Spiral sub-feature
    tsEnabled = false, tsUnlock = false,
    tsText = "FREE MOVEMENT", tsColorR = 0.53, tsColorG = 1, tsColorB = 0,
    tsPoint = "CENTER", tsX = 0, tsY = 100, tsWidth = 200, tsHeight = 40,
    tsSoundEnabled = false, tsSoundID = 8959,
    tsTtsEnabled = false, tsTtsMessage = "Free movement", tsTtsVolume = 50, tsTtsRate = 0,
    -- Gateway Shard sub-feature
    gwEnabled = false, gwUnlock = false, gwCombatOnly = true,
    gwText = "GATEWAY READY", gwColorR = 0.5, gwColorG = 0, gwColorB = 0.8, gwColorUseClassColor = false,
    gwPoint = "CENTER", gwX = 0, gwY = 150, gwWidth = 200, gwHeight = 40,
    gwSoundEnabled = true, gwSoundID = 8959,
    gwTtsEnabled = false, gwTtsMessage = "Gateway ready", gwTtsVolume = 50,
}

local RANGE_CHECK_DEFAULTS = {
    enabled = false, rangeEnabled = true, rangeUnlock = false, rangeFont = BRIGHT_FONT,
    rangeColorR = 0.01, rangeColorG = 0.56, rangeColorB = 0.91,
    rangePoint = "CENTER", rangeX = 0, rangeY = 0, rangeWidth = 200, rangeHeight = 40, rangeCombatOnly = false,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
    displayMode = "text",
    meleeText = "- OUT OF RANGE -",
    meleeColorR = 1, meleeColorG = 0, meleeColorB = 0,
    meleeSoundEnabled = false, meleeSoundID = 8959, meleeSoundInterval = 3,
}



local FOCUS_CAST_BAR_DEFAULTS = {
    enabled = false, unlock = false, point = "CENTER", x = 0, y = 100, width = 250, height = 24,
    barStyle = "Solid",
    barColorR = 0.01, barColorG = 0.56, barColorB = 0.91,
    barColorCdR = 0.5, barColorCdG = 0.5, barColorCdB = 0.5,
    bgColorR = 0.12, bgColorG = 0.12, bgColorB = 0.12, bgAlpha = 0.8,
    showIcon = true, iconSize = 24, iconPosition = "LEFT", autoSizeIcon = true,
    showSpellName = true, showTimeRemaining = true, spellNameTruncate = 0, font = BRIGHT_FONT, fontSize = 12,
    textColorR = 1, textColorG = 1, textColorB = 1, hideFriendlyCasts = false,
    showEmpowerStages = true, showShieldIcon = true,
    showInterruptTick = true, tickColorR = 1, tickColorG = 1, tickColorB = 1, tickColorUseClassColor = false,
    colorNonInterrupt = true,
    nonIntColorR = 0.8, nonIntColorG = 0.2, nonIntColorB = 0.2, nonIntColorUseClassColor = false,
    soundEnabled = false, sound = "Raid Warning",
    ttsEnabled = false, ttsMessage = "Interrupt", ttsVolume = 50, ttsRate = 0, ttsVoiceID = 0,
    hideOnCooldown = false, anchorTo = "CENTER",
    showBorder = true,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
}

local TALENT_REMINDER_DEFAULTS = {
    enabled = false,
}

local PERSONAL_REMINDERS_DEFAULTS = {
    enabled     = false,
    unlock      = false,
    iconSize    = 36,
    spacing     = 4,
    iconsPerRow = 10,
    point       = "TOP",
    x           = 0,
    y           = -165,
    reminders   = {},
}

local RAID_ALERTS_DEFAULTS = {
    enabled = false,
}

local POISON_REMINDER_DEFAULTS = {
    enabled = false,
}

local EQUIPMENT_REMINDER_DEFAULTS = {
    enabled = false,
    showOnInstance = true,
    showOnReadyCheck = true,
    autoHideDelay = 10,
    iconSize = 40,
    point = "CENTER",
    x = 0,
    y = 100,
    -- Enchant Checker settings (flattened with ec prefix)
    ecEnabled = false,
    ecUseAllSpecs = true,  -- Use same rules for all specs
    ecSpecRules = {
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
},  -- { [specID] = { [slotID] = enchantID } } or { [0] = {...} } for all specs
}

local CURSOR_TRACKER_DEFAULTS = {
    enabled = false,
    size = 48,
    shape = "ring.tga",
    color = { r = 1.0, g = 0.66, b = 0.0 },
    showOutOfCombat = true,
    opacityInCombat = 1.0,
    opacityOutOfCombat = 1.0,
    trailEnabled = false,
    trailDuration = 0.6,
    gcdEnabled = true,
    gcdColor = { r = 0.004, g = 0.56, b = 0.91 },
    gcdReadyColor = { r = 0.0, g = 0.8, b = 0.3 },
    gcdReadyMatchSwipe = false,
    gcdAlpha = 1.0,
    hideOnMouseClick = false,
    hideBackground = false,
    castSwipeEnabled = true,
    castSwipeColor = { r = 1.0, g = 0.66, b = 0.0 },
}

local MOUSE_RING_DEFAULTS = {
    enabled = false,
    size = 48,
    shape = "ring.tga",
    colorR = 1.0, colorG = 0.66, colorB = 0.0,
    useClassColor = false,
    showOutOfCombat = true,
    opacityInCombat = 1.0,
    opacityOutOfCombat = 1.0,
    trailEnabled = false,
    trailDuration = 0.6,
    trailR = 1.0, trailG = 1.0, trailB = 1.0,
    gcdEnabled = true,
    gcdR = 0.004, gcdG = 0.56, gcdB = 0.91,
    gcdReadyR = 0.0, gcdReadyG = 0.8, gcdReadyB = 0.3,
    gcdReadyMatchSwipe = false,
    gcdAlpha = 1.0,
    hideOnMouseClick = false,
    hideBackground = false,
    castSwipeEnabled = true,
    castSwipeR = 0.004, castSwipeG = 0.56, castSwipeB = 0.91,
}

local CREZ_DEFAULTS = {
    -- Combat Rez Timer
    enabled = false, unlock = false,
    point = "CENTER", x = 0, y = 150, iconSize = 40,
    timerFontSize = 11, timerColorR = 1, timerColorG = 1, timerColorB = 1, timerAlpha = 1.0,
    countFontSize = 11, countColorR = 1, countColorG = 1, countColorB = 1, countAlpha = 1.0,
    -- Unlock Label
    labelText = "Rez Timer",
    labelFont = "Interface\\AddOns\\BrightQoL\\Assets\\Fonts\\Bright.ttf",
    labelFontSize = 10,
    labelColorR = 1, labelColorG = 0.66, labelColorB = 0,
    labelAlpha = 1.0,
    -- Death Warning
    deathWarning = false,
}

local PET_TRACKER_DEFAULTS = {
    enabled = false, unlock = false,
    showIcon = true, onlyInInstance = false,
    point = "CENTER", x = 0, y = 200,
    width = 200, height = 50,
    textSize = 20, iconSize = 32,
    missingText = "Pet Missing",
    passiveText = "Pet Passive",
    wrongPetText = "Wrong Pet",
    colorR = 1, colorG = 0, colorB = 0,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
}

local COTANK_DEFAULTS = {
    enabled = false, unlock = false,
    point = "CENTER", x = 200, y = 0,
    width = 150, height = 20, bgAlpha = 0.6,
    useClassColor = false,
    healthColorR = 0, healthColorG = 0.8, healthColorB = 0.2,
    showName = true, nameFormat = "full", nameLength = 8,
    font = BRIGHT_FONT,
    nameFontSize = 12, nameColorUseClassColor = false,
    nameColorR = 1, nameColorG = 1, nameColorB = 1,
}

local HEALER_MANA_DEFAULTS = {
    enabled      = false,
    unlock       = false,
    point        = "TOPLEFT", relPoint = "TOPLEFT", x = 200, y = -200,
    width        = 160,
    barHeight    = 16,
    barGap       = 2,
    buffRight    = false,
    buffIcon     = false,
    scale        = 1.0,
    fontSize     = 10,
    font         = "Fonts\\FRIZQT__.TTF",
    fontOutline      = true,
    fontOutlineStyle = "OUTLINE",
    showBackground = false,
    showBorder     = false,
    borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
    trackedBuffs = {
        [29166]  = true,   -- Innervate
        [64901]  = true,   -- Symbol of Hope
        [16191]  = true,   -- Mana Tide Totem
        [388019] = true,   -- Blessing of Winter
    },
}

local SWIFT_STATS_DEFAULTS = {
    enabled            = false,
    unlock             = false,
    point              = "TOPLEFT", relPoint = "TOPLEFT", x = 200, y = -300,
    scale              = 1.0,
    fontSize           = 14,
    font               = "Fonts\\FRIZQT__.TTF",
    showBackground     = true,
    showBorder         = true,
    showRating         = false,
    showPercentage     = true,
    matchValueColor    = false,
    showStrength       = false,
    showAgility        = false,
    showIntellect      = false,
    showTertiary       = false,
    hideZeroTertiary   = false,
    showLeech          = true,
    showAvoidance      = true,
    showSpeed          = true,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0, borderUseClassColor = false,
    colorCritR         = 1.0,  colorCritG    = 0.0,  colorCritB    = 0.0,
    colorHasteR        = 0.0,  colorHasteG   = 0.5,  colorHasteB   = 1.0,
    colorMasteryR      = 0.0,  colorMasteryG = 1.0,  colorMasteryB = 0.0,
    colorVersR         = 1.0,  colorVersG    = 1.0,  colorVersB    = 0.0,
    colorLeechR        = 0.8,  colorLeechG   = 0.2,  colorLeechB   = 0.8,
    colorAvoidanceR    = 0.2,  colorAvoidanceG = 0.8, colorAvoidanceB = 0.8,
    colorSpeedR        = 1.0,  colorSpeedG   = 0.65, colorSpeedB   = 0.0,
    colorPrimaryR      = 1.0,  colorPrimaryG = 0.84, colorPrimaryB = 0.0,
    colorRatingR       = 0.7,  colorRatingG  = 0.7,  colorRatingB  = 0.7,
    colorPercentageR   = 1.0,  colorPercentageG = 1.0, colorPercentageB = 1.0,
}

local DRAGONRIDING_DEFAULTS = {
    enabled            = false,
    unlocked           = false,
    totalWidth         = 216,   -- total bar width; segments = totalWidth / 6
    speedHeight        = 14,
    chargeHeight       = 14,
    gap                = 0,
    showSpeedText      = true,
    swapPosition       = false,
    hideWhenGroundedFull = false,
    showSecondWind     = true,
    showWhirlingSurge  = true,
    point              = "BOTTOM",
    posX               = 0,
    posY               = 200,
    barStyle           = "Solid",
    speedColorR        = 0.00, speedColorG = 0.49, speedColorB = 0.79,
    thrillColorR       = 0.00, thrillColorG = 1.00, thrillColorB = 0.678,
    chargeColorR       = 0.01, chargeColorG = 0.56, chargeColorB = 0.91,
    speedFont          = "Bright",
    speedFontSize      = 12,
    speedTextOffsetX   = 0,
    speedTextOffsetY   = 0,
    surgeIconSize      = 0,
    surgeAnchor        = "RIGHT",
    surgeOffsetX       = 6,
    surgeOffsetY       = 0,
    bgColorR           = 0.12, bgColorG = 0.12, bgColorB = 0.12, bgAlpha = 0.8,
    borderColorR       = 0.00, borderColorG = 0.00, borderColorB = 0.00,
    borderAlpha        = 1.0,  borderSize = 1,
    iconBorderColorR   = 0,    iconBorderColorG = 0, iconBorderColorB = 0,
    iconBorderAlpha    = 1.0,  iconBorderSize = 1,
    showThrillTick     = false,
}

local MYTHIC_TIMER_DEFAULTS = {
    enabled              = false,
    unlock               = false,
    scale                = 1.0,
    showBossKillTimes    = true,
    showEnemyPercent     = true,
    showEnemyCount       = true,
    hideObjectiveTracker = false,
    colors = {
        dungeonName       = { r=1,    g=1,    b=1    },
        keyLevel          = { r=1,    g=1,    b=1    },
        timerText         = { r=1,    g=1,    b=1    },
        timerOvertime     = { r=1,    g=0,    b=0    },
        upgradeTwo        = { r=1,    g=1,    b=0    },
        upgradeThree      = { r=1,    g=1,    b=0    },
        upgradeDepleted   = { r=0.5,  g=0.5,  b=0.5  },
        enemyBar          = { r=0.45, g=0,    b=0.85 },
        enemyText         = { r=1,    g=1,    b=1    },
        enemyTextComplete = { r=0,    g=1,    b=0    },
        bossAlive         = { r=1,    g=1,    b=1    },
        bossDead          = { r=0,    g=1,    b=0    },
        deathText         = { r=1,    g=1,    b=1    },
        deathPenalty      = { r=1,    g=0,    b=0    },
        affixText         = { r=0.5,  g=0.5,  b=0.5  },
    },
}

-- Expose module defaults for restore functionality


local BRIGHT_CHAT_DEFAULTS = {
    enabled         = false,
    -- Background
    bgColorR        = 0.08, bgColorG = 0.08, bgColorB = 0.10,
    bgAlpha         = 0.80,
    padLeft         = 4,    padRight  = 4, padTop = 4, padBottom = 4,
    showBorder      = false,
    -- Tabs
    -- Tab active
    tabColorR         = 0.0,  tabColorG = 207/255, tabColorB = 1.0,
    tabUseClassColor  = false,
    tabSelectorStyle  = "NONE",
    tabSelectorColorR = 0.0,  tabSelectorColorG = 207/255, tabSelectorColorB = 1.0,
    -- Tab inactive
    tabInactiveAlpha  = 0.7,
    tabInactiveColorR = 0.75, tabInactiveColorG = 0.75,    tabInactiveColorB = 0.75,
    -- Tab font
    tabFont           = nil,
    tabFontSize       = 11,
    tabFontOutline    = "OUTLINE",
    -- Tab layout
    tabHeight         = 22,
    -- Alerts
    tabGlow           = true,
    -- EditBox
    editBoxTop      = false,
    hideButtons     = false,
    chatLocked      = false,
    classColorNames = true,
    position        = nil,
    chatSize        = nil,
}

ns.ModuleDefaults = {
    combatTimer = COMBAT_TIMER_DEFAULTS,
    combatAlert = COMBAT_ALERT_DEFAULTS,
    crosshair = CROSSHAIR_DEFAULTS,
    combatLogger = COMBAT_LOGGER_DEFAULTS,
    buffTracker = BUFF_TRACKER_DEFAULTS,
    gcdTracker = GCD_TRACKER_DEFAULTS,
    stealthReminder = STEALTH_REMINDER_DEFAULTS,
    movementAlert = MOVEMENT_ALERT_DEFAULTS,
    rangeCheck = RANGE_CHECK_DEFAULTS,
    focusCastBar = FOCUS_CAST_BAR_DEFAULTS,
    talentReminder = TALENT_REMINDER_DEFAULTS,
    personalReminders = PERSONAL_REMINDERS_DEFAULTS,
    raidAlerts = RAID_ALERTS_DEFAULTS,
    poisonReminder = POISON_REMINDER_DEFAULTS,
    equipmentReminder = EQUIPMENT_REMINDER_DEFAULTS,
    mouseRing = MOUSE_RING_DEFAULTS,
    cRez = CREZ_DEFAULTS,
    petTracker = PET_TRACKER_DEFAULTS,
    coTank = COTANK_DEFAULTS,
    healerMana = HEALER_MANA_DEFAULTS,
    swiftStats = SWIFT_STATS_DEFAULTS,
    mythicTimer   = MYTHIC_TIMER_DEFAULTS,
    dragonriding  = DRAGONRIDING_DEFAULTS,
    markers = {
        enabled = false, locked = false, mouseOver = false, alpha = 0.9,
        showBackdrop = false, showBorder = true,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0,
        borderUseClassColor = false,
        iconSize = 26, modifier = "shift", tooltips = true,
        readyCheckEnabled = true, roleCheckEnabled = false,
        timerEnabled = true, mainTankEnabled = false,
        mainAssistEnabled = false, countdownTime = 5,
    },
    bloodlust = {
        enabled = true, sound = "MEME", channel = "Master", debug = false,
        detection = { fade_threshold = 115, sustain_needed = 2 },
    },
    brightChat = BRIGHT_CHAT_DEFAULTS,
    gameBar = {
        enabled = false, locked = false, mouseOver = false, alpha = 1.0,
        hideBlizzBar = false,
        buttonSize = 26, spacing = 4, padding = 6,
        clockWidth = 80, clockHeight = 40,
        localTime = true, twentyFour = true,
        left  = { "CHARACTER", "TALENTS", "QUEST_LOG", "GROUP_FINDER", "FRIENDS" },
        right = { "BAGS", "COLLECTIONS", "ACHIEVEMENTS", "SCREENSHOT", "GAME_MENU" },
        showBackdrop = true, showBorder = true,
        borderColorR = 0, borderColorG = 207/255, borderColorB = 1.0,
        borderUseClassColor = false,
    },
    general = {
        accentR = 0, accentG = 207/255, accentB = 1.0,
    },
}

-- Restore a module to default settings
function ns:RestoreModuleDefaults(moduleName, skipKeys)
    local defaults = ns.ModuleDefaults[moduleName]
    if not defaults then return false end

    -- CursorTracker stores settings per-spec
    local db
    if moduleName == "CursorTracker" then
        local specIndex = GetSpecialization()
        local specName = specIndex and select(2, GetSpecializationInfo(specIndex)) or "NoSpec"
        BrightQoL.CursorTracker = BrightQoL.CursorTracker or {}
        BrightQoL.CursorTracker[specName] = BrightQoL.CursorTracker[specName] or {}
        db = BrightQoL.CursorTracker[specName]
    else
        db = BrightQoL[moduleName]
    end

    if not db then return false end

    skipKeys = skipKeys or {}
    local skipSet = {}
    for _, k in ipairs(skipKeys) do skipSet[k] = true end

    for k, v in pairs(defaults) do
        if not skipSet[k] then
            -- Deep copy tables
            if type(v) == "table" then
                db[k] = {}
                for tk, tv in pairs(v) do
                    db[k][tk] = tv
                end
            else
                db[k] = v
            end
        end
    end
    return true
end

local function MigrateAutoShotBlocklist()
    -- Add Auto Shot (id:75) to GCD blocklist for existing users
    -- New users already get it via the default; this migrates existing profiles
    if BrightQoL.gcdTracker then
        local bl = BrightQoL.gcdTracker.blocklist
        if bl and bl[75] == nil then
            bl[75] = true
        end
    end
end

local function InitializeDB()
    -- Initialize locale
    BrightQoL.locale = BrightQoL.locale or "enUS"
    ns:SetLocale(BrightQoL.locale)

    BrightQoL.config = BrightQoL.config or {}
    ApplyDefaults(BrightQoL.config, ns.DefaultConfig.config)
    BrightQoL.seenTabs = BrightQoL.seenTabs or {}

    BrightQoL.combatTimer = BrightQoL.combatTimer or {}
    ApplyDefaults(BrightQoL.combatTimer, COMBAT_TIMER_DEFAULTS)

    BrightQoL.combatAlert = BrightQoL.combatAlert or {}
    ApplyDefaults(BrightQoL.combatAlert, COMBAT_ALERT_DEFAULTS)

    -- Action Halo (per-spec settings managed by MouseCursor.lua)
    BrightQoL.CursorTracker = BrightQoL.CursorTracker or {}

    BrightQoL.mouseRing = BrightQoL.mouseRing or {}
    ApplyDefaults(BrightQoL.mouseRing, MOUSE_RING_DEFAULTS)

    BrightQoL.crosshair = BrightQoL.crosshair or {}
    ApplyDefaults(BrightQoL.crosshair, CROSSHAIR_DEFAULTS)

    BrightQoL.combatLogger = BrightQoL.combatLogger or {}
    ApplyDefaults(BrightQoL.combatLogger, COMBAT_LOGGER_DEFAULTS)
    BrightQoL.combatLogger.instances = BrightQoL.combatLogger.instances or {}

    BrightQoL.misc = BrightQoL.misc or {}
    local misc = BrightQoL.misc
    if misc.autoFillDelete == nil then misc.autoFillDelete = true end
    if misc.fasterLoot == nil then misc.fasterLoot = true end
    if misc.suppressLootWarnings == nil then misc.suppressLootWarnings = true end
    if misc.hideAlerts == nil then misc.hideAlerts = false end
    if misc.hideTalkingHead == nil then misc.hideTalkingHead = false end
    if misc.hideEventToasts == nil then misc.hideEventToasts = false end
    if misc.hideZoneText == nil then misc.hideZoneText = false end
    if misc.autoRepair == nil then misc.autoRepair = false end
    if misc.guildRepair == nil then misc.guildRepair = false end
    if misc.durabilityWarning == nil then misc.durabilityWarning = true end
    if misc.durabilityThreshold == nil then misc.durabilityThreshold = 30 end
    if misc.autoSellJunk == nil then misc.autoSellJunk = false end
    if misc.autoSellJunkChat == nil then misc.autoSellJunkChat = true end
    if misc.autoSlotKeystone == nil then misc.autoSlotKeystone = true end
    if misc.skipQueueConfirm == nil then misc.skipQueueConfirm = false end
    if misc.deathReleaseProtection == nil then misc.deathReleaseProtection = false end
    if misc.ahCurrentExpansion == nil then misc.ahCurrentExpansion = false end

    -- GCD Tracker uses a defaults table since it has a lot of keys
    BrightQoL.gcdTracker = BrightQoL.gcdTracker or {}
    local gtDefaults = {
        enabled = false, unlock = false, duration = 5, iconSize = 32,
        direction = "RIGHT", spacing = 4, fadeStart = 0.5,
        stackOverlapping = true,
        point = "CENTER", x = 0, y = -100, combatOnly = false,
        showInDungeon = true, showInRaid = true, showInArena = true,
        showInBattleground = true, showInWorld = true,
        blocklist = { [6603] = true, [75] = true },
        timelineColorR = 0.01, timelineColorG = 0.56, timelineColorB = 0.91,
        timelineHeight = 4,
        downtimeSummaryEnabled = false,
    }
    for k, v in pairs(gtDefaults) do
        if BrightQoL.gcdTracker[k] == nil then BrightQoL.gcdTracker[k] = v end
    end

    -- Stealth Reminder
    BrightQoL.stealthReminder = BrightQoL.stealthReminder or {}
    ApplyDefaults(BrightQoL.stealthReminder, STEALTH_REMINDER_DEFAULTS)

    -- Movement Alert
    BrightQoL.movementAlert = BrightQoL.movementAlert or {}
    ApplyDefaults(BrightQoL.movementAlert, MOVEMENT_ALERT_DEFAULTS)
    if not BrightQoL.movementAlert.disabledClasses then BrightQoL.movementAlert.disabledClasses = {} end
    if not BrightQoL.movementAlert.spellOverrides then BrightQoL.movementAlert.spellOverrides = {} end

    -- Range Check
    BrightQoL.rangeCheck = BrightQoL.rangeCheck or {}
    ApplyDefaults(BrightQoL.rangeCheck, RANGE_CHECK_DEFAULTS)


    -- Focus Cast Bar
    BrightQoL.focusCastBar = BrightQoL.focusCastBar or {}
    ApplyDefaults(BrightQoL.focusCastBar, FOCUS_CAST_BAR_DEFAULTS)
    -- Migrate old soundID (number) to new sound (string name) format
    if BrightQoL.focusCastBar.soundID and not BrightQoL.focusCastBar.sound then
        BrightQoL.focusCastBar.sound = "Raid Warning"
        BrightQoL.focusCastBar.soundID = nil
    end

    -- Talent Reminder
    BrightQoL.talentReminder = BrightQoL.talentReminder or {}

    -- Personal Reminders
    BrightQoL.personalReminders = BrightQoL.personalReminders or {}
    ApplyDefaults(BrightQoL.personalReminders, PERSONAL_REMINDERS_DEFAULTS)
    BrightQoL.personalReminders.reminders = BrightQoL.personalReminders.reminders or {}
    local tr = BrightQoL.talentReminder
    if tr.enabled == nil then tr.enabled = false end
    tr.loadouts = tr.loadouts or {}

    -- Combat Rez
    BrightQoL.cRez = BrightQoL.cRez or {}
    local cr = BrightQoL.cRez
    -- Rez Timer
    if cr.enabled           == nil then cr.enabled           = false     end
    if cr.unlock            == nil then cr.unlock            = false     end
    if cr.point             == nil then cr.point             = "CENTER"  end
    if cr.x                 == nil then cr.x                 = 0         end
    if cr.y                 == nil then cr.y                 = 150       end
    if cr.iconSize          == nil then cr.iconSize          = 40        end
    if cr.font              == nil then cr.font              = ""         end
    if cr.timerFontSize     == nil then cr.timerFontSize     = 11        end
    if cr.timerColorR       == nil then cr.timerColorR       = 1         end
    if cr.timerColorG       == nil then cr.timerColorG       = 1         end
    if cr.timerColorB       == nil then cr.timerColorB       = 1         end
    if cr.timerAlpha        == nil then cr.timerAlpha        = 1.0       end
    if cr.countFontSize     == nil then cr.countFontSize     = 11        end
    if cr.countColorR       == nil then cr.countColorR       = 1         end
    if cr.countColorG       == nil then cr.countColorG       = 1         end
    if cr.countColorB       == nil then cr.countColorB       = 1         end
    if cr.countAlpha        == nil then cr.countAlpha        = 1.0       end
    -- Death Warning
    if cr.deathWarning      == nil then cr.deathWarning      = false     end

    -- Pet Tracker
    BrightQoL.petTracker = BrightQoL.petTracker or {}
    ApplyDefaults(BrightQoL.petTracker, PET_TRACKER_DEFAULTS)

    BrightQoL.coTank = BrightQoL.coTank or {}
    ApplyDefaults(BrightQoL.coTank, COTANK_DEFAULTS)

    BrightQoL.healerMana = BrightQoL.healerMana or {}
    ApplyDefaults(BrightQoL.healerMana, HEALER_MANA_DEFAULTS)
    -- Ensure trackedBuffs table exists for existing users
    if not BrightQoL.healerMana.trackedBuffs then
        BrightQoL.healerMana.trackedBuffs = {
            [29166]  = true,
            [64901]  = true,
            [16191]  = true,
            [388019] = true,
        }
    end

    BrightQoL.swiftStats = BrightQoL.swiftStats or {}
    ApplyDefaults(BrightQoL.swiftStats, SWIFT_STATS_DEFAULTS)

    BrightQoL.mythicTimer = BrightQoL.mythicTimer or {}
    ApplyDefaults(BrightQoL.mythicTimer, MYTHIC_TIMER_DEFAULTS)
    -- Ensure nested colors table is fully initialised
    BrightQoL.mythicTimer.colors = BrightQoL.mythicTimer.colors or {}
    for k, v in pairs(MYTHIC_TIMER_DEFAULTS.colors) do
        if not BrightQoL.mythicTimer.colors[k] then
            BrightQoL.mythicTimer.colors[k] = { r = v.r, g = v.g, b = v.b }
        end
    end

    -- Equipment Reminder
    BrightQoL.equipmentReminder = BrightQoL.equipmentReminder or {}
    ApplyDefaults(BrightQoL.equipmentReminder, EQUIPMENT_REMINDER_DEFAULTS)
    -- Ensure ecSpecRules table exists
    if BrightQoL.equipmentReminder.ecSpecRules == nil then
        BrightQoL.equipmentReminder.ecSpecRules = {}
    end

    -- Dragonriding / Skyriding HUD
    BrightQoL.dragonriding = BrightQoL.dragonriding or {}
    ApplyDefaults(BrightQoL.dragonriding, DRAGONRIDING_DEFAULTS)

    -- Marker Frames – unified single bar (enabled = false by default)
    BrightQoL.markers = BrightQoL.markers or {}
    local mk = BrightQoL.markers
    if mk.enabled           == nil then mk.enabled           = false   end
    if mk.locked            == nil then mk.locked            = false   end
    if mk.mouseOver         == nil then mk.mouseOver         = false   end
    if mk.alpha             == nil then mk.alpha             = 0.9     end
    if mk.showBackdrop      == nil then mk.showBackdrop      = false   end
    if mk.showBorder        == nil then mk.showBorder        = true    end
    if mk.borderColorR      == nil then mk.borderColorR      = 0        end
    if mk.borderColorG      == nil then mk.borderColorG      = 207/255  end
    if mk.borderColorB      == nil then mk.borderColorB      = 1.0      end
    if mk.borderUseClassColor == nil then mk.borderUseClassColor = false end
    if mk.iconSize          == nil then mk.iconSize          = 26      end
    if mk.modifier          == nil then mk.modifier          = "shift" end
    if mk.tooltips          == nil then mk.tooltips          = true    end
    if mk.readyCheckEnabled == nil then mk.readyCheckEnabled = true    end
    if mk.roleCheckEnabled  == nil then mk.roleCheckEnabled  = false   end
    if mk.timerEnabled      == nil then mk.timerEnabled      = true    end
    if mk.mainTankEnabled   == nil then mk.mainTankEnabled   = false   end
    if mk.mainAssistEnabled == nil then mk.mainAssistEnabled = false   end
    if mk.countdownTime     == nil then mk.countdownTime     = 5       end
    -- Migrate: nil out old v15/v16 schema keys that break the new unified bar
    mk.raidEnabled = nil; mk.worldEnabled = nil; mk.raidLocked = nil
    mk.worldLocked = nil; mk.raidShowParty = nil; mk.worldShowParty = nil
    mk.raidMouseover = nil; mk.worldMouseover = nil; mk.controlEnabled = nil
    mk.clearAllTogether = nil; mk.controlWorldMarkers = nil
    mk.raidScale = nil; mk.raidAlpha = nil; mk.worldScale = nil; mk.worldAlpha = nil
    mk.raidPos = nil; mk.worldPos = nil
    mk.visibility = nil  -- CRITICAL: old state driver "[noexists,nogroup] hide; show" hides bar
    BrightQoL.gameBar = BrightQoL.gameBar or {}
    local gb = BrightQoL.gameBar
    if gb.enabled     == nil then gb.enabled     = false      end
    if gb.locked      == nil then gb.locked      = false      end
    if gb.mouseOver   == nil then gb.mouseOver   = false      end
    if gb.alpha       == nil then gb.alpha       = 1.0        end
    if gb.buttonSize  == nil then gb.buttonSize  = 26         end
    if gb.spacing     == nil then gb.spacing     = 4          end
    if gb.padding     == nil then gb.padding     = 6          end
    if gb.clockWidth  == nil then gb.clockWidth  = 80         end
    if gb.clockHeight == nil then gb.clockHeight = 40         end
    if gb.localTime   == nil then gb.localTime   = true       end
    if gb.twentyFour  == nil then gb.twentyFour  = true       end
    if gb.left  == nil then gb.left  = { "CHARACTER", "TALENTS", "QUEST_LOG", "GROUP_FINDER", "FRIENDS" } end
    if gb.right      == nil then gb.right      = { "BAGS", "COLLECTIONS", "ACHIEVEMENTS", "SCREENSHOT", "GAME_MENU" } end
    if gb.showBackdrop  == nil then gb.showBackdrop  = true  end
    if gb.showBorder    == nil then gb.showBorder    = true  end
    if gb.borderColorR  == nil then gb.borderColorR  = 0        end
    if gb.borderColorG  == nil then gb.borderColorG  = 207/255  end
    if gb.borderColorB  == nil then gb.borderColorB  = 1.0      end
    if gb.borderUseClassColor == nil then gb.borderUseClassColor = false end

    -- Slash Commands
    BrightQoL.slashCommands = BrightQoL.slashCommands or {}
    local sc = BrightQoL.slashCommands
    if sc.enabled == nil then sc.enabled = true end
    if sc.commands == nil then
        sc.commands = {
            { name = "cdm", frame = "CooldownViewerSettings", enabled = true, default = true },
            { name = "em", frame = "EditModeManagerFrame", enabled = true, default = true },
        }
    end
    -- General (GUI theme)
    BrightQoL.general = BrightQoL.general or {}
    local gen = BrightQoL.general
    local BRIGHT_FONT_PATH = "Interface\\AddOns\\BrightQoL\\Assets\\Fonts\\Bright.ttf"
    if gen.accentR             == nil then gen.accentR             = 0         end
    if gen.accentG             == nil then gen.accentG             = 207/255   end
    if gen.accentB             == nil then gen.accentB             = 1.0       end
    if gen.font                == nil then gen.font                = BRIGHT_FONT_PATH end

    -- Minimap module enabled flag (BrightQoL control)
    BrightQoL.minimap = BrightQoL.minimap or {}
    if BrightQoL.minimap.enabled        == nil then BrightQoL.minimap.enabled        = false end
    if BrightQoL.minimap.unlock         == nil then BrightQoL.minimap.unlock         = false end
    if BrightQoL.minimap.freeButtonPos  == nil then BrightQoL.minimap.freeButtonPos  = {} end
    if BrightQoL.minimap.hideButtonCircles == nil then BrightQoL.minimap.hideButtonCircles = true end
    if BrightQoL.minimap.textFont  == nil then BrightQoL.minimap.textFont  = "" end

    -- DataPanels (BrightDataTexts) settings
    BrightQoL.dataPanels = BrightQoL.dataPanels or {}
    -- DataPanels global (account-wide) — gold tracking, Ara_Broker settings
    BrightQoL.dataPanelsGlobal = BrightQoL.dataPanelsGlobal or {}
    BrightQoL.dataPanelsGlobal.gold      = BrightQoL.dataPanelsGlobal.gold or {}
    BrightQoL.dataPanelsGlobal.AraBroker = BrightQoL.dataPanelsGlobal.AraBroker or {}    
    BrightQoL.brightChat = BrightQoL.brightChat or {}
    ApplyDefaults(BrightQoL.brightChat, BRIGHT_CHAT_DEFAULTS)

    ApplyDefaults(BrightQoL.dataPanels, BRIGHT_DATAPANELS_DEFAULTS)
    if BrightQoL.dataPanels.bars == nil then BrightQoL.dataPanels.bars = {} end
    if BrightQoL.dataPanels.moduleSettings == nil then BrightQoL.dataPanels.moduleSettings = {} end
    if BrightQoL.dataPanels.minimap == nil then BrightQoL.dataPanels.minimap = { hide = false } end

    -- Migrate: Convert old frameToggle format to new format, keep all valid commands
    if sc.commands then
        local cleaned = {}
        for _, cmd in ipairs(sc.commands) do
            if cmd.actionType == "command" and cmd.command then
                -- Keep slash command aliases as-is
                table.insert(cleaned, cmd)
            elseif cmd.frame then
                -- Keep frame toggle commands as-is
                table.insert(cleaned, cmd)
            elseif cmd.actionType == "frameToggle" and cmd.action then
                -- Convert old frameToggle format to new format
                table.insert(cleaned, {
                    name = cmd.name,
                    actionType = "frame",
                    frame = cmd.action,
                    enabled = cmd.enabled,
                })
            end
        end
        sc.commands = cleaned
    end
end




local function DefaultFontPath()
    if ns.Media then return ns.Media.ResolveFont("Bright") end
    local loc = GetLocale()
    local asia = (loc == "koKR" or loc == "zhCN" or loc == "zhTW")
    return "Interface\\AddOns\\BrightQoL\\Assets\\Fonts\\Bright.ttf"
end
ns.DefaultFontPath = DefaultFontPath

function ns:ApplyFPSOptimization()

    SetCVar("gxVSync", 0)
    SetCVar("MSAAAlphaTest", 0)
    SetCVar("tripleBuffering", 0)


    SetCVar("shadowMode", 1)
    SetCVar("SSAO", 0)


    SetCVar("liquidDetail", 2)
    SetCVar("depthEffects", 0)
    SetCVar("computeEffects", 0)
    SetCVar("fringeEffect", 0)


    SetCVar("textureFilteringMode", 5)
    SetCVar("ResampleAlwaysSharpen", 0)


    SetCVar("gxMaxBackgroundFPS", 30)
    SetCVar("targetFPS", 0)


    SetCVar("processPriority", 3)
    SetCVar("WorldTextScale", 1)
    SetCVar("nameplateMaxDistance", 41)

    BrightQoL.config.optimized = true

    ns:LogSuccess("FPS optimization applied.")
    StaticPopup_Show("BRIGHT_QOL_RELOAD")
end


function ns:OptimizeNetwork()
    SetCVar("SpellQueueWindow", 150)
    SetCVar("reducedLagTolerance", 1)
    SetCVar("MaxSpellQueueWindow", 150)

    ns:LogSuccess("Network optimized (150ms Spell Queue).")
end


function ns:DeepGraphicsPurge()
    SetCVar("physicsLevel", 0)
    SetCVar("groundEffectDist", 40)
    SetCVar("groundEffectDensity", 16)
    SetCVar("worldBaseTickRate", 150)
    SetCVar("clutterFarDist", 20)

    ns:LogSuccess("Physics and clutter purged.")
end


local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:SetScript("OnEvent", function(self, event, name)
    if name == addonName then
        InitializeDB()
        self:UnregisterEvent("ADDON_LOADED")
    end
end)

-- Suppress notifications slash command (session only, resets on reload)
SLASH_BRIGHTQOLSUP1 = "/nsup"
SlashCmdList["BRIGHTQOLSUP"] = function()
    ns.notificationsSuppressed = not ns.notificationsSuppressed
    if ns.notificationsSuppressed then
        ns:Log("Notifications suppressed until reload", "00ff00")
    else
        ns:Log("Notifications re-enabled", "00ff00")
    end
end
