
-- ============================================================
-- FILE: Settings.lua
-- MODULE: MidnightUI_Settings  (M = MidnightUI_Settings)
--
-- PURPOSE:
--   Central configuration hub for the entire MidnightUI addon.
--   Owns the DEFAULTS table that defines every setting's initial
--   value, merges saved settings with defaults on load, runs
--   migrations for layout format changes, and exposes Apply*()
--   functions that other modules call to push settings onto live
--   WoW frames. Also owns the Movement Mode HUD (drag-to-reposition
--   overlay) and the Overlay Manager diagnostic window.
--
-- HOW IT WORKS:
--   On ADDON_LOADED → InitializeSettings() runs:
--     1. Ensures every MidnightUISettings sub-table exists
--     2. MergeDefaults() deep-copies DEFAULTS into gaps
--     3. MigratePositionsToMovers() upgrades old position arrays
--        to MoverRegistry string format (one-time, flag-guarded)
--     4. Legacy debuff overlay migration
--   On PLAYER_ENTERING_WORLD → all Apply* functions are called
--   to push the final settings onto live frames.
--
--   Apply* functions call into each subsystem module via _G globals
--   (e.g. _G.MidnightUI_ApplyCooldownManagerSettings) rather than
--   require(), so load order doesn't matter.
--
-- DEPENDS ON:
--   Bootstrap.lua   — MidnightUI_Debug
--   All subsystem modules — called via _G optionals
--   Blizzard Settings API — Settings.OpenToCategory / RegisterAddOnCategory
--
-- EXPORTS (M table + globals):
--   M.InitializeSettings, M.ApplyGlobalTheme
--   M.Apply{Messenger,PlayerFrame,TargetFrame,FocusFrame,Pet,
--            ActionBar,CastBar,Nameplate,PartyFrames,RaidFrames,
--            Minimap,UnitFrameBarStyle,SharedUnitFrameAppearance,
--            QuestObjectiveVisibility}Settings
--   M.DEFAULTS, M.SettingsCategory, M.GridFrame, M.MoveHUD, M.DrawGrid
--   M.Controls (SettingsControls registry)
--   _G.MidnightUI_GetSharedUnitAppearanceSettings
--   _G.MidnightUI_ApplySharedUnitTextStyle
--   _G.MidnightUI_ApplySharedUnitFrameAppearance
--   _G.MidnightUI_ApplyQuestObjectivesVisibility
--   _G.MidnightUI_ToggleOverlayManager
--   MidnightUI_GetActionBarSettings()
--   MidnightUI_SetActionBarSetting(barKey, settings)
--   MidnightUI_GetMessengerSettings()
--   MidnightUI_SetMessengerSetting(key, value)
--
-- OWNED SAVEDVARIABLES:
--   MidnightUISettings — all subsystem settings plus:
--     .movers{}             — frame positions in MoverRegistry string format
--     ._moversMigrated      — one-time migration flag
--     .PendingReset         — if true, settings are wiped on next load
--     .PendingResetReopenWelcome — if true, welcome wizard re-opens after reset
--
-- EVENTS REGISTERED:
--   ADDON_LOADED         — InitializeSettings, category registration
--   PLAYER_ENTERING_WORLD — full Apply* cascade
--   PLAYER_REGEN_DISABLED / PLAYER_REGEN_ENABLED — quest tracker visibility
--   PLAYER_REGEN_ENABLED — deferred unit frame apply flush
--
-- FRAMES CREATED:
--   MidnightUI_MoveHUD (BackdropTemplate, UIParent, TOOLTIP strata)
--   MidnightUI_MoveHUDRestore (Button, UIParent)
--   MidnightUI_OverlayManagerFrame (BackdropTemplate, UIParent)
--   GridFrame (Frame, UIParent, BACKGROUND strata) — snap grid overlay
--
-- GOTCHAS:
--   • InCombatLockdown check in ApplyMessengerSettings prevents
--     unlocking overlays mid-combat — sets locked=true if attempted.
--   • Unit frame Apply* calls are batched and deferred until
--     PLAYER_REGEN_ENABLED to avoid SetPoint errors in combat.
--   • MigratePositionsToMovers() is irreversible after it sets
--     _moversMigrated=true. The migration table is hardcoded here.
--   • DEFAULTS.ActionBars bars 1-8 are generated in a loop at
--     file-scope (not inside the DEFAULTS literal) because the
--     iteration count is a constant, not a literal table.
-- ============================================================

-- ============================================================
-- SECTION: Module Init + SavedVariable Schema + DEFAULTS Table
-- Initializes the two module tables (MidnightUISettings SavedVar
-- and MidnightUI_Settings module), ensures every subsystem sub-table
-- exists, and defines the DEFAULTS table — the single source of truth
-- for every setting's initial value across all subsystems.
--
-- READS:  MidnightUISettings (from SavedVariables, may be nil/partial)
-- WRITES: MidnightUISettings.* sub-tables (created if missing)
--         M.Controls = SettingsControls (empty registry table)
-- ============================================================
MidnightUISettings = MidnightUISettings or {}
MidnightUI_Settings = MidnightUI_Settings or {}
local M = MidnightUI_Settings

if not MidnightUISettings.Messenger then MidnightUISettings.Messenger = {} end
if not MidnightUISettings.ActionBars then MidnightUISettings.ActionBars = {} end
if not MidnightUISettings.PlayerFrame then MidnightUISettings.PlayerFrame = {} end
if not MidnightUISettings.TargetFrame then MidnightUISettings.TargetFrame = {} end
if not MidnightUISettings.FocusFrame then MidnightUISettings.FocusFrame = {} end
if not MidnightUISettings.RaidFrames then MidnightUISettings.RaidFrames = {} end
if not MidnightUISettings.MainTankFrames then MidnightUISettings.MainTankFrames = {} end
if not MidnightUISettings.CastBars then MidnightUISettings.CastBars = {} end
if not MidnightUISettings.Nameplates then MidnightUISettings.Nameplates = {} end
if not MidnightUISettings.Keybinds then MidnightUISettings.Keybinds = {} end
if not MidnightUISettings.Minimap then MidnightUISettings.Minimap = {} end
if not MidnightUISettings.InterfaceMenu then MidnightUISettings.InterfaceMenu = {} end
if not MidnightUISettings.ConsumableBars then MidnightUISettings.ConsumableBars = {} end
if not MidnightUISettings.CooldownManager then MidnightUISettings.CooldownManager = {} end
if not MidnightUISettings.InterruptTracker then MidnightUISettings.InterruptTracker = { enabled = true, iconSize = 28, spacing = 4, orientation = "HORIZONTAL", showNonMUIUsers = true } end
-- Migrate legacy key showStrangers → showNonMUIUsers
if MidnightUISettings.InterruptTracker and MidnightUISettings.InterruptTracker.showStrangers ~= nil and MidnightUISettings.InterruptTracker.showNonMUIUsers == nil then
    MidnightUISettings.InterruptTracker.showNonMUIUsers = MidnightUISettings.InterruptTracker.showStrangers
    MidnightUISettings.InterruptTracker.showStrangers = nil
end
if not MidnightUISettings.QuestInterface then MidnightUISettings.QuestInterface = {} end
if not MidnightUISettings.General then MidnightUISettings.General = {} end
if not MidnightUISettings.Combat then MidnightUISettings.Combat = {} end
if not MidnightUISettings.Profiles then MidnightUISettings.Profiles = {} end
if not MidnightUISettings.WelcomeSeen then MidnightUISettings.WelcomeSeen = {} end
if not MidnightUISettings.StandaloneWindows then MidnightUISettings.StandaloneWindows = {} end
if not MidnightUISettings.StandaloneWindowNextID then MidnightUISettings.StandaloneWindowNextID = 1 end
if not MidnightUISettings.Market then MidnightUISettings.Market = {} end

local MidnightSettingsCategory = nil
local SettingsControls = {}
M.Controls = SettingsControls


  local DEFAULTS = {
    Messenger = { enabled = true, locked = true, alpha = 0.85, scale = 1.0, width = 570, height = 300, fontSize = 14, mainTabSpacing = 40, showTimestamp = true, style = "Default", position = {"BOTTOMLEFT", "BOTTOMLEFT", 19.42, 22.92}, hideGlobal = false, hideLoginStates = false, showDefaultChatInterface = false, keepDebugHidden = false, defaultTab = false},
    ActionBars = { boxColor = {0.08, 0.12, 0.22, 0.92}, borderColor = {0.25, 0.45, 0.75, 0.85}, hotkeyColor = {0.85, 0.90, 1.00}, highlightColor = {0.45, 0.75, 1.00, 0.25}, globalStyle = "Disabled" },
    GlobalStyle = "Default",
      PlayerFrame = {
          enabled = true,
          width = 380,
          height = 66,
          scale = 100,
          alpha = 0.95,
          position = {"BOTTOM", "BOTTOM", -280.00, 240.67},
          auras = {
              enabled = true,
              scale = 100,
              alpha = 1.0,
              position = {"CENTER", "CENTER", -369.95, -255.06},
              alignment = "Right",
              maxShown = 32,
          },
          debuffs = {
              enabled = true,
              scale = 80,
              alpha = 1.0,
              position = {"BOTTOM", "BOTTOM", -449.51, 193.03},
              alignment = "Right",
              maxShown = 16,
              perRow = 8,
          },
      },
      TargetFrame = {
          enabled = true,
          width = 380,
          height = 66,
          scale = 100,
          alpha = 0.95,
          rangeSpell = "",
          showTargetOfTarget = false,
          targetOfTarget = {
              scale = 100,
              alpha = 0.95,
              position = nil,
          },
          position = {"BOTTOM", "BOTTOM", 295.00, 242.88},
          auras = {
              enabled = true,
              scale = 100,
              alpha = 1.0,
              position = {"CENTER", "CENTER", 197.00, -271.22},
              alignment = "Right",
              maxShown = 16,
              perRow = 29,
          },
          debuffs = {
              enabled = true,
              scale = 80,
              alpha = 1.0,
              position = {"BOTTOM", "BOTTOM", 460.89, 192.30},
              alignment = "Right",
              filterMode = "AUTO",
              maxShown = 16,
              perRow = 8,
          },
          personalPromotion = {
              enabled = true,
              scaleBoost = 1.0,
              othersAlpha = 0.50,
              desaturateOthers = true,
              sortExpiring = true,
              pandemicPulse = true,
              pandemicWindow = 0.30,
              specFilter = true,
          },
      },
      FocusFrame = {
          enabled = true,
          width = 320,
          height = 58,
          scale = 65,
          alpha = 0.95,
          position = {"CENTER", "CENTER", -297.42, -276.15},
      },
      RaidFrames = {
          enabled = true,
          useDefaultFrames = false,
          position = {"LEFT", "LEFT", 17.22, 120.23},
          width = 92,
          height = 28,
          scale = 100,
          columns = 5,
          spacingX = 6,
          spacingY = 4,
          groupBy = true,
          colorByGroup = true,
          groupBrackets = true,
          layoutStyle = "Detailed",
          showHealthPct = true,
          textSize = 9,
      },
    PartyFrames = {
        enabled = true,
        useDefaultFrames = false,
        position = {"TOPLEFT", "TOPLEFT", 31.67, -31.77},
        width = 240,
        height = 58,
        scale = 100,
        layout = "Vertical",
        spacingX = 8,
        spacingY = 8,
        style = "Rendered",
        hide2DPortrait = false,
        showTooltip = true,
        hideInRaid = false,
    },
    MainTankFrames = {
        enabled = true,
        position = {"TOPLEFT", "TOPLEFT", 20, -620},
        width = 260,
        height = 58,
        spacing = 6,
        scale = 1.0,
    },
    Nameplates = {
        enabled = true,
        debugPartyThreat = false,
        scale = 100,
        showFactionBorder = true,
        targetBorder = true,
        targetPulse = true,
        healthBar = { width = 200, height = 20, alpha = 1.0, nonTargetAlpha = 1.0, nameFontSize = 10, healthPctFontSize = 9, nameAlign = "LEFT", healthPctDisplay = "RIGHT" },
        threatBar = { enabled = true, width = 200, height = 5, alpha = 1.0 },
        castBar = { width = 200, height = 16, alpha = 1.0, fontSize = 12 },
        target = {
            scale = 100,
            healthBar = { width = 240, height = 24, nameFontSize = 10, healthPctFontSize = 9 },
            threatBar = { width = 240, height = 6 },
            castBar = { width = 240, height = 18 },
        },
    },
      CastBars = {
          player = { enabled = true, width = 360, height = 24 },
          target = { enabled = true, width = 360, height = 24 },
          focus  = { enabled = true, width = 320, height = 20 },
          nameplate = { enabled = true },
          air = { enabled = true, width = 200, height = 16 },
          colors = {
              interruptable    = { 0.85, 0.68, 0.20 },
              notInterruptable = { 0.55, 0.58, 0.62 },
              channel          = { 0.25, 0.78, 0.78 },
              interrupted      = { 0.85, 0.15, 0.15 },
              succeeded        = { 1.00, 1.00, 1.00 },
              air              = { 0.20, 0.55, 0.85 },
              crafting         = { 0.80, 0.55, 0.25 },
          },
      },
      Combat = {
          debuffBorderEnabled = true,
          debuffOverlayGlobalEnabled = true,
          debuffOverlayPlayerEnabled = true,
          debuffOverlayFocusEnabled = true,
          debuffOverlayPartyEnabled = true,
          debuffOverlayRaidEnabled = true,
          debuffOverlayTargetOfTargetEnabled = true,
          dispelTrackingEnabled = true,
          dispelTrackingMaxShown = 8,
          dispelTrackingIconScale = 160,
          dispelTrackingOrientation = "HORIZONTAL",
          dispelTrackingAlpha = 1.0,
          dispelTrackingPosition = {"CENTER", "CENTER", -454.44, -179.67},
          partyDispelTrackingEnabled = true,
          partyDispelTrackingIconScale = 100,
          partyDispelTrackingAlpha = 1.0,
      },
      ConsumableBars = {
          enabled = true,
          width = 220,
          height = 10,
          spacing = 4,
          scale = 100,
          position = {"BOTTOMRIGHT", "BOTTOMRIGHT", -133.89, 33.89},
          hideInactive = false,
          showInInstancesOnly = false,
          debug = false,
          debugVerbose = false,
      },
      CooldownManager = {
          enabled = true,
          essential = {
              enabled = true,
              scale = 100,
              alpha = 100,
              offsetX = 0,
              offsetY = 0,
              visibility = "Always",
              showBorder = false,
              darkSwipe = true,
              oocFadeEnabled = false,
              oocFadeAlpha = 60,
              position = {"BOTTOM", "BOTTOM", 0, 180},
          },
          utility = {
              enabled = true,
              scale = 100,
              alpha = 100,
              offsetX = 0,
              offsetY = 0,
              visibility = "Always",
              showBorder = false,
              darkSwipe = true,
              oocFadeEnabled = false,
              oocFadeAlpha = 60,
              position = {"BOTTOM", "BOTTOM", 0, 240},
          },
          buffIcon = {
              enabled = true,
              scale = 100,
              alpha = 100,
              offsetX = 0,
              offsetY = 0,
              visibility = "Always",
              showBorder = false,
              darkSwipe = false,
              oocFadeEnabled = false,
              oocFadeAlpha = 60,
              position = {"BOTTOM", "BOTTOM", 0, 300},
          },
          buffBar = {
              enabled = true,
              scale = 100,
              alpha = 100,
              offsetX = 0,
              offsetY = 0,
              visibility = "Always",
              showBorder = false,
              darkSwipe = false,
              oocFadeEnabled = false,
              oocFadeAlpha = 60,
              position = {"RIGHT", "RIGHT", -24, 0},
          },
      },
    QuestInterface = {
        titleFontSize     = 24,
        bodyFontSize      = 14,
        sectionFontSize   = 15,
        objectiveFontSize = 13,
    },
    XPBar = {
        enabled = true,
        position = {"TOPRIGHT", "TOPRIGHT", -24.45, -294.55},
    },
    RepBar = {
        enabled = true,
        position = {"RIGHT", "RIGHT", -24.45, 279.12},
    },
    Minimap = {
        enabled = true,
        scale = 100,
        shape = "Circle",
        position = nil,
        coordsEnabled = true,
        infoBarStyle = "Default",
        useCustomStatusBars = true,
        infoPanelEnabled = true,
        infoPanelScale = 100,
        infoPanelPosition = nil,
        mailIconPosition = nil,
    },
    Map = {
        coordsEnabled = true,
    },
    InterfaceMenu = {
        enabled = true,
        scale = 100,
        position = {"BOTTOMRIGHT", "BOTTOMRIGHT", -36.11, 96.67},
    },
    LootWindow = {
        enabled = true,
        scale = 100,
    },
    General = {
        useBlizzardGroupFinder = false,
        useBlizzardCharacterPanel = false,
        useBlizzardGuildPanel = false,
        useBlizzardQuestingInterface = false,
        hideQuestObjectivesInCombat = false,
        hideQuestObjectivesAlways = false,
        useDefaultSuperTrackedIcon = false,
        globalScale = 100,
        unitFrameBarStyle = "Gradient",
        unitFrameNameScale = 100,
        unitFrameValueScale = 100,
        unitFrameTextOutline = "OUTLINE",
        unitFrameHideLevelText = false,
        unitFrameHidePowerText = false,
        snapToFrames = true,
        allowSecretHealthPercent = true,
        healthTextMode = "percent",
        debugCastBar = false,
        diagnosticsEnabled = true,
    },
    Inventory = {
        enabled = true,
        separateBags = true,
        dockScale = 105,
        bagSlotSize = 30,
        bagContentScale = 100,
        bagColumns = 4,
        bagSpacing = 4,
    },
    PetBar = {
        enabled = true,
        scale = 85,
        alpha = 0.95,
        buttonSize = 32,
        spacing = 15,
        buttonsPerRow = 10,
        position = {"BOTTOM", "BOTTOM", 0, 140},
    },
      StanceBar = {
          enabled = true,
          scale = 100,
          alpha = 1.0,
          buttonSize = 32,
          spacing = 4,
        buttonsPerRow = 3,
        position = {"BOTTOM", "BOTTOM", 197.77, 154.45},
      },
      PetFrame = {
          enabled = true,
          scale = 85,
          width = 245,
          height = 48,
          alpha = 0.95,
          position = {"CENTER", "CENTER", -227.47, -159.83},
      },
      SoulShards = {
          enabled = true,
          scale = 100,
          alpha = 1.0,
      },
      ComboPoints = {
          enabled = true,
          scale = 100,
          alpha = 1.0,
      },
      DKRunes = {
          enabled = true,
          scale = 100,
          alpha = 1.0,
      },
      MonkChi = {
          enabled = true,
          scale = 100,
          alpha = 1.0,
      },
      HolyPower = {
          enabled = true,
          scale = 100,
          alpha = 1.0,
      },
      ArcaneCharges = {
          enabled = true,
          scale = 100,
          alpha = 1.0,
      },
      EvokerEssence = {
          enabled = true,
          scale = 100,
          alpha = 1.0,
      },
      Keybinds = {},
  }

  local function DeepCopyTable(src)
      if type(src) ~= "table" then return src end
      local t = {}
      for k, v in pairs(src) do
          t[k] = DeepCopyTable(v)
      end
      return t
  end

  local function MergeDefaults(current, defaults)
      if type(defaults) ~= "table" or type(current) ~= "table" then return end
      for k, v in pairs(defaults) do
          if current[k] == nil then
              if type(v) == "table" then
                  current[k] = DeepCopyTable(v)
              else
                  current[k] = v
              end
          elseif type(v) == "table" and type(current[k]) == "table" then
              MergeDefaults(current[k], v)
          end
      end
  end

-- ============================================================
-- SECTION: Settings Merge Utilities + Legacy Migrations
-- Provides the deep-copy and recursive merge helpers used to fill
-- gaps in saved settings with DEFAULTS values, plus the one-time
-- migration that converts old array-format positions into MoverRegistry
-- string format and the legacy debuff overlay flag migration.
--
-- READS:
--   MidnightUISettings.* — checked for missing keys
--   DEFAULTS             — source for merge
--
-- WRITES:
--   MidnightUISettings.movers{} — populated by MigratePositionsToMovers
--   MidnightUISettings._moversMigrated — set true after migration
--
-- FUNCTIONS:
--   DeepCopyTable(t)             — returns a deep copy of table t
--   MergeDefaults(target, src)   — for each key in src that is missing or
--                                   nil in target, copies it; recurses for
--                                   table values
--   ResetSettingsToDefaults(reopenWelcome) — deep-copies DEFAULTS into
--                                   MidnightUISettings, optionally wipes
--                                   WelcomeSeen to force the wizard
--   MigratePositionsToMovers()   — walks a hardcoded MIGRATION_MAP of
--                                   {path, key} pairs; reads each legacy
--                                   position array from MidnightUISettings
--                                   and writes the MoverRegistry string to
--                                   movers[key]; sets _moversMigrated=true
-- ============================================================
  local function MigratePositionsToMovers()
      local s = MidnightUISettings
      if s._moversMigrated then return end
      if not s.movers then s.movers = {} end

      local MIGRATION_MAP = {
          { path = {"PlayerFrame", "position"}, key = "PlayerFrame" },
          { path = {"TargetFrame", "position"}, key = "TargetFrame" },
          { path = {"FocusFrame", "position"}, key = "FocusFrame" },
          { path = {"RaidFrames", "position"}, key = "RaidFrames" },
          { path = {"PartyFrames", "position"}, key = "PartyFrames" },
          { path = {"MainTankFrames", "position"}, key = "MainTankFrames" },
          { path = {"Messenger", "position"}, key = "Messenger" },
          { path = {"ConsumableBars", "position"}, key = "ConsumableBars" },
          { path = {"Minimap", "position"}, key = "Minimap" },
          { path = {"InterfaceMenu", "position"}, key = "InterfaceMenu" },
          { path = {"XPBar", "position"}, key = "XPBar" },
          { path = {"RepBar", "position"}, key = "RepBar" },
          { path = {"PetBar", "position"}, key = "PetBar" },
          { path = {"StanceBar", "position"}, key = "StanceBar" },
          { path = {"PetFrame", "position"}, key = "PetFrame" },
          { path = {"PlayerFrame", "auras", "position"}, key = "PlayerAuras" },
          { path = {"PlayerFrame", "debuffs", "position"}, key = "PlayerDebuffs" },
          { path = {"TargetFrame", "auras", "position"}, key = "TargetAuras" },
          { path = {"TargetFrame", "debuffs", "position"}, key = "TargetDebuffs" },
          { path = {"TargetFrame", "targetOfTarget", "position"}, key = "TargetOfTarget" },
          { path = {"Combat", "dispelTrackingPosition"}, key = "DispelTracking" },
          { path = {"CastBars", "player", "position"}, key = "CastBar_player" },
          { path = {"CastBars", "target", "position"}, key = "CastBar_target" },
          { path = {"CastBars", "focus", "position"}, key = "CastBar_focus" },
      }
      for i = 1, 8 do
          MIGRATION_MAP[#MIGRATION_MAP + 1] = {
              path = {"ActionBars", "bar" .. i, "position"},
              key = "ActionBar" .. i,
          }
      end

      for _, entry in ipairs(MIGRATION_MAP) do
          if not s.movers[entry.key] then
              local val = s
              for _, seg in ipairs(entry.path) do
                  if type(val) == "table" then val = val[seg] else val = nil; break end
              end
              if type(val) == "table" and #val >= 4 then
                  local posStr
                  if #val >= 5 and type(val[4]) == "number" and type(val[5]) == "number" then
                      posStr = tostring(val[1]) .. "," .. tostring(val[2]) .. ","
                          .. tostring(val[3]) .. ","
                          .. string.format("%.2f", val[4]) .. ","
                          .. string.format("%.2f", val[5])
                  else
                      posStr = tostring(val[1]) .. ",UIParent,"
                          .. tostring(val[2]) .. ","
                          .. string.format("%.2f", val[3]) .. ","
                          .. string.format("%.2f", val[4])
                  end
                  s.movers[entry.key] = posStr
              end
          end
      end
      s._moversMigrated = true
  end

  local function ResetSettingsToDefaults(reopenWelcome)
      if not MidnightUISettings then return end
      local preserved = {
          Profiles = MidnightUISettings.Profiles,
          WelcomeSeen = MidnightUISettings.WelcomeSeen,
          WhatsNewSeen = MidnightUISettings.WhatsNewSeen,
      }
      if wipe then
          wipe(MidnightUISettings)
      else
          for k in pairs(MidnightUISettings) do MidnightUISettings[k] = nil end
      end
      for k, v in pairs(DEFAULTS) do
          MidnightUISettings[k] = DeepCopyTable(v)
      end
      MidnightUISettings.movers = {}
      MidnightUISettings.Profiles = preserved.Profiles or {}
      MidnightUISettings.WelcomeSeen = preserved.WelcomeSeen or {}
      MidnightUISettings.WhatsNewSeen = preserved.WhatsNewSeen or {}
      MidnightUISettings.Messenger.locked = true
      MidnightUISettings._moversMigrated = true
      MidnightUISettings._wasReset = true
      if reopenWelcome == true then
          MidnightUISettings.WelcomeSeen = {}
          MidnightUISettings.PendingResetForceWelcome = true
      end
  end
for i = 1, 8 do
    local key = "bar"..i
    local barEnabled = (i <= 3)
    DEFAULTS.ActionBars[key] = { enabled = barEnabled, rows = 1, iconsPerRow = 12, scale = 100, spacing = 6, style = "Class Color", position = nil }
    if not MidnightUISettings.ActionBars[key] then MidnightUISettings.ActionBars[key] = {} end
end

-- ============================================================
-- SECTION: Settings Initialization
-- Called once on ADDON_LOADED. Validates the full MidnightUISettings
-- tree, merges defaults, runs migrations, and clamps any values that
-- are out of range (text scales 50-150, outline type enum, etc.).
--
-- READS:
--   MidnightUISettings.* — checked and patched in place
--   DEFAULTS             — merge source
--
-- WRITES:
--   MidnightUISettings.* — filled, clamped, and migrated
--
-- FUNCTIONS:
--   InitializeSettings()   — entry point; called from the loader frame's
--                             ADDON_LOADED handler; must be called before
--                             any Apply* function
-- ============================================================
  local function InitializeSettings()
    if not MidnightUISettings.Profiles then MidnightUISettings.Profiles = {} end
    if not MidnightUISettings.WelcomeSeen then MidnightUISettings.WelcomeSeen = {} end
    if not MidnightUISettings.movers then MidnightUISettings.movers = {} end

    if MidnightUISettings.PendingReset then
        ResetSettingsToDefaults(MidnightUISettings.PendingResetReopenWelcome == true)
    end

    MergeDefaults(MidnightUISettings, DEFAULTS)

    for i = 1, 8 do
        local key = "bar" .. i
        if not MidnightUISettings.ActionBars[key] then
            MidnightUISettings.ActionBars[key] = DeepCopyTable(DEFAULTS.ActionBars[key])
        else
            MergeDefaults(MidnightUISettings.ActionBars[key], DEFAULTS.ActionBars[key])
        end
    end


    MigratePositionsToMovers()


    if MidnightUISettings.Combat.debuffOverlayDefaultMigrationApplied ~= true then
        local combat = MidnightUISettings.Combat
        local legacyAllDisabled =
            combat.debuffOverlayGlobalEnabled == false and
            combat.debuffOverlayPlayerEnabled == false and
            combat.debuffOverlayFocusEnabled == false and
            combat.debuffOverlayPartyEnabled == false and
            combat.debuffOverlayRaidEnabled == false and
            combat.debuffOverlayTargetOfTargetEnabled == false
        local legacyGlobalOnlyDisabled =
            combat.debuffOverlayGlobalEnabled == false and
            combat.debuffOverlayPlayerEnabled ~= false and
            combat.debuffOverlayFocusEnabled ~= false and
            combat.debuffOverlayPartyEnabled ~= false and
            combat.debuffOverlayRaidEnabled ~= false and
            combat.debuffOverlayTargetOfTargetEnabled ~= false
        if legacyAllDisabled then
            combat.debuffOverlayGlobalEnabled = true
            combat.debuffOverlayPlayerEnabled = true
            combat.debuffOverlayFocusEnabled = true
            combat.debuffOverlayPartyEnabled = true
            combat.debuffOverlayRaidEnabled = true
            combat.debuffOverlayTargetOfTargetEnabled = true
        elseif legacyGlobalOnlyDisabled then
            combat.debuffOverlayGlobalEnabled = true
        end
        combat.debuffOverlayDefaultMigrationApplied = true
    end

    if not MidnightUISettings._posArraySanitized then
        local POS_PATHS = {
            { "MainTankFrames", "position" },
            { "PetBar", "position" },
            { "StanceBar", "position" },
        }
        for _, path in ipairs(POS_PATHS) do
            local node = MidnightUISettings
            for i = 1, #path - 1 do
                if type(node) == "table" then node = node[path[i]] else node = nil break end
            end
            if type(node) == "table" then
                local pos = node[path[#path]]
                if type(pos) == "table" and pos[5] ~= nil then
                    pos[5] = nil
                end
            end
        end
        MidnightUISettings._posArraySanitized = true
    end

    if MidnightUISettings.General.unitFrameBarStyle ~= "Gradient"
        and MidnightUISettings.General.unitFrameBarStyle ~= "Flat" then
        MidnightUISettings.General.unitFrameBarStyle = "Gradient"
    end
    local unitFrameNameScale = tonumber(MidnightUISettings.General.unitFrameNameScale)
    if not unitFrameNameScale then unitFrameNameScale = 100 end
    MidnightUISettings.General.unitFrameNameScale = math.max(50, math.min(150, math.floor(unitFrameNameScale + 0.5)))
    local unitFrameValueScale = tonumber(MidnightUISettings.General.unitFrameValueScale)
    if not unitFrameValueScale then unitFrameValueScale = 100 end
    MidnightUISettings.General.unitFrameValueScale = math.max(50, math.min(150, math.floor(unitFrameValueScale + 0.5)))
    if MidnightUISettings.General.unitFrameTextOutline ~= "NONE"
        and MidnightUISettings.General.unitFrameTextOutline ~= "OUTLINE"
        and MidnightUISettings.General.unitFrameTextOutline ~= "THICKOUTLINE" then
        MidnightUISettings.General.unitFrameTextOutline = "OUTLINE"
    end
end


-- ============================================================
-- SECTION: Frame Settings Appliers
-- One Apply* function per subsystem. Each reads from
-- MidnightUISettings and pushes values onto the live WoW frames via
-- _G globals to the relevant module. All unit-frame Apply* calls are
-- batched through _FlushPendingUnitFrameApplies() which defers them
-- until PLAYER_REGEN_ENABLED if called during combat.
--
-- ApplyMessengerSettings is the "master" applier — it locks/unlocks
-- the drag overlay AND cascades to every other subsystem, making it
-- the single call needed to re-apply all settings at once (called
-- after the settings panel saves changes).
--
-- READS:
--   MidnightUISettings.* — all subsystem settings
--   InCombatLockdown()   — blocks overlay unlock in combat
--
-- WRITES:
--   _G.MyMessengerFrame, _G.MidnightUI_MoverRegistry, live WoW frames
--   _pendingUnitFrameApplies{} — queue for deferred combat applies
--
-- FUNCTIONS:
--   ApplyMessengerSettings()          — master apply; locks overlay,
--                                       cascades to all other appliers
--   EnsureCastBarsSettings()          — ensures CastBars sub-table exists
--   ApplyCastBarSettings()            — pushes cast bar settings to
--                                       MidnightUI_CastBar module
--   EnsureNameplateSettings()         — ensures Nameplates sub-table
--   ApplyNameplateSettings()          — pushes to MidnightUI_Nameplates
--   ApplyUnitFrameSettings(name, s)   — generic UF settings pusher
--   ApplyPlayerSettings()             — applies PlayerFrame settings
--   ApplyTargetSettings()             — applies TargetFrame settings
--   ApplyFocusSettings()              — applies FocusFrame settings
--   ApplyPetSettings()                — applies PetFrame settings
--   ApplyActionBarSettings()          — deferred ActionBar apply
--   ApplyActionBarSettingsImmediate() — immediate ActionBar apply
--   ApplyPartyFramesSettings()        — applies PartyFrames settings
--   ApplyRaidFramesSettings()         — applies RaidFrames settings
--   ApplyMinimapSettings()            — applies Minimap settings
--   ApplyUnitFrameBarStyle()          — triggers bar gradient style
--                                       refresh on all unit frames
--   _FlushPendingUnitFrameApplies()   — drains the pending apply queue
--   _EnsureUnitFrameFlushHook()       — registers the PLAYER_REGEN_ENABLED
--                                       listener for the flush on demand
-- ============================================================
local function ApplyMessengerSettings()
    if _G.MyMessengerFrame then
        local s = MidnightUISettings.Messenger
        if s.locked == false and InCombatLockdown and InCombatLockdown() then
            s.locked = true
            if UIErrorsFrame and UIErrorsFrame.AddMessage then
                UIErrorsFrame:AddMessage("MidnightUI: Can't unlock overlays while in combat.", 1, 0.2, 0.2)
            end
            return
        end
        local moveHud = _G.MidnightUI_MoveHUD
        local moveHudRestore = _G.MidnightUI_MoveHUDRestore
        if s.locked then
            if moveHud then moveHud:Hide() end
            if moveHudRestore then moveHudRestore:Hide() end
        else
            if not (moveHudRestore and moveHudRestore:IsShown()) then
                if moveHud then moveHud:Show() end
                if moveHudRestore then moveHudRestore:Hide() end
            end
        end
        _G.MyMessengerFrame:SetSize(s.width, s.height)
        _G.MyMessengerFrame:SetScale(s.scale or 1.0)
        if _G.MyMessengerMessageFrame and _G.MyMessengerMessageFrame.GetFont and _G.MyMessengerMessageFrame.SetFont then
            local fontPath, _, fontFlags = _G.MyMessengerMessageFrame:GetFont()
            local fontSize = math.floor(tonumber(s.fontSize) or 14)
            if fontSize < 8 then fontSize = 8 end
            if fontSize > 24 then fontSize = 24 end
            if fontPath then
                _G.MyMessengerMessageFrame:SetFont(fontPath, fontSize, fontFlags)
                _G.MyMessengerMessageFrame:SetSpacing(math.max(1, math.floor(fontSize * 0.28)))
            end
        end
        if _G.MyMessengerFrame.dragOverlay then if s.locked then _G.MyMessengerFrame.dragOverlay:Hide() else _G.MyMessengerFrame.dragOverlay:Show() end end
        if _G.MidnightUI_MoverRegistry then
            _G.MidnightUI_MoverRegistry.SetAllLocked(s.locked)
        end
        if _G.MidnightUI_ApplyInterfaceMenuSettings then _G.MidnightUI_ApplyInterfaceMenuSettings() end
        if _G.MidnightUI_Settings then
            if _G.MidnightUI_Settings.ApplyPlayerSettings then _G.MidnightUI_Settings.ApplyPlayerSettings() end
            if _G.MidnightUI_Settings.ApplyTargetSettings then _G.MidnightUI_Settings.ApplyTargetSettings() end
            if _G.MidnightUI_Settings.ApplyFocusSettings then _G.MidnightUI_Settings.ApplyFocusSettings() end
            if _G.MidnightUI_Settings.ApplyPetSettings then _G.MidnightUI_Settings.ApplyPetSettings() end
            if _G.MidnightUI_Settings.ApplyPartyFramesSettings then _G.MidnightUI_Settings.ApplyPartyFramesSettings() end
            if _G.MidnightUI_Settings.ApplyRaidFramesSettings then _G.MidnightUI_Settings.ApplyRaidFramesSettings() end
            if _G.MidnightUI_Settings.ApplyCastBarSettings then _G.MidnightUI_Settings.ApplyCastBarSettings() end
            if _G.MidnightUI_ApplyCooldownManagerSettings then _G.MidnightUI_ApplyCooldownManagerSettings() end
            if _G.MidnightUI_Settings.ApplyActionBarSettingsImmediate then _G.MidnightUI_Settings.ApplyActionBarSettingsImmediate() end
            if _G.MidnightUI_Settings.ApplyMinimapSettings then _G.MidnightUI_Settings.ApplyMinimapSettings() end
        end
        if _G.MidnightUI_ApplyMessengerTheme then _G.MidnightUI_ApplyMessengerTheme() end
        if _G.MyMessenger_RefreshDisplay then _G.MyMessenger_RefreshDisplay() end
    end
    if _G.MidnightUI_ApplyDefaultChatInterfaceVisibility then
        _G.MidnightUI_ApplyDefaultChatInterfaceVisibility()
    end
end

local function EnsureCastBarsSettings()
    if not MidnightUISettings.CastBars then MidnightUISettings.CastBars = {} end
    if _G.MidnightUI_CastBars and _G.MidnightUI_CastBars.EnsureCastBarSettings then
        _G.MidnightUI_CastBars.EnsureCastBarSettings()
        return
    end
    if not MidnightUISettings.CastBars.player then MidnightUISettings.CastBars.player = {} end
    if not MidnightUISettings.CastBars.target then MidnightUISettings.CastBars.target = {} end
    if not MidnightUISettings.CastBars.focus then MidnightUISettings.CastBars.focus = {} end
    if not MidnightUISettings.CastBars.nameplate then MidnightUISettings.CastBars.nameplate = {} end
    if not MidnightUISettings.CastBars.air then MidnightUISettings.CastBars.air = {} end
    if not MidnightUISettings.CastBars.colors then MidnightUISettings.CastBars.colors = {} end
    if DEFAULTS and DEFAULTS.CastBars then
        for k, v in pairs(DEFAULTS.CastBars) do
            if type(v) == "table" then
                if not MidnightUISettings.CastBars[k] then MidnightUISettings.CastBars[k] = {} end
                for kk, vv in pairs(v) do
                    if MidnightUISettings.CastBars[k][kk] == nil then
                        if type(vv) == "table" then
                            MidnightUISettings.CastBars[k][kk] = { vv[1], vv[2], vv[3] }
                        else
                            MidnightUISettings.CastBars[k][kk] = vv
                        end
                    end
                end
            end
        end
    end
end

local function EnsureNameplateSettings()
    if not MidnightUISettings.Nameplates then MidnightUISettings.Nameplates = {} end
    if MidnightUISettings.Nameplates.enabled == nil then MidnightUISettings.Nameplates.enabled = true end
    if MidnightUISettings.Nameplates.debugPartyThreat == nil then MidnightUISettings.Nameplates.debugPartyThreat = false end
    if MidnightUISettings.Nameplates.scale == nil then MidnightUISettings.Nameplates.scale = 100 end
    if MidnightUISettings.Nameplates.showFactionBorder == nil then MidnightUISettings.Nameplates.showFactionBorder = true end
    if MidnightUISettings.Nameplates.targetBorder == nil then MidnightUISettings.Nameplates.targetBorder = true end
    if MidnightUISettings.Nameplates.targetPulse == nil then MidnightUISettings.Nameplates.targetPulse = true end
    if not MidnightUISettings.Nameplates.healthBar then MidnightUISettings.Nameplates.healthBar = {} end
    if not MidnightUISettings.Nameplates.threatBar then MidnightUISettings.Nameplates.threatBar = {} end
    if not MidnightUISettings.Nameplates.castBar then MidnightUISettings.Nameplates.castBar = {} end
    if not MidnightUISettings.Nameplates.target then MidnightUISettings.Nameplates.target = {} end
    if MidnightUISettings.Nameplates.target.scale == nil then MidnightUISettings.Nameplates.target.scale = 100 end
    if not MidnightUISettings.Nameplates.target.healthBar then MidnightUISettings.Nameplates.target.healthBar = {} end
    if not MidnightUISettings.Nameplates.target.threatBar then MidnightUISettings.Nameplates.target.threatBar = {} end
    if not MidnightUISettings.Nameplates.target.castBar then MidnightUISettings.Nameplates.target.castBar = {} end
    if not MidnightUISettings.Nameplates.hover then MidnightUISettings.Nameplates.hover = {} end
    if MidnightUISettings.Nameplates.hover.enabled == nil then MidnightUISettings.Nameplates.hover.enabled = true end
    if MidnightUISettings.Nameplates.hover.borderSize == nil then MidnightUISettings.Nameplates.hover.borderSize = 2 end
    if MidnightUISettings.Nameplates.hover.pulse == nil then MidnightUISettings.Nameplates.hover.pulse = true end
end

local _pendingUnitFrameApplies = _pendingUnitFrameApplies or {}
local _unitFrameFlushHooked = _unitFrameFlushHooked or false
local _DoApplyUnitFrameSettings

local function _FlushPendingUnitFrameApplies()
    if InCombatLockdown and InCombatLockdown() then return end
    local pending = _pendingUnitFrameApplies
    _pendingUnitFrameApplies = {}
    for frameName, settings in pairs(pending) do
        local ok, err = pcall(_DoApplyUnitFrameSettings, frameName, settings)
        if not ok and _G.MidnightUI_DebugSource then
            _G.MidnightUI_DebugSource("ApplyUnitFrameSettings", "deferred apply failed: " .. tostring(err))
        end
    end
end

local function _EnsureUnitFrameFlushHook()
    if _unitFrameFlushHooked then return end
    _unitFrameFlushHooked = true
    local f = CreateFrame("Frame")
    f:RegisterEvent("PLAYER_REGEN_ENABLED")
    f:SetScript("OnEvent", _FlushPendingUnitFrameApplies)
end

local function ApplyUnitFrameSettings(frameName, settings)
    if InCombatLockdown and InCombatLockdown() then
        _pendingUnitFrameApplies[frameName] = settings or {}
        _EnsureUnitFrameFlushHook()
        return
    end
    return _DoApplyUnitFrameSettings(frameName, settings)
end

_DoApplyUnitFrameSettings = function(frameName, settings)
    local frame = _G[frameName]
    if not frame then return end
    if not settings then settings = {} end
    if settings.enabled == false then
        if UnregisterUnitWatch then UnregisterUnitWatch(frame) end
        frame:Hide()
        return
    else
        local overlaysUnlocked = (MidnightUISettings and MidnightUISettings.Messenger and MidnightUISettings.Messenger.locked == false)
        local isConditionalUnit = (frameName == "MidnightUI_TargetFrame" or frameName == "MidnightUI_FocusFrame" or frameName == "MidnightUI_PetFrame")
        if overlaysUnlocked and isConditionalUnit then
            if UnregisterUnitWatch then UnregisterUnitWatch(frame) end
            frame:Show()
        else
            if RegisterUnitWatch then RegisterUnitWatch(frame) end
            if frameName == "MidnightUI_PlayerFrame" then frame:Show() end
        end
    end

    local Clean = (_G.MidnightUI_Core and _G.MidnightUI_Core.CleanNumber) or _G.MidnightUI_CleanNumber
    local sScale = (settings.scale or 100) / 100
    local sAlpha = settings.alpha or 0.95
    local sWidth = settings.width or Clean(frame:GetWidth(), 256)
    local sHeight = settings.height or Clean(frame:GetHeight(), 64)
    frame:SetScale(sScale); frame:SetAlpha(sAlpha); frame:SetSize(sWidth, sHeight)
    local hH = sHeight * 0.64; local pH = sHeight * 0.25
    if frame.healthBar and frame.healthBar:GetParent() then frame.healthBar:GetParent():SetHeight(hH); if frame.healthBar.shine then frame.healthBar.shine:SetHeight(hH * 0.35) end end
    if frame.powerBar and frame.powerBar:GetParent() then frame.powerBar:GetParent():SetHeight(pH) end

    local MR = _G.MidnightUI_MoverRegistry
    if MR and MR.ApplyPosition then
        local FRAME_TO_KEY = {
            MidnightUI_PlayerFrame = "PlayerFrame",
            MidnightUI_TargetFrame = "TargetFrame",
            MidnightUI_FocusFrame  = "FocusFrame",
            MidnightUI_PetFrame    = "PetFrame",
        }
        local moverKey = FRAME_TO_KEY[frameName]
        if moverKey then
            MR.ApplyPosition(moverKey)
        end
    end
end

local function ApplyNameplateSettings()
    if _G.MidnightUI_RefreshNameplates then
        _G.MidnightUI_RefreshNameplates()
    end
end

function MidnightUI_RefreshAllUnitFrames()
    if _G.MidnightUI_RefreshPlayerFrame then _G.MidnightUI_RefreshPlayerFrame() end
    if _G.MidnightUI_RefreshTargetFrame then _G.MidnightUI_RefreshTargetFrame() end
    if _G.MidnightUI_RefreshFocusFrame then _G.MidnightUI_RefreshFocusFrame() end
    if _G.MidnightUI_RefreshPartyFrames then _G.MidnightUI_RefreshPartyFrames() end
    if _G.MidnightUI_UpdateRaidVisibility then _G.MidnightUI_UpdateRaidVisibility() end
    if _G.MidnightUI_RefreshMainTankFrames then _G.MidnightUI_RefreshMainTankFrames() end
    if _G.MidnightUI_RefreshNameplates then _G.MidnightUI_RefreshNameplates() end
end
local function ApplyPlayerSettings() ApplyUnitFrameSettings("MidnightUI_PlayerFrame", MidnightUISettings.PlayerFrame) end
local function ApplyTargetSettings() ApplyUnitFrameSettings("MidnightUI_TargetFrame", MidnightUISettings.TargetFrame) end
local function ApplyFocusSettings() ApplyUnitFrameSettings("MidnightUI_FocusFrame", MidnightUISettings.FocusFrame) end
local function ApplyPetSettings() ApplyUnitFrameSettings("MidnightUI_PetFrame", MidnightUISettings.PetFrame) end
local function ApplyActionBarSettings() if _G.MyActionBars_ReloadSettings then _G.MyActionBars_ReloadSettings() end end
local function ApplyActionBarSettingsImmediate()
    if _G.MyActionBars_ReloadSettingsImmediate then
        _G.MyActionBars_ReloadSettingsImmediate()
    elseif _G.MyActionBars_ReloadSettings then
        _G.MyActionBars_ReloadSettings()
    end
end
local function ApplyCastBarSettings() if _G.MidnightUI_ApplyCastBarSettings then _G.MidnightUI_ApplyCastBarSettings() end end
local function ApplyMinimapSettings() if _G.MidnightUI_ApplyMinimapSettings then _G.MidnightUI_ApplyMinimapSettings() end end
local function ApplyRaidFramesSettings() if _G.MidnightUI_ApplyRaidFramesLayout then _G.MidnightUI_ApplyRaidFramesLayout() end end
local function ApplyPartyFramesSettings()
    if _G.MidnightUI_ApplyPartyFramesLayout then _G.MidnightUI_ApplyPartyFramesLayout() end
    if _G.MidnightUI_UpdatePartyVisibility then _G.MidnightUI_UpdatePartyVisibility() end
    if _G.MidnightUI_RefreshPartyDispelTrackingOverlay then
        _G.MidnightUI_RefreshPartyDispelTrackingOverlay(false)
    end
end

  local function ApplyUnitFrameBarStyle()
      if _G.MidnightUI_ApplyPlayerFrameBarStyle then _G.MidnightUI_ApplyPlayerFrameBarStyle() end
      if _G.MidnightUI_ApplyTargetFrameBarStyle then _G.MidnightUI_ApplyTargetFrameBarStyle() end
      if _G.MidnightUI_ApplyFocusFrameBarStyle then _G.MidnightUI_ApplyFocusFrameBarStyle() end
      if _G.MidnightUI_ApplyPetFrameBarStyle then _G.MidnightUI_ApplyPetFrameBarStyle() end
      if _G.MidnightUI_ApplyPartyFramesBarStyle then _G.MidnightUI_ApplyPartyFramesBarStyle() end
      if _G.MidnightUI_ApplyRaidFramesBarStyle then _G.MidnightUI_ApplyRaidFramesBarStyle() end
      if _G.MidnightUI_ApplyMainTankFramesBarStyle then _G.MidnightUI_ApplyMainTankFramesBarStyle() end
  end

local function ClampUnitFrameTextScale(value)
    value = tonumber(value) or 100
    return math.max(50, math.min(150, math.floor(value + 0.5)))
end

-- ============================================================
-- SECTION: Shared Unit Frame Appearance System
-- Manages font size scale, text outline type, and show/hide flags
-- (level text, power text) that apply uniformly to ALL unit frames
-- (Player, Target, Focus, Pet, Party, Raid). Stored under
-- MidnightUISettings.General.unitFrame*.
--
-- Changes are debounced via EnsureUnitFrameAppearanceRefreshFrame():
-- rapid successive calls coalesce into a single refresh tick.
--
-- READS:
--   MidnightUISettings.General.unitFrameNameScale (50-150)
--   MidnightUISettings.General.unitFrameValueScale (50-150)
--   MidnightUISettings.General.unitFrameTextOutline (NONE/OUTLINE/THICKOUTLINE)
--   MidnightUISettings.General.unitFrameHideLevelText
--   MidnightUISettings.General.unitFrameHidePowerText
--
-- WRITES:
--   FontString objects on live unit frames (via module callbacks)
--
-- FUNCTIONS:
--   ClampUnitFrameTextScale(v)          — clamps v to 50-150
--   EnsureSharedUnitAppearanceSettings() — creates/validates the
--                                          General.unitFrame* keys
--   GetSharedUnitAppearanceSettings()   — returns a settings snapshot
--                                          table {nameScale, valueScale,
--                                          outline, showLevelText,
--                                          showPowerText}; exported as
--                                          _G.MidnightUI_GetSharedUnitAppearanceSettings
--   ApplySharedTextShadow(fontString, alpha)
--                                       — sets shadow offset+color
--   ApplySharedUnitFont(fontString, fontPath, baseSize, scalePercent, outline)
--                                       — resizes font with scale%
--   ApplySharedUnitTextStyle(frame, opts)
--                                       — applies font+shadow to named
--                                          fontstrings on frame; exported
--                                          as _G.MidnightUI_ApplySharedUnitTextStyle
--   RunSharedUnitFrameAppearanceRefresh()
--                                       — calls each registered module's
--                                          ApplySharedUnitFrameAppearance callback
--   EnsureUnitFrameAppearanceRefreshFrame()
--                                       — creates a 0.1s debounce ticker
--                                          that coalesces rapid calls
--   ApplySharedUnitFrameAppearance()    — public entry point; ensures
--                                          settings, schedules refresh;
--                                          exported as
--                                          _G.MidnightUI_ApplySharedUnitFrameAppearance
-- ============================================================
local function EnsureSharedUnitAppearanceSettings()
    if not MidnightUISettings then MidnightUISettings = {} end
    if not MidnightUISettings.General then MidnightUISettings.General = {} end

    if MidnightUISettings.General.unitFrameNameScale == nil then
        MidnightUISettings.General.unitFrameNameScale = 100
    end
    if MidnightUISettings.General.unitFrameValueScale == nil then
        MidnightUISettings.General.unitFrameValueScale = 100
    end
    if MidnightUISettings.General.unitFrameTextOutline ~= "NONE"
        and MidnightUISettings.General.unitFrameTextOutline ~= "OUTLINE"
        and MidnightUISettings.General.unitFrameTextOutline ~= "THICKOUTLINE" then
        MidnightUISettings.General.unitFrameTextOutline = "OUTLINE"
    end
    if MidnightUISettings.General.unitFrameHideLevelText == nil then
        MidnightUISettings.General.unitFrameHideLevelText = false
    end
    if MidnightUISettings.General.unitFrameHidePowerText == nil then
        MidnightUISettings.General.unitFrameHidePowerText = false
    end

    MidnightUISettings.General.unitFrameNameScale = ClampUnitFrameTextScale(MidnightUISettings.General.unitFrameNameScale)
    MidnightUISettings.General.unitFrameValueScale = ClampUnitFrameTextScale(MidnightUISettings.General.unitFrameValueScale)
end

local function GetSharedUnitAppearanceSettings()
    EnsureSharedUnitAppearanceSettings()

    local outline = MidnightUISettings.General.unitFrameTextOutline
    if outline == "NONE" then
        outline = nil
    end

    return {
        nameScale = ClampUnitFrameTextScale(MidnightUISettings.General.unitFrameNameScale),
        valueScale = ClampUnitFrameTextScale(MidnightUISettings.General.unitFrameValueScale),
        outline = outline,
        showLevelText = not MidnightUISettings.General.unitFrameHideLevelText,
        showPowerText = not MidnightUISettings.General.unitFrameHidePowerText,
    }
end

local function ApplySharedTextShadow(fontString, alpha)
    if not fontString then return end
    alpha = tonumber(alpha)
    if alpha == nil then alpha = 1 end
    fontString:SetShadowOffset(1, -1)
    fontString:SetShadowColor(0, 0, 0, alpha)
end

local function ApplySharedUnitFont(fontString, fontPath, baseSize, scalePercent, outline)
    if not fontString or type(fontString.SetFont) ~= "function" then return end

    local size = math.max(6, math.floor((baseSize or 10) * (scalePercent / 100) + 0.5))
    if outline then
        fontString:SetFont(fontPath, size, outline)
    else
        fontString:SetFont(fontPath, size)
    end
end

local function ApplySharedUnitTextStyle(frame, options)
    if not frame then return end

    local shared = GetSharedUnitAppearanceSettings()
    local opts = options or {}

    if frame.nameText and opts.nameFont and opts.nameSize then
        ApplySharedUnitFont(frame.nameText, opts.nameFont, opts.nameSize, shared.nameScale, shared.outline)
        ApplySharedTextShadow(frame.nameText, opts.nameShadowAlpha)
    end

    if frame.healthText and opts.healthFont and opts.healthSize then
        ApplySharedUnitFont(frame.healthText, opts.healthFont, opts.healthSize, shared.valueScale, shared.outline)
        ApplySharedTextShadow(frame.healthText, opts.healthShadowAlpha)
    end

    if frame.levelText and opts.levelFont and opts.levelSize then
        ApplySharedUnitFont(frame.levelText, opts.levelFont, opts.levelSize, shared.valueScale, shared.outline)
        ApplySharedTextShadow(frame.levelText, opts.levelShadowAlpha)
        frame.levelText:SetAlpha(shared.showLevelText and 1 or 0)
    end

    if frame.powerText and opts.powerFont and opts.powerSize then
        ApplySharedUnitFont(frame.powerText, opts.powerFont, opts.powerSize, shared.valueScale, shared.outline)
        ApplySharedTextShadow(frame.powerText, opts.powerShadowAlpha)
        frame.powerText:SetAlpha(shared.showPowerText and 1 or 0)
    end
end

local sharedUnitAppearanceRefreshFrame
local sharedUnitAppearanceRefreshPending = false

local function RunSharedUnitFrameAppearanceRefresh()
    ApplyUnitFrameBarStyle()

    if ApplyPlayerSettings then ApplyPlayerSettings() end
    if ApplyTargetSettings then ApplyTargetSettings() end
    if ApplyFocusSettings then ApplyFocusSettings() end
    if ApplyPetSettings then ApplyPetSettings() end
    if ApplyPartyFramesSettings then ApplyPartyFramesSettings() end
    if ApplyRaidFramesSettings then ApplyRaidFramesSettings() end
end

local function EnsureUnitFrameAppearanceRefreshFrame()
    if sharedUnitAppearanceRefreshFrame then
        return sharedUnitAppearanceRefreshFrame
    end

    sharedUnitAppearanceRefreshFrame = CreateFrame("Frame")
    sharedUnitAppearanceRefreshFrame:SetScript("OnEvent", function(self, event)
        if event ~= "PLAYER_REGEN_ENABLED" or not sharedUnitAppearanceRefreshPending then
            return
        end

        sharedUnitAppearanceRefreshPending = false
        self:UnregisterEvent("PLAYER_REGEN_ENABLED")
        RunSharedUnitFrameAppearanceRefresh()
    end)

    return sharedUnitAppearanceRefreshFrame
end

local function ApplySharedUnitFrameAppearance()
    EnsureSharedUnitAppearanceSettings()

    if InCombatLockdown() then
        sharedUnitAppearanceRefreshPending = true
        EnsureUnitFrameAppearanceRefreshFrame():RegisterEvent("PLAYER_REGEN_ENABLED")
        return false
    end

    sharedUnitAppearanceRefreshPending = false
    if sharedUnitAppearanceRefreshFrame then
        sharedUnitAppearanceRefreshFrame:UnregisterEvent("PLAYER_REGEN_ENABLED")
    end

    RunSharedUnitFrameAppearanceRefresh()
    return true
end

_G.MidnightUI_GetSharedUnitAppearanceSettings = GetSharedUnitAppearanceSettings
_G.MidnightUI_ApplySharedUnitTextStyle = ApplySharedUnitTextStyle
_G.MidnightUI_ApplySharedUnitFrameAppearance = ApplySharedUnitFrameAppearance


-- ============================================================
-- SECTION: Quest Objective Visibility System
-- Controls the visibility of the ObjectiveTrackerFrame based on
-- two settings: hideQuestObjectivesAlways and
-- hideQuestObjectivesInCombat. Uses UIFrameFadeIn/FadeOut with a
-- 0.25s duration for smooth transitions.
--
-- The tracker's original alpha is captured the first time before
-- any hide occurs (tracker.midnightRestoreAlpha) so it can be
-- restored accurately even if Blizzard changes it.
--
-- READS:
--   MidnightUISettings.General.hideQuestObjectivesAlways
--   MidnightUISettings.General.hideQuestObjectivesInCombat
--   InCombatLockdown() / PLAYER_REGEN_DISABLED event
--   _G.ObjectiveTrackerFrame
--
-- WRITES:
--   tracker.midnightRestoreAlpha — saved original alpha
--   tracker alpha via UIFrameFadeOut/In
--
-- EVENTS:
--   ADDON_LOADED, PLAYER_ENTERING_WORLD, PLAYER_REGEN_DISABLED,
--   PLAYER_REGEN_ENABLED — all trigger ApplyQuestObjectiveVisibility
--
-- FUNCTIONS:
--   EnsureQuestObjectiveSettings()    — initializes General.hide* flags
--   GetObjectiveTracker()             — returns ObjectiveTrackerFrame
--   CaptureRestoreAlpha(tracker)      — saves current alpha if > 0
--   FadeTracker(tracker, toAlpha)     — fades tracker to toAlpha
--   ApplyQuestObjectiveVisibility()   — main logic: hides always, hides
--                                       in combat, or restores; exported
--                                       as _G.MidnightUI_ApplyQuestObjectivesVisibility
-- ============================================================
local QUEST_FADE_DURATION = 0.25

local function EnsureQuestObjectiveSettings()
    if not MidnightUISettings then MidnightUISettings = {} end
    if not MidnightUISettings.General then MidnightUISettings.General = {} end
    if MidnightUISettings.General.useBlizzardGroupFinder == nil then
        MidnightUISettings.General.useBlizzardGroupFinder = false
    end
    if MidnightUISettings.General.useBlizzardCharacterPanel == nil then
        MidnightUISettings.General.useBlizzardCharacterPanel = false
    end
    if MidnightUISettings.General.useBlizzardGuildPanel == nil then
        MidnightUISettings.General.useBlizzardGuildPanel = false
    end
    if type(MidnightUISettings.LootWindow) ~= "table" then
        MidnightUISettings.LootWindow = { enabled = true, scale = 100 }
    else
        if MidnightUISettings.LootWindow.enabled == nil then
            MidnightUISettings.LootWindow.enabled = true
        end
        if type(MidnightUISettings.LootWindow.scale) ~= "number" then
            MidnightUISettings.LootWindow.scale = 100
        end
    end
    if MidnightUISettings.General.useBlizzardQuestingInterface == nil then
        MidnightUISettings.General.useBlizzardQuestingInterface = false
    end
    if MidnightUISettings.General.hideQuestObjectivesInCombat == nil then
        MidnightUISettings.General.hideQuestObjectivesInCombat = false
    end
    if MidnightUISettings.General.hideQuestObjectivesAlways == nil then
        MidnightUISettings.General.hideQuestObjectivesAlways = false
    end
    if MidnightUISettings.General.debugCastBar == nil then
        MidnightUISettings.General.debugCastBar = false
    end
end

local function GetObjectiveTracker()
    return _G.ObjectiveTrackerFrame
end

local function CaptureRestoreAlpha(tracker)
    if not tracker then return end
    local current = tracker:GetAlpha() or 1
    if current and current > 0 then
        tracker.midnightRestoreAlpha = current
    elseif tracker.midnightRestoreAlpha == nil then
        tracker.midnightRestoreAlpha = 1
    end
end

local function FadeTracker(tracker, toAlpha)
    if not tracker then return end
    if not tracker:IsShown() then
        tracker:SetAlpha(toAlpha)
        return
    end

    local fromAlpha = tracker:GetAlpha() or 1
    if toAlpha <= 0 then
        UIFrameFadeOut(tracker, QUEST_FADE_DURATION, fromAlpha, 0)
    else
        UIFrameFadeIn(tracker, QUEST_FADE_DURATION, fromAlpha, toAlpha)
    end
end

-- Child modules of ObjectiveTrackerFrame that are *not* the Mythic+ /
-- scenario timer. Hiding these keeps the keystone timer on screen while
-- still respecting the "hide quest objectives" preference.
local QUEST_TRACKER_MODULE_NAMES = {
    "QuestObjectiveTracker",
    "CampaignQuestObjectiveTracker",
    "BonusObjectiveTracker",
    "WorldQuestObjectiveTracker",
    "AchievementObjectiveTracker",
    "ProfessionsRecipeTracker",
    "MonthlyActivitiesObjectiveTracker",
    "AdventureObjectiveTracker",
    "DelveCompanionObjectiveTracker",
    "UIWidgetObjectiveTracker",
}

local function ForEachQuestModule(fn)
    for _, name in ipairs(QUEST_TRACKER_MODULE_NAMES) do
        local mod = _G[name]
        if mod then fn(mod) end
    end
end

local function FadeTrackerModule(mod, toAlpha)
    if not mod then return end
    if mod.midnightRestoreAlpha == nil then
        local current = (mod.GetAlpha and mod:GetAlpha()) or 1
        if current and current > 0 then mod.midnightRestoreAlpha = current end
    end
    if mod.IsShown and not mod:IsShown() then
        if mod.SetAlpha then mod:SetAlpha(toAlpha) end
        return
    end
    local fromAlpha = (mod.GetAlpha and mod:GetAlpha()) or 1
    if toAlpha <= 0 then
        if UIFrameFadeOut then UIFrameFadeOut(mod, QUEST_FADE_DURATION, fromAlpha, 0)
        elseif mod.SetAlpha then mod:SetAlpha(0) end
    else
        if UIFrameFadeIn then UIFrameFadeIn(mod, QUEST_FADE_DURATION, fromAlpha, toAlpha)
        elseif mod.SetAlpha then mod:SetAlpha(toAlpha) end
    end
end

local function ApplyQuestObjectiveVisibility()
    EnsureQuestObjectiveSettings()
    local tracker = GetObjectiveTracker()
    if not tracker then return end

    local hideAlways = MidnightUISettings.General.hideQuestObjectivesAlways == true
    local hideInCombat = MidnightUISettings.General.hideQuestObjectivesInCombat == true
    local inCombat = (InCombatLockdown and InCombatLockdown()) or UnitAffectingCombat("player")
    local keyActive = C_ChallengeMode and C_ChallengeMode.IsChallengeModeActive and C_ChallengeMode.IsChallengeModeActive() or false

    local shouldHide = hideAlways or (hideInCombat and inCombat)

    if keyActive then
        -- Keep the parent tracker visible so the keystone timer renders,
        -- but hide the quest modules specifically if the user wants them
        -- hidden. This way a M+ key honors the "hide quests" setting
        -- without hiding the timer itself.
        local restoreAlpha = tracker.midnightRestoreAlpha or 1
        FadeTracker(tracker, restoreAlpha)
        ForEachQuestModule(function(mod)
            if shouldHide then
                FadeTrackerModule(mod, 0)
            else
                local r = mod.midnightRestoreAlpha or 1
                FadeTrackerModule(mod, r)
            end
        end)
        return
    end

    -- Outside of M+: restore per-module alpha and apply the original
    -- whole-tracker fade behavior.
    ForEachQuestModule(function(mod)
        local r = mod.midnightRestoreAlpha or 1
        if mod.SetAlpha then mod:SetAlpha(r) end
    end)

    if shouldHide then
        CaptureRestoreAlpha(tracker)
        FadeTracker(tracker, 0)
    else
        local restoreAlpha = tracker.midnightRestoreAlpha or 1
        FadeTracker(tracker, restoreAlpha)
    end
end

_G.MidnightUI_ApplyQuestObjectivesVisibility = ApplyQuestObjectiveVisibility

local function EnsureObjectiveTrackerScaleSetting()
    if not MidnightUISettings then MidnightUISettings = {} end
    if type(MidnightUISettings.ObjectiveTracker) ~= "table" then
        MidnightUISettings.ObjectiveTracker = {}
    end
    if type(MidnightUISettings.ObjectiveTracker.scale) ~= "number" then
        MidnightUISettings.ObjectiveTracker.scale = 100
    end
    return MidnightUISettings.ObjectiveTracker
end

local function ApplyObjectiveTrackerScale()
    local s = EnsureObjectiveTrackerScaleSetting()
    local tracker = _G.ObjectiveTrackerFrame
    if not tracker or not tracker.SetScale then return end
    local pct = tonumber(s.scale) or 100
    if pct < 25 then pct = 25 elseif pct > 300 then pct = 300 end
    pcall(tracker.SetScale, tracker, pct / 100)
end
_G.MidnightUI_ApplyObjectiveTrackerScale = ApplyObjectiveTrackerScale

local questListener = CreateFrame("Frame")
questListener:RegisterEvent("ADDON_LOADED")
questListener:RegisterEvent("PLAYER_ENTERING_WORLD")
questListener:RegisterEvent("PLAYER_REGEN_DISABLED")
questListener:RegisterEvent("PLAYER_REGEN_ENABLED")
questListener:RegisterEvent("CHALLENGE_MODE_START")
questListener:RegisterEvent("CHALLENGE_MODE_COMPLETED")
questListener:RegisterEvent("CHALLENGE_MODE_RESET")
questListener:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" then
        local addonName = ...
        if addonName ~= "MidnightUI" then return end
        EnsureQuestObjectiveSettings()
        EnsureObjectiveTrackerScaleSetting()
        C_Timer.After(0.2, ApplyQuestObjectiveVisibility)
        C_Timer.After(0.2, ApplyObjectiveTrackerScale)
        return
    end

    if event == "PLAYER_ENTERING_WORLD" then
        C_Timer.After(0.2, ApplyQuestObjectiveVisibility)
        C_Timer.After(0.2, ApplyObjectiveTrackerScale)
        return
    end

    if event == "PLAYER_REGEN_DISABLED" or event == "PLAYER_REGEN_ENABLED" then
        ApplyQuestObjectiveVisibility()
        return
    end

    if event == "CHALLENGE_MODE_START" or event == "CHALLENGE_MODE_COMPLETED" or event == "CHALLENGE_MODE_RESET" then
        ApplyQuestObjectiveVisibility()
        return
    end
end)

-- ============================================================
-- SECTION: Global Theme System
-- Provides a debounced "apply everything" call. ApplyGlobalTheme()
-- queues a C_Timer.After(0) that coalesces multiple rapid calls
-- into a single tick that applies ActionBars, Messenger, and Minimap
-- settings together. Used when the user switches the GlobalStyle
-- dropdown in the Settings UI.
--
-- FUNCTIONS:
--   ApplyGlobalTheme()  — queues a deferred apply of ActionBars +
--                          Messenger + Minimap settings; re-entrant
--                          calls within the same frame are no-ops
-- ============================================================
local globalThemeQueued = false
local function ApplyGlobalTheme()
    if globalThemeQueued then return end
    globalThemeQueued = true
    C_Timer.After(0, function()
        globalThemeQueued = false
        ApplyActionBarSettingsImmediate()
        ApplyMessengerSettings()
        ApplyMinimapSettings()
    end)
end


-- ============================================================
-- SECTION: Movement Grid + MoveHUD (Movement Mode UI)
-- The GridFrame draws a snap-grid overlay (minor lines every 20px,
-- major lines every 160px) across the screen during Movement Mode.
-- The MoveHUD is a floating panel that appears when overlays are
-- unlocked. It lists all repositionable frames with enable/disable
-- checkboxes and drives drag-to-reposition mode.
--
-- READS:
--   MidnightUISettings.Messenger.locked — determines overlay state
--   MidnightUI_MoverRegistry            — SavePosition, SetAllLocked
--   _G.MidnightUI_MovementModeFromWizard — set by Welcome wizard
--
-- WRITES:
--   MidnightUISettings.Messenger.locked — set to true on Lock & Save
--   _G.MidnightUI_MoveHUD               — exported for external modules
--   _G.MidnightUI_MoveHUDRestore        — exported restore button
--
-- FRAMES:
--   MidnightUI_MoveHUD (BackdropTemplate, UIParent, TOOLTIP/500)
--   MidnightUI_MoveHUDRestore (Button, UIParent)
--   GridFrame (Frame, UIParent, BACKGROUND)
--
-- FUNCTIONS:
--   StyleCustomButton(button)     — delegates to Core or Settings button styler
--   DrawGrid()                    — creates grid line textures lazily (called once)
--   SetOverlayEnabled(key, enabled) — toggles a named overlay on/off via
--                                     MoverRegistry or module API; logs result
--                                     to diagnostics
--   GetOverlayEnabled(key)        — returns current enable state for a named overlay
-- ============================================================
local GridFrame = CreateFrame("Frame", nil, UIParent)
GridFrame:SetAllPoints(); GridFrame:SetFrameStrata("BACKGROUND"); GridFrame:Hide()

local MOVEMENT_VISUALS = {
    gridMinorStep = 20,
    gridMajorEvery = 8,
    gridMinorAlpha = 0.052,
    gridMajorAlpha = 0.140,
    gridColor = { 0.40, 0.56, 0.70 },
    gridCenter = { 0.34, 0.78, 1.00, 0.62 },
    gridDim = { 0.02, 0.03, 0.05, 0.20 },
    hudBg = { 0.04, 0.06, 0.09, 0.94 },
    hudBorder = { 0.21, 0.30, 0.39, 0.90 },
    hudHeader = { 0.08, 0.14, 0.22, 0.96 },
    hudAccent = { 0.22, 0.54, 0.72, 0.62 },
    hudInset = { 0.03, 0.07, 0.11, 0.70 },
}

local function StyleCustomButton(button)
    if not button then return end
    if _G.MidnightUI_StyleButton then
        _G.MidnightUI_StyleButton(button)
        return
    end
    if _G.MidnightUI_Settings and _G.MidnightUI_Settings.StyleSettingsButton then
        _G.MidnightUI_Settings.StyleSettingsButton(button)
        return
    end
end

local function DrawGrid()
    if GridFrame.lines then return end
    GridFrame.lines = {}
    local width, height = UIParent:GetSize()
    local step = MOVEMENT_VISUALS.gridMinorStep
    local majorStride = step * MOVEMENT_VISUALS.gridMajorEvery
    local function CL(r, g, b, a)
        local t = GridFrame:CreateTexture(nil, "BACKGROUND")
        t:SetColorTexture(r, g, b, a)
        return t
    end

    local dim = CL(
        MOVEMENT_VISUALS.gridDim[1],
        MOVEMENT_VISUALS.gridDim[2],
        MOVEMENT_VISUALS.gridDim[3],
        MOVEMENT_VISUALS.gridDim[4]
    )
    dim:SetAllPoints()
    GridFrame.dim = dim

    for x = 0, width, step do
        local isMajor = (x % majorStride) == 0
        local alpha = isMajor and MOVEMENT_VISUALS.gridMajorAlpha or MOVEMENT_VISUALS.gridMinorAlpha
        local l = CL(
            MOVEMENT_VISUALS.gridColor[1],
            MOVEMENT_VISUALS.gridColor[2],
            MOVEMENT_VISUALS.gridColor[3],
            alpha
        )
        l:SetSize(isMajor and 2 or 1, height)
        if isMajor and l.SetBlendMode then
            l:SetBlendMode("ADD")
        end
        l:SetPoint("TOPLEFT", x, 0)
        GridFrame.lines[#GridFrame.lines + 1] = l
    end
    for y = 0, height, step do
        local isMajor = (y % majorStride) == 0
        local alpha = isMajor and MOVEMENT_VISUALS.gridMajorAlpha or MOVEMENT_VISUALS.gridMinorAlpha
        local l = CL(
            MOVEMENT_VISUALS.gridColor[1],
            MOVEMENT_VISUALS.gridColor[2],
            MOVEMENT_VISUALS.gridColor[3],
            alpha
        )
        l:SetSize(width, isMajor and 2 or 1)
        if isMajor and l.SetBlendMode then
            l:SetBlendMode("ADD")
        end
        l:SetPoint("TOPLEFT", 0, -y)
        GridFrame.lines[#GridFrame.lines + 1] = l
    end

    local vC = CL(
        MOVEMENT_VISUALS.gridCenter[1],
        MOVEMENT_VISUALS.gridCenter[2],
        MOVEMENT_VISUALS.gridCenter[3],
        MOVEMENT_VISUALS.gridCenter[4]
    )
    vC:SetSize(2, height)
    vC:SetPoint("CENTER")
    local hC = CL(
        MOVEMENT_VISUALS.gridCenter[1],
        MOVEMENT_VISUALS.gridCenter[2],
        MOVEMENT_VISUALS.gridCenter[3],
        MOVEMENT_VISUALS.gridCenter[4]
    )
    hC:SetSize(width, 2)
    hC:SetPoint("CENTER")
    GridFrame.lines[#GridFrame.lines + 1] = vC
    GridFrame.lines[#GridFrame.lines + 1] = hC
end

local MoveHUD = CreateFrame("Frame", "MidnightUI_MoveHUD", UIParent, "BackdropTemplate")
local MOVEHUD_HEADER_H = 64
MoveHUD:SetSize(540, 288); MoveHUD:SetPoint("TOP", 0, -50); MoveHUD:SetFrameStrata("TOOLTIP"); MoveHUD:SetFrameLevel(500)
MoveHUD:SetBackdrop({
    bgFile = "Interface\\Buttons\\WHITE8X8",
    edgeFile = "Interface\\Buttons\\WHITE8X8",
    edgeSize = 1,
    insets = { left = 1, right = 1, top = 1, bottom = 1 }
})
MoveHUD:SetBackdropColor(
    MOVEMENT_VISUALS.hudBg[1],
    MOVEMENT_VISUALS.hudBg[2],
    MOVEMENT_VISUALS.hudBg[3],
    MOVEMENT_VISUALS.hudBg[4]
)
MoveHUD:SetBackdropBorderColor(
    MOVEMENT_VISUALS.hudBorder[1],
    MOVEMENT_VISUALS.hudBorder[2],
    MOVEMENT_VISUALS.hudBorder[3],
    MOVEMENT_VISUALS.hudBorder[4]
)
MoveHUD:Hide()

local moveHeader = MoveHUD:CreateTexture(nil, "ARTWORK")
moveHeader:SetTexture("Interface\\Buttons\\WHITE8X8")
moveHeader:SetPoint("TOPLEFT", 1, -1)
moveHeader:SetPoint("TOPRIGHT", -1, -1)
moveHeader:SetHeight(MOVEHUD_HEADER_H)
moveHeader:SetVertexColor(
    MOVEMENT_VISUALS.hudHeader[1],
    MOVEMENT_VISUALS.hudHeader[2],
    MOVEMENT_VISUALS.hudHeader[3],
    MOVEMENT_VISUALS.hudHeader[4]
)

local moveHeaderAccent = MoveHUD:CreateTexture(nil, "ARTWORK")
moveHeaderAccent:SetTexture("Interface\\Buttons\\WHITE8X8")
moveHeaderAccent:SetPoint("TOPLEFT", 1, -1)
moveHeaderAccent:SetPoint("TOPRIGHT", -1, -1)
moveHeaderAccent:SetHeight(2)
moveHeaderAccent:SetVertexColor(
    MOVEMENT_VISUALS.hudAccent[1],
    MOVEMENT_VISUALS.hudAccent[2],
    MOVEMENT_VISUALS.hudAccent[3],
    MOVEMENT_VISUALS.hudAccent[4]
)

local moveBodyInset = MoveHUD:CreateTexture(nil, "BACKGROUND")
moveBodyInset:SetTexture("Interface\\Buttons\\WHITE8X8")
moveBodyInset:SetPoint("TOPLEFT", 8, -(MOVEHUD_HEADER_H + 8))
moveBodyInset:SetPoint("BOTTOMRIGHT", -8, 8)
moveBodyInset:SetVertexColor(
    MOVEMENT_VISUALS.hudInset[1],
    MOVEMENT_VISUALS.hudInset[2],
    MOVEMENT_VISUALS.hudInset[3],
    MOVEMENT_VISUALS.hudInset[4]
)

local ht = MoveHUD:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
ht:SetPoint("TOPLEFT", 14, -14)
ht:SetPoint("RIGHT", -92, 0)
ht:SetJustifyH("LEFT")
ht:SetText("Movement Mode")
ht:SetTextColor(0.94, 0.97, 1.00)
ht:SetShadowColor(0, 0, 0, 0.8)
ht:SetShadowOffset(1, -1)

local htSub = MoveHUD:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
htSub:SetPoint("TOPLEFT", 14, -39)
htSub:SetPoint("RIGHT", -92, 0)
htSub:SetJustifyH("LEFT")
htSub:SetText("Drag overlays to reposition. Right-click overlays for per-frame options.")
htSub:SetTextColor(0.70, 0.77, 0.85)

local moveDivider = MoveHUD:CreateTexture(nil, "ARTWORK")
moveDivider:SetTexture("Interface\\Buttons\\WHITE8X8")
moveDivider:SetPoint("TOP", MoveHUD, "TOP", 0, -(MOVEHUD_HEADER_H + 16))
moveDivider:SetPoint("BOTTOM", MoveHUD, "BOTTOM", 0, 68)
moveDivider:SetWidth(1)
moveDivider:SetVertexColor(0.20, 0.27, 0.35, 0.78)

local leftInstructionBg = MoveHUD:CreateTexture(nil, "BACKGROUND")
leftInstructionBg:SetTexture("Interface\\Buttons\\WHITE8X8")
leftInstructionBg:SetPoint("TOPLEFT", 14, -(MOVEHUD_HEADER_H + 22))
leftInstructionBg:SetPoint("BOTTOMRIGHT", MoveHUD, "BOTTOM", -8, 70)
leftInstructionBg:SetVertexColor(0.05, 0.09, 0.14, 0.46)

local rightInstructionBg = MoveHUD:CreateTexture(nil, "BACKGROUND")
rightInstructionBg:SetTexture("Interface\\Buttons\\WHITE8X8")
rightInstructionBg:SetPoint("TOPLEFT", MoveHUD, "TOP", 8, -(MOVEHUD_HEADER_H + 22))
rightInstructionBg:SetPoint("BOTTOMRIGHT", -14, 70)
rightInstructionBg:SetVertexColor(0.05, 0.09, 0.14, 0.46)


local OverlayManagerFrame = nil
local OverlayManagerRows = {}

local OVERLAY_GROUPS = {
    { id = "unit", label = "Unit Frames" },
    { id = "cast", label = "Cast Bars" },
    { id = "auras", label = "Auras & Tracking" },
    { id = "bars", label = "Action & Utility Bars" },
    { id = "world", label = "World Frames" },
}

local OVERLAY_DEFINITIONS = {
    { key = "PlayerFrame", name = "Player Frame", settingPath = "PlayerFrame.enabled", applyFunc = "ApplyPlayerSettings", group = "unit" },
    { key = "TargetFrame", name = "Target Frame", settingPath = "TargetFrame.enabled", applyFunc = "ApplyTargetSettings", group = "unit" },
    { key = "TargetOfTarget", name = "Target of Target", settingPath = "TargetFrame.showTargetOfTarget", applyFunc = "MidnightUI_ApplyTargetOfTargetSettings", group = "unit" },
    { key = "FocusFrame", name = "Focus Frame", settingPath = "FocusFrame.enabled", applyFunc = "ApplyFocusSettings", group = "unit" },
    { key = "PetFrame", name = "Pet Frame", settingPath = "PetFrame.enabled", applyFunc = "ApplyPetSettings", group = "unit" },
    { key = "PartyFrames", name = "Party Frames", settingPath = "PartyFrames.enabled", applyFunc = "ApplyPartyFramesSettings", group = "unit" },
    { key = "RaidFrames", name = "Raid Frames", settingPath = "RaidFrames.enabled", applyFunc = "ApplyRaidFramesSettings", group = "unit" },
    { key = "MainTankFrames", name = "Main Tank Frames", settingPath = "MainTankFrames.enabled", applyFunc = "MidnightUI_ApplyMainTankSettings", group = "unit" },
    { key = "SoulShards", name = "Soul Shards (Warlock)", settingPath = "SoulShards.enabled", applyFunc = "MidnightUI_ApplySoulShardsSettings", group = "unit" },
    { key = "ComboPoints", name = "Combo Points (Rogue/Druid)", settingPath = "ComboPoints.enabled", applyFunc = "MidnightUI_ApplyComboPointsSettings", group = "unit" },
    { key = "DKRunes", name = "DK Runes (Death Knight)", settingPath = "DKRunes.enabled", applyFunc = "MidnightUI_ApplyDKRunesSettings", group = "unit" },
    { key = "MonkChi", name = "Chi (Monk)", settingPath = "MonkChi.enabled", applyFunc = "MidnightUI_ApplyMonkChiSettings", group = "unit" },
    { key = "HolyPower", name = "Holy Power (Paladin)", settingPath = "HolyPower.enabled", applyFunc = "MidnightUI_ApplyHolyPowerSettings", group = "unit" },
    { key = "ArcaneCharges", name = "Arcane Charges (Mage)", settingPath = "ArcaneCharges.enabled", applyFunc = "MidnightUI_ApplyArcaneChargesSettings", group = "unit" },
    { key = "EvokerEssence", name = "Essence (Evoker)", settingPath = "EvokerEssence.enabled", applyFunc = "MidnightUI_ApplyEvokerEssenceSettings", group = "unit" },
    { key = "CastBar_player", name = "Player Cast Bar", settingPath = "CastBars.player.enabled", applyFunc = "ApplyCastBarSettings", group = "cast" },
    { key = "CastBar_target", name = "Target Cast Bar", settingPath = "CastBars.target.enabled", applyFunc = "ApplyCastBarSettings", group = "cast" },
    { key = "CastBar_focus", name = "Focus Cast Bar", settingPath = "CastBars.focus.enabled", applyFunc = "ApplyCastBarSettings", group = "cast" },
    { key = "NameplateCastBar", name = "Nameplate Cast Bars", settingPath = "CastBars.nameplate.enabled", applyFunc = "ApplyCastBarSettings", group = "cast" },
    { key = "AirBar", name = "Air Bar (Underwater)", settingPath = "CastBars.air.enabled", applyFunc = "ApplyCastBarSettings", group = "cast" },
    { key = "PlayerBuffs", name = "Player Buffs", settingPath = "PlayerFrame.auras.enabled", applyFunc = "MidnightUI_ApplyPlayerAuraSettings", group = "auras" },
    { key = "PlayerDebuffs", name = "Player Debuffs", settingPath = "PlayerFrame.debuffs.enabled", applyFunc = "MidnightUI_ApplyPlayerDebuffSettings", group = "auras" },
    { key = "TargetBuffs", name = "Target Buffs", settingPath = "TargetFrame.auras.enabled", applyFunc = "MidnightUI_ApplyTargetAuraSettings", group = "auras" },
    { key = "TargetDebuffs", name = "Target Debuffs", settingPath = "TargetFrame.debuffs.enabled", applyFunc = "MidnightUI_ApplyTargetDebuffSettings", group = "auras" },
    { key = "DispelTracking", name = "Dispel Tracking", settingPath = "Combat.dispelTrackingEnabled", applyFunc = "MidnightUI_RefreshDispelTrackingOverlay", group = "auras" },
    { key = "PartyDispelTracking", name = "Party Dispel Icons", settingPath = "Combat.partyDispelTrackingEnabled", applyFunc = "MidnightUI_RefreshPartyDispelTrackingOverlay", group = "auras" },
    { key = "InterruptTracker", name = "Interrupt Tracker", settingPath = "InterruptTracker.enabled", applyFunc = "MidnightUI_RefreshInterruptTracker", group = "auras" },
    { key = "ActionBar1", name = "Action Bar 1", settingPath = "ActionBars.bar1.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "ActionBar2", name = "Action Bar 2", settingPath = "ActionBars.bar2.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "ActionBar3", name = "Action Bar 3", settingPath = "ActionBars.bar3.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "ActionBar4", name = "Action Bar 4", settingPath = "ActionBars.bar4.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "ActionBar5", name = "Action Bar 5", settingPath = "ActionBars.bar5.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "ActionBar6", name = "Action Bar 6", settingPath = "ActionBars.bar6.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "ActionBar7", name = "Action Bar 7", settingPath = "ActionBars.bar7.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "ActionBar8", name = "Action Bar 8", settingPath = "ActionBars.bar8.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "PetBar", name = "Pet Bar", settingPath = "PetBar.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "StanceBar", name = "Stance Bar", settingPath = "StanceBar.enabled", applyFunc = "MyActionBars_ReloadSettingsImmediate", group = "bars" },
    { key = "ConsumableBars", name = "Consumable Bars", settingPath = "ConsumableBars.enabled", applyFunc = "MidnightUI_ApplyConsumableBarsSettings", group = "bars" },
    { key = "Inventory", name = "Bag Bar (Inventory)", settingPath = "Inventory.enabled", applyFunc = "MidnightUI_ApplyInventorySettings", group = "bars" },
    { key = "LootWindow", name = "Loot Window", settingPath = "LootWindow.enabled", applyFunc = "MidnightUI_ApplyLootWindowSettings", group = "world" },
    { key = "InterfaceMenu", name = "Game Menu", settingPath = "InterfaceMenu.enabled", applyFunc = "MidnightUI_ApplyInterfaceMenuSettings", group = "bars" },
    { key = "XPBar", name = "XP Bar", settingPath = "XPBar.enabled", applyFunc = "MidnightUI_RefreshStatusBars", group = "world" },
    { key = "RepBar", name = "Reputation Bar", settingPath = "RepBar.enabled", applyFunc = "MidnightUI_RefreshStatusBars", group = "world" },
    { key = "Messenger", name = "Messenger (Chat)", settingPath = "Messenger.enabled", applyFunc = "ApplyMessengerSettings", group = "world" },
    { key = "Minimap", name = "Minimap", settingPath = "Minimap.enabled", applyFunc = "ApplyMinimapSettings", group = "world" },
    { key = "MinimapInfoPanel", name = "Info Panel", settingPath = "Minimap.infoPanelEnabled", applyFunc = "ApplyMinimapSettings", group = "world" },
    { key = "Nameplates", name = "Nameplates", settingPath = "Nameplates.enabled", applyFunc = "ApplyNameplateSettings", group = "world" },
    { key = "Map", name = "Map & Quest Log", settingPath = "General.useBlizzardQuestingInterface",
      enabledCheck = function()
          local s = MidnightUISettings
          return not (s and s.General and s.General.useBlizzardQuestingInterface == true)
      end,
      setEnabled = function(enabled)
          if not MidnightUISettings.General then MidnightUISettings.General = {} end
          MidnightUISettings.General.useBlizzardQuestingInterface = not enabled
      end,
      group = "world" },
}

local function GetOverlayEnabled(def)
    local path = def.settingPath
    local parts = {}
    for part in string.gmatch(path, "[^%.]+") do
        table.insert(parts, part)
    end
    
    local current = MidnightUISettings
    for i, part in ipairs(parts) do
        if type(current) ~= "table" then return false end
        current = current[part]
    end
    
    if def.enabledCheck then
        return def.enabledCheck()
    end
    
    return current ~= false
end

local function OverlayManagerLog(msg)
    if _G.MidnightUI_Diagnostics
        and _G.MidnightUI_Diagnostics.LogDebugSource
        and _G.MidnightUI_Diagnostics.IsEnabled
        and _G.MidnightUI_Diagnostics.IsEnabled()
    then
        _G.MidnightUI_Diagnostics.LogDebugSource("Movement/OverlayManager", tostring(msg))
        return
    end
    if _G.MidnightUI_Debug then
        _G.MidnightUI_Debug("[OverlayManager] " .. tostring(msg))
    end
end

local function SetOverlayEnabled(def, enabled)
    if InCombatLockdown and InCombatLockdown() then
        OverlayManagerLog("Blocked in combat: " .. tostring(def and def.key))
        if UIErrorsFrame and UIErrorsFrame.AddMessage then
            UIErrorsFrame:AddMessage("MidnightUI: Can't toggle overlay while in combat.", 1, 0.2, 0.2)
        end
        return false
    end
    local path = def.settingPath
    local parts = {}
    for part in string.gmatch(path, "[^%.]+") do
        table.insert(parts, part)
    end
    
    local current = MidnightUISettings
    for i = 1, #parts - 1 do
        local part = parts[i]
        if type(current[part]) ~= "table" then
            current[part] = {}
        end
        current = current[part]
    end
    
    current[parts[#parts]] = enabled
    
    local key = def.key

    local BLIZZARD_DEFAULTS = {
        PlayerFrame = "PlayerFrame",
        TargetFrame = "TargetFrame",
        FocusFrame  = "FocusFrame",
        PetFrame    = "PetActionBarFrame",
    }

    local function ToggleBlizzardDefault(blizzName, show)
        local blizz = _G[blizzName]
        if not blizz then return end
        if show then
            blizz:Show()
            if RegisterUnitWatch and blizz.GetAttribute and blizz:GetAttribute("unit") then
                pcall(RegisterUnitWatch, blizz)
            end
        else
            if UnregisterUnitWatch then pcall(UnregisterUnitWatch, blizz) end
            blizz:Hide()
        end
    end

    if key == "PlayerFrame" then
        ApplyPlayerSettings()
        local frame = _G.MidnightUI_PlayerFrame
        if frame then
            if enabled then
                frame:Show()
                ToggleBlizzardDefault("PlayerFrame", false)
            else
                frame:Hide()
                ToggleBlizzardDefault("PlayerFrame", true)
            end
        end
    elseif key == "TargetFrame" then
        ApplyTargetSettings()
        local frame = _G.MidnightUI_TargetFrame
        if frame then
            if enabled then
                if UnitExists("target") then frame:Show() end
                ToggleBlizzardDefault("TargetFrame", false)
            else
                frame:Hide()
                ToggleBlizzardDefault("TargetFrame", true)
            end
        end
    elseif key == "TargetOfTarget" then
        if _G.MidnightUI_ApplyTargetOfTargetSettings then
            _G.MidnightUI_ApplyTargetOfTargetSettings()
        end
    elseif key == "FocusFrame" then
        ApplyFocusSettings()
        local frame = _G.MidnightUI_FocusFrame
        if frame then
            if enabled then
                if UnitExists("focus") then frame:Show() end
                ToggleBlizzardDefault("FocusFrame", false)
            else
                frame:Hide()
                ToggleBlizzardDefault("FocusFrame", true)
            end
        end
    elseif key == "PetFrame" then
        ApplyPetSettings()
        local frame = _G.MidnightUI_PetFrame
        if frame then
            if enabled then
                if UnitExists("pet") then frame:Show() end
            else
                frame:Hide()
            end
        end
    elseif key == "CastBar_player" or key == "CastBar_target" or key == "CastBar_focus" then
        ApplyCastBarSettings()
    elseif key == "PlayerBuffs" then
        if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
        if _G.BuffFrame then
            if enabled then _G.BuffFrame:Show() else _G.BuffFrame:Hide() end
        end
    elseif key == "PlayerDebuffs" then
        if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
        if _G.DebuffFrame then
            if enabled then _G.DebuffFrame:Show() else _G.DebuffFrame:Hide() end
        end
    elseif key == "DispelTracking" then
        if _G.MidnightUI_RefreshDispelTrackingOverlay then
            _G.MidnightUI_RefreshDispelTrackingOverlay(_G.MidnightUI_PlayerFrame)
        end
        local locked = true
        if MidnightUISettings and MidnightUISettings.Messenger and MidnightUISettings.Messenger.locked ~= nil then
            locked = MidnightUISettings.Messenger.locked ~= false
        end
        if _G.MidnightUI_SetDispelTrackingLocked then
            _G.MidnightUI_SetDispelTrackingLocked(locked)
        end
    elseif key == "PartyDispelTracking" then
        if _G.MidnightUI_RefreshPartyDispelTrackingOverlay then
            _G.MidnightUI_RefreshPartyDispelTrackingOverlay(false)
        elseif _G.MidnightUI_RefreshPartyFrames then
            _G.MidnightUI_RefreshPartyFrames()
        end
    elseif key == "TargetBuffs" then
        if _G.MidnightUI_ApplyTargetAuraSettings then _G.MidnightUI_ApplyTargetAuraSettings() end
    elseif key == "TargetDebuffs" then
        if _G.MidnightUI_ApplyTargetDebuffSettings then _G.MidnightUI_ApplyTargetDebuffSettings() end
    elseif key == "ConsumableBars" then
        if _G.MidnightUI_ApplyConsumableBarsSettings then _G.MidnightUI_ApplyConsumableBarsSettings() end
    elseif key == "LootWindow" then
        if _G.MidnightUI_ApplyLootWindowSettings then _G.MidnightUI_ApplyLootWindowSettings() end
    elseif key == "Inventory" then
        if _G.MidnightUI_ApplyInventorySettings then
            _G.MidnightUI_ApplyInventorySettings()
        end
        if enabled then
            if _G.ContainerFrameCombinedBags and _G.ContainerFrameCombinedBags.Hide then
                _G.ContainerFrameCombinedBags:Hide()
            end
            if _G.BagsBar and _G.BagsBar.Hide then
                _G.BagsBar:Hide()
            end
            if _G.MidnightDockMain and _G.MidnightDockMain.Show then
                _G.MidnightDockMain:Show()
            end
        else
            if _G.MidnightDockMain and _G.MidnightDockMain.Hide then
                _G.MidnightDockMain:Hide()
            end
            if _G.BagsBar then
                _G.BagsBar:Show()
                _G.BagsBar:EnableMouse(true)
            end
        end
    elseif key == "XPBar" or key == "RepBar" then
        if _G.MidnightUI_RefreshStatusBars then _G.MidnightUI_RefreshStatusBars() end
    elseif key == "Minimap" then
        ApplyMinimapSettings()
        local cluster = _G.MidnightUI_MinimapCluster
        if enabled then
            if cluster then cluster:Show() end
            if _G.MinimapCluster then pcall(function() _G.MinimapCluster:Hide() end) end
            if _G.MidnightUI_StatusBarMain then _G.MidnightUI_StatusBarMain:Show() end
            if _G.MidnightUI_StatusBarSecondary then _G.MidnightUI_StatusBarSecondary:Show() end
            if _G.MidnightUI_DrawerSettingsIcon then _G.MidnightUI_DrawerSettingsIcon:Show() end
        else
            if cluster then cluster:Hide() end
            if _G.MidnightUI_StatusBarMain then _G.MidnightUI_StatusBarMain:Hide() end
            if _G.MidnightUI_StatusBarSecondary then _G.MidnightUI_StatusBarSecondary:Hide() end
            if _G.MidnightUI_DrawerSettingsIcon then _G.MidnightUI_DrawerSettingsIcon:Hide() end
            if _G.MidnightUI_LibDBIconDock then _G.MidnightUI_LibDBIconDock:Hide() end
            if _G.MinimapCluster then pcall(function() _G.MinimapCluster:Show() end) end
        end
    elseif key == "MinimapInfoPanel" then
        ApplyMinimapSettings()
    elseif key == "PetBar" or key == "StanceBar" then
        ApplyActionBarSettingsImmediate()
    elseif key == "Nameplates" then
        ApplyNameplateSettings()
    elseif key == "RaidFrames" then
        ApplyRaidFramesSettings()
        local anchor = _G.MidnightUI_RaidAnchor
        if enabled then
            if anchor then
                if _G.MidnightUI_UpdateRaidVisibility then _G.MidnightUI_UpdateRaidVisibility() end
            end
            if _G.CompactRaidFrameManager then pcall(function() _G.CompactRaidFrameManager:Hide() end) end
            if _G.CompactRaidFrameContainer then pcall(function() _G.CompactRaidFrameContainer:Hide() end) end
        else
            if anchor then anchor:Hide() end
            if _G.CompactRaidFrameManager then pcall(function() _G.CompactRaidFrameManager:SetParent(UIParent); _G.CompactRaidFrameManager:Show() end) end
            if _G.CompactRaidFrameContainer then pcall(function() _G.CompactRaidFrameContainer:SetParent(UIParent); _G.CompactRaidFrameContainer:Show() end) end
        end
    elseif key == "PartyFrames" then
        ApplyPartyFramesSettings()
        local anchor = _G.MidnightUI_PartyAnchor
        if enabled then
            if anchor then
                if _G.MidnightUI_UpdatePartyVisibility then _G.MidnightUI_UpdatePartyVisibility() end
            end
        else
            if anchor then anchor:Hide() end
        end
    elseif key == "MainTankFrames" then
        if _G.MidnightUI_ApplyMainTankSettings then _G.MidnightUI_ApplyMainTankSettings() end
        local anchor = _G.MidnightUI_MainTankAnchor
        if anchor then
            if enabled then
                anchor:Show()
            else
                anchor:Hide()
            end
        end
    elseif key == "SoulShards" then
        if _G.MidnightUI_ApplySoulShardsSettings then _G.MidnightUI_ApplySoulShardsSettings() end
    elseif key == "ComboPoints" then
        if _G.MidnightUI_ApplyComboPointsSettings then _G.MidnightUI_ApplyComboPointsSettings() end
    elseif key == "DKRunes" then
        if _G.MidnightUI_ApplyDKRunesSettings then _G.MidnightUI_ApplyDKRunesSettings() end
    elseif key == "MonkChi" then
        if _G.MidnightUI_ApplyMonkChiSettings then _G.MidnightUI_ApplyMonkChiSettings() end
    elseif key == "HolyPower" then
        if _G.MidnightUI_ApplyHolyPowerSettings then _G.MidnightUI_ApplyHolyPowerSettings() end
    elseif key == "ArcaneCharges" then
        if _G.MidnightUI_ApplyArcaneChargesSettings then _G.MidnightUI_ApplyArcaneChargesSettings() end
    elseif key == "EvokerEssence" then
        if _G.MidnightUI_ApplyEvokerEssenceSettings then _G.MidnightUI_ApplyEvokerEssenceSettings() end
    elseif key == "Map" then
        if not MidnightUISettings.General then MidnightUISettings.General = {} end
        MidnightUISettings.General.useBlizzardQuestingInterface = not enabled
        if not enabled and _G.MidnightUI_QuestInterface and _G.MidnightUI_QuestInterface.ForceClose then
            _G.MidnightUI_QuestInterface.ForceClose()
        end
        StaticPopup_Show("MIDNIGHTUI_QUESTING_RELOAD")
        return true
    end
    return true
end

-- ============================================================
-- SECTION: Overlay Manager Window
-- A 472×610 diagnostic window (MidnightUI_OverlayManagerFrame) that
-- lists every MidnightUI overlay with enable/disable toggle buttons
-- and a Refresh button. Opened via /mui overlays or
-- MidnightUI_ToggleOverlayManager(). Created lazily on first open.
--
-- Overlays are defined in a OVERLAY_DEFS table inside the function.
-- Each entry has: key (for MoverRegistry), label, blizzardConditional
-- (for frames Blizzard can show/hide independently), and optional
-- enableFn/disableFn overrides.
--
-- READS:
--   GetOverlayEnabled(key)   — determines current toggle state
--
-- WRITES:
--   toggle buttons visuals   — updated on Refresh
--
-- FRAMES:
--   MidnightUI_OverlayManagerFrame (BackdropTemplate, UIParent, FULLSCREEN_DIALOG)
--
-- FUNCTIONS:
--   CreateOverlayManagerWindow()  — builds the window on first call;
--                                    subsequent calls are no-ops
--   ToggleOverlayManager()        — shows or hides the manager;
--                                    creates it if needed; exported as
--                                    _G.MidnightUI_ToggleOverlayManager
-- ============================================================
local function CreateOverlayManagerWindow()
    if OverlayManagerFrame then return end
    
    local UI_COLORS = {
        bg = {0.04, 0.06, 0.09},
        panel = {0.08, 0.14, 0.21},
        border = {0.18, 0.25, 0.34},
        text = {0.90, 0.94, 0.98},
        accent = {0.22, 0.54, 0.72},
        enabled = {0.46, 0.76, 0.58},
        disabled = {0.82, 0.52, 0.48},
    }
    
    local HEADER_H = 70
    local frame = CreateFrame("Frame", "MidnightUI_OverlayManagerFrame", UIParent, "BackdropTemplate")
    frame:SetSize(472, 610)
    frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
    frame:SetFrameStrata("FULLSCREEN_DIALOG")
    frame:SetFrameLevel(600)
    table.insert(UISpecialFrames, "MidnightUI_OverlayManagerFrame")
    frame:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
        insets = { left = 1, right = 1, top = 1, bottom = 1 },
    })
    frame:SetBackdropColor(UI_COLORS.bg[1], UI_COLORS.bg[2], UI_COLORS.bg[3], 0.95)
    frame:SetBackdropBorderColor(UI_COLORS.border[1], UI_COLORS.border[2], UI_COLORS.border[3], 0.95)
    frame:SetClampedToScreen(true)
    frame:SetMovable(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", frame.StartMoving)
    frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
    frame:Hide()
    
    local headerBg = frame:CreateTexture(nil, "ARTWORK")
    headerBg:SetPoint("TOPLEFT", 1, -1)
    headerBg:SetPoint("TOPRIGHT", -1, -1)
    headerBg:SetHeight(HEADER_H)
    headerBg:SetColorTexture(UI_COLORS.panel[1], UI_COLORS.panel[2], UI_COLORS.panel[3], 0.97)
    
    local headerAccent = frame:CreateTexture(nil, "ARTWORK")
    headerAccent:SetPoint("TOPLEFT", 1, -1)
    headerAccent:SetPoint("TOPRIGHT", -1, -1)
    headerAccent:SetHeight(2)
    headerAccent:SetColorTexture(UI_COLORS.accent[1], UI_COLORS.accent[2], UI_COLORS.accent[3], 0.78)

    local headerLine = frame:CreateTexture(nil, "BACKGROUND")
    headerLine:SetTexture("Interface\\Buttons\\WHITE8X8")
    headerLine:SetPoint("TOPLEFT", 10, -(HEADER_H + 1))
    headerLine:SetPoint("TOPRIGHT", -10, -(HEADER_H + 1))
    headerLine:SetHeight(1)
    headerLine:SetVertexColor(UI_COLORS.border[1], UI_COLORS.border[2], UI_COLORS.border[3], 0.72)
    
    local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOPLEFT", 14, -14)
    title:SetPoint("TOPRIGHT", -162, -14)
    title:SetJustifyH("LEFT")
    title:SetText("Overlay Manager")
    title:SetTextColor(UI_COLORS.text[1], UI_COLORS.text[2], UI_COLORS.text[3])

    local subtitle = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    subtitle:SetPoint("TOPLEFT", 14, -40)
    subtitle:SetPoint("TOPRIGHT", -162, -40)
    subtitle:SetJustifyH("LEFT")
    subtitle:SetText("Toggle movement overlays without leaving edit mode.")
    subtitle:SetTextColor(0.67, 0.74, 0.82)
    
    local closeBtn = CreateFrame("Button", nil, frame, "BackdropTemplate")
    closeBtn:SetSize(24, 24)
    closeBtn:SetPoint("TOPRIGHT", -10, -10)
    closeBtn:SetText("X")
    closeBtn:SetNormalFontObject("GameFontNormalSmall")
    StyleCustomButton(closeBtn)
    closeBtn:SetScript("OnClick", function() frame:Hide() end)

    local minBtnOverlayManager = CreateFrame("Button", nil, frame, "BackdropTemplate")
    minBtnOverlayManager:SetSize(24, 24)
    minBtnOverlayManager:SetPoint("RIGHT", closeBtn, "LEFT", -6, 0)
    minBtnOverlayManager:SetText("_")
    minBtnOverlayManager:SetNormalFontObject("GameFontNormalSmall")
    if minBtnOverlayManager.GetFontString and minBtnOverlayManager:GetFontString() then
        minBtnOverlayManager:GetFontString():SetPoint("CENTER", 0, 1)
    end
    StyleCustomButton(minBtnOverlayManager)
    minBtnOverlayManager:SetScript("OnClick", function() frame:Hide() end)

    local statusText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    statusText:SetPoint("TOPRIGHT", minBtnOverlayManager, "BOTTOMRIGHT", 0, -6)
    statusText:SetTextColor(0.66, 0.74, 0.82)
    statusText:SetText("")
    frame.statusText = statusText

    local controlsBar = CreateFrame("Frame", nil, frame)
    controlsBar:SetPoint("TOPLEFT", 10, -(HEADER_H + 12))
    controlsBar:SetPoint("TOPRIGHT", -32, -(HEADER_H + 12))
    controlsBar:SetHeight(26)

    local btnEnableAll = CreateFrame("Button", nil, controlsBar, "UIPanelButtonTemplate")
    btnEnableAll:SetSize(94, 24)
    btnEnableAll:SetPoint("LEFT", 0, 0)
    btnEnableAll:SetText("Enable All")
    StyleCustomButton(btnEnableAll)

    local btnDisableAll = CreateFrame("Button", nil, controlsBar, "UIPanelButtonTemplate")
    btnDisableAll:SetSize(94, 24)
    btnDisableAll:SetPoint("LEFT", btnEnableAll, "RIGHT", 8, 0)
    btnDisableAll:SetText("Disable All")
    StyleCustomButton(btnDisableAll)

    local controlsHint = controlsBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    controlsHint:SetPoint("LEFT", btnDisableAll, "RIGHT", 12, 0)
    controlsHint:SetPoint("RIGHT", controlsBar, "RIGHT", -2, 0)
    controlsHint:SetJustifyH("RIGHT")
    controlsHint:SetText("Tip: right-click overlays for advanced options")
    controlsHint:SetTextColor(0.60, 0.68, 0.77)
    frame.controlsHint = controlsHint
    frame.btnEnableAll = btnEnableAll
    frame.btnDisableAll = btnDisableAll
    
    local FOOTER_H = 44
    local footerLine = frame:CreateTexture(nil, "ARTWORK")
    footerLine:SetTexture("Interface\\Buttons\\WHITE8X8")
    footerLine:SetPoint("BOTTOMLEFT", 10, FOOTER_H)
    footerLine:SetPoint("BOTTOMRIGHT", -10, FOOTER_H)
    footerLine:SetHeight(1)
    footerLine:SetVertexColor(UI_COLORS.border[1], UI_COLORS.border[2], UI_COLORS.border[3], 0.60)

    local pendingLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    pendingLabel:SetPoint("BOTTOMLEFT", 14, 14)
    pendingLabel:SetText("")
    pendingLabel:SetTextColor(1.0, 0.82, 0.2)
    frame.pendingLabel = pendingLabel

    local btnApplyReload = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    btnApplyReload:SetSize(140, 26)
    btnApplyReload:SetPoint("BOTTOMRIGHT", -14, 10)
    btnApplyReload:SetText("Apply & Reload")
    StyleCustomButton(btnApplyReload)
    btnApplyReload:Hide()
    btnApplyReload:SetScript("OnClick", function()
        ReloadUI()
    end)
    frame.btnApplyReload = btnApplyReload

    frame._pendingChanges = false

    local function MarkPending()
        frame._pendingChanges = true
        pendingLabel:SetText("Changes pending — click Apply & Reload")
        btnApplyReload:Show()
    end

    frame.MarkPending = MarkPending

    local listBg = frame:CreateTexture(nil, "BACKGROUND")
    listBg:SetTexture("Interface\\Buttons\\WHITE8X8")
    listBg:SetPoint("TOPLEFT", 10, -(HEADER_H + 44))
    listBg:SetPoint("BOTTOMRIGHT", -30, FOOTER_H + 4)
    listBg:SetVertexColor(0.03, 0.05, 0.08, 0.58)

    local scroll = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
    scroll:SetPoint("TOPLEFT", 10, -(HEADER_H + 44))
    scroll:SetPoint("BOTTOMRIGHT", -30, FOOTER_H + 4)
    
    local content = CreateFrame("Frame", nil, scroll)
    content:SetPoint("TOPLEFT", 0, 0)
    content:SetPoint("TOPRIGHT", 0, 0)
    content:SetHeight(1)
    scroll:SetScrollChild(content)
    
    frame.scroll = scroll
    frame.content = content

    local function SetAllOverlaysEnabled(enabled)
        for _, def in ipairs(OVERLAY_DEFINITIONS) do
            local path = def.settingPath
            local parts = {}
            for part in string.gmatch(path, "[^%.]+") do
                table.insert(parts, part)
            end
            local cur = MidnightUISettings
            for i = 1, #parts - 1 do
                if type(cur[parts[i]]) ~= "table" then cur[parts[i]] = {} end
                cur = cur[parts[i]]
            end
            cur[parts[#parts]] = enabled
        end
        frame:MarkPending()
        if frame.Refresh then frame:Refresh() end
    end

    btnEnableAll:SetScript("OnClick", function()
        SetAllOverlaysEnabled(true)
    end)
    btnDisableAll:SetScript("OnClick", function()
        SetAllOverlaysEnabled(false)
    end)

    frame.Refresh = function(self)
        for _, row in ipairs(OverlayManagerRows) do
            if row then
                row:Hide()
                row:SetParent(nil)
            end
        end
        wipe(OverlayManagerRows)

        local grouped = {}
        for _, grp in ipairs(OVERLAY_GROUPS) do
            grouped[grp.id] = {}
        end
        for _, def in ipairs(OVERLAY_DEFINITIONS) do
            local gid = def.group or "world"
            if not grouped[gid] then
                grouped[gid] = {}
            end
            grouped[gid][#grouped[gid] + 1] = def
        end

        local y = -4
        local totalEnabled = 0

        local function AddGroupHeader(labelText)
            local header = CreateFrame("Frame", nil, content)
            header:SetSize(408, 20)
            header:SetPoint("TOPLEFT", 4, y)

            local text = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            text:SetPoint("LEFT", 2, 0)
            text:SetText(labelText)
            text:SetTextColor(0.78, 0.85, 0.93)

            local line = header:CreateTexture(nil, "ARTWORK")
            line:SetTexture("Interface\\Buttons\\WHITE8X8")
            line:SetPoint("LEFT", text, "RIGHT", 8, 0)
            line:SetPoint("RIGHT", -2, 0)
            line:SetHeight(1)
            line:SetVertexColor(0.17, 0.24, 0.32, 0.82)

            header:Show()
            OverlayManagerRows[#OverlayManagerRows + 1] = header
            y = y - 22
        end

        local function AddOverlayRow(def)
            local enabled = GetOverlayEnabled(def)
            if enabled then
                totalEnabled = totalEnabled + 1
            end

            local row = CreateFrame("Frame", nil, content, "BackdropTemplate")
            row:SetSize(408, 32)
            row:SetPoint("TOPLEFT", 4, y)
            row:SetBackdrop({
                bgFile = "Interface\\Buttons\\WHITE8X8",
                edgeFile = "Interface\\Buttons\\WHITE8X8",
                edgeSize = 1,
            })
            row:SetBackdropColor(0.06, 0.09, 0.13, 0.88)
            row:SetBackdropBorderColor(0.15, 0.22, 0.30, 0.84)

            local status = row:CreateTexture(nil, "ARTWORK")
            status:SetSize(4, 20)
            status:SetPoint("LEFT", 5, 0)
            if enabled then
                status:SetColorTexture(UI_COLORS.enabled[1], UI_COLORS.enabled[2], UI_COLORS.enabled[3], 1)
            else
                status:SetColorTexture(UI_COLORS.disabled[1], UI_COLORS.disabled[2], UI_COLORS.disabled[3], 1)
            end

            local name = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            name:SetPoint("LEFT", 16, 0)
            name:SetText(def.name)
            name:SetTextColor(UI_COLORS.text[1], UI_COLORS.text[2], UI_COLORS.text[3])

            local state = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            state:SetPoint("RIGHT", -88, 0)
            state:SetText(enabled and "Enabled" or "Disabled")
            if enabled then
                state:SetTextColor(UI_COLORS.enabled[1], UI_COLORS.enabled[2], UI_COLORS.enabled[3])
            else
                state:SetTextColor(UI_COLORS.disabled[1], UI_COLORS.disabled[2], UI_COLORS.disabled[3])
            end

            local toggle = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
            toggle:SetSize(76, 22)
            toggle:SetPoint("RIGHT", -6, 0)
            toggle:SetText(enabled and "Disable" or "Enable")
            toggle:SetScript("OnClick", function()
                local newEnabled = not GetOverlayEnabled(def)
                if def.setEnabled then
                    def.setEnabled(newEnabled)
                else
                    local path = def.settingPath
                    local parts = {}
                    for part in string.gmatch(path, "[^%.]+") do
                        table.insert(parts, part)
                    end
                    local cur = MidnightUISettings
                    for i = 1, #parts - 1 do
                        if type(cur[parts[i]]) ~= "table" then cur[parts[i]] = {} end
                        cur = cur[parts[i]]
                    end
                    cur[parts[#parts]] = newEnabled
                end
                frame:MarkPending()
                self:Refresh()
            end)
            StyleCustomButton(toggle)

            local hover = row:CreateTexture(nil, "ARTWORK")
            hover:SetTexture("Interface\\Buttons\\WHITE8X8")
            hover:SetPoint("TOPLEFT", 1, -1)
            hover:SetPoint("BOTTOMRIGHT", -1, 1)
            hover:SetVertexColor(UI_COLORS.accent[1], UI_COLORS.accent[2], UI_COLORS.accent[3], 0.09)
            hover:Hide()

            row:EnableMouse(true)
            row:SetScript("OnEnter", function()
                hover:Show()
            end)
            row:SetScript("OnLeave", function()
                hover:Hide()
            end)

            row:Show()
            OverlayManagerRows[#OverlayManagerRows + 1] = row
            y = y - 36
        end

        for _, grp in ipairs(OVERLAY_GROUPS) do
            local defs = grouped[grp.id]
            if defs and #defs > 0 then
                AddGroupHeader(grp.label)
                for _, def in ipairs(defs) do
                    AddOverlayRow(def)
                end
                y = y - 4
            end
        end

        local total = #OVERLAY_DEFINITIONS
        if self.statusText then
            self.statusText:SetText(string.format("%d/%d enabled", totalEnabled, total))
        end

        local totalHeight = math.max(40, math.abs(y) + 8)
        content:SetHeight(totalHeight)
        content:SetSize(408, totalHeight)
        if self.scroll and self.scroll.SetVerticalScroll then
            self.scroll:SetVerticalScroll(0)
        end
    end
    
    OverlayManagerFrame = frame
end

local function ToggleOverlayManager()
    if not OverlayManagerFrame then
        CreateOverlayManagerWindow()
    end
    
    if OverlayManagerFrame:IsShown() then
        OverlayManagerFrame:Hide()
    else
        OverlayManagerFrame._pendingChanges = false
        if OverlayManagerFrame.pendingLabel then OverlayManagerFrame.pendingLabel:SetText("") end
        if OverlayManagerFrame.btnApplyReload then OverlayManagerFrame.btnApplyReload:Hide() end
        OverlayManagerFrame:Refresh()
        OverlayManagerFrame:Show()
        OverlayManagerFrame:Raise()
    end
end

_G.MidnightUI_ToggleOverlayManager = ToggleOverlayManager

local function RunOverlayStateDiagnostic()
    local Log = _G.MidnightUI_Diagnostics and _G.MidnightUI_Diagnostics.LogDebugSource
    if not Log then return end

    local OVERLAY_FRAMES = {
        { key = "PlayerFrame",      setting = "PlayerFrame.enabled",              custom = "MidnightUI_PlayerFrame",    blizzard = "PlayerFrame" },
        { key = "TargetFrame",      setting = "TargetFrame.enabled",              custom = "MidnightUI_TargetFrame",    blizzard = "TargetFrame", blizzardConditional = true },
        { key = "FocusFrame",       setting = "FocusFrame.enabled",               custom = "MidnightUI_FocusFrame",     blizzard = "FocusFrame", blizzardConditional = true },
        { key = "PetFrame",         setting = "PetFrame.enabled",                 custom = "MidnightUI_PetFrame" },
        { key = "TargetOfTarget",   setting = "TargetFrame.showTargetOfTarget",   custom = "MidnightUI_TargetOfTargetFrame" },
        { key = "RaidFrames",       setting = "RaidFrames.enabled",               custom = "MidnightUI_RaidAnchor",     blizzard = "CompactRaidFrameManager", blizzardConditional = true },
        { key = "PartyFrames",      setting = "PartyFrames.enabled",              custom = "MidnightUI_PartyAnchor" },
        { key = "MainTankFrames",   setting = "MainTankFrames.enabled",           custom = "MidnightUI_MainTankAnchor" },
        { key = "PlayerAuras",      setting = "PlayerFrame.auras.enabled",        custom = "BuffFrame",  blizzardOwned = true },
        { key = "PlayerDebuffs",    setting = "PlayerFrame.debuffs.enabled",      custom = "DebuffFrame", blizzardOwned = true },
        { key = "TargetAuras",      setting = "TargetFrame.auras.enabled" },
        { key = "TargetDebuffs",    setting = "TargetFrame.debuffs.enabled" },
        { key = "CastBar_player",   setting = "CastBars.player.enabled",          custom = "MidnightUI_CastBar_player" },
        { key = "CastBar_target",   setting = "CastBars.target.enabled",          custom = "MidnightUI_CastBar_target" },
        { key = "CastBar_focus",    setting = "CastBars.focus.enabled",           custom = "MidnightUI_CastBar_focus" },
        { key = "Messenger",        setting = "Messenger.enabled",                custom = "MyMessengerFrame",          blizzard = "GeneralDockManager" },
        { key = "Minimap",          setting = "Minimap.enabled",                  custom = "MidnightUI_MinimapCluster", blizzard = "MinimapCluster" },
        { key = "MinimapInfoPanel", setting = "Minimap.infoPanelEnabled",         custom = "MidnightUI_InfoBar" },
        { key = "XPBar",            setting = "XPBar.enabled",                    custom = "MidnightUI_StatusBarMain" },
        { key = "RepBar",           setting = "RepBar.enabled",                   custom = "MidnightUI_StatusBarSecondary" },
        { key = "InterfaceMenu",    setting = "InterfaceMenu.enabled",            custom = "MidnightUI_InterfaceMenuDock" },
        { key = "ConsumableBars",   setting = "ConsumableBars.enabled",           custom = "MidnightUI_ConsumableBars" },
        { key = "Inventory",        setting = "Inventory.enabled",                custom = "MidnightDockMain",          blizzard = "BagsBar" },
        { key = "ActionBar1",       setting = "ActionBars.bar1.enabled",          custom = "MyActionBarContainer1" },
        { key = "ActionBar2",       setting = "ActionBars.bar2.enabled",          custom = "MyActionBarContainer2" },
        { key = "ActionBar3",       setting = "ActionBars.bar3.enabled",          custom = "MyActionBarContainer3" },
        { key = "ActionBar4",       setting = "ActionBars.bar4.enabled",          custom = "MyActionBarContainer4" },
        { key = "ActionBar5",       setting = "ActionBars.bar5.enabled",          custom = "MyActionBarContainer5" },
        { key = "ActionBar6",       setting = "ActionBars.bar6.enabled",          custom = "MyActionBarContainer6" },
        { key = "ActionBar7",       setting = "ActionBars.bar7.enabled",          custom = "MyActionBarContainer7" },
        { key = "ActionBar8",       setting = "ActionBars.bar8.enabled",          custom = "MyActionBarContainer8" },
        { key = "PetBar",           setting = "PetBar.enabled",                   custom = "MyPetBarContainer" },
        { key = "StanceBar",        setting = "StanceBar.enabled",                custom = "MyStanceBarContainer" },
        { key = "DispelTracking",   setting = "Combat.dispelTrackingEnabled",     custom = "MidnightUI_DispelTrackingFrame" },
        { key = "SoulShards",       setting = "SoulShards.enabled" },
        { key = "ComboPoints",      setting = "ComboPoints.enabled" },
        { key = "DKRunes",          setting = "DKRunes.enabled" },
        { key = "MonkChi",          setting = "MonkChi.enabled" },
        { key = "HolyPower",        setting = "HolyPower.enabled" },
        { key = "ArcaneCharges",    setting = "ArcaneCharges.enabled" },
        { key = "EvokerEssence",    setting = "EvokerEssence.enabled" },
        { key = "DrawerSettingsIcon", setting = "Minimap.enabled",                custom = "MidnightUI_DrawerSettingsIcon" },
        { key = "LibDBIconDock",    setting = "Minimap.enabled",                  custom = "MidnightUI_LibDBIconDock" },
        { key = "StatusBarHost",    setting = "Minimap.enabled",                  custom = "MidnightUI_StatusBarHost" },
    }

    local function ReadSetting(path)
        local parts = {}
        for part in string.gmatch(path, "[^%.]+") do parts[#parts + 1] = part end
        local cur = MidnightUISettings
        for _, p in ipairs(parts) do
            if type(cur) ~= "table" then return nil end
            cur = cur[p]
        end
        return cur
    end

    local function FrameDetail(globalName)
        if not globalName then return "nil", "" end
        local f = _G[globalName]
        if not f then return "NOFRAME", "" end
        local vis = (f.IsShown and f:IsShown()) and "VISIBLE" or "HIDDEN"
        local alpha = (f.GetAlpha and string.format("%.2f", f:GetAlpha())) or "?"
        local mouse = (f.IsMouseEnabled and f:IsMouseEnabled()) and "mouse=Y" or "mouse=N"
        local parent = "parent=?"
        if f.GetParent then
            local p = f:GetParent()
            if p == UIParent then parent = "parent=UIParent"
            elseif p and p.GetName then parent = "parent=" .. (p:GetName() or "anon")
            elseif p then parent = "parent=anon"
            else parent = "parent=nil"
            end
        end
        local w, h = 0, 0
        if f.GetSize then w, h = f:GetSize() end
        local size = string.format("%.0fx%.0f", w or 0, h or 0)
        local detail = string.format("a=%s %s %s %s", alpha, mouse, parent, size)
        return vis, detail
    end

    Log("OverlayDiag", "=== OVERLAY STATE DIAGNOSTIC (COMPREHENSIVE) ===")
    local issues = 0
    for _, def in ipairs(OVERLAY_FRAMES) do
        local enabled = ReadSetting(def.setting)
        local isEnabled = (enabled ~= false)
        local customVis, customDetail = FrameDetail(def.custom)
        local blizzVis, blizzDetail = "", ""
        if def.blizzard then
            blizzVis, blizzDetail = FrameDetail(def.blizzard)
        end

        local problems = {}
        if not isEnabled and customVis == "VISIBLE" and not def.blizzardOwned then
            problems[#problems + 1] = "CUSTOM_VISIBLE"
        end
        if not isEnabled and customVis == "HIDDEN" and not def.blizzardOwned then
            local f = def.custom and _G[def.custom]
            if f and f.GetAlpha and f:GetAlpha() > 0 then
            end
        end
        if not isEnabled and def.blizzard and blizzVis == "HIDDEN" and not def.blizzardConditional then
            local f = _G[def.blizzard]
            local alpha = f and f.GetAlpha and f:GetAlpha() or 1
            if alpha < 0.01 then
                problems[#problems + 1] = "BLIZZARD_HIDDEN(alpha=0)"
            else
                problems[#problems + 1] = "BLIZZARD_HIDDEN"
            end
        end
        if not isEnabled and def.blizzard then
            local f = _G[def.blizzard]
            if f and f.GetAlpha and f:GetAlpha() < 0.01 and f.IsShown and f:IsShown() then
                problems[#problems + 1] = "BLIZZARD_ALPHA_ZERO"
            end
        end
        if not isEnabled and def.blizzard then
            local f = _G[def.blizzard]
            if f and f.IsMouseEnabled and not f:IsMouseEnabled() then
                problems[#problems + 1] = "BLIZZARD_MOUSE_DISABLED"
            end
        end
        if not isEnabled and def.blizzard then
            local f = _G[def.blizzard]
            if f and f.GetParent then
                local p = f:GetParent()
                if p and p ~= UIParent and p ~= _G.MinimapCluster then
                    local pName = (p.GetName and p:GetName()) or "anon"
                    problems[#problems + 1] = "BLIZZARD_REPARENTED(" .. pName .. ")"
                end
            end
        end

        local status = #problems > 0 and ("BUG:" .. table.concat(problems, "+")) or "OK"
        if #problems > 0 then issues = issues + #problems end
        if isEnabled and customVis == "HIDDEN" and not def.blizzardOwned then
            status = status == "OK" and "INFO:CONDITIONAL_HIDE" or status
        end

        local msg = string.format("%-22s en=%-5s", def.key, tostring(isEnabled))
        if def.custom then
            msg = msg .. string.format(" C:%-7s [%s]", customVis, customDetail)
        end
        if def.blizzard then
            msg = msg .. string.format(" B:%-7s [%s]", blizzVis, blizzDetail)
        end
        msg = msg .. " " .. status
        Log("OverlayDiag", msg)
    end
    Log("OverlayDiag", "=== DONE: " .. issues .. " issues found ===")
end


local gearBtn = CreateFrame("Button", nil, MoveHUD)
gearBtn:SetSize(26, 26)
gearBtn:SetPoint("TOPRIGHT", -38, -8)

local gearBg = gearBtn:CreateTexture(nil, "BACKGROUND")
gearBg:SetTexture("Interface\\Buttons\\WHITE8X8")
gearBg:SetAllPoints()
gearBg:SetVertexColor(0.08, 0.13, 0.20, 0.86)

local gearNormal = gearBtn:CreateTexture(nil, "ARTWORK")
gearNormal:SetSize(18, 18)
gearNormal:SetPoint("CENTER", 0, 0)
gearNormal:SetTexture("Interface\\Icons\\INV_Misc_Gear_01")
gearNormal:SetVertexColor(0.63, 0.79, 0.92, 1.0)
gearBtn:SetNormalTexture(gearNormal)

local gearHighlight = gearBtn:CreateTexture(nil, "HIGHLIGHT")
gearHighlight:SetSize(20, 20)
gearHighlight:SetPoint("CENTER", 0, 0)
gearHighlight:SetTexture("Interface\\Icons\\INV_Misc_Gear_01")
gearHighlight:SetVertexColor(0.84, 0.93, 1.0, 0.95)
gearHighlight:SetBlendMode("ADD")
gearBtn:SetHighlightTexture(gearHighlight)

local gearPushed = gearBtn:CreateTexture(nil, "ARTWORK")
gearPushed:SetSize(16, 16)
gearPushed:SetPoint("CENTER", 1, -1)
gearPushed:SetTexture("Interface\\Icons\\INV_Misc_Gear_01")
gearPushed:SetVertexColor(0.47, 0.66, 0.82, 1.0)
gearBtn:SetPushedTexture(gearPushed)

gearBtn:SetScript("OnEnter", function(self)
    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    GameTooltip:SetText("Overlay Manager", 1, 1, 1)
    GameTooltip:AddLine("Click to manage overlay visibility by section.", 0.9, 0.9, 0.9, true)
    GameTooltip:AddLine("Includes quick Enable All / Disable All actions.", 0.8, 0.84, 0.90, true)
    GameTooltip:Show()
end)
gearBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
gearBtn:SetScript("OnClick", ToggleOverlayManager)

local leftHeader = MoveHUD:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
leftHeader:SetPoint("TOP", MoveHUD, "TOP", -122, -(MOVEHUD_HEADER_H + 28))
leftHeader:SetText("Left Drag")
leftHeader:SetTextColor(0.84, 0.90, 0.98)
leftHeader:SetShadowColor(0, 0, 0, 0.7)
leftHeader:SetShadowOffset(1, -1)
leftHeader:SetWidth(180)
leftHeader:SetJustifyH("CENTER")

local leftBody = MoveHUD:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
leftBody:SetPoint("TOP", leftHeader, "BOTTOM", 0, -10)
leftBody:SetText("Move frame overlays")
leftBody:SetTextColor(0.77, 0.84, 0.92)
leftBody:SetWidth(180)
leftBody:SetJustifyH("CENTER")

local rightHeader = MoveHUD:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
rightHeader:SetPoint("TOP", MoveHUD, "TOP", 122, -(MOVEHUD_HEADER_H + 28))
rightHeader:SetText("Right Click")
rightHeader:SetTextColor(0.84, 0.90, 0.98)
rightHeader:SetShadowColor(0, 0, 0, 0.7)
rightHeader:SetShadowOffset(1, -1)
rightHeader:SetWidth(180)
rightHeader:SetJustifyH("CENTER")

local rightBody = MoveHUD:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
rightBody:SetPoint("TOP", rightHeader, "BOTTOM", 0, -10)
rightBody:SetText("Open per-overlay options")
rightBody:SetTextColor(0.77, 0.84, 0.92)
rightBody:SetWidth(180)
rightBody:SetJustifyH("CENTER")

local moveHint = MoveHUD:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
moveHint:SetPoint("TOPLEFT", leftInstructionBg, "BOTTOMLEFT", 0, -6)
moveHint:SetPoint("TOPRIGHT", rightInstructionBg, "BOTTOMRIGHT", 0, -6)
moveHint:SetText("Use the gear icon to quickly enable or disable overlays.")
moveHint:SetTextColor(0.55, 0.62, 0.70)
moveHint:SetJustifyH("CENTER")

local toolStrip = CreateFrame("Frame", nil, MoveHUD, "BackdropTemplate")
toolStrip:SetHeight(52)
toolStrip:SetPoint("BOTTOMLEFT", MoveHUD, "BOTTOMLEFT", 1, 44)
toolStrip:SetPoint("BOTTOMRIGHT", MoveHUD, "BOTTOMRIGHT", -1, 44)
toolStrip:SetBackdrop({
    bgFile = "Interface\\Buttons\\WHITE8X8",
    edgeFile = "Interface\\Buttons\\WHITE8X8",
    edgeSize = 1,
    insets = { left = 0, right = 0, top = 1, bottom = 0 },
})
toolStrip:SetBackdropColor(0.05, 0.08, 0.12, 0.70)
toolStrip:SetBackdropBorderColor(0.18, 0.26, 0.35, 0.50)

local toolDivider = toolStrip:CreateTexture(nil, "ARTWORK")
toolDivider:SetTexture("Interface\\Buttons\\WHITE8X8")
toolDivider:SetSize(1, 36)
toolDivider:SetPoint("CENTER", toolStrip, "CENTER", 0, 0)
toolDivider:SetVertexColor(0.20, 0.27, 0.35, 0.60)

local snapIcon = toolStrip:CreateTexture(nil, "ARTWORK")
snapIcon:SetTexture("Interface\\Buttons\\WHITE8X8")
snapIcon:SetSize(3, 3)
snapIcon:SetPoint("LEFT", toolStrip, "LEFT", 18, 6)
snapIcon:SetVertexColor(0.34, 0.78, 1.00, 0.80)

local snapTitle = toolStrip:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
snapTitle:SetPoint("LEFT", snapIcon, "RIGHT", 6, 0)
snapTitle:SetText("Smart Snap")
snapTitle:SetTextColor(0.88, 0.92, 0.96)

local snapDesc = toolStrip:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
snapDesc:SetPoint("TOPLEFT", snapIcon, "BOTTOMLEFT", -2, -4)
snapDesc:SetText("Align to nearby frames & center")
snapDesc:SetTextColor(0.52, 0.58, 0.66)

local chkSnap = CreateFrame("CheckButton", nil, toolStrip, "UICheckButtonTemplate")
chkSnap:SetSize(20, 20)
chkSnap:SetPoint("LEFT", toolStrip, "CENTER", -32, 0)
chkSnap:SetChecked(not (MidnightUISettings and MidnightUISettings.General
    and MidnightUISettings.General.snapToFrames == false))
chkSnap:SetScript("OnClick", function(self)
    if not MidnightUISettings.General then MidnightUISettings.General = {} end
    MidnightUISettings.General.snapToFrames = (self:GetChecked() == true)
end)

local scaleIcon = toolStrip:CreateTexture(nil, "ARTWORK")
scaleIcon:SetTexture("Interface\\Buttons\\WHITE8X8")
scaleIcon:SetSize(3, 3)
scaleIcon:SetPoint("LEFT", toolDivider, "RIGHT", 14, 6)
scaleIcon:SetVertexColor(0.34, 0.78, 1.00, 0.80)

local scaleTitle = toolStrip:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
scaleTitle:SetPoint("LEFT", scaleIcon, "RIGHT", 6, 0)
scaleTitle:SetText("UI Scale")
scaleTitle:SetTextColor(0.88, 0.92, 0.96)

local scaleValue = toolStrip:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
scaleValue:SetPoint("RIGHT", toolStrip, "RIGHT", -16, 6)
scaleValue:SetTextColor(0.34, 0.78, 1.00)

local scaleSlider = CreateFrame("Slider", "MidnightUI_MoveHUDScaleSlider", toolStrip, "OptionsSliderTemplate")
scaleSlider:SetPoint("BOTTOMLEFT", toolDivider, "BOTTOMRIGHT", 14, 6)
scaleSlider:SetPoint("BOTTOMRIGHT", toolStrip, "BOTTOMRIGHT", -16, 6)
scaleSlider:SetHeight(12)
scaleSlider:SetMinMaxValues(50, 150)
scaleSlider:SetValueStep(5)
scaleSlider:SetObeyStepOnDrag(true)
if _G[scaleSlider:GetName().."Low"]  then _G[scaleSlider:GetName().."Low"]:Hide()  end
if _G[scaleSlider:GetName().."High"] then _G[scaleSlider:GetName().."High"]:Hide() end
if _G[scaleSlider:GetName().."Text"] then _G[scaleSlider:GetName().."Text"]:Hide() end
local initScale = (MidnightUISettings and MidnightUISettings.General
    and MidnightUISettings.General.globalScale) or 100
scaleSlider:SetValue(initScale)
scaleValue:SetText(initScale .. "%")

scaleSlider:SetScript("OnValueChanged", function(self, v)
    v = math.floor(v + 0.5)
    scaleValue:SetText(v .. "%")
    if _G.MidnightUI_ApplyGlobalScale then
        _G.MidnightUI_ApplyGlobalScale(v)
    end
end)

local hb = CreateFrame("Button", nil, MoveHUD, "UIPanelButtonTemplate")
hb:SetPoint("BOTTOMRIGHT", MoveHUD, "BOTTOM", -4, 12)
hb:SetSize(200, 30)
hb:SetText("Lock & Save")
hb:SetScript("OnClick", function()
    if _G.MidnightUI_MovementModeFromWizard then
        _G.MidnightUI_MovementModeFromWizard = false
        if _G.MidnightUI_ReturnToWelcomeWizard then
            _G.MidnightUI_ReturnToWelcomeWizard()
        end
        return
    end
    MidnightUISettings.Messenger.locked = true; ApplyMessengerSettings(); GridFrame:Hide(); MoveHUD:Hide()
    if _G.MidnightUI_MoveHUDRestore then _G.MidnightUI_MoveHUDRestore:Hide() end
    local cat = M.SettingsCategory
    if cat and Settings and Settings.OpenToCategory then Settings.OpenToCategory(cat.ID)
    elseif SettingsPanel then SettingsPanel:Show() elseif InterfaceOptionsFrame then InterfaceOptionsFrame:Show() end
end)
StyleCustomButton(hb)
if hb.GetFontString and hb:GetFontString() then hb:GetFontString():SetPoint("CENTER", 0, 0) end

local resetBtn = CreateFrame("Button", nil, MoveHUD, "UIPanelButtonTemplate")
resetBtn:SetPoint("BOTTOMLEFT", MoveHUD, "BOTTOM", 4, 12)
resetBtn:SetSize(200, 30)
resetBtn:SetText("Reset All Defaults")
StyleCustomButton(resetBtn)
if resetBtn.GetFontString and resetBtn:GetFontString() then resetBtn:GetFontString():SetPoint("CENTER", 0, 0) end
resetBtn:SetScript("OnClick", function()
    if InCombatLockdown and InCombatLockdown() then
        if UIErrorsFrame then UIErrorsFrame:AddMessage("MidnightUI: Can't reset in combat.", 1, 0.2, 0.2) end
        return
    end
    if _G.MidnightUI_ResetOverlayDefaults then
        _G.MidnightUI_ResetOverlayDefaults(true)
    else
        if MidnightUISettings then
            MidnightUISettings.PendingReset = true
            MidnightUISettings.PendingResetReopenWelcome = true
        end
        if type(ReloadUI) == "function" then ReloadUI() end
    end
end)

local minBtn = CreateFrame("Button", nil, MoveHUD, "UIPanelButtonTemplate")
minBtn:SetSize(24, 24)
minBtn:SetPoint("TOPRIGHT", -8, -8)
minBtn:SetText("-")
StyleCustomButton(minBtn)
if minBtn.GetFontString and minBtn:GetFontString() then minBtn:GetFontString():SetPoint("CENTER", 0, 0) end
minBtn:SetScript("OnClick", function()
    MoveHUD:Hide()
    if _G.MidnightUI_MoveHUDRestore then _G.MidnightUI_MoveHUDRestore:Show() end
end)
minBtn:SetScript("OnEnter", function(self)
    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    GameTooltip:SetText("Minimize", 1, 1, 1)
    GameTooltip:AddLine("Hide this window. Click \"Show Movement Overlay\" to bring it back.", 0.9, 0.9, 0.9, true)
    GameTooltip:Show()
end)
minBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)

local MoveHUDRestore = CreateFrame("Button", "MidnightUI_MoveHUDRestore", UIParent, "UIPanelButtonTemplate")
MoveHUDRestore:SetSize(192, 28)
MoveHUDRestore:SetPoint("TOP", 0, -50)
MoveHUDRestore:SetText("Show Movement Overlay")
MoveHUDRestore:Hide()
StyleCustomButton(MoveHUDRestore)
if MoveHUDRestore.GetFontString and MoveHUDRestore:GetFontString() then
    MoveHUDRestore:GetFontString():SetPoint("CENTER", 0, 0)
end
MoveHUDRestore:SetScript("OnClick", function()
    MoveHUD:Show()
    MoveHUDRestore:Hide()
end)

-- ============================================================
-- SECTION: Module Exports + Public API
-- Assigns all public functions to the M (MidnightUI_Settings) table
-- so other modules can call them via _G.MidnightUI_Settings.Apply*().
-- Also assigns critical _G globals for cross-module use.
-- ============================================================
M.DEFAULTS = DEFAULTS
M.SettingsCategory = MidnightSettingsCategory
M.GridFrame = GridFrame
M.MoveHUD = MoveHUD
M.DrawGrid = DrawGrid
M.ApplyMessengerSettings = ApplyMessengerSettings
M.ApplyPlayerSettings = ApplyPlayerSettings
M.ApplyTargetSettings = ApplyTargetSettings
M.ApplyFocusSettings = ApplyFocusSettings
M.ApplyPetSettings = ApplyPetSettings
M.ApplyActionBarSettings = ApplyActionBarSettings
M.ApplyActionBarSettingsImmediate = ApplyActionBarSettingsImmediate
M.ApplyCastBarSettings = ApplyCastBarSettings
M.ApplyMinimapSettings = ApplyMinimapSettings
  M.ApplyPartyFramesSettings = ApplyPartyFramesSettings
  M.ApplyRaidFramesSettings = ApplyRaidFramesSettings
M.ApplyUnitFrameBarStyle = ApplyUnitFrameBarStyle
M.ApplySharedUnitFrameAppearance = ApplySharedUnitFrameAppearance
M.ApplyNameplateSettings = ApplyNameplateSettings
M.ApplyGlobalTheme = ApplyGlobalTheme
M.EnsureCastBarsSettings = EnsureCastBarsSettings
M.EnsureNameplateSettings = EnsureNameplateSettings
M.ApplyQuestObjectiveVisibility = ApplyQuestObjectiveVisibility
M.InitializeSettings = InitializeSettings



-- ============================================================
-- SECTION: Slash Commands + Event System + Settings Getters/Setters
-- Registers the /midnight and /mui slash commands with sub-command
-- dispatch for: questdebug, tainttrace, debugtest, debug actions,
-- consumable controls, and the default (open settings panel).
-- Also registers the loader frame that drives InitializeSettings and
-- all Apply* cascades on ADDON_LOADED and PLAYER_ENTERING_WORLD.
-- Provides four global settings accessor functions.
--
-- EVENTS:
--   ADDON_LOADED       — InitializeSettings() + Blizzard category register
--   PLAYER_ENTERING_WORLD — full Apply* cascade
--
-- FUNCTIONS:
--   MidnightUI_GetActionBarSettings()           — returns ActionBars table
--   MidnightUI_SetActionBarSetting(key, tbl)    — merges tbl into bar key
--   MidnightUI_GetMessengerSettings()           — returns Messenger table
--   MidnightUI_SetMessengerSetting(key, value)  — sets one Messenger key
-- ============================================================
SLASH_MIDNIGHTUI1 = "/midnight"
SLASH_MIDNIGHTUI2 = "/mui"
SlashCmdList["MIDNIGHTUI"] = function(msg)
    msg = (msg and msg:lower() or ""):gsub("^%s+", ""):gsub("%s+$", "")
    if msg == "questdebug" then
        if _G.MidnightUI_QuestVisuals_Debug then
            _G.MidnightUI_QuestVisuals_Debug()
        else
            print("MidnightUI QuestVisuals Debug not available.")
        end
        return
    end
    if msg == "tainttrace" or msg == "ttrace" then
        if not MidnightUISettings.General then MidnightUISettings.General = {} end
        MidnightUISettings.General.taintTrace = not (MidnightUISettings.General.taintTrace == true)
        local state = MidnightUISettings.General.taintTrace and "ON" or "OFF"
        if C_Timer and C_Timer.After then
            C_Timer.After(0, function()
                if _G.MidnightUI_Diagnostics and _G.MidnightUI_Diagnostics.LogDebugSource
                    and _G.MidnightUI_Diagnostics.IsEnabled
                    and _G.MidnightUI_Diagnostics.IsEnabled() then
                    _G.MidnightUI_Diagnostics.LogDebugSource("SlashCmd", "Taint trace " .. state)
                elseif _G.MidnightUI_Debug then
                    _G.MidnightUI_Debug("[SlashCmd] Taint trace " .. state)
                end
            end)
        end
        return
    end
    if msg == "debugtest" then
        if _G.MidnightUI_ForceDebugMessage then
            _G.MidnightUI_ForceDebugMessage("Debug tab test")
            if _G.MyMessenger_RefreshDisplay then _G.MyMessenger_RefreshDisplay() end
            if _G.UpdateTabLayout then _G.UpdateTabLayout() end
        else
            print("MidnightUI_ForceDebugMessage missing.")
        end
        return
    end
    local debugAction = msg:match("^debug%s+(%S+)$")
    if debugAction then
        local function SlashOut(text)
            if _G.DEFAULT_CHAT_FRAME and _G.DEFAULT_CHAT_FRAME.AddMessage then
                _G.DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffMidnightUI:|r " .. tostring(text))
            else
                print("MidnightUI: " .. tostring(text))
            end
        end
        if not MidnightUISettings.General then MidnightUISettings.General = {} end
        local current = (MidnightUISettings.General.diagnosticsEnabled ~= false)
        local nextState = current
        if debugAction == "on" or debugAction == "enable" then
            nextState = true
        elseif debugAction == "off" or debugAction == "disable" then
            nextState = false
        elseif debugAction == "toggle" then
            nextState = not current
        elseif debugAction == "status" then
            SlashOut("Diagnostics debug is " .. (current and "ON" or "OFF") .. ". Use /mui debug on|off|toggle.")
            return
        else
            SlashOut("Usage: /mui debug on | /mui debug off | /mui debug toggle | /mui debug status")
            return
        end
        MidnightUISettings.General.diagnosticsEnabled = nextState
        local statusText = nextState and "ON" or "OFF"
        if nextState and _G.MidnightUI_Diagnostics and _G.MidnightUI_Diagnostics.LogDebugSource then
            _G.MidnightUI_Diagnostics.LogDebugSource("SlashCmd", "Diagnostics debug toggled " .. statusText .. " via /mui")
        end
        SlashOut("Diagnostics debug is now " .. statusText .. ".")
        return
    end
    local cdebugAction = msg:match("^cdebug%s*(%S*)$") or msg:match("^consumabledebug%s*(%S*)$")
    if cdebugAction then
        local function SlashOut(text)
            if _G.DEFAULT_CHAT_FRAME and _G.DEFAULT_CHAT_FRAME.AddMessage then
                _G.DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffMidnightUI:|r " .. tostring(text))
            else
                print("MidnightUI: " .. tostring(text))
            end
        end
        if not MidnightUISettings.ConsumableBars then MidnightUISettings.ConsumableBars = {} end
        local current = (MidnightUISettings.ConsumableBars.debug == true)
        local verbose = (MidnightUISettings.ConsumableBars.debugVerbose == true)
        local action = cdebugAction
        if action == "" then action = "toggle" end
        if action == "verbose" or action == "v" then
            MidnightUISettings.ConsumableBars.debugVerbose = not verbose
            SlashOut("Consumables debug verbose is now " .. (MidnightUISettings.ConsumableBars.debugVerbose and "ON" or "OFF") .. ".")
            return
        end
        local nextState = current
        if action == "on" or action == "enable" then
            nextState = true
        elseif action == "off" or action == "disable" then
            nextState = false
        elseif action == "toggle" then
            nextState = not current
        elseif action == "status" then
            SlashOut("Consumables debug is " .. (current and "ON" or "OFF") .. ". Verbose=" .. (verbose and "ON" or "OFF") .. ". Use /mui cdebug on|off|toggle|status|verbose.")
            return
        else
            SlashOut("Usage: /mui cdebug on | /mui cdebug off | /mui cdebug toggle | /mui cdebug status | /mui cdebug verbose")
            return
        end
        MidnightUISettings.ConsumableBars.debug = nextState
        local statusText = nextState and "ON" or "OFF"
        if nextState and _G.MidnightUI_Diagnostics and _G.MidnightUI_Diagnostics.LogDebugSource then
            _G.MidnightUI_Diagnostics.LogDebugSource("SlashCmd", "Consumables debug toggled " .. statusText .. " via /mui")
        end
        SlashOut("Consumables debug is now " .. statusText .. ".")
        return
    end
    if msg == "debug" then
        if _G.MidnightUI_Diagnostics and _G.MidnightUI_Diagnostics.Open then
            local ok = _G.MidnightUI_Diagnostics.Open()
            if ok == false and _G.MidnightUI_ShowDiagnosticsStatus then
                _G.MidnightUI_ShowDiagnosticsStatus("Diagnostics failed to open")
            end
        else
            if _G.MidnightUI_ShowDiagnosticsStatus then
                _G.MidnightUI_ShowDiagnosticsStatus("Diagnostics API missing")
            else
                print("MidnightUI Diagnostics not available.")
            end
        end
        return
    end
    if msg == "consumableshow" or msg == "cshow" then
        if _G.MidnightUI_ForceShowConsumableOverlay then
            _G.MidnightUI_ForceShowConsumableOverlay()
            if _G.MidnightUI_LogDebug then _G.MidnightUI_LogDebug("Consumables overlay forced show") end
        else
            print("Consumables overlay not available.")
        end
        return
    end
    if msg == "cdump" or msg == "consumabledump" then
        if _G.MidnightUI_Consumables_DumpAuras then
            _G.MidnightUI_Consumables_DumpAuras(60)
        else
            print("Consumables dump not available.")
        end
        return
    end
    if msg == "cstatus" or msg == "consumablestatus" then
        if _G.MidnightUI_Consumables_DebugStatus then
            _G.MidnightUI_Consumables_DebugStatus()
        else
            print("Consumables debug status not available.")
        end
        return
    end
    if msg == "consumablesreset" or msg == "creset" then
        if _G.MidnightUI_ResetConsumablePosition then
            _G.MidnightUI_ResetConsumablePosition()
            if _G.MidnightUI_LogDebug then _G.MidnightUI_LogDebug("Consumables position reset") end
        else
            print("Consumables overlay not available.")
        end
        return
    end
    local cat = M.SettingsCategory
    if Settings and Settings.OpenToCategory and cat then
        Settings.OpenToCategory(cat.ID)
    elseif M.ConfigFrame and InterfaceOptionsFrame_OpenToCategory then
        InterfaceOptionsFrame_OpenToCategory(M.ConfigFrame)
        InterfaceOptionsFrame_OpenToCategory(M.ConfigFrame)
    end
end


function MidnightUI_GetActionBarSettings()
    return MidnightUISettings.ActionBars
end

function MidnightUI_SetActionBarSetting(barKey, settings)
    if MidnightUISettings.ActionBars[barKey] then
        for k, v in pairs(settings) do
            MidnightUISettings.ActionBars[barKey][k] = v
        end
    end
end

function MidnightUI_GetMessengerSettings()
    return MidnightUISettings.Messenger
end

function MidnightUI_SetMessengerSetting(key, value)
    if not MidnightUISettings or not MidnightUISettings.Messenger then return end
    MidnightUISettings.Messenger[key] = value
end

local loader = CreateFrame("Frame")
loader:RegisterEvent("ADDON_LOADED")
loader:RegisterEvent("PLAYER_ENTERING_WORLD")
local settingsApplied = false
loader:SetScript("OnEvent", function(s, e, a)
    if e == "ADDON_LOADED" and a == "MidnightUI" then 
        InitializeSettings()
        MidnightUISettings.Messenger.locked = true
        return
    end
    if e == "PLAYER_ENTERING_WORLD" and not settingsApplied then
        settingsApplied = true
        local wasReset = MidnightUISettings._wasReset
        MidnightUISettings._wasReset = nil
        C_Timer.After(0.2, function()
            ApplyMessengerSettings()
            ApplyPlayerSettings()
            ApplyTargetSettings()
            ApplyFocusSettings()
            ApplyPetSettings()
            ApplyCastBarSettings()
            if _G.MidnightUI_ApplyConsumableBarsSettings then _G.MidnightUI_ApplyConsumableBarsSettings() end
            if _G.MidnightUI_ApplyCooldownManagerSettings then _G.MidnightUI_ApplyCooldownManagerSettings() end
            ApplyNameplateSettings()
            if wasReset then
                if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
                if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
                if _G.MidnightUI_ApplyTargetAuraSettings then _G.MidnightUI_ApplyTargetAuraSettings() end
                if _G.MidnightUI_ApplyTargetDebuffSettings then _G.MidnightUI_ApplyTargetDebuffSettings() end
                if _G.MyActionBars_ResetToDefaults then _G.MyActionBars_ResetToDefaults() end
            end
        end)
        if wasReset then
            C_Timer.After(1.5, function()
                if _G.MidnightUI_ApplyTargetAuraSettings then _G.MidnightUI_ApplyTargetAuraSettings() end
                if _G.MidnightUI_ApplyTargetDebuffSettings then _G.MidnightUI_ApplyTargetDebuffSettings() end
                if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
                if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
                if _G.MyActionBars_ReloadSettings then _G.MyActionBars_ReloadSettings() end
                local msgFrame = _G.MyMessengerFrame
                local msgPos = MidnightUISettings and MidnightUISettings.Messenger and MidnightUISettings.Messenger.position
                if msgFrame and msgPos and #msgPos >= 4 then
                    msgFrame:ClearAllPoints()
                    msgFrame:SetPoint(msgPos[1], UIParent, msgPos[2], msgPos[3], msgPos[4])
                end
                local raidPos = MidnightUISettings and MidnightUISettings.RaidFrames and MidnightUISettings.RaidFrames.position
                local raidAnchor = _G.MidnightUI_RaidAnchor
                if raidAnchor and raidPos and #raidPos >= 4 then
                    raidAnchor:ClearAllPoints()
                    if raidPos[5] then
                        raidAnchor:SetPoint(raidPos[1], UIParent, raidPos[3], raidPos[4], raidPos[5])
                    else
                        raidAnchor:SetPoint(raidPos[1], UIParent, raidPos[2], raidPos[3], raidPos[4])
                    end
                end
            end)
        end
    end
end)
