-- ============================================================================
-- ARENACORE MASTER FILE - CHUNK 1: INITIALIZATION & CONSTANTS
-- Sections 1-2: Initialization & Database System
-- ============================================================================

-- ============================================================================
-- 1. INITIALIZATION & CONSTANTS
-- ============================================================================

local AC = _G.ArenaCore or {}
_G.ArenaCore = AC

-- CRITICAL FIX: Use FORWARD slashes (WoW prefers these over backslashes!)
AC.FONT_PATH = "Interface/AddOns/ArenaCore/Media/Fonts/arenacore.ttf"
AC.CUSTOM_FONT = "Fonts\\\\FRIZQT__.TTF"

-- ============================================================================
-- PHASE 1.2: CENTRAL TICKER MANAGEMENT SYSTEM
-- Prevents memory leaks from orphaned tickers
-- ============================================================================

AC.activeTickers = AC.activeTickers or {}

-- Register a ticker for automatic cleanup
function AC:RegisterTicker(ticker, name)
    if not ticker then return end
    if not name then
        name = "ticker_" .. tostring(ticker)
    end
    
    self.activeTickers[name] = ticker
    return ticker
end

-- Cancel a specific ticker by name
function AC:CancelTicker(name)
    local ticker = self.activeTickers[name]
    if ticker and ticker.Cancel then
        ticker:Cancel()
    end
    self.activeTickers[name] = nil
end

-- Cancel all registered tickers
function AC:CancelAllTickers()
    local count = 0
    for name, ticker in pairs(self.activeTickers) do
        if ticker and ticker.Cancel then
            ticker:Cancel()
            count = count + 1
        end
    end
    wipe(self.activeTickers)
    if count > 0 then
        print("|cffFF0000[TickerRegistry]|r Cancelled " .. count .. " active tickers")
    end
end

-- Wrapper function for easy ticker creation with auto-registration
function AC:CreateTicker(interval, callback, name)
    local ticker = C_Timer.NewTicker(interval, callback)
    self:RegisterTicker(ticker, name)
    return ticker
end

-- Enhanced TickerChecker integration
function AC:CheckActiveTickers()
    local count = 0
    for name, ticker in pairs(self.activeTickers) do
        if ticker then
            count = count + 1
            print("|cff00FFFF[TickerCheck]|r Active: " .. name)
        end
    end
    
    if count == 0 then
        print("|cffFFAA00[TickerCheck]|r No active tickers found.")
        print("|cffFFAA00[TickerCheck]|r Tickers are created when:")
        print("  - Test mode is enabled (/ac test)")
        print("  - You enter an arena match")
        print("  - Certain features are active")
    else
        print("|cff00FF00[TickerCheck]|r Total active tickers: " .. count)
    end
    
    return count
end

-- ============================================================================
-- PHASE 2.3: EVENT FRAME CLEANUP SYSTEM
-- Ensures proper cleanup when features disabled or on logout
-- ============================================================================

AC.eventFrames = AC.eventFrames or {}

-- Register an event frame for automatic cleanup
function AC:RegisterEventFrame(frame, name)
    if not frame then return end
    if not name then
        name = "eventframe_" .. tostring(frame)
    end
    
    self.eventFrames[name] = frame
    return frame
end

-- Unregister a specific event frame by name
function AC:UnregisterEventFrame(name)
    local frame = self.eventFrames[name]
    if frame and frame.UnregisterAllEvents then
        frame:UnregisterAllEvents()
    end
    self.eventFrames[name] = nil
end

-- Unregister all tracked event frames
function AC:UnregisterAllEvents()
    local count = 0
    for name, frame in pairs(self.eventFrames) do
        if frame and frame.UnregisterAllEvents then
            frame:UnregisterAllEvents()
            count = count + 1
        end
    end
    wipe(self.eventFrames)
    if count > 0 then
        print("|cffFF0000[EventCleanup]|r Unregistered " .. count .. " event frames")
    end
end

-- Check active event frames (debug command)
function AC:CheckActiveEventFrames()
    local count = 0
    for name, frame in pairs(self.eventFrames) do
        if frame then
            count = count + 1
            print("|cff00FFFF[EventCheck]|r Active: " .. name)
        end
    end
    
    if count == 0 then
        print("|cffFFAA00[EventCheck]|r No active event frames found.")
    else
        print("|cff00FF00[EventCheck]|r Total active event frames: " .. count)
    end
    
    return count
end

-- Comprehensive cleanup on logout or disable
function AC:Cleanup()
    print("|cffFFAA00[ArenaCore]|r Performing cleanup...")
    
    -- Cancel all tickers
    self:CancelAllTickers()
    
    -- Unregister all events
    self:UnregisterAllEvents()
    
    print("|cff00FF00[ArenaCore]|r Cleanup complete!")
end

-- ============================================================================
-- PHASE 3.2: DATABASE SETTINGS CACHE
-- Eliminates repeated table lookups for 2-5 FPS gain
-- ============================================================================

AC.cachedSettings = AC.cachedSettings or {}

-- Refresh settings cache from database
function AC:RefreshSettingsCache()
    if not self.DB or not self.DB.profile then
        self:EnsureDB()
    end
    
    local db = self.DB.profile
    
    -- Cache frequently accessed settings
    self.cachedSettings = {
        -- Arena Frames
        arenaFrames = {
            width = db.arenaFrames and db.arenaFrames.sizing and db.arenaFrames.sizing.width or 200,
            height = db.arenaFrames and db.arenaFrames.sizing and db.arenaFrames.sizing.height or 50,
            spacing = db.arenaFrames and db.arenaFrames.positioning and db.arenaFrames.positioning.spacing or 21,
            horizontal = db.arenaFrames and db.arenaFrames.positioning and db.arenaFrames.positioning.horizontal or 0,
            vertical = db.arenaFrames and db.arenaFrames.positioning and db.arenaFrames.positioning.vertical or 0,
            growthDirection = db.arenaFrames and db.arenaFrames.positioning and db.arenaFrames.positioning.growthDirection or "Down",
            useClassColors = db.arenaFrames and db.arenaFrames.general and db.arenaFrames.general.useClassColors ~= false,
            usePercentage = db.arenaFrames and db.arenaFrames.general and db.arenaFrames.general.usePercentage or false,
            showArenaServerNames = db.arenaFrames and db.arenaFrames.general and db.arenaFrames.general.showArenaServerNames or false,
            playerNameX = db.arenaFrames and db.arenaFrames.general and db.arenaFrames.general.playerNameX or 52,
            playerNameY = db.arenaFrames and db.arenaFrames.general and db.arenaFrames.general.playerNameY or 0,
        },
        
        -- Trinkets
        trinkets = {
            fontSize = db.trinkets and db.trinkets.sizing and db.trinkets.sizing.fontSize or 12,
            iconDesign = db.trinkets and db.trinkets.iconDesign or "retail",
        },
        
        -- Racials
        racials = {
            fontSize = db.racials and db.racials.sizing and db.racials.sizing.fontSize or 12,
        },
        
        -- Debuffs
        debuffs = {
            enabled = db.moreGoodies and db.moreGoodies.debuffs and db.moreGoodies.debuffs.enabled ~= false,
            horizontal = db.moreGoodies and db.moreGoodies.debuffs and db.moreGoodies.debuffs.positioning and db.moreGoodies.debuffs.positioning.horizontal or 8,
            vertical = db.moreGoodies and db.moreGoodies.debuffs and db.moreGoodies.debuffs.positioning and db.moreGoodies.debuffs.positioning.vertical or 6,
        },
        
        -- Absorbs
        absorbs = {
            enabled = db.moreGoodies and db.moreGoodies.absorbs and db.moreGoodies.absorbs.enabled or false,
        },
        
        -- Cast Bars
        castBars = {
            horizontal = db.castBars and db.castBars.positioning and db.castBars.positioning.horizontal or 2,
            vertical = db.castBars and db.castBars.positioning and db.castBars.positioning.vertical or -30,
            width = db.castBars and db.castBars.sizing and db.castBars.sizing.width or 196,
            height = db.castBars and db.castBars.sizing and db.castBars.sizing.height or 18,
        },
        
        -- Textures
        textures = {
            healthWidth = db.textures and db.textures.sizing and db.textures.sizing.healthWidth or 128,
            healthHeight = db.textures and db.textures.sizing and db.textures.sizing.healthHeight or 18,
        },
        
        -- DR
        diminishingReturns = {
            enabled = db.diminishingReturns and db.diminishingReturns.enabled ~= false,
        },
    }
    
    -- Debug output
    -- print("|cff00FF00[SettingsCache]|r Cache refreshed with " .. self:CountCachedSettings() .. " settings")
end

-- Count cached settings (for debug)
function AC:CountCachedSettings()
    local count = 0
    for category, settings in pairs(self.cachedSettings) do
        for _ in pairs(settings) do
            count = count + 1
        end
    end
    return count
end

-- Get cached setting with fallback to database
function AC:GetCachedSetting(category, key, default)
    -- Try cache first
    if self.cachedSettings[category] and self.cachedSettings[category][key] ~= nil then
        return self.cachedSettings[category][key]
    end
    
    -- Fallback to database
    self:RefreshSettingsCache()
    return self.cachedSettings[category] and self.cachedSettings[category][key] or default
end

-- Safe font setter - never crashes even if font path is invalid
function AC.SafeSetFont(fontString, desiredFont, height, flags)
    if not fontString or not fontString.SetFont then return nil end
    
    -- Resolve font path
    local fontToUse = AC.CUSTOM_FONT -- Safe fallback
    if type(desiredFont) == "string" and desiredFont ~= "" then
        fontToUse = desiredFont
    elseif type(AC.FONT_PATH) == "string" and AC.FONT_PATH ~= "" then
        fontToUse = AC.FONT_PATH
    end
    
    -- Normalize parameters
    height = tonumber(height) or 12
    flags = (flags and flags ~= "") and flags or nil
    
    -- Safe call with pcall
    local success = pcall(function()
        fontString:SetFont(fontToUse, height, flags)
    end)
    
    if not success then
        -- Ultimate fallback to WoW default
        fontString:SetFont("Fonts\\\\FRIZQT__.TTF", height, flags)
    end
    
    -- CRITICAL ARENA FIX: Mark font as set to prevent "Font not set" errors
    fontString._fontIsSet = true
    
    return fontToUse
end

-- CRITICAL ARENA FIX: Wrap SetText to auto-set font if missing (ArenaCore frames only)
local function SafeSetText(fontString, text)
    if fontString and not fontString._fontIsSet then
        -- Font was never set - apply emergency font
        AC.SafeSetFont(fontString, AC.FONT_PATH, 12, "OUTLINE")
    end
    if fontString and fontString.SetText then
        fontString:SetText(text or "")
    end
end

-- REMOVED: Global hook was affecting all WoW UI fonts
-- Instead, we'll ensure all ArenaCore FontStrings use SafeSetFont

-- Create reusable FontObjects for UI (prevents "Font not set" errors)
function AC.EnsureUIFontObjects()
    if AC.UIFont then return end -- Already created
    
    local function makeFontObject(name, size, flags)
        local font = CreateFont(name)
        local path = AC.FONT_PATH or "Fonts\\\\FRIZQT__.TTF"
        font:SetFont(path, size, flags or "")
        return font
    end
    
    AC.UIFont = makeFontObject("ArenaCoreUIFont", 12, nil)
    AC.UIFontSmall = makeFontObject("ArenaCoreUIFontSmall", 11, nil)
    AC.UIFontMedium = makeFontObject("ArenaCoreUIFontMedium", 13, nil)
    AC.UIFontLarge = makeFontObject("ArenaCoreUIFontLarge", 16, "OUTLINE")
    AC.UIFontHeader = makeFontObject("ArenaCoreUIFontHeader", 20, "OUTLINE")
    
    -- DEBUG: UI FontObjects created
    -- print("|cff00FF00[ArenaCore]|r UI FontObjects created successfully")
end

-- REMOVED: Global CreateFontString hook (caused 500MB+ memory leak)
-- ArenaCore frames now set fonts explicitly using SafeSetFont()

-- Recursively fix any FontStrings that don't have fonts set
function AC.FixUIFontsRecursively(frame)
    if not frame then return end
    
    -- Fix all FontString regions in this frame
    for _, region in ipairs({frame:GetRegions()}) do
        if region.GetObjectType and region:GetObjectType() == "FontString" then
            local path = region:GetFont()
            if not path then
                -- FontString has no font - set it to our default
                AC.EnsureUIFontObjects()
                region:SetFontObject(AC.UIFont)
            end
        end
    end
    
    -- Recursively fix all child frames
    for _, child in ipairs({frame:GetChildren()}) do
        AC.FixUIFontsRecursively(child)
    end
end

-- Master Frame Manager - The one system to rule them all
AC.MasterFrameManager = AC.MasterFrameManager or {}
local MFM = AC.MasterFrameManager

-- FrameManager alias for compatibility with consolidated functions
AC.FrameManager = AC.MasterFrameManager
local FrameManager = AC.FrameManager

local function GetCastBarModule()
    if AC.MasterFrameManager and AC.MasterFrameManager.CastBars then
        return AC.MasterFrameManager.CastBars
    end
    if AC.FrameManager and AC.FrameManager.CastBars then
        return AC.FrameManager.CastBars
    end
    return nil
end

-- State tracking
MFM.frames = {}  -- Our unified frames array
MFM.isInArena = false
MFM.isTestMode = false
MFM.instanceType = nil
MFM.eventFrame = nil

-- ============================================================================
-- HELPER METHODS for FrameManager
-- ============================================================================

-- Get frames array (used by debuff system and other components)
function FrameManager:GetFrames()
    return self.frames or {}
end

-- Check if frames exist and are available
function FrameManager:FramesExist()
    return self.frames and #self.frames > 0
end

-- Delegate trinket icon lookup to the TrinketsRacials module for consistency.
local function GetTrinketIcon()
    if AC.TrinketsRacials and AC.TrinketsRacials.GetUserTrinketIcon then
        return AC.TrinketsRacials:GetUserTrinketIcon()
    end
    return 1322720 -- Retail Gladiator Medallion fallback
end

local function GetDebuffModule()
    if AC.MasterFrameManager and AC.MasterFrameManager.Debuffs then
        return AC.MasterFrameManager.Debuffs
    end
    if AC.FrameManager and AC.FrameManager.Debuffs then
        return AC.FrameManager.Debuffs
    end
    return nil
end

local function GetDispelModule()
    if AC.MasterFrameManager and AC.MasterFrameManager.Dispels then
        return AC.MasterFrameManager.Dispels
    end
    if AC.FrameManager and AC.FrameManager.Dispels then
        return AC.FrameManager.Dispels
    end
    return nil
end

local function GetDRModule()
    if AC.MasterFrameManager and AC.MasterFrameManager.DR then
        return AC.MasterFrameManager.DR
    end
    if AC.FrameManager and AC.FrameManager.DR then
        return AC.FrameManager.DR
    end
    return nil
end

-- Constants
local MAX_ARENA_ENEMIES = 3
local FRAME_PREFIX = "ArenaCoreFrame"
local FRAME_WIDTH, FRAME_HEIGHT = 350, 72
local FRAME_SPACING = 12
local HEALTH_W, HEALTH_H = 245, 18
local MANA_W, MANA_H = 245, 8
local HEALTH_X, HEALTH_Y = 56, 6
local MANA_X, MANA_Y = 56, -14
local SELECTION_TINT = {0.97, 0.27, 0.30, 1}
local DR_TIME = 18.5

-- ArenaTracking now uses frames created by FrameLayout via _G.ArenaCore.arenaFrames
local updateTicker
local DR_SEVERITY_COLORS = {
    [1] = { 0, 1, 0, 1}, -- Green
    [2] = { 1, 1, 0, 1}, -- Yellow
    [3] = { 1, 0, 0, 1}, -- Red
}

local config = {
  position = { x = 200, y = -200 },
  scale = 1.0,
  growthDirection = "Down",
}

AC.testModeEnabled = false
AC.framesLocked = true

-- Define DR categories for frame creation (must match UI_DetailedDRSettings.lua)
AC.DR_CATEGORIES = {
    "stun", "silence", "root", "incapacitate", "disorient", "fear", "mc", "cyclone", "banish", "knockback", "disarm"
}

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

-- Texture paths
local A = "Interface\\AddOns\\ArenaCore\\Media\\Textures\\"
local TEX = {
  frame_bg = A.."frame_background.tga", frame_border = A.."frame_border.tga",
  frame_hover = A.."frame_hover.tga", class_icon_bg = A.."class_icon_bg.tga",
  spec_icon_bg = A.."spec_icon_bg.tga", spec_icon_border = A.."spec_icon_border.tga",
  health_bg = A.."health_bg.tga", health_border = A.."health_border.tga",
  health_fill = A.."health_fill.tga", mana_bg = A.."mana_bg.tga",
  mana_border = A.."mana_border.tga", mana_fill = A.."mana_fill.tga",
  cast_bg = A.."cast_bg.tga", cast_fill = A.."cast_fill.tga",
  target_arrow = A.."target_arrow.tga", trinket_bg = A.."trinket_bg.tga",
  trinket_border = A.."trinket_border.tga", debuff_bg = A.."debuff_bg.tga",
  debuff_border = A.."debuff_border.tga",
}

-- Racial data
local racialData = {
    ["Human"] = { texture = 59752, sharedCD = 90 },
    ["Scourge"] = { texture = 7744, sharedCD = 30 },
    ["Dwarf"] = { texture = 20594, sharedCD = 30 },
    ["NightElf"] = { texture = 58984, sharedCD = 0 },
    ["Gnome"] = { texture = 20589, sharedCD = 0 },
    ["Draenei"] = { texture = 59542, sharedCD = 0 },
    ["Worgen"] = { texture = 68992, sharedCD = 0 },
    ["Pandaren"] = { texture = 107079, sharedCD = 0 },
    ["Orc"] = { texture = 33697, sharedCD = 0 },
    ["Tauren"] = { texture = 20549, sharedCD = 0 },
    ["Troll"] = { texture = 26297, sharedCD = 0 },
    ["BloodElf"] = { texture = 202719, sharedCD = 0 },
    ["Goblin"] = { texture = 69070, sharedCD = 0 },
    ["LightforgedDraenei"] = { texture = 255647, sharedCD = 0 },
    ["HighmountainTauren"] = { texture = 255654, sharedCD = 0 },
    ["Nightborne"] = { texture = 260364, sharedCD = 0 },
    ["MagharOrc"] = { texture = 274738, sharedCD = 0 },
    ["DarkIronDwarf"] = { texture = 265221, sharedCD = 30 },
    ["ZandalariTroll"] = { texture = 291944, sharedCD = 0 },
    ["VoidElf"] = { texture = 256948, sharedCD = 0 },
    ["KulTiran"] = { texture = 287712, sharedCD = 0 },
    ["Mechagnome"] = { texture = 312924, sharedCD = 0 },
    ["Vulpera"] = { texture = 312411, sharedCD = 0 },
    ["Dracthyr"] = { texture = 368970, sharedCD = 0 },
    ["EarthenDwarf"] = { texture = 436344, sharedCD = 0 }
}

-- Frame positioning anchor - CRITICAL: Must be movable for dragging to work
-- IMPORTANT: Uses TOPLEFT positioning like old system for smooth StartMoving()
AC.ArenaFramesAnchor = AC.ArenaFramesAnchor or CreateFrame("Frame", "ArenaCoreFramesAnchor", UIParent)
AC.ArenaFramesAnchor:SetSize(1, 1)
AC.ArenaFramesAnchor:SetMovable(true) -- Required for drag system
AC.ArenaFramesAnchor:SetUserPlaced(false) -- Don't save Blizzard position
AC.ArenaFramesAnchor:SetClampedToScreen(true) -- Keep on screen
AC.ArenaFramesAnchor:SetFrameStrata("MEDIUM")
AC.ArenaFramesAnchor:SetFrameLevel(1)
AC.ArenaFramesAnchor:EnableMouse(false) -- Anchor itself doesn't need mouse
-- Initial position will be set by UpdateFramePositions or from saved settings

-- ============================================================================
-- 2. DATABASE & SETTINGS SYSTEM  
-- ============================================================================

function AC:EnsureDB()
    _G.ArenaCoreDB = _G.ArenaCoreDB or { profile = {} }
    self.DB = _G.ArenaCoreDB
    self.DB.profile = self.DB.profile or {}
    
    -- CRITICAL FIX: Only ensure structure exists, NEVER overwrite existing settings!
    if not self.DB.profile.arenaFrames then
        self.DB.profile.arenaFrames = {}
    end
    if not self.DB.profile.arenaFrames.positioning then
        self.DB.profile.arenaFrames.positioning = {}
    end
    if not self.DB.profile.arenaFrames.sizing then
        self.DB.profile.arenaFrames.sizing = {}
    end
    if not self.DB.profile.arenaFrames.general then
        self.DB.profile.arenaFrames.general = {}
    end
    
    -- Only set defaults if the specific values don't exist
    -- This preserves user's carefully configured settings!
    -- Note: Defaults are properly set in Init.lua
    local pos = self.DB.profile.arenaFrames.positioning
    local siz = self.DB.profile.arenaFrames.sizing
    local gen = self.DB.profile.arenaFrames.general
end

-- Utility to set a dot-path inside a table (e.g., "arenaFrames.positioning.horizontal")
function AC:SetPath(root, path, value)
    if not root or not path then return end
    local t = root
    local parts = { strsplit('.', path) }
    for i = 1, #parts - 1 do
        local k = parts[i]
        t[k] = t[k] or {}
        t = t[k]
    end
    t[parts[#parts]] = value
end

-- Create main tracking container
local ArenaTracking = CreateFrame("Frame", "ArenaCore_ArenaTrackingContainer", UIParent)
AC.ArenaTracking = ArenaTracking

-- ============================================================================
AC.drTimerSystem = AC.drTimerSystem or {}

-- ============================================================================
-- ARENACORE MASTER FILE - CHUNK 3: ICON STYLING SYSTEM
-- Section 4: Complete Icon Styling & Border System
-- ============================================================================

-- ============================================================================
-- 4. ICON STYLING SYSTEM
-- ============================================================================

AC.IconStyling = AC.IconStyling or {}
local IconStyling = AC.IconStyling

-- Classic WoW icon textures from Blizzard Classic theme
local CLASSIC_TEXTURES = {
    backdrop = "Interface\\Buttons\\UI-Quickslot",
    normal = "Interface\\Buttons\\UI-Quickslot2", 
    border = "Interface\\Buttons\\UI-ActionButton-Border",
    highlight = "Interface\\Buttons\\CheckButtonHilight"
}

-- Icon styling configuration
local ICON_CONFIG = {
    iconTexCoords = {0, 1, 0, 1}, -- Full icon texture - no cropping
    normalTexCoords = {0, 1, 0, 1}, -- Normal frame texCoords
    backdropColor = {1, 1, 1, 0.4},
    borderColor = {0, 0, 0, 0.8}, -- Black border for rounded corners
    normalColor = {1, 1, 1, 1},
    highlightBlend = "ADD"
}

--[[
    Creates a classic WoW styled icon frame with rounded edges and dark border
    
    @param parent - Parent frame to attach the icon to
    @param size - Icon size (default: 36)
    @param showBorder - Whether to show the dark border (default: true)
    @return iconFrame - Styled icon frame with .icon, .border properties
]]
function IconStyling:CreateStyledIcon(parent, size, showBorder)
    if not parent then return nil end
    
    size = size or 36
    showBorder = showBorder ~= false -- Default to true
    
    -- Create main icon frame with backdrop template
    local iconFrame = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    iconFrame:SetSize(size, size)
    
    -- Create the actual icon texture (full brightness, no modifications)
    local icon = iconFrame:CreateTexture(nil, "ARTWORK")
    icon:SetAllPoints(iconFrame) -- Full size, no shrinking
    icon:SetTexCoord(0, 1, 0, 1) -- Full texture, no cropping
    
    -- Create simple thick black border using textures
    local border = nil
    if showBorder then
        -- Create 4 border textures for top, bottom, left, right
        local borderTop = iconFrame:CreateTexture(nil, "OVERLAY")
        borderTop:SetTexture("Interface\\Buttons\\WHITE8X8")
        borderTop:SetVertexColor(0, 0, 0, 1)
        borderTop:SetPoint("TOPLEFT", iconFrame, "TOPLEFT", 0, 0)
        borderTop:SetPoint("TOPRIGHT", iconFrame, "TOPRIGHT", 0, 0)
        borderTop:SetHeight(2)
        
        local borderBottom = iconFrame:CreateTexture(nil, "OVERLAY")
        borderBottom:SetTexture("Interface\\Buttons\\WHITE8X8")
        borderBottom:SetVertexColor(0, 0, 0, 1)
        borderBottom:SetPoint("BOTTOMLEFT", iconFrame, "BOTTOMLEFT", 0, 0)
        borderBottom:SetPoint("BOTTOMRIGHT", iconFrame, "BOTTOMRIGHT", 0, 0)
        borderBottom:SetHeight(2)
        
        local borderLeft = iconFrame:CreateTexture(nil, "OVERLAY")
        borderLeft:SetTexture("Interface\\Buttons\\WHITE8X8")
        borderLeft:SetVertexColor(0, 0, 0, 1)
        borderLeft:SetPoint("TOPLEFT", iconFrame, "TOPLEFT", 0, 0)
        borderLeft:SetPoint("BOTTOMLEFT", iconFrame, "BOTTOMLEFT", 0, 0)
        borderLeft:SetWidth(2)
        
        local borderRight = iconFrame:CreateTexture(nil, "OVERLAY")
        borderRight:SetTexture("Interface\\Buttons\\WHITE8X8")
        borderRight:SetVertexColor(0, 0, 0, 1)
        borderRight:SetPoint("TOPRIGHT", iconFrame, "TOPRIGHT", 0, 0)
        borderRight:SetPoint("BOTTOMRIGHT", iconFrame, "BOTTOMRIGHT", 0, 0)
        borderRight:SetWidth(2)
        
        border = {borderTop, borderBottom, borderLeft, borderRight}
    end
    
    -- Store references for easy access
    iconFrame.icon = icon
    iconFrame.border = border
    
    -- Helper method to set icon texture
    function iconFrame:SetIconTexture(texture)
        if self.icon and texture then
            self.icon:SetTexture(texture)
        end
    end
    
    -- Helper method to set border color
    function iconFrame:SetBorderColor(r, g, b, a)
        if self.border then
            for _, borderTex in ipairs(self.border) do
                borderTex:SetVertexColor(r, g, b, a or 1)
            end
        end
    end
    
    return iconFrame
end

--[[
    Applies classic styling to an existing icon texture
    
    @param iconTexture - Existing icon texture to style
    @param parentFrame - Parent frame (optional, for adding border)
    @param addBorder - Whether to add dark border (default: true)
]]
function IconStyling:StyleExistingIcon(iconTexture, parentFrame, addBorder)
    if not iconTexture then return end
    
    -- CRITICAL FIX: Don't apply styling to class icons that have preventStyling flag
    if parentFrame and parentFrame.preventStyling then
        return -- Skip styling for class icons
    end
    
    -- DON'T modify the icon texture at all - keep it full brightness and original texCoords
    
    -- Add simple thick black border using 4 textures with DYNAMIC SCALING
    if addBorder ~= false and parentFrame then
        if not parentFrame.styledBorder then
            -- Calculate border thickness as percentage of icon size (12% of smaller dimension)
            -- This ensures borders scale proportionally with icon size
            -- CRITICAL: Use parent frame size, not texture size (texture size may not be set yet)
            local iconWidth = parentFrame:GetWidth() or 32
            local iconHeight = parentFrame:GetHeight() or 32
            local minDimension = math.min(iconWidth, iconHeight)
            local borderThickness = math.max(2, math.floor(minDimension * 0.12)) -- 12% of size, minimum 2px
            
            -- Create 4 border textures for top, bottom, left, right with high sublevel
            -- Use sublevel 7 (max allowed) to ensure borders appear above the icon (sublevel 5)
            -- CRITICAL: Anchor to PARENT FRAME edges, not icon texture edges (for consistent borders)
            local borderTop = parentFrame:CreateTexture(nil, "OVERLAY", nil, 7)
            borderTop:SetTexture("Interface\\Buttons\\WHITE8X8")
            borderTop:SetVertexColor(0, 0, 0, 1)
            borderTop:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0)
            borderTop:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", 0, 0)
            borderTop:SetHeight(borderThickness)
            
            local borderBottom = parentFrame:CreateTexture(nil, "OVERLAY", nil, 7)
            borderBottom:SetTexture("Interface\\Buttons\\WHITE8X8")
            borderBottom:SetVertexColor(0, 0, 0, 1)
            borderBottom:SetPoint("BOTTOMLEFT", parentFrame, "BOTTOMLEFT", 0, 0)
            borderBottom:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", 0, 0)
            borderBottom:SetHeight(borderThickness)
            
            local borderLeft = parentFrame:CreateTexture(nil, "OVERLAY", nil, 7)
            borderLeft:SetTexture("Interface\\Buttons\\WHITE8X8")
            borderLeft:SetVertexColor(0, 0, 0, 1)
            borderLeft:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0)
            borderLeft:SetPoint("BOTTOMLEFT", parentFrame, "BOTTOMLEFT", 0, 0)
            borderLeft:SetWidth(borderThickness)
            
            local borderRight = parentFrame:CreateTexture(nil, "OVERLAY", nil, 7)
            borderRight:SetTexture("Interface\\Buttons\\WHITE8X8")
            borderRight:SetVertexColor(0, 0, 0, 1)
            borderRight:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", 0, 0)
            borderRight:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", 0, 0)
            borderRight:SetWidth(borderThickness)
            
            -- Store border data for potential updates
            parentFrame.styledBorder = {
                top = borderTop,
                bottom = borderBottom,
                left = borderLeft,
                right = borderRight,
                thickness = borderThickness,
                iconTexture = iconTexture
            }
        end
    end
end

--[[
    Update border thickness when icon size changes
    Call this after changing icon size to maintain proportional borders
    
    @param parentFrame - Frame containing the styledBorder
]]
function IconStyling:UpdateBorderThickness(parentFrame)
    if not parentFrame or not parentFrame.styledBorder then return end
    
    local border = parentFrame.styledBorder
    
    -- Recalculate border thickness based on new parent frame size
    -- CRITICAL: Use parent frame size for consistency
    local iconWidth = parentFrame:GetWidth() or 32
    local iconHeight = parentFrame:GetHeight() or 32
    local minDimension = math.min(iconWidth, iconHeight)
    local borderThickness = math.max(2, math.floor(minDimension * 0.12)) -- 12% of size, minimum 2px
    
    -- Update border sizes
    if border.top then border.top:SetHeight(borderThickness) end
    if border.bottom then border.bottom:SetHeight(borderThickness) end
    if border.left then border.left:SetWidth(borderThickness) end
    if border.right then border.right:SetWidth(borderThickness) end
    
    -- Store new thickness
    border.thickness = borderThickness
end

--[[
    Batch applies styling to multiple icons
    
    @param icons - Table of {texture = iconTexture, parent = parentFrame} entries
]]
function IconStyling:StyleMultipleIcons(icons)
    if not icons then return end
    
    for _, iconData in pairs(icons) do
        if iconData.texture then
            self:StyleExistingIcon(iconData.texture, iconData.parent, iconData.addBorder)
        end
    end
end

-- Global helper function for easy access
function AC:CreateStyledIcon(parent, size, showBorder, showBackdrop)
    return IconStyling:CreateStyledIcon(parent, size, showBorder, showBackdrop)
end

function AC:StyleIcon(iconTexture, parentFrame, addBorder)
    return IconStyling:StyleExistingIcon(iconTexture, parentFrame, addBorder)
end

function AC:UpdateIconBorder(parentFrame)
    return IconStyling:UpdateBorderThickness(parentFrame)
end

-- Create flat colored texture helper
function AC:CreateFlatTexture(parent, layer, sublevel, color, alpha)
    if not parent or not color then return nil end
    
    local texture = parent:CreateTexture(nil, layer, nil, sublevel)
    texture:SetTexture("Interface\\Buttons\\WHITE8X8")
    texture:SetVertexColor(color[1], color[2], color[3], alpha or color[4] or 1)
    
    return texture
end

-- Enhanced icon creation system for complex styling
function AC:CreateAdvancedIcon(parent, config)
    if not parent or not config then return nil end
    
    local size = config.size or 32
    local iconFrame = CreateFrame("Frame", nil, parent)
    iconFrame:SetSize(size, size)
    
    if config.point then
        iconFrame:SetPoint(config.point, config.x or 0, config.y or 0)
    end
    
    -- Background layer
    if config.background then
        local bg = iconFrame:CreateTexture(nil, "BACKGROUND")
        bg:SetAllPoints()
        if config.background.texture then
            bg:SetTexture(config.background.texture)
            bg:SetTexCoord(unpack(config.background.texCoords or {0, 1, 0, 1}))
        else
            bg:SetTexture("Interface\\Buttons\\WHITE8X8")
        end
        if config.background.color then
            bg:SetVertexColor(unpack(config.background.color))
        end
        iconFrame.background = bg
    end
    
    -- Main icon
    local icon = iconFrame:CreateTexture(nil, "ARTWORK")
    if config.iconInset then
        icon:SetSize(size - config.iconInset, size - config.iconInset)
        icon:SetPoint("CENTER")
    else
        icon:SetAllPoints()
    end
    icon:SetTexCoord(unpack(config.texCoords or {0.1, 0.9, 0.1, 0.9}))
    
    -- Border/overlay
    if config.border then
        local border = iconFrame:CreateTexture(nil, "OVERLAY")
        border:SetAllPoints()
        border:SetTexture(config.border.texture)
        border:SetTexCoord(unpack(config.border.texCoords or {0, 1, 0, 1}))
        if config.border.color then
            border:SetVertexColor(unpack(config.border.color))
        end
        iconFrame.border = border
    end
    
    -- Store references
    iconFrame.icon = icon
    iconFrame.size = size
    
    -- Helper methods
    function iconFrame:SetIconTexture(texture)
        if self.icon then
            self.icon:SetTexture(texture)
        end
    end
    
    function iconFrame:SetBorderColor(r, g, b, a)
        if self.border then
            self.border:SetVertexColor(r, g, b, a or 1)
        end
    end
    
    return iconFrame
end

-- Spell school colors for cast bars
local SPELL_SCHOOL_COLORS = {
  [1] = {1.0, 1.0, 1.0},    -- Physical (White)
  [2] = {1.0, 1.0, 0.0},    -- Holy (Yellow)
  [4] = {1.0, 0.5, 0.0},    -- Fire (Orange)
  [8] = {0.3, 0.8, 1.0},    -- Nature (Light Blue)
  [16] = {0.5, 1.0, 1.0},   -- Frost (Cyan)
  [32] = {0.5, 0.0, 1.0},   -- Shadow (Purple)
  [64] = {1.0, 0.1, 1.0},   -- Arcane (Magenta)
}

-- ============================================================================
-- ARENACORE MASTER FILE - CHUNK 4: HELPER FUNCTIONS & UTILITIES
-- Section 5: Complete Helper Functions & Utility System
-- ============================================================================

-- ============================================================================
-- 5. HELPER FUNCTIONS & UTILITIES
-- ============================================================================

-- Bridge variable for compatibility
local arenaFrames = MFM.frames
local isTestMode = false

local function tContains(tbl, val)
    for _, v in ipairs(tbl) do
        if v == val then return true end
    end
    return false
end

-- Helper functions to bridge old and new systems
local function GetFrameSettings()
    if not AC.DB or not AC.DB.profile then AC:EnsureDB() end
    return AC.DB.profile.arenaFrames
end

local function GetArenaFrames()
    return MFM.frames
end

-- REMOVED: Duplicate GetTrinketIcon() function (line 696)
-- This was a duplicate of the function at line 194
-- The main GetTrinketIcon() at line 194 is used throughout the codebase

-- Helper function to check if a unit is a healer with null safety
local function IsHealer(unit)
    if not unit then return false end
    
    local id = string.match(unit, "arena(%d)")
    if not id then return false end
    
    local specID = GetArenaOpponentSpec and GetArenaOpponentSpec(tonumber(id))
    if specID and specID > 0 then
        local _, _, _, _, role = GetSpecializationInfoByID(specID)
        return role == "HEALER"
    end
    return false
end

-- Helper function to get remaining cooldown time with safety checks
local function GetRemainingCD(cooldownFrame)
    if not cooldownFrame or not cooldownFrame.GetCooldownTimes then return 0 end
    
    local startTime, duration = cooldownFrame:GetCooldownTimes()
    if not startTime or startTime == 0 or not duration or duration == 0 then return 0 end
    
    local currTime = GetTime()
    local remaining = (startTime + duration) / 1000 - currTime
    return math.max(0, remaining) -- Never return negative values
end

-- Function to update cooldown timer text with test mode awareness
local function UpdateCooldownText(indicator)
    if not indicator or not indicator.cooldown or not indicator.cooldown.Text then return end
    
    -- In test mode, let FrameLayout handle cooldown displays
    if AC.testModeEnabled then return end
    
    local start, duration = indicator.cooldown:GetCooldownTimes()
    if start == 0 or duration == 0 then
        indicator.cooldown.Text:SetText("")
        indicator.cooldown.Text:Hide()
        return
    end
    
    local remaining = (start + duration) / 1000 - GetTime()
    if remaining > 0.5 then
        indicator.cooldown.Text:SetText(math.ceil(remaining))
        indicator.cooldown.Text:Show()
    else
        indicator.cooldown.Text:SetText("")
        indicator.cooldown.Text:Hide()
    end
end

-- Helper function to get class colors setting consistently
-- REMOVED DUPLICATE: This function is defined at line 9927 (authoritative version)
-- Using simpler logic: general.useClassColors ~= false (defaults to true)

-- REMOVED: Duplicate GetRemainingCooldown() function (lines 755-760)
-- This was a duplicate of GetRemainingCD() at line 715
-- GetRemainingCD() has better null safety checks

-- ENHANCED: Consolidated font string creation with consistent patterns
local function CreateFontString(parent, fontSize, outline, point, x, y, width, height)
    -- CRITICAL FIX: Use double backslashes and SafeSetFont
    local fontPath = "Interface\\\\AddOns\\\\ArenaCore\\\\Media\\\\Fonts\\\\arenacore.ttf"
    local fs = parent:CreateFontString(nil, "OVERLAY")
    
    -- Set font with optional outline using SafeSetFont
    local fontFlags = outline and "OUTLINE" or ""
    if AC and AC.SafeSetFont then
        AC.SafeSetFont(fs, fontPath, fontSize or 12, fontFlags)
    else
        fs:SetFont(fontPath, fontSize or 12, fontFlags)
    end
    
    -- Set position and size
    if point then
        fs:SetPoint(point, x or 0, y or 0)
    end
    if width and height then
        fs:SetSize(width, height)
        fs:SetJustifyH("LEFT")
    end
    
    fs:SetTextColor(1, 1, 1, 1)
    return fs
end

-- OPTIMIZED: Specific font string creators using consolidated function
local function CreateName(parent)
    return CreateFontString(parent, 13, false, "TOPLEFT", 56, -6, 270, 18)
end

local function CreateStatusText(parent)
    local fs = CreateFontString(parent, 10, true, "CENTER", 0, 0)
    fs:Hide()
    return fs
end

local function FormatHealth(n)
    if not n or n < 1000 then return tostring(n or 0) end
    if n >= 1000000 then return string.format("%.1fm", n / 1000000) end
    if n >= 1000 then return string.format("%.0fk", n / 1000) end
end

local function Trim(texture)
    if texture and texture.SetTexCoord then
        texture:SetTexCoord(0.1, 0.9, 0.1, 0.9)
    end
end

local function StopTicker()
    if updateTicker then
        updateTicker:Cancel()
        updateTicker = nil
    end
end

local function StartTicker()
    StopTicker()
    -- PHASE 3: Reduced from 0.5s to 1.0s (50% reduction, events handle real-time updates)
    updateTicker = AC:CreateTicker(1.0, UpdateAllFrames, "main_update_ticker")
end

local FadeIn = _G.UIFrameFadeIn or function(f, t, from, to) if f then f:SetAlpha(to or 1) end end
local FadeOut = _G.UIFrameFadeOut or function(f, t, from, to) if f then f:SetAlpha(to or 0) end end

-- Enhanced TriBadges attachment with comprehensive test mode support
local function AttachTriBadgesAll()
    -- CRITICAL: Don't interfere with FrameLayout test mode but ensure opacity
    if AC.testModeEnabled then 
        -- In test mode, ensure TriBadges have proper opacity
        local arenaFrames = _G.ArenaCore and _G.ArenaCore.arenaFrames
        if arenaFrames and _G.ArenaCore.TriBadges then
            for i = 1, MAX_ARENA_ENEMIES do
                if arenaFrames[i] and arenaFrames[i].TriBadges then
                    arenaFrames[i].TriBadges:SetAlpha(1.0)
                    if arenaFrames[i].TriBadges:IsShown() then
                        -- Ensure test mode TriBadges are fully visible
                        arenaFrames[i].TriBadges:Show()
                    end
                end
            end
        end
        return 
    end
    
    -- Normal attachment for live arena
    if _G.ArenaCore and _G.ArenaCore.TriBadges and _G.ArenaCore.TriBadges.Attach then
        local arenaFrames = _G.ArenaCore and _G.ArenaCore.arenaFrames
        if not arenaFrames then return end
        
        for i = 1, MAX_ARENA_ENEMIES do
            if arenaFrames[i] then
                _G.ArenaCore.TriBadges:Attach(arenaFrames[i], "arena"..i)
                if _G.ArenaCore.TriBadges.RefreshAll then
                    _G.ArenaCore.TriBadges:RefreshAll()
                end
            end
        end
    end
end

-- REMOVED: First duplicate UpdateFramePosition function (legacy system)
-- Kept the one at line ~5267 which works with MasterFrameManager

function AC:UpdateFrameSpacing()
    local settings = GetFrameSettings()
    if not settings then return end
    
    local positioning = settings.positioning or {}
    -- Update spacing using MasterFrameManager
    if MFM and MFM.UpdateFramePositions then
        MFM:UpdateFramePositions() -- This applies spacing changes
    end
end

-- REMOVED: First duplicate UpdateFrameScale function (legacy system)
-- Kept the one at line ~5359 which works with MasterFrameManager

-- REMOVED: First duplicate UpdateFrameSize function (legacy system)
-- Kept the one at line ~5377 which works with MasterFrameManager

function AC:SetGrowthDirection(direction)
    if not direction or not tContains({"Down", "Up", "Right", "Left"}, direction) then return end
    
    -- Store in database (avoid undefined config global)
    local settings = GetFrameSettings()
    if settings then
        settings.positioning = settings.positioning or {}
        settings.positioning.growthDirection = direction
        
        -- Notify MasterFrameManager system of growth direction change
        if MFM and MFM.UpdateFramePositions then
            MFM:UpdateFramePositions() -- This applies growth direction changes
        end
    end
end

-- REMOVED: Duplicate ResetToDefaultSettings function with dangerous legacy globals
-- This one had undefined globals: config, FRAME_SPACING, FRAME_WIDTH, FRAME_HEIGHT
-- Kept the clean one at line ~5363 which works with MasterFrameManager

-- Component-specific clear helpers for better maintainability
local function ClearHealthBars(frame)
    if frame.healthBar then
        frame.healthBar:SetMinMaxValues(0, 100)
        frame.healthBar:SetValue(100)
        if frame.healthBar.statusText then
            frame.healthBar.statusText:SetText("")
            frame.healthBar.statusText:Hide()
        end
    end
    
    if frame.manaBar then
        frame.manaBar:SetMinMaxValues(0, 100)
        frame.manaBar:SetValue(100)
    end
end

local function ClearIconsAndIndicators(frame)
    -- Clear class icon and spec chips
    if frame.classIcon then
        if frame.classIcon.icon then frame.classIcon.icon:SetTexture(nil) end
        if frame.classIcon.overlay then frame.classIcon.overlay:SetTexture(nil) end
        if frame.classIcon.background then frame.classIcon.background:SetVertexColor(1, 1, 1, 1) end
        if frame.classIcon.specChip then
            frame.classIcon.specChip:Hide()
            if frame.classIcon.specChip.icon then frame.classIcon.specChip.icon:SetTexture(nil) end
        end
    end
    
    -- Clear trinket and racial indicators
    if frame.trinketIndicator then
        if frame.trinketIndicator.texture then frame.trinketIndicator.texture:SetTexture(nil) end
        if frame.trinketIndicator.cooldown then frame.trinketIndicator.cooldown:Clear() end
        frame.trinketIndicator:Hide()
    end
    
    if frame.racialIndicator then
        if frame.racialIndicator.texture then frame.racialIndicator.texture:SetTexture(nil) end
        if frame.racialIndicator.cooldown then frame.racialIndicator.cooldown:Clear() end
        frame.racialIndicator:Hide()
    end
end

local function ClearDebuffsAndDR(frame)
    -- Clear all debuffs/auras
    if frame.debuffContainer and frame.debuffContainer.debuffs then
        for _, debuff in pairs(frame.debuffContainer.debuffs) do
            if debuff then
                debuff:Hide()
                if debuff.icon then debuff.icon:SetTexture(nil) end
                if debuff.cooldown then debuff.cooldown:Clear() end
            end
        end
        wipe(frame.debuffContainer.debuffs)
    end
    
    -- Clear all DR (Diminishing Returns) icons
    if frame.drIcons then
        for category, dr in pairs(frame.drIcons) do
            if dr then
                dr:Hide()
                if dr.icon then dr.icon:SetTexture(nil) end
                if dr.cooldown then 
                    dr.cooldown.duration = 0
                    dr.cooldown.startTime = 0
                end
                if dr.timerText then dr.timerText:Hide() end
                dr.severity = 1
            end
        end
    end
end

local function ClearMiscElements(frame)
    -- Clear cast bars
    if frame.castBar then
        if frame.castBar.Icon then frame.castBar.Icon:SetTexture(nil) end
        if frame.castBar.Text then frame.castBar.Text:SetText("") end
        frame.castBar:SetValue(0)
        frame.castBar:Hide()
    end
    
    -- Clear target indicator
    if frame.targetIndicator then
        frame.targetIndicator:SetAlpha(0)
    end
    
    -- Clear player name and stored race data
    if frame.playerName then frame.playerName:SetText("") end
    frame.race = nil
end

-- OPTIMIZED: Clean, modular ClearArenaData function
function AC:ClearArenaData()
    -- CRITICAL: Complete test mode protection with frame restoration
    if AC.testModeEnabled then
        if not AC._clearDataWarningShown then
            print("|cffFF0000[CRITICAL]|r ClearArenaData blocked during TEST MODE - Restoring frame opacity")
            AC._clearDataWarningShown = true
        end
        
        local arenaFrames = _G.ArenaCore and _G.ArenaCore.arenaFrames
        if arenaFrames then
            for i = 1, MAX_ARENA_ENEMIES do
                if arenaFrames[i] then
                    local f = arenaFrames[i]
                    f:SetAlpha(1.0)
                    if f.playerName and (not f.playerName:GetText() or f.playerName:GetText() == "") then
                        local testNames = {"Survivable", "Patymorph", "Easymodex"}
                        f.playerName:SetText(testNames[i] or ("TestPlayer" .. i))
                        -- CRITICAL FIX: Only show if enabled in settings
                        local db = AC.DB and AC.DB.profile
                        local general = db and db.arenaFrames and db.arenaFrames.general or {}
                        if general.showNames ~= false and not InCombatLockdown() then
                            f.playerName:Show()
                        end
                    end
                end
            end
        end
        return
    end
    AC._clearDataWarningShown = false
    
    -- STAGE 2: REMOVED _stealthTimers clearing
    
    -- Use FrameLayout frames with proper null safety
    local arenaFrames = _G.ArenaCore and _G.ArenaCore.arenaFrames
    if not arenaFrames then return end
    
    -- Use modular helper functions for clean, maintainable clearing
    for i = 1, MAX_ARENA_ENEMIES do
        if arenaFrames[i] then
            local f = arenaFrames[i]
            ClearHealthBars(f)
            ClearIconsAndIndicators(f)
            ClearDebuffsAndDR(f)
            ClearMiscElements(f)
        end
    end
end

-- Function to refresh all trinket icons when iconDesign setting changes
function AC:RefreshTrinketIcons()
  -- Use the correct frame array that has trinket indicators
  local frames = AC.arenaFrames or arenaFrames
  
  -- Also handle new FrameManager frames
  if AC.FrameManager and AC.FrameManager.GetFrames then
    local fmFrames = AC.FrameManager:GetFrames()
    if fmFrames and #fmFrames > 0 then
      frames = fmFrames
    end
  end
  
  for i = 1, MAX_ARENA_ENEMIES do
    if frames[i] and frames[i].trinketIndicator then
      -- Get the custom trinket icon based on current setting
      local trinketIcon = GetTrinketIcon()
      local trinketTexture
      
      -- Handle both texture paths (strings) and spell IDs (numbers)
      if type(trinketIcon) == "string" then
        trinketTexture = trinketIcon -- Direct texture path
      else
        trinketTexture = C_Spell.GetSpellTexture(trinketIcon) -- Spell ID lookup
      end
      
      if trinketTexture then
        -- Use .icon property based on CreateTrinket function (FrameManager structure)
        if frames[i].trinketIndicator.icon then
          frames[i].trinketIndicator.icon:SetTexture(trinketTexture)
        elseif frames[i].trinketIndicator.texture then
          frames[i].trinketIndicator.texture:SetTexture(trinketTexture)
        end
      end
    end
  end
end

-- ============================================================================
-- ARENACORE MASTER FILE - CHUNK 5: FRAME CREATION SYSTEM
-- Section 6: Complete Frame Creation & Master Frame Manager
-- ============================================================================

-- ============================================================================
-- 6. FRAME CREATION SYSTEM
-- ============================================================================

function MFM:CreateArenaFrame(index)
    local frameName = FRAME_PREFIX .. index
    local unit = "arena" .. index
    
    -- Check if frame already exists
    if self.frames[index] then
        return self.frames[index]
    end
    
    -- DEBUG: Creating unified frame
    -- print("|cffFFAA00ArenaCore Master:|r Creating unified frame " .. index)
    
    -- Create frame using SecureUnitButtonTemplate (PROVEN TO WORK!)
    local frame = CreateFrame("Button", frameName, AC.ArenaFramesAnchor, "SecureUnitButtonTemplate")
    
    -- Basic setup
    frame:SetSize(200, 60)  -- Default size, will be updated by settings
    frame:SetFrameStrata("MEDIUM")
    frame:SetFrameLevel(2)
    frame:SetMovable(false) -- Frame itself doesn't move - anchor moves instead
    frame:SetClampedToScreen(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    
    -- Set unit attributes for secure clicking
    frame:SetAttribute("*type1", "target")          -- Left-click to target
    frame:SetAttribute("*type2", "focus")           -- Right-click to focus (direct, no menu)
    frame:SetAttribute("unit", unit)
    frame.unit = unit
    frame.id = index
    
    -- Register for clicks
    frame:RegisterForClicks("AnyUp")
    
    -- DEBUG: Frame created
    -- print("|cff00FF00[Frame Created]|r " .. frameName .. " - Left-click=target, Right-click=focus")
    
    -- DRAG HANDLERS: Ctrl+Alt+LeftClick to drag (only when unlocked)
    frame:SetScript("OnDragStart", function(self)
        -- Combat check
        if InCombatLockdown() then
            -- Silently prevent frame movement in combat (no chat spam for end users)
            return
        end
        
        -- Require frames to be unlocked
        if AC.framesLocked then
            print("|cffFF4444ArenaCore:|r Frames are locked! Use Unlock button first.")
            return
        end
        
        -- Require Ctrl+Alt+Left Click
        if not (IsControlKeyDown() and IsAltKeyDown()) then
            print("|cffFFAA00ArenaCore:|r Hold Ctrl+Alt while dragging to move frames.")
            return
        end
        
        -- Start moving the anchor (which moves all frames together)
        if AC.ArenaFramesAnchor then
            AC.ArenaFramesAnchor:Show()
            AC.ArenaFramesAnchor:StartMoving()
            AC.isDragging = true
            
            -- Store last position to minimize unnecessary updates
            local lastX, lastY = AC.ArenaFramesAnchor:GetCenter()
            
            -- Get settings once at drag start
            local db = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.positioning
            local spacing = (db and db.spacing)
            if not spacing or spacing == 0 then spacing = 21 end
            local growthDirection = (db and db.growthDirection) or "Down"
            
            -- Minimal OnUpdate - only reposition if anchor actually moved
            AC.ArenaFramesAnchor:SetScript("OnUpdate", function()
                local currentX, currentY = AC.ArenaFramesAnchor:GetCenter()
                if currentX ~= lastX or currentY ~= lastY then
                    lastX, lastY = currentX, currentY
                    
                    for i = 1, 3 do
                        local childFrame = _G["ArenaCoreFrame" .. i]
                        if childFrame and childFrame:IsShown() then
                            local offsetX, offsetY = 0, 0
                            if growthDirection == "Down" then
                                offsetY = -((i-1) * (childFrame:GetHeight() + spacing))
                            elseif growthDirection == "Up" then
                                offsetY = ((i-1) * (childFrame:GetHeight() + spacing))
                            elseif growthDirection == "Right" then
                                offsetX = ((i-1) * (childFrame:GetWidth() + spacing))
                            elseif growthDirection == "Left" then
                                offsetX = -((i-1) * (childFrame:GetWidth() + spacing))
                            end
                            childFrame:ClearAllPoints()
                            childFrame:SetPoint("TOPLEFT", AC.ArenaFramesAnchor, "TOPLEFT", offsetX, offsetY)
                        end
                    end
                end
            end)
            
            -- DEBUG: Dragging frames
            -- print("|cff00FF00ArenaCore:|r Dragging arena frames...")
        end
    end)
    
    frame:SetScript("OnDragStop", function(self)
        if AC.isDragging and AC.ArenaFramesAnchor then
            -- Stop OnUpdate before stopping movement
            AC.ArenaFramesAnchor:SetScript("OnUpdate", nil)
            
            AC.ArenaFramesAnchor:StopMovingOrSizing()
            AC.isDragging = false
            
            -- Use GetLeft/GetTop with GetEffectiveScale for pixel-perfect positioning
            local scale = AC.ArenaFramesAnchor:GetEffectiveScale()
            local left = AC.ArenaFramesAnchor:GetLeft()
            local top = AC.ArenaFramesAnchor:GetTop()
            
            if left and top and scale then
                -- Calculate absolute screen coordinates
                local newX = math.floor(left * scale + 0.5)
                local newY = math.floor(top * scale + 0.5)
                
                -- DRAG + SLIDER SYSTEM: Save dragged position as base, reset slider offsets
                -- This allows: Final Position = Dragged Base + Slider Offset
                if AC.ProfileManager and AC.ProfileManager.SetSetting then
                    -- Save the dragged position as the new base position
                    AC.ProfileManager:SetSetting("arenaFrames.positioning.draggedBaseX", newX)
                    AC.ProfileManager:SetSetting("arenaFrames.positioning.draggedBaseY", newY)
                    
                    -- Reset slider offsets to 0 (sliders now work as adjustments from dragged position)
                    AC.ProfileManager:SetSetting("arenaFrames.positioning.sliderOffsetX", 0)
                    AC.ProfileManager:SetSetting("arenaFrames.positioning.sliderOffsetY", 0)
                    
                    -- Update main position (base + offset = final)
                    AC.ProfileManager:SetSetting("arenaFrames.positioning.horizontal", newX)
                    AC.ProfileManager:SetSetting("arenaFrames.positioning.vertical", newY)
                    
                    print("|cff00FF00ArenaCore:|r Dragged to X:" .. newX .. ", Y:" .. newY)
                    print("|cffFFAA00Tip:|r Sliders now adjust from this position (range: ±800px)")
                    
                    -- CRITICAL: Reposition child elements after dragging
                    if AC.Layout then
                        C_Timer.After(0.1, function()
                            if AC.Layout.PositionChildFrames then
                                AC.Layout:PositionChildFrames(self)
                            end
                            if AC.Layout.PositionTrinkets then
                                AC.Layout:PositionTrinkets()
                            end
                            if AC.Layout.PositionRacials then
                                AC.Layout:PositionRacials()
                            end
                            if AC.Layout.PositionCastBars then
                                AC.Layout:PositionCastBars()
                            end
                            if AC.Layout.PositionDebuffs then
                                AC.Layout:PositionDebuffs()
                            end
                            if AC.Layout.PositionDispelContainers then
                                AC.Layout:PositionDispelContainers(self)
                            end
                            if AC.Layout.PositionDiminishingReturns then
                                AC.Layout:PositionDiminishingReturns()
                            end
                            -- DEBUG: Child elements repositioned
                            -- print("|cff00FF00ArenaCore:|r Child elements repositioned to respect frame parent.")
                        end)
                    end
                else
                    print("|cffFF4444ArenaCore:|r Error: Could not save position!")
                end
            end
        end
    end)
    
    -- Create all visual elements
    self:CreateFrameElements(frame, index)
    
    -- Register events for this frame
    self:RegisterFrameEvents(frame, unit)
    
    -- Store frame in our unified array
    self.frames[index] = frame
    
    -- CRITICAL FIX: Set global AC.arenaFrames reference for backward compatibility
    -- This ensures modules (KickBar, DispelTracker, TrinketsRacials) can find frames
    if not AC.arenaFrames then
        AC.arenaFrames = self.frames
    end
    
    -- Hide initially
    frame:Hide()
    
    return frame
end

function MFM:CreateFrameElements(frame, index)
    -- ========================================================================  
    -- ENHANCED WITH BEAUTIFUL ARENACORE TEXTURES
    -- ========================================================================
    
    -- DEBUG: CreateFrameElements called
    -- print("|cffFF0000[DEBUG]|r CreateFrameElements called for frame " .. index .. " - YOU SHOULD SEE THIS!")
    
    -- ArenaCore texture paths (same as your existing system)
    local A = "Interface\\AddOns\\ArenaCore\\Media\\Textures\\"
    local TEX = {
        frame_bg = A.."frame_background.tga", 
        frame_border = A.."frame_border.tga",
        frame_hover = A.."frame_hover.tga", 
        health_bg = A.."health_bg.tga", 
        health_border = A.."health_border.tga",
        health_fill = A.."health_fill.tga", 
        mana_bg = A.."mana_bg.tga",
        mana_border = A.."mana_border.tga", 
        mana_fill = A.."mana_fill.tga",
        cast_bg = A.."cast_bg.tga", 
        cast_fill = A.."cast_fill.tga"
    }
    
    -- NOTE: AC.FONT_PATH is now set at the top of the file (line 15) for early access
    
    -- Frame Background (BEAUTIFUL TEXTURE instead of solid color)
    frame.bg = frame:CreateTexture(nil, "BACKGROUND")
    frame.bg:SetAllPoints()
    frame.bg:SetTexture(TEX.frame_bg)
    frame.bg:SetTexCoord(0, 1, 0, 1) -- Full texture for proper edges
    
    -- Frame Border (BEAUTIFUL TEXTURE)
    frame.border = frame:CreateTexture(nil, "BORDER")
    frame.border:SetAllPoints()
    frame.border:SetTexture(TEX.frame_border)
    frame.border:SetTexCoord(0, 1, 0, 1) -- Full texture for proper edges
    
    -- Get bar sizes from database to avoid resize issues
    local texDb = AC.DB and AC.DB.profile and AC.DB.profile.textures
    local healthWidth = (texDb and texDb.sizing and texDb.sizing.healthWidth) or 128
    local healthHeight = (texDb and texDb.sizing and texDb.sizing.healthHeight) or 18
    -- DEBUG: Initial healthWidth
    -- print("|cffFF00FF[BAR SIZE DEBUG]|r Frame " .. index .. " - Initial healthWidth: " .. healthWidth .. " (from DB: " .. tostring(texDb and texDb.sizing and texDb.sizing.healthWidth) .. ")")
    
    -- Health Bar Container
    frame.healthBarContainer = CreateFrame("Frame", nil, frame)
    frame.healthBarContainer:SetSize(healthWidth, healthHeight + 17) -- Extra height for padding
    frame.healthBarContainer:SetPoint("TOP", frame, "TOP", 0, -5)
    
    -- Health Bar (BEAUTIFUL STYLING)
    frame.healthBar = CreateFrame("StatusBar", nil, frame.healthBarContainer)
    frame.healthBar:SetSize(healthWidth, healthHeight) -- Use database size, not SetAllPoints
    frame.healthBar:SetPoint("CENTER", frame.healthBarContainer, "CENTER", 0, 0) -- Center in container
    -- Use custom ArenaCore health texture (straight edges, not rounded)
    local healthTexturePath = TEX.health_fill
    -- DEBUG: Health texture path
    -- print("|cffFF0000[TEXTURE DEBUG]|r Health texture path: " .. tostring(healthTexturePath))
    
    -- Try multiple methods to ensure texture loads
    frame.healthBar:SetStatusBarTexture(healthTexturePath)
    
    -- Alternative method if first doesn't work
    local healthBarTexture = frame.healthBar:GetStatusBarTexture()
    if healthBarTexture then
        healthBarTexture:SetTexture(healthTexturePath)
        healthBarTexture:SetTexCoord(0, 1, 0, 1)
        healthBarTexture:SetHorizTile(false)
        healthBarTexture:SetVertTile(false)
        -- DEBUG: Health bar texture applied
        -- print("|cffFF0000[TEXTURE DEBUG]|r Health bar texture applied directly")
        
        -- Test if texture actually loaded by trying fallback
        C_Timer.After(0.1, function()
            local textureFileID = healthBarTexture:GetTexture()
            if not textureFileID or textureFileID == 0 then
                -- DEBUG: Texture failed, using fallback
                -- print("|cffFF0000[TEXTURE ERROR]|r Health texture failed to load, trying fallback")
                healthBarTexture:SetTexture("Interface\\TargetingFrame\\UI-StatusBar")
            else
                -- DEBUG: Texture loaded successfully
                -- print("|cffFF0000[TEXTURE SUCCESS]|r Health texture loaded with ID: " .. tostring(textureFileID))
            end
        end)
    end
    
    frame.healthBar:SetMinMaxValues(0, 100)
    frame.healthBar:SetValue(100)
    frame.healthBar:SetStatusBarColor(0, 1, 0)  -- Green color to make texture visible
    
    -- Health Bar Background (BEAUTIFUL TEXTURE - confirmed to exist)
    local healthBg = frame.healthBar:CreateTexture(nil, "BACKGROUND")
    healthBg:SetAllPoints()
    healthBg:SetTexture(TEX.health_bg)
    healthBg:SetTexCoord(0, 1, 0, 1)
    -- CRITICAL FIX: Set background to dark color (missing health color)
    -- Default WoW uses dark gray/black for missing health, never white
    healthBg:SetVertexColor(0.3, 0.3, 0.3, 1) -- Dark gray background
    frame.healthBar.bg = healthBg -- Store reference for potential updates
    
    -- Health Bar Border (BEAUTIFUL TEXTURE - confirmed to exist)
    local healthBorder = frame.healthBar:CreateTexture(nil, "BORDER")
    healthBorder:SetAllPoints()
    healthBorder:SetTexture(TEX.health_border)
    healthBorder:SetTexCoord(0, 1, 0, 1)
    
    -- Health Bar Text (BEAUTIFUL FONT)
    frame.healthBar.text = frame.healthBar:CreateFontString(nil, "OVERLAY")
    if AC.SafeSetFont then
        AC.SafeSetFont(frame.healthBar.text, AC.FONT_PATH, 12, "OUTLINE")
    else
        frame.healthBar.text:SetFont("Fonts\\\\FRIZQT__.TTF", 12, "OUTLINE")
    end
    frame.healthBar.text:SetPoint("CENTER", frame.healthBar, "CENTER", 0, 0)
    frame.healthBar.text:SetTextColor(1, 1, 1)
    frame.healthBar.text:SetText("100%")
    
    -- CRITICAL FIX: Create alias for compatibility with test mode code
    frame.healthBar.statusText = frame.healthBar.text
    
    -- Get mana bar sizes from database
    local resourceWidth = (texDb and texDb.sizing and texDb.sizing.resourceWidth) or 136
    local resourceHeight = (texDb and texDb.sizing and texDb.sizing.resourceHeight) or 8
    
    -- Mana Bar Container
    frame.manaBarContainer = CreateFrame("Frame", nil, frame)
    frame.manaBarContainer:SetSize(resourceWidth, resourceHeight + 7) -- Extra height for padding
    frame.manaBarContainer:SetPoint("TOP", frame.healthBarContainer, "BOTTOM", 0, -2)
    
    -- Mana Bar (BEAUTIFUL STYLING)
    frame.manaBar = CreateFrame("StatusBar", nil, frame.manaBarContainer)
    frame.manaBar:SetSize(resourceWidth, resourceHeight) -- Use database size, not SetAllPoints
    frame.manaBar:SetPoint("CENTER", frame.manaBarContainer, "CENTER", 0, 0) -- Center in container
    -- Use custom ArenaCore mana texture (straight edges, not rounded)
    local manaTexturePath = TEX.mana_fill
    -- DEBUG: Mana texture path
    -- print("|cffFF0000[TEXTURE DEBUG]|r Mana texture path: " .. tostring(manaTexturePath))
    
    -- Try multiple methods to ensure texture loads
    frame.manaBar:SetStatusBarTexture(manaTexturePath)
    
    -- Alternative method if first doesn't work
    local manaBarTexture = frame.manaBar:GetStatusBarTexture()
    if manaBarTexture then
        manaBarTexture:SetTexture(manaTexturePath)
        manaBarTexture:SetTexCoord(0, 1, 0, 1)
        manaBarTexture:SetHorizTile(false)
        manaBarTexture:SetVertTile(false)
        -- DEBUG: Mana bar texture applied
        -- print("|cffFF0000[TEXTURE DEBUG]|r Mana bar texture applied directly")
    end
    
    frame.manaBar:SetStatusBarColor(0, 0.5, 1)  -- Blue color to make texture visible
    frame.manaBar:SetMinMaxValues(0, 100)
    frame.manaBar:SetValue(100)
    
    -- Mana Bar Background (BEAUTIFUL TEXTURE - confirmed to exist)
    local manaBg = frame.manaBar:CreateTexture(nil, "BACKGROUND")
    manaBg:SetAllPoints()
    manaBg:SetTexture(TEX.mana_bg)
    manaBg:SetTexCoord(0, 1, 0, 1)
    
    -- Mana Bar Border (BEAUTIFUL TEXTURE - confirmed to exist)
    local manaBorder = frame.manaBar:CreateTexture(nil, "BORDER")
    manaBorder:SetAllPoints()
    manaBorder:SetTexture(TEX.mana_border)
    manaBorder:SetTexCoord(0, 1, 0, 1)
    
    -- Mana Bar Text (BEAUTIFUL FONT)
    frame.manaBar.text = frame.manaBar:CreateFontString(nil, "OVERLAY")
    if AC.SafeSetFont then
        AC.SafeSetFont(frame.manaBar.text, AC.FONT_PATH, 10, "OUTLINE")
    else
        frame.manaBar.text:SetFont("Fonts\\\\FRIZQT__.TTF", 10, "OUTLINE")
    end
    frame.manaBar.text:SetPoint("CENTER", frame.manaBar, "CENTER", 0, 0)
    frame.manaBar.text:SetTextColor(1, 1, 1)
    frame.manaBar.text:SetText("100%")
    
    -- CRITICAL FIX: Create alias for compatibility with test mode code
    frame.manaBar.statusText = frame.manaBar.text
    
    -- ========================================================================
    -- ABSORB BAR CREATION (Delegated to Absorbs module)
    -- ========================================================================
    if AC.Absorbs and AC.Absorbs.CreateAbsorbElements then
        AC.Absorbs:CreateAbsorbElements(frame)
    end
    
    -- ========================================================================
    -- FIXED: Create invisible overlay frame at MEDIUM strata to hold text (stay below bags)
    frame.textOverlayFrame = CreateFrame("Frame", nil, frame)
    frame.textOverlayFrame:SetAllPoints(frame)
    frame.textOverlayFrame:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
    frame.textOverlayFrame:SetFrameLevel(100) -- High level within MEDIUM strata
    
    -- Move .text (ArenaCore.lua uses .text, not .statusText) to the high-strata overlay frame
    if frame.healthBar.text then
        frame.healthBar.text:SetParent(frame.textOverlayFrame)
        frame.healthBar.text:SetDrawLayer("OVERLAY", 7)
    end
    if frame.manaBar and frame.manaBar.text then
        frame.manaBar.text:SetParent(frame.textOverlayFrame)
        frame.manaBar.text:SetDrawLayer("OVERLAY", 7)
    end
    
    -- Absorb bar created
    -- ========================================================================
    
    -- Player Name (BEAUTIFUL FONT) - Anchor to frame (not health bar) for independent positioning
    frame.playerName = frame:CreateFontString(nil, "OVERLAY")
    if AC.SafeSetFont then
        AC.SafeSetFont(frame.playerName, AC.FONT_PATH, 12, "OUTLINE")
    else
        frame.playerName:SetFont("Fonts\\\\FRIZQT__.TTF", 12, "OUTLINE")
    end
    -- NOTE: Position will be set at end of CreateArenaFrame after healthBar exists
    frame.playerName:SetTextColor(1, 1, 1)
    frame.playerName:SetText("Arena " .. index)
    
    -- Arena Number (BEAUTIFUL FONT)
    frame.arenaNumber = frame:CreateFontString(nil, "OVERLAY")
    if AC.SafeSetFont then
        AC.SafeSetFont(frame.arenaNumber, AC.FONT_PATH, 16, "OUTLINE")
    else
        frame.arenaNumber:SetFont("Fonts\\\\FRIZQT__.TTF", 16, "OUTLINE")
    end
    frame.arenaNumber:SetPoint("RIGHT", frame, "LEFT", -5, 0)
    frame.arenaNumber:SetText(index)
    frame.arenaNumber:SetTextColor(1, 0.82, 0)  -- Gold color
    
    -- Class Icon (same structure, no texture changes needed)
    frame.classIcon = frame:CreateTexture(nil, "ARTWORK")
    frame.classIcon:SetSize(32, 32)
    frame.classIcon:SetPoint("LEFT", frame, "RIGHT", 5, 0)
    -- CRITICAL: Set texture filtering to prevent distortion when scaled
    -- Using TRILINEAR filtering (true, true) for smooth scaling without blur
    frame.classIcon.SetTexture = function(self, texture, ...)
        getmetatable(self).__index.SetTexture(self, texture, true, true)
    end
    frame.classIcon:Hide()  -- Hidden until we have class data
    
    -- DELETED: Basic trinket/racial frames - using orange-bordered frames instead
    -- The actual trinket/racial frames are created via CreateTrinket/CreateRacial
    -- and stored in frame.trinketIndicator and frame.racialIndicator
    -- Our cooldown code needs to reference those instead
    
    -- Cast Bar (BEAUTIFUL STYLING)
    -- Read position from database immediately (prevents jump on reload)
    local castBarDB = AC.DB and AC.DB.profile and AC.DB.profile.castBars
    local castBarPos = castBarDB and castBarDB.positioning or {}
    local castBarSize = castBarDB and castBarDB.sizing or {}
    local cbHorizontal = castBarPos.horizontal or 2
    local cbVertical = castBarPos.vertical or -81
    local cbWidth = castBarSize.width or 227
    local cbHeight = castBarSize.height or 18
    
    frame.castBar = CreateFrame("StatusBar", nil, frame)
    frame.castBar:SetSize(cbWidth, cbHeight)
    frame.castBar:SetPoint("TOP", frame, "TOP", cbHorizontal, cbVertical)
    frame.castBar:SetStatusBarTexture(TEX.cast_fill)  -- Your beautiful texture
    -- REMOVED: Hardcoded orange color - let CastBar module handle spell school colors dynamically
    -- frame.castBar:SetStatusBarColor(1, 0.7, 0)  -- Orange
    frame.castBar:SetMinMaxValues(0, 1)
    frame.castBar:Hide()
    
    -- Cast Bar Background
    frame.castBar.bg = frame.castBar:CreateTexture(nil, "BACKGROUND")
    frame.castBar.bg:SetAllPoints()
    frame.castBar.bg:SetTexture(TEX.cast_bg, true, true) -- TRILINEAR filtering for smooth scaling
    frame.castBar.bg:SetTexCoord(0, 1, 0, 1)
    
    -- Uninterruptible Cast Overlay (angled lines texture from absorb shield)
    frame.castBar.shieldOverlay = frame.castBar:CreateTexture(nil, "ARTWORK", nil, 1)
    frame.castBar.shieldOverlay:SetAllPoints()
    frame.castBar.shieldOverlay:SetTexture("Interface\\RaidFrame\\Shield-Overlay", true, true) -- Already has filtering
    frame.castBar.shieldOverlay:SetAlpha(0.5) -- Semi-transparent overlay
    frame.castBar.shieldOverlay:Hide() -- Hidden by default, shown for uninterruptible casts
    
    -- Cast Bar Border Frame (MEDIUM strata to stay below bags)
    frame.castBar.borderFrame = CreateFrame("Frame", nil, frame.castBar)
    frame.castBar.borderFrame:SetAllPoints()
    frame.castBar.borderFrame:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
    frame.castBar.borderFrame:SetFrameLevel(100) -- High level within MEDIUM strata
    
    -- Thick black border on all 4 sides (2px for prominence)
    if AC and AC.CreateFlatTexture then
        -- Top border
        local topBorder = AC:CreateFlatTexture(frame.castBar.borderFrame, "OVERLAY", 7, {0, 0, 0, 1}, 1)
        topBorder:SetPoint("TOPLEFT", 0, 0)
        topBorder:SetPoint("TOPRIGHT", 0, 0)
        topBorder:SetHeight(2)
        
        -- Bottom border
        local bottomBorder = AC:CreateFlatTexture(frame.castBar.borderFrame, "OVERLAY", 7, {0, 0, 0, 1}, 1)
        bottomBorder:SetPoint("BOTTOMLEFT", 0, 0)
        bottomBorder:SetPoint("BOTTOMRIGHT", 0, 0)
        bottomBorder:SetHeight(2)
        
        -- Left border
        local leftBorder = AC:CreateFlatTexture(frame.castBar.borderFrame, "OVERLAY", 7, {0, 0, 0, 1}, 1)
        leftBorder:SetPoint("TOPLEFT", 0, 0)
        leftBorder:SetPoint("BOTTOMLEFT", 0, 0)
        leftBorder:SetWidth(2)
        
        -- Right border
        local rightBorder = AC:CreateFlatTexture(frame.castBar.borderFrame, "OVERLAY", 7, {0, 0, 0, 1}, 1)
        rightBorder:SetPoint("TOPRIGHT", 0, 0)
        rightBorder:SetPoint("BOTTOMRIGHT", 0, 0)
        rightBorder:SetWidth(2)
    end
    
    -- CRITICAL FIX: Hide border frame by default (only show when casting)
    frame.castBar.borderFrame:Hide()
    
    -- Cast Bar Text (BEAUTIFUL FONT)
    frame.castBar.text = frame.castBar:CreateFontString(nil, "OVERLAY")
    if AC.SafeSetFont then
        AC.SafeSetFont(frame.castBar.text, AC.FONT_PATH, 10, "OUTLINE")
    else
        frame.castBar.text:SetFont("Fonts\\\\FRIZQT__.TTF", 10, "OUTLINE")
    end
    frame.castBar.text:SetPoint("CENTER")
    frame.castBar.text:SetTextColor(1, 1, 1)
    
    -- Add cast bar spell icon (positioned to the left of cast bar)
    frame.castBar.spellIcon = CreateFrame("Frame", nil, frame.castBar)
    frame.castBar.spellIcon:SetSize(16, 16)
    frame.castBar.spellIcon:SetPoint("RIGHT", frame.castBar, "LEFT", -4, 0)
    
    -- Class-specific spells for realistic test mode
    local classSpells = {
        ["Deathknight"] = {
            name = "Death Coil",
            icon = "Interface\\Icons\\Spell_Shadow_DeathCoil"
        },
        ["Mage"] = {
            name = "Fireball", 
            icon = "Interface\\Icons\\Spell_Fire_FlameBolt"
        },
        ["Hunter"] = {
            name = "Aimed Shot",
            icon = "Interface\\Icons\\INV_Spear_07"
        }
    }
    
    -- Get spell info for this frame's class
    local testClasses = {"Deathknight", "Mage", "Hunter"}
    local testClass = testClasses[index] or "Mage"
    local spellInfo = classSpells[testClass] or classSpells["Mage"]
    frame.castBar.text:SetText(spellInfo.name)
    
    -- Spell icon texture with class-specific icon
    local spellIconTexture = frame.castBar.spellIcon:CreateTexture(nil, "ARTWORK")
    spellIconTexture:SetAllPoints()
    spellIconTexture:SetTexture(spellInfo.icon, true, true) -- TRILINEAR filtering for smooth scaling
    spellIconTexture:SetTexCoord(0, 1, 0, 1) -- Full texture for rounded edges
    
    -- Apply ArenaCore's proper black border styling to spell icon
    if AC and AC.StyleIcon then
        AC:StyleIcon(spellIconTexture, frame.castBar.spellIcon, true)
    end
    
    frame.castBar.spellIcon.texture = spellIconTexture
    
    -- MISSING CRITICAL COMPONENTS: Add proper class icon creation
    local testClasses = {"Deathknight", "Mage", "Hunter"} -- Match your test setup
    local testClass = testClasses[index]
    frame.classIcon = self:CreateClassIcon(frame, testClass)
    
    -- MISSING CRITICAL COMPONENTS: Add trinket and racial with proper styling
    frame.trinketIndicator = self:CreateTrinket(frame)
    
    -- PHASE 3: Use TrinketsRacials module for racial creation (centralized logic)
    if AC.TrinketsRacials and AC.TrinketsRacials.CreateRacialIndicator then
        frame.racialIndicator = AC.TrinketsRacials:CreateRacialIndicator(frame, index)
    end
    -- No fallback - module must be loaded for racials to work
    
    -- MISSING CRITICAL COMPONENTS: Add spec icon (bottom-left small icon)
    frame.specIcon = self:CreateSpecIcon(frame, testClass)
    
    -- Update player name to use realistic test names
    local testPlayerNames = {"Survivable", "Patymorph", "Easymodex"}
    frame.playerName:SetText(testPlayerNames[index] or "Player " .. index)
    
    -- CRITICAL: Create DR (Diminishing Returns) icons for frame via module
    local drModule = GetDRModule()
    if drModule and drModule.EnsureFrame then
        drModule:EnsureFrame(frame)
    end
    
    -- DEBUFF SYSTEM INTEGRATION: Create debuff container for enemy aura tracking
    local debuffModule = GetDebuffModule()
    if debuffModule and debuffModule.EnsureContainer then
        frame.debuffContainer = debuffModule:EnsureContainer(frame)
    else
        frame.debuffContainer = self:CreateDebuffContainer(frame)
    end

    -- DISPEL SYSTEM INTEGRATION
    local dispelModule = GetDispelModule()
    if dispelModule and dispelModule.EnsureFrame then
        dispelModule:EnsureFrame(frame)
    end
    
    -- CRITICAL FIX: TRINKETS & RACIALS MODULE ATTACHMENT
    -- This is what makes trinkets and racials actually track cooldowns!
    if AC.TrinketsRacials and AC.TrinketsRacials.Attach then
        AC.TrinketsRacials:Attach(frame, unit)
    end
    
    -- CRITICAL FIX: TRIBADGES (CLASS PACKS) ATTACHMENT - Attach TriBadges to frame
    -- This is what makes the "Enable Class Packs" checkbox actually work!
    if _G.ArenaCore and _G.ArenaCore.TriBadges and _G.ArenaCore.TriBadges.Attach then
        C_Timer.After(0.1, function()
            -- Use frame.unit since 'unit' local variable is out of scope in this closure
            _G.ArenaCore.TriBadges:Attach(frame, frame.unit)
            -- DEBUG: TriBadges attached
            -- print("|cff00FF00[TRIBADGES]|r Attached to frame " .. index .. " (" .. frame.unit .. ")")
        end)
    end
    
    -- TARGET HIGHLIGHT SYSTEM: Create target outline texture
    if AC.TargetHighlight and AC.TargetHighlight.CreateOutline then
        AC.TargetHighlight:CreateOutline(frame)
    end
    
    -- CRITICAL FIX: Anchor player name to FRAME (not healthBar) for independent positioning
    if frame.playerName then
        local general = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.general
        if general then
            local nameX = general.playerNameX or 52
            local nameY = general.playerNameY or 0
            frame.playerName:ClearAllPoints()
            -- CRITICAL: Anchor to frame, not healthBar, so it doesn't move when bars move
            frame.playerName:SetPoint("TOPLEFT", frame, "TOPLEFT", nameX, nameY)
        end
    end
    
    -- DEBUG: Frame created with styling
    -- print("|cffFFAA00ArenaCore Master:|r Frame " .. index .. " created with BEAUTIFUL STYLING! 🎨")
end

-- ============================================================================
-- MISSING CRITICAL VISUAL COMPONENT CREATORS
-- ============================================================================

function MFM:CreateClassIcon(parent, class)
    -- Create class icon frame with HIGH strata to show above health/mana bars
    local icon = CreateFrame("Frame", nil, parent)
    icon:SetSize(36, 36)
    icon:SetPoint("LEFT", 8, 0)
    icon:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
    icon:SetFrameLevel(50) -- High level within MEDIUM strata to appear above health/mana bars
    
    -- CRITICAL: Prevent any automatic styling systems
    icon.preventStyling = true
    
    -- Create class icon texture - will show above bars due to HIGH strata
    local classIcon = icon:CreateTexture(nil, "ARTWORK")
    classIcon:SetSize(28, 28)
    classIcon:SetPoint("CENTER")
    
    -- Create overlay for your custom borders - HIGHEST priority
    local overlay = icon:CreateTexture(nil, "OVERLAY")
    overlay:SetAllPoints(icon) -- Use full frame size for detailed borders
    overlay:SetTexCoord(0, 1, 0, 1) -- NO CROPPING - preserve full overlay detail
    
    -- Dynamic class detection for arena/open world
    local function UpdateClassIcon(overrideClass)
        local detectedClass = overrideClass or class
        
        -- If no class provided, try to detect from unit
        if not detectedClass or detectedClass == "" then
            if parent.unit then
                local unitClass = UnitClass(parent.unit)
                if unitClass then
                    detectedClass = unitClass:upper()
                end
            end
        end
        
        -- CRITICAL FIX: Ensure detectedClass is uppercase for consistency
        if detectedClass then
            detectedClass = detectedClass:upper()
        end
        
        -- Set textures based on detected class
        if detectedClass and detectedClass ~= "" then
            local iconPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\" .. detectedClass .. ".tga"
            classIcon:SetTexture(iconPath, true, true) -- TRILINEAR filtering for smooth scaling
            
            local overlayPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\" .. detectedClass:lower() .. "overlay.tga"
            overlay:SetTexture(overlayPath, true, true) -- TRILINEAR filtering for smooth scaling
            
            -- CRITICAL FIX: Apply class-specific texture coordinate adjustments AFTER setting texture
            -- This ensures coordinates are applied every time the class icon is updated
            if detectedClass == "DEATHKNIGHT" then
                classIcon:SetTexCoord(0.02, 0.98, 0.02, 0.98) -- Much tighter crop for Death Knight
            elseif detectedClass == "WARRIOR" then
                classIcon:SetTexCoord(0.1, 0.9, 0.1, 0.9) -- Standard crop (same as other classes)
            else
                classIcon:SetTexCoord(0.1, 0.9, 0.1, 0.9) -- Standard crop for other classes
            end
        else
            -- Fallback for test mode or unknown class
            classIcon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
            classIcon:SetTexCoord(0.1, 0.9, 0.1, 0.9) -- Standard crop for fallback
            overlay:SetTexture("Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\orangeoverlay.tga")
        end
    end
    
    -- Initial update
    UpdateClassIcon()
    
    -- Store references (maintain compatibility with both old and new systems)
    icon.classIcon = classIcon -- New system reference
    icon.icon = classIcon -- Old system reference for compatibility
    icon.overlay = overlay
    icon.UpdateClassIcon = UpdateClassIcon -- Allow external updates
    
    return icon
end

function MFM:CreateTrinket(parent)
    local t = CreateFrame("Frame", nil, parent)
    t:SetSize(20, 20)
    t:SetPoint("TOPRIGHT", -6, -6)
    -- Set MEDIUM strata to stay below bags while rendering above health/mana bars
    t:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
    t:SetFrameLevel(50) -- High level within MEDIUM strata
    
    -- Orange overlay border only (ArenaCore styling - no background texture)
    local br = t:CreateTexture(nil, "OVERLAY")
    br:SetAllPoints()
    br:SetTexture("Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\orangeoverlay.tga")
    br:SetTexCoord(0, 1, 0, 1) -- Full texture for rounded overlay
    
    -- Trinket icon texture (full texture for rounded edges)
    local icon = t:CreateTexture(nil, "ARTWORK")
    icon:SetSize(16, 16)
    icon:SetPoint("CENTER")
    icon:SetTexCoord(0, 1, 0, 1) -- Full texture for rounded edges
    
    -- REMOVED: Local GetTrinketIcon() duplicate (lines 1818-1831)
    -- Using main GetTrinketIcon() function which delegates to TrinketsRacials module
    
    -- Set initial trinket icon using main function
    local trinketTexture = GetTrinketIcon()
    icon:SetTexture(trinketTexture)
    
    -- Create cooldown frame (using helper to block OmniCC)
    local cd = AC:CreateCooldown(t, nil, "CooldownFrameTemplate")
    cd:SetAllPoints()
    cd:SetDrawBling(false) -- Disable bling animation
    cd:SetHideCountdownNumbers(true) -- CRITICAL: Hide Blizzard's numbers so our custom text shows
    
    -- Create cooldown text with user-configurable font size
    local txt = cd:CreateFontString(nil, "OVERLAY")
    local trinketSettings = AC.DB and AC.DB.profile and AC.DB.profile.trinkets
    local fontSize = trinketSettings and trinketSettings.sizing and trinketSettings.sizing.fontSize or 12
    txt:SetFont("Interface\\AddOns\\ArenaCore\\Media\\Fonts\\arenacore.ttf", fontSize, "OUTLINE")
    txt:SetPoint("CENTER", t, "CENTER", 0, 0)
    txt:SetTextColor(1, 1, 1, 1)
    txt:SetJustifyH("CENTER")
    txt:SetJustifyV("MIDDLE")
    txt:Show()
    cd.Text = txt
    
    -- Store references
    t.texture = icon
    t.icon = icon  -- Also store as .icon for consistency
    t.cooldown = cd
    t.border = br
    t.text = txt  -- Make text accessible for UpdateElement
    t.txt = txt   -- Also store as .txt for looping cooldown code
    
    return t
end

-- PHASE 3: REMOVED - Racial creation now handled by TrinketsRacials module
-- Use AC.TrinketsRacials:CreateRacialIndicator() instead

function MFM:CreateSpecIcon(parent, class)
    -- Create spec icon frame (small icon in bottom-left)
    local specIcon = CreateFrame("Frame", nil, parent)
    specIcon:SetSize(20, 20)
    specIcon:SetPoint("BOTTOMLEFT", 5, 5) -- Bottom-left corner positioning
    specIcon:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
    specIcon:SetFrameLevel(60) -- High level within MEDIUM strata
    
    -- Spec icon texture
    local icon = specIcon:CreateTexture(nil, "ARTWORK")
    icon:SetSize(16, 16)
    icon:SetPoint("CENTER")
    icon:SetTexCoord(0.1, 0.9, 0.1, 0.9)
    
    -- Class-specific spec icons for test mode
    local classSpecs = {
        ["Deathknight"] = {
            name = "Unholy",
            icon = "Interface\\Icons\\Spell_Deathknight_UnholyPresence"
        },
        ["Mage"] = {
            name = "Fire",
            icon = "Interface\\Icons\\Spell_Fire_FlameBolt"
        },
        ["Hunter"] = {
            name = "Marksmanship",
            icon = "Interface\\Icons\\Ability_Hunter_FocusedAim"
        }
    }
    
    -- Set spec icon based on class
    local specInfo = classSpecs[class] or classSpecs["Deathknight"]
    icon:SetTexture(specInfo.icon)
    
    -- Apply ArenaCore's proper black border styling
    if AC and AC.StyleIcon then
        AC:StyleIcon(icon, specIcon, true)
    end
    
    -- Store references
    specIcon.icon = icon
    specIcon.specName = specInfo.name
    
    return specIcon
end

function MFM:CreateDebuffContainer(parent)
    -- Create debuff container matching ArenaTracking.lua system
    local c = CreateFrame("Frame", nil, parent)
    c:SetSize(220, 24)
    
    -- Apply user positioning settings
    local debuffSettings = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies and AC.DB.profile.moreGoodies.debuffs
    local horizontal = debuffSettings and debuffSettings.positioning and debuffSettings.positioning.horizontal or 8
    local vertical = debuffSettings and debuffSettings.positioning and debuffSettings.positioning.vertical or 6
    
    c:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", horizontal, vertical)
    c:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
    c:SetFrameLevel(40) -- Above health bars, below trinkets/racials within MEDIUM strata
    c.debuffs = {}
    c:Hide() -- Hidden by default, shown when debuffs detected
    return c
end



function MFM:RegisterFrameEvents(frame, unit)
    -- Set up individual frame event handling
    frame:SetScript("OnEvent", function(self, event, eventUnit, ...)
        -- Only process events for our unit
        if eventUnit and eventUnit ~= unit then return end
        
        MFM:HandleFrameEvent(self, event, eventUnit, ...)
    end)
    
    -- Register unit-specific events (PROVEN TO WORK!)
    frame:RegisterUnitEvent("UNIT_HEALTH", unit)
    frame:RegisterUnitEvent("UNIT_MAXHEALTH", unit)
    frame:RegisterUnitEvent("UNIT_POWER_UPDATE", unit)
    frame:RegisterUnitEvent("UNIT_MAXPOWER", unit)
    frame:RegisterUnitEvent("UNIT_NAME_UPDATE", unit)
    
    -- REMOVED: Cast bar events now handled by ArenaTracking (lines 9320-9325)
    -- Duplicate registration was causing events to fire twice per spell cast
    -- frame:RegisterUnitEvent("UNIT_SPELLCAST_START", unit)
    -- frame:RegisterUnitEvent("UNIT_SPELLCAST_STOP", unit)
    -- frame:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_START", unit)
    -- frame:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", unit)
    -- frame:RegisterUnitEvent("UNIT_SPELLCAST_INTERRUPTED", unit)
    -- frame:RegisterUnitEvent("UNIT_SPELLCAST_FAILED", unit)
    -- frame:RegisterUnitEvent("UNIT_SPELLCAST_EMPOWER_START", unit)
    -- frame:RegisterUnitEvent("UNIT_SPELLCAST_EMPOWER_STOP", unit)
    
    -- CRITICAL FIX: Absorb and Aura events for real-time tracking
    frame:RegisterUnitEvent("UNIT_ABSORB_AMOUNT_CHANGED", unit)
    frame:RegisterUnitEvent("UNIT_HEAL_ABSORB_AMOUNT_CHANGED", unit)
    frame:RegisterUnitEvent("UNIT_AURA", unit)
    
    -- REMOVED: MFM event frame handles this globally (line 2660)
    -- Duplicate registration was causing prep room event to fire 4 times
    -- frame:RegisterEvent("ARENA_PREP_OPPONENT_SPECIALIZATIONS")
    
    -- DEBUG: Events registered
    -- print("|cffFFAA00ArenaCore Master:|r Events registered for frame " .. frame.id)
end

function MFM:HandleFrameEvent(frame, event, eventUnit, ...)
    -- Handle events based on current state
    if self.isTestMode then
        -- Test mode - ignore real events
        return
    end
    
    local unit = frame.unit
    
    if event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
        self:UpdateHealth(frame)
    elseif event == "UNIT_POWER_UPDATE" or event == "UNIT_MAXPOWER" then
        self:UpdatePower(frame)
    elseif event == "UNIT_NAME_UPDATE" then
        self:UpdateName(frame)
    elseif event == "ARENA_PREP_OPPONENT_SPECIALIZATIONS" then
        self:HandlePrepRoom(frame)
    elseif event:match("^UNIT_SPELLCAST") then
        -- Cast bar events need unit parameter
        self:UpdateCastBar(frame, frame.unit, event)
    elseif event == "UNIT_ABSORB_AMOUNT_CHANGED" or event == "UNIT_HEAL_ABSORB_AMOUNT_CHANGED" then
        -- CRITICAL FIX: Update absorb bars when shields change
        if AC.Absorbs and AC.Absorbs.UpdateAbsorbBar then
            AC.Absorbs:UpdateAbsorbBar(frame)
        end
    elseif event == "UNIT_AURA" then
        -- CRITICAL FIX: Update absorbs and immunities on aura changes
        if AC.Absorbs and AC.Absorbs.UpdateAbsorbBar then
            AC.Absorbs:UpdateAbsorbBar(frame)
        end
        if AC.ImmunityTracker and AC.ImmunityTracker.UpdateImmunityGlow then
            AC.ImmunityTracker:UpdateImmunityGlow(frame, unit)
        end
    end
end

-- Update health bar
function MFM:UpdateHealth(frame)
    if not frame or not frame.healthBar then return end
    
    local unit = frame.unit
    if not unit or not UnitExists(unit) then return end
    
    local health = UnitHealth(unit)
    local maxHealth = UnitHealthMax(unit)
    
    frame.healthBar:SetMinMaxValues(0, maxHealth)
    frame.healthBar:SetValue(health)
    
    -- CRITICAL FIX: Check if unit is feigning death - preserve dimmed alpha
    local isFeigning = frame.__isFeign
    
    -- CRITICAL FIX: Only update health bar color if NOT feigning
    -- This prevents overriding feign death visual states
    if not isFeigning then
        -- CRITICAL: Update health bar color based on class (fix for green bar bug)
        local general = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.general
        local useClassColors = general and general.useClassColors ~= false
        
        if useClassColors then
            local _, classFile = UnitClass(unit)
            if classFile and RAID_CLASS_COLORS[classFile] then
                local classColor = RAID_CLASS_COLORS[classFile]
                frame.healthBar:SetStatusBarColor(classColor.r, classColor.g, classColor.b, 1)
                
                -- DEBUG: Track color changes (DISABLED - uncomment for debugging)
                -- print("|cff00FF00[HEALTH COLOR]|r " .. unit .. " set to class color: " .. classFile .. " (" .. classColor.r .. ", " .. classColor.g .. ", " .. classColor.b .. ")")
            else
                -- Fallback to green if class not detected
                frame.healthBar:SetStatusBarColor(0, 1, 0, 1)
                
                -- DEBUG: Track when class not detected (DISABLED - uncomment for debugging)
                -- print("|cffFFAA00[HEALTH COLOR]|r " .. unit .. " class not detected, using green fallback")
            end
        else
            -- Use default green when class colors are disabled
            frame.healthBar:SetStatusBarColor(0, 1, 0, 1)
        end
    end
    
    -- Update health text if it exists
    if frame.healthBar.text then
        local healthPercent = (maxHealth > 0) and math.floor((health / maxHealth) * 100) or 0
        frame.healthBar.text:SetText(healthPercent .. "%")
    end
    
    -- CRITICAL FIX: Restore feign death alpha if unit is feigning
    -- This ensures health updates don't override the dimmed state
    if isFeigning and frame.healthBar then
        frame.healthBar:SetAlpha(0.4)
    end
    
    -- Update absorb bars
    if AC.Absorbs and AC.Absorbs.UpdateAbsorbBar then
        AC.Absorbs:UpdateAbsorbBar(frame)
    end
    
    -- Update immunity glow
    if AC.ImmunityTracker and AC.ImmunityTracker.UpdateImmunityGlow then
        AC.ImmunityTracker:UpdateImmunityGlow(frame, frame.unit)
    end
end

-- Update power/mana bar
function MFM:UpdatePower(frame)
    if not frame or not frame.manaBar then return end
    
    local unit = frame.unit
    if not unit or not UnitExists(unit) then return end
    
    local power = UnitPower(unit)
    local maxPower = UnitPowerMax(unit)
    
    frame.manaBar:SetMinMaxValues(0, maxPower)
    frame.manaBar:SetValue(power)
    
    -- Update power text if it exists
    if frame.manaBar.text then
        local powerPercent = (maxPower > 0) and math.floor((power / maxPower) * 100) or 0
        frame.manaBar.text:SetText(powerPercent .. "%")
    end
    
    -- CRITICAL FIX: Restore feign death alpha if unit is feigning
    -- This ensures power updates don't override the dimmed state
    local isFeigning = frame.__isFeign
    if isFeigning and frame.manaBar then
        frame.manaBar:SetAlpha(0.4)
    end
end

-- Update player name
function MFM:UpdateName(frame)
    if not frame or not frame.playerName then return end
    
    -- CRITICAL FIX: Don't override test mode names
    if AC.testModeEnabled then return end
    
    local unit = frame.unit
    if not unit or not UnitExists(unit) then return end
    
    local name = UnitName(unit)
    if name then
        frame.playerName:SetText(name)
    end
end

-- Update all frame data
function MFM:UpdateFrameData(frame)
    if not frame then return end
    
    self:UpdateHealth(frame)
    self:UpdatePower(frame)
    self:UpdateName(frame)
end

-- Handle prep room (arena gates)
function MFM:HandlePrepRoom(frame)
    if not frame then return end
    
    -- In prep room, update spec icons and class info
    local unit = frame.unit
    if not unit or not UnitExists(unit) then return end
    
    -- Get spec info
    local specID = GetArenaOpponentSpec(frame.id)
    if specID and specID > 0 then
        local _, specName, _, icon, _, class = GetSpecializationInfoByID(specID)
        
        -- Update class icon if available - USE CUSTOM ARENACORE ICONS
        if frame.classIcon and class then
            local classFile = class
            -- FIXED: Check if class icons are enabled before showing
            local db = AC.DB and AC.DB.profile
            local classEnabled = db and db.classIcons and db.classIcons.enabled
            if classEnabled ~= false then
                -- CRITICAL FIX: Use custom ArenaCore class icons instead of default WoW icons
                -- This fixes the bug where fresh installs show default icons until reload
                if frame.classIcon.UpdateClassIcon then
                    -- New system with custom icons
                    frame.classIcon.UpdateClassIcon(classFile)
                    frame.classIcon:Show()
                elseif frame.classIcon.icon then
                    -- Fallback: set custom class icon texture directly
                    local iconPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\" .. classFile .. ".tga"
                    frame.classIcon.icon:SetTexture(iconPath, true, true)
                    frame.classIcon:Show()
                else
                    -- Legacy fallback (should not be used)
                    local iconPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\" .. classFile .. ".tga"
                    frame.classIcon:SetTexture(iconPath, true, true)
                    frame.classIcon:Show()
                end
            else
                frame.classIcon:Hide()
            end
        end
        
        -- Update spec icon if available
        if frame.specIcon and icon then
            -- FIXED: Check if spec icons are enabled before showing
            local db = AC.DB and AC.DB.profile
            local specEnabled = db and db.specIcons and db.specIcons.enabled
            if specEnabled ~= false then
                -- CRITICAL FIX: Use .icon not .texture (consistent with frame structure)
                if frame.specIcon.icon then
                    frame.specIcon.icon:SetTexture(icon)
                end
                frame.specIcon:Show()
            else
                frame.specIcon:Hide()
            end
        end
        
        -- PHASE 3: Racial visibility now handled by TrinketsRacials module
        -- Module's RefreshFrame handles prep room hiding automatically
    end
    
    -- Update basic info
    self:UpdateFrameData(frame)
end

-- OLD MFM HANDLERS - NO LONGER USED (using FrameManager UNIT_SPELLCAST_SUCCEEDED instead)
-- These are kept for reference but not called anywhere
--[[
function MFM:HandleCombatLog()
    -- DEPRECATED: Now using FrameManager:HandleTrinketRacialSpell with UNIT_SPELLCAST_SUCCEEDED
end

function MFM:HandleTrinketSpell(frame, spellID)
    -- DEPRECATED: Now using FrameManager:UpdateTrinket
end

function MFM:HandleRacialSpell(frame, spellID)
    -- DEPRECATED: Now using FrameManager:UpdateRacial
end
--]]

-- ============================================================================
-- FRAME CREATION HELPERS
-- ============================================================================

local function CreateFrameBackground(frame)
  local bg = frame:CreateTexture(nil, "BACKGROUND"); bg:SetAllPoints(); bg:SetTexture(TEX.frame_bg); Trim(bg);
  local border = frame:CreateTexture(nil, "BORDER"); border:SetAllPoints(); border:SetTexture(TEX.frame_border); Trim(border);
  local hover = frame:CreateTexture(nil, "OVERLAY", nil, 1); hover:SetAllPoints(); hover:SetTexture(TEX.frame_hover); Trim(hover); hover:SetAlpha(0);
  local sel = frame:CreateTexture(nil, "OVERLAY", nil, 2); sel:SetAllPoints(); sel:SetTexture(TEX.frame_hover); Trim(sel); sel:SetBlendMode("ADD"); sel:SetVertexColor(unpack(SELECTION_TINT)); sel:SetAlpha(0);
  frame.background, frame.border, frame.hoverGlow, frame.selectionGlow = bg, border, hover, sel
end

local function CreateBar(parent, w, h, bgTex, borderTex, fillTex, x, y)
  -- Create actual StatusBar widget
  local statusBar = CreateFrame("StatusBar", nil, parent)
  statusBar:SetSize(w, h)
  -- Don't set initial position here - let UpdateBarPositions handle it
  statusBar:SetMinMaxValues(0, 100)
  statusBar:SetValue(100)
  statusBar:SetStatusBarTexture(fillTex)
  
  -- Background texture
  local bg = statusBar:CreateTexture(nil, "BACKGROUND")
  bg:SetAllPoints()
  bg:SetTexture(bgTex)
  Trim(bg)
  
  -- Border texture  
  local border = statusBar:CreateTexture(nil, "BORDER")
  border:SetAllPoints()
  border:SetTexture(borderTex)
  Trim(border)
  
  -- Store references for compatibility
  statusBar.background = bg
  statusBar.border = border
  statusBar.fill = statusBar:GetStatusBarTexture() -- This is the StatusBar's built-in fill
  
  return statusBar
end

-- PHASE 1.3: Duplicate CreateTrinket removed (kept MFM:CreateTrinket at line ~1843)

-- PHASE 1.3: Duplicate CreateRacial removed (kept MFM:CreateRacial at line ~1899)

-- PHASE 1.3: Duplicate CreateSpecIcon removed (kept MFM:CreateSpecIcon at line ~1972)

-- PHASE 1.3: Duplicate CreateDebuffContainer removed (kept MFM:CreateDebuffContainer at line ~2020)

local function CreateTargetIndicator(parent)
  local ind = CreateFrame("Frame", nil, parent); ind:SetSize(8,16); ind:SetPoint("RIGHT", parent, "LEFT", 4, 0); ind:SetAlpha(0);
  local t = ind:CreateTexture(nil, "OVERLAY"); t:SetAllPoints(); t:SetTexture(TEX.target_arrow); Trim(t);
  ind.texture = t
  return ind
end

-- ============================================================================
-- MASTER FRAME MANAGER INITIALIZATION
-- ============================================================================

function MFM:CreateFrames()
    -- Create all arena frames if they don't exist
    if not self.frames[1] then
        -- DEBUG: Creating arena frames
        -- print("|cffFFAA00ArenaCore Master:|r Creating arena frames...")
        
        for i = 1, MAX_ARENA_ENEMIES do
            self:CreateArenaFrame(i)
        end
        
        -- DEBUG: Arena frames created
        -- print("|cffFFAA00ArenaCore Master:|r " .. MAX_ARENA_ENEMIES .. " arena frames created")
    else
        -- DEBUG: Arena frames already exist
        -- print("|cffFFAA00ArenaCore Master:|r Arena frames already exist")
    end
end

function MFM:Initialize()
    -- Prevent duplicate initialization
    if self.frames[1] then
        -- DEBUG: Already initialized
        -- print("|cffFFAA00ArenaCore Master:|r Already initialized")
        return
    end
    
    -- DEBUG: Initializing Master Frame System
    -- print("|cffFFAA00ArenaCore Master:|r Initializing Master Frame System")
    
    -- Create frames ONCE at initialization
    for i = 1, MAX_ARENA_ENEMIES do
        self:CreateArenaFrame(i)
    end
    
    -- CRITICAL FIX: Set global AC.arenaFrames reference for module compatibility
    AC.arenaFrames = self.frames
    
    -- Set up global event handling
    self:SetupGlobalEvents()
    
    -- CRITICAL: Register cast bar, trinket, and racial events
    AC.FrameManager:RegisterDebuffEvents()
    
    -- Apply saved settings to frames
    self:UpdateFramePositions()
    
    -- Hide all frames initially
    self:HideAllFrames()
    
    -- DEBUG: Master System initialized
    -- print("|cffFFAA00ArenaCore Master:|r Master System initialized - 3 unified frames created")
end

function MFM:SetupGlobalEvents()
    -- Create global event frame
    if not self.eventFrame then
        self.eventFrame = CreateFrame("Frame")
    end
    
    -- CONSOLIDATION: Register ALL arena events in ONE place (no more dual systems!)
    -- Basic zone/login events
    self.eventFrame:RegisterEvent("PLAYER_LOGIN")
    self.eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
    self.eventFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
    
    -- Arena-specific events
    self.eventFrame:RegisterEvent("ARENA_PREP_OPPONENT_SPECIALIZATIONS") -- Prep room
    self.eventFrame:RegisterEvent("ARENA_OPPONENT_UPDATE") -- CRITICAL: Stealth handling
    self.eventFrame:RegisterEvent("GROUP_ROSTER_UPDATE") -- Solo Shuffle round transitions
    
    -- Unit update events (for arena units only - registered per-frame)
    self.eventFrame:RegisterEvent("UNIT_HEALTH")
    self.eventFrame:RegisterEvent("UNIT_MAXHEALTH")
    self.eventFrame:RegisterEvent("UNIT_POWER_UPDATE")
    self.eventFrame:RegisterEvent("UNIT_MAXPOWER")
    self.eventFrame:RegisterEvent("UNIT_NAME_UPDATE")
    
    -- Match state events
    self.eventFrame:RegisterEvent("PVP_MATCH_STATE_CHANGED")
    self.eventFrame:RegisterEvent("UNIT_TARGETABLE_CHANGED")
    
    -- Set up event handler
    self.eventFrame:SetScript("OnEvent", function(self, event, ...)
        MFM:HandleGlobalEvent(event, ...)
    end)
    
    -- Debug removed for clean release
    -- print("|cff00FF00[ArenaCore]|r MasterFrameManager: ALL events consolidated into ONE system ✅")
end

function MFM:HandleGlobalEvent(event, ...)
    -- Skip all arena updates if in test mode (except LOGIN)
    if event ~= "PLAYER_LOGIN" and self.isTestMode then return end
    
    if event == "PLAYER_LOGIN" then
        -- Initialize on login if not already done
        if not self.frames[1] then
            self:Initialize()
        end
        
    elseif event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then
        -- Check arena state (PROVEN PATTERN!)
        self:CheckArenaState()
        
        -- CRITICAL FIX: Update dispel frames when entering world/changing zones (like other modules)
        if AC.UpdateDispelFrames then
            C_Timer.After(0.5, function()
                AC:UpdateDispelFrames()
            end)
        end
        
    elseif event == "ARENA_PREP_OPPONENT_SPECIALIZATIONS" then
        -- Prep room - show frames with specs
        self:HandlePrepRoom()
        
    elseif event == "ARENA_OPPONENT_UPDATE" then
        -- CRITICAL: Re-enabled for racial/trinket/dispel updates
        -- ArenaFrameStealth.lua handles alpha, MismatchHandler handles spec/class
        -- This handler updates racials, trinkets, dispels, health, power, names
        local unit, updateType = ...
        self:HandleArenaOpponentUpdate(unit, updateType)
        
    elseif event == "GROUP_ROSTER_UPDATE" then
        -- Solo Shuffle round transitions
        self:HandleSoloShuffleTransition()
        
    elseif event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
        -- Health updates for arena units
        local unit = ...
        if unit and unit:match("^arena[1-3]$") then
            self:UpdateUnitHealth(unit)
        end
        
    elseif event == "UNIT_POWER_UPDATE" or event == "UNIT_MAXPOWER" then
        -- Power updates for arena units
        local unit = ...
        if unit and unit:match("^arena[1-3]$") then
            self:UpdateUnitPower(unit)
        end
        
    elseif event == "UNIT_NAME_UPDATE" then
        -- Name updates for arena units
        local unit = ...
        if unit and unit:match("^arena[1-3]$") then
            self:UpdateUnitName(unit)
        end
        
    elseif event == "PVP_MATCH_STATE_CHANGED" then
        -- Match state changed (start/end)
        self:HandleMatchStateChange()
        
    elseif event == "UNIT_TARGETABLE_CHANGED" then
        -- Targeting changed
        local unit = ...
        if unit and unit:match("^arena[1-3]$") then
            self:UpdateUnitTargetable(unit)
        end
    end
end

function MFM:CheckArenaState()
    local _, instanceType = IsInInstance()
    
    -- Check if we entered or left arena
    if instanceType == "arena" and not self.isInArena then
        self:EnteredArena()
    elseif instanceType ~= "arena" and self.isInArena then
        self:LeftArena()
    end
    
    self.instanceType = instanceType
end

function MFM:EnteredArena()
    -- User notification: Entered arena
    print("|cffFFAA00ArenaCore:|r Entered Arena")
    self.isInArena = true
    self.isTestMode = false
    
    -- REMOVED: DR reset call - using auto-expiration now!
    -- DRs automatically expire after 18 seconds, no manual reset needed
    -- This prevents bugs from missed arena enter events
    
    -- CRITICAL: Hide Blizzard's default arena trinket/racial frames (multiple attempts)
    C_Timer.After(0.1, function()
        for i = 1, 3 do
            -- Try different frame names
            local frames = {
                _G["CompactArenaFramePlayer" .. i],
                _G["ArenaEnemyFrame" .. i],
                _G["CompactArenaFrame" .. i]
            }
            
            for _, blizzFrame in ipairs(frames) do
                if blizzFrame then
                    -- Hide all possible trinket/racial elements
                    if blizzFrame.trinket then 
                        blizzFrame.trinket:Hide()
                        blizzFrame.trinket:SetAlpha(0)
                        -- DEBUG: Hidden Blizzard trinket
                        -- print("|cffFFAA00[HIDE BLIZZ]|r Hidden trinket for " .. blizzFrame:GetName())
                    end
                    if blizzFrame.Trinket then 
                        blizzFrame.Trinket:Hide()
                        blizzFrame.Trinket:SetAlpha(0)
                    end
                    if blizzFrame.trinketButton then 
                        blizzFrame.trinketButton:Hide()
                        blizzFrame.trinketButton:SetAlpha(0)
                    end
                    if blizzFrame.racial then
                        blizzFrame.racial:Hide()
                        blizzFrame.racial:SetAlpha(0)
                    end
                    if blizzFrame.Racial then
                        blizzFrame.Racial:Hide()
                        blizzFrame.Racial:SetAlpha(0)
                    end
                end
            end
        end
    end)
    
    -- CRITICAL: Clear ALL test mode elements when entering arena
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = self.frames[i]
        if frame then
            -- Clear cast bars completely (hide ALL elements)
            if frame.castBar then
                frame.castBar:Hide()
                frame.castBar:SetValue(0)
                frame.castBar.isCasting = false
                frame.castBar.isChanneling = false
                -- Hide background
                if frame.castBar.bg then
                    frame.castBar.bg:Hide()
                end
                -- Clear text
                if frame.castBar.text then
                    frame.castBar.text:SetText("")
                    frame.castBar.text:Hide()
                end
                -- Hide spell icon
                if frame.castBar.spellIcon then
                    frame.castBar.spellIcon:Hide()
                    if frame.castBar.spellIcon.texture then
                        frame.castBar.spellIcon.texture:SetTexture(nil)
                    end
                end
                -- Cancel any update timers
                if frame.castBar.updateTimer then
                    frame.castBar.updateTimer:Cancel()
                    frame.castBar.updateTimer = nil
                end
            end
            
            -- Clear debuffs in prep room
            if frame.debuffContainer then
                for _, debuff in pairs(frame.debuffContainer.debuffs or {}) do
                    debuff:Hide()
                end
            end
            
            -- Trinket icons are always visible with user's chosen design (use orange-bordered frame)
            local trinket = frame.trinketIndicator or frame.trinket
            if trinket and trinket.icon then
                local trinketIcon = self:GetUserTrinketIcon()
                trinket.icon:SetTexture(trinketIcon)
                trinket.icon:SetTexCoord(0.1, 0.9, 0.1, 0.9)
                trinket:Show()
            end
            
            -- PHASE 3: Racial visibility now handled by TrinketsRacials module
            
            -- Hide absorbs in prep room (will be shown after gates open)
            if AC.Absorbs and AC.Absorbs.HideAbsorbs then
                AC.Absorbs:HideAbsorbs(frame)
            end
        end
    end
    
    -- MismatchHandler now controls frame visibility via events
    -- No manual ShowArenaFrames needed - ARENA_OPPONENT_UPDATE and ARENA_PREP_OPPONENT_SPECIALIZATIONS handle it
    
    -- Check for prep room
    local numOpps = GetNumArenaOpponentSpecs and GetNumArenaOpponentSpecs() or 0
    if numOpps > 0 then
        -- Prep room detected - MismatchHandler will handle frame visibility
        self:HandlePrepRoom()
    end
end

function MFM:LeftArena()
    -- DEBUG: Left Arena
    -- print("|cffFFAA00ArenaCore Master:|r Left Arena - deactivating")
    self.isInArena = false
    
    -- Hide frames when leaving arena
    if not self.isTestMode then
        self:HideAllFrames()
    end
    
    -- Reset frame data
    for i = 1, MAX_ARENA_ENEMIES do
        self:ResetFrame(self.frames[i])
    end
end

-- ============================================================================
-- CONSOLIDATION: NEW EVENT HANDLERS (ArenaCore patterns)
-- ============================================================================

-- STEALTH HANDLING REMOVED: ArenaFrameStealth.lua now handles ALL stealth/alpha changes
-- This function kept for spec/class icon updates and other non-stealth functionality
function MFM:HandleArenaOpponentUpdate(unit, updateType)
    -- CRITICAL: Block battleground events
    if not IsActiveBattlefieldArena() then
        return
    end
    
    if not unit or not updateType then return end
    
    -- CRITICAL: Validate unit exists before processing
    if not UnitExists(unit) then return end
    
    -- Extract arena index (arena1 → 1, arena2 → 2, etc.)
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex or arenaIndex < 1 or arenaIndex > 3 then return end
    
    local frame = self.frames[arenaIndex]
    if not frame then return end
    
    -- Show frame (alpha handled by ArenaFrameStealth.lua)
    if not InCombatLockdown() then
        frame:Show()
    end
    
    -- Update class and spec data
    local specID = GetArenaOpponentSpec and GetArenaOpponentSpec(arenaIndex)
    
    if specID and specID > 0 then
        -- We have spec data - use it for both class and spec icons
        local _, specName, _, specIcon, _, classFile = GetSpecializationInfoByID(specID)
        
        -- Update spec icon
        if frame.specIcon and frame.specIcon.icon and specIcon then
            local db = AC.DB and AC.DB.profile
            local specEnabled = db and db.specIcons and db.specIcons.enabled
            if specEnabled ~= false then
                frame.specIcon.icon:SetTexture(specIcon)
                frame.specIcon:Show()
            else
                frame.specIcon:Hide()
            end
        end
        
        -- Update class icon with real class data
        if frame.classIcon and classFile then
            local db = AC.DB and AC.DB.profile
            local classEnabled = db and db.classIcons and db.classIcons.enabled
            if classEnabled ~= false then
                if frame.classIcon.UpdateClassIcon then
                    frame.classIcon.UpdateClassIcon(classFile)
                else
                    local iconPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\" .. classFile .. ".tga"
                    frame.classIcon.icon:SetTexture(iconPath)
                    
                    if frame.classIcon.overlay then
                        local overlayPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\" .. classFile:lower() .. "overlay.tga"
                        frame.classIcon.overlay:SetTexture(overlayPath, true, true)
                    end
                end
                frame.classIcon:Show()
            else
                frame.classIcon:Hide()
            end
        end
    else
        -- No spec data - fallback to UnitClass
        local _, classFile = UnitClass(unit)
        
        -- Hide spec icon if no spec data available
        if frame.specIcon then
            frame.specIcon:Hide()
        end
        
        -- Update class icon with fallback class data
        if frame.classIcon and classFile then
            local db = AC.DB and AC.DB.profile
            local classEnabled = db and db.classIcons and db.classIcons.enabled
            if classEnabled ~= false then
                if frame.classIcon.UpdateClassIcon then
                    frame.classIcon.UpdateClassIcon(classFile)
                else
                    local iconPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\" .. classFile .. ".tga"
                    frame.classIcon.icon:SetTexture(iconPath)
                    
                    if frame.classIcon.overlay then
                        local overlayPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\" .. classFile:lower() .. "overlay.tga"
                        frame.classIcon.overlay:SetTexture(overlayPath, true, true)
                    end
                end
                frame.classIcon:Show()
            else
                frame.classIcon:Hide()
            end
        end
    end
    
    -- Update dispels for this unit
    if AC.UpdateDispelFrames then
        C_Timer.After(0.05, function()
            AC:UpdateDispelFrames()
        end)
    end
    
    -- Update trinkets and racials for this unit
    if AC.TrinketsRacials and AC.TrinketsRacials.RefreshFrame then
        AC.TrinketsRacials:RefreshFrame(frame, unit)
    end
    
    -- Update frame data on "seen" event
    if updateType == "seen" then
        if not AC.testModeEnabled then
            self:UpdateName(frame)
        end
        self:UpdateHealth(frame)
        self:UpdatePower(frame)
    end
end

-- Solo Shuffle round transitions (GROUP_ROSTER_UPDATE)
-- STEALTH STATE CLEARING REMOVED: ArenaFrameStealth.lua handles all stealth state
function MFM:HandleSoloShuffleTransition()
    local _, instanceType = IsInInstance()
    if instanceType ~= "arena" then return end
    
    -- MismatchHandler controls frame visibility via events
    -- ARENA_OPPONENT_UPDATE will fire for new round and handle frame visibility automatically
end

-- Prep room handling (ARENA_PREP_OPPONENT_SPECIALIZATIONS)
function MFM:HandlePrepRoom()
    local _, instanceType = IsInInstance()
    if instanceType ~= "arena" then return end
    
    -- CRITICAL: Block battleground events
    if not IsActiveBattlefieldArena() then
        return
    end
    
    -- DEBUG: Track prep room event (DISABLED for production)
    -- print("|cff00FF00[PREP_ROOM]|r ARENA_PREP_OPPONENT_SPECIALIZATIONS fired")
    
    -- CRITICAL FIX: Event-driven prep room
    -- Only show frames for opponents that actually have specs
    -- This handles mismatched teams in prep room (e.g., 2v3 before match starts)
    
    -- First, hide all frames that don't have specs
    for i = 1, 3 do
        local specID = GetArenaOpponentSpec and GetArenaOpponentSpec(i)
        -- print(string.format("|cff00FF00[PREP_ROOM]|r arena%d specID: %s", i, tostring(specID)))
        local frame = self.frames[i]
        
        if frame then
            if not specID or specID == 0 then
                -- No opponent in this slot - hide frame
                frame:SetAlpha(0)
                if not InCombatLockdown() then
                    frame:Hide()
                end
            else
                -- Opponent exists in this slot - show frame with spec info
                -- CRITICAL FIX: Clear test mode names and set prep room names
                if frame.playerName and not AC.testModeEnabled then
                    frame.playerName:SetText("Arena " .. i)
                end
                
                -- Update spec icon
                local _, specName, _, specIcon, _, classFile = GetSpecializationInfoByID(specID)
                if frame.specIcon and specIcon then
                    frame.specIcon.icon:SetTexture(specIcon)
                    frame.specIcon:Show()
                end
                
                -- Update class icon
                if frame.classIcon and classFile then
                    local iconPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\" .. classFile .. ".tga"
                    frame.classIcon.icon:SetTexture(iconPath)
                    
                    -- CRITICAL FIX: Update overlay to match detected class (prep room fix)
                    if frame.classIcon.overlay then
                        local overlayPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\" .. classFile:lower() .. "overlay.tga"
                        frame.classIcon.overlay:SetTexture(overlayPath, true, true)
                    end
                    
                    frame.classIcon:Show()
                end
                
                -- Apply class-based health bar color (ArenaCore exclusive!)
                if frame.healthBar and classFile and RAID_CLASS_COLORS[classFile] then
                    local classColor = RAID_CLASS_COLORS[classFile]
                    frame.healthBar:SetStatusBarColor(classColor.r, classColor.g, classColor.b)
                    -- CRITICAL FIX: Set health to 100% in prep room (units don't exist yet)
                    frame.healthBar:SetMinMaxValues(0, 100)
                    frame.healthBar:SetValue(100)
                end
                
                -- CRITICAL FIX: Set mana bar to 100% in prep room
                if frame.manaBar then
                    frame.manaBar:SetMinMaxValues(0, 100)
                    frame.manaBar:SetValue(100)
                    -- Set default mana color (will update when unit exists)
                    frame.manaBar:SetStatusBarColor(0, 0, 1, 1) -- Blue
                end
                
                -- CRITICAL FIX: Apply class-based border color (matches health bar)
                if frame.border and classFile and RAID_CLASS_COLORS[classFile] then
                    local classColor = RAID_CLASS_COLORS[classFile]
                    frame.border:SetVertexColor(classColor.r, classColor.g, classColor.b, 1)
                end
                
                -- CRITICAL FIX: Hide immunity glows in prep room (test mode artifacts)
                if frame.immunityGlow then
                    frame.immunityGlow:Hide()
                    if frame.immunityGlowAnim then
                        frame.immunityGlowAnim:Stop()
                    end
                end
                
                -- CRITICAL FIX: Set 0.5 opacity in prep room for stealth detection
                frame:SetAlpha(0.5)
                
                -- Show frame
                if not InCombatLockdown() then
                    frame:Show()
                end
            end
        end
    end
end

-- Unit health updates
function MFM:UpdateUnitHealth(unit)
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex then return end
    
    local frame = self.frames[arenaIndex]
    if not frame or not frame.healthBar then return end
    
    local health = UnitHealth(unit) or 0
    local maxHealth = UnitHealthMax(unit) or 1
    frame.healthBar:SetMinMaxValues(0, maxHealth)
    frame.healthBar:SetValue(health)
    
    -- CRITICAL FIX: Update health text (sArena proven method)
    if frame.healthBar.text then
        local general = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.general
        local usePercentage = general and general.usePercentage ~= false
        
        if usePercentage then
            local percent = maxHealth > 0 and math.ceil((health / maxHealth) * 100) or 0
            frame.healthBar.text:SetText(percent .. "%")
        else
            -- Use AbbreviateLargeNumbers like sArena for shorter display
            frame.healthBar.text:SetText(AbbreviateLargeNumbers(health))
        end
    end
end

-- Unit power updates
function MFM:UpdateUnitPower(unit)
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex then return end
    
    local frame = self.frames[arenaIndex]
    if not frame or not frame.manaBar then return end
    
    local power = UnitPower(unit) or 0
    local maxPower = UnitPowerMax(unit) or 1
    frame.manaBar:SetMinMaxValues(0, maxPower)
    frame.manaBar:SetValue(power)
    
    -- CRITICAL FIX: Update mana/power text (sArena proven method)
    if frame.manaBar.text then
        local general = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.general
        local usePercentage = general and general.usePercentage ~= false
        
        if usePercentage then
            local percent = maxPower > 0 and math.ceil((power / maxPower) * 100) or 0
            frame.manaBar.text:SetText(percent .. "%")
        else
            -- Use AbbreviateLargeNumbers like sArena for shorter display
            frame.manaBar.text:SetText(AbbreviateLargeNumbers(power))
        end
    end
end

-- Unit name updates
function MFM:UpdateUnitName(unit)
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex then return end
    
    local frame = self.frames[arenaIndex]
    if not frame or not frame.playerName then return end
    
    local name = UnitName(unit)
    if name then
        frame.playerName:SetText(name)
    end
end

-- Match state changes
function MFM:HandleMatchStateChange()
    -- MismatchHandler now controls frame visibility via events
    -- ARENA_OPPONENT_UPDATE and ARENA_PREP_OPPONENT_SPECIALIZATIONS handle everything
    -- No manual frame showing needed
end

-- Unit targetable changes
function MFM:UpdateUnitTargetable(unit)
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex then return end
    
    local frame = self.frames[arenaIndex]
    if not frame then return end
    
    -- Update frame visibility based on targetable state
    if UnitExists(unit) and not InCombatLockdown() then
        frame:Show()
    end
end

function MFM:UpdateArenaState()
    -- Update arena detection and frame visibility
    self:CheckArenaState()
    
    -- MismatchHandler now controls frame visibility via events
    -- ARENA_OPPONENT_UPDATE and ARENA_PREP_OPPONENT_SPECIALIZATIONS handle everything
    -- No manual frame showing needed
    if not self.isInArena and not self.isTestMode then
        self:HideAllFrames()
    end
end

-- ============================================================================
-- 6. VISUAL COMPONENT CREATORS  
-- ============================================================================
-- PHASE 1.3: FrameManager duplicate functions removed (kept MFM versions at lines ~1770-2037)
-- Removed: CreateDebuffContainer, CreateClassIcon, CreateTrinket, CreateRacial, CreateSpecIcon
-- These are exact duplicates of the MFM (MasterFrameManager) versions
-- ============================================================================

-- Kept: CreateTargetIndicator and CreateDRIcon (not duplicated in MFM)
-- Note: Duplicate FrameManager functions removed below (lines 2701-2929)

-- PHASE 1.3: FrameManager duplicate functions removed (220 lines) ✅ COMPLETE
-- Removed: CreateDebuffContainer, CreateClassIcon, CreateTrinket, CreateRacial, CreateSpecIcon
-- These were exact duplicates of MFM versions at lines 1770-2037
-- Kept only: CreateTargetIndicator and CreateDRIcon (unique to FrameManager)

function FrameManager:CreateTargetIndicator(parent)
    local ind = CreateFrame("Frame", nil, parent)
    ind:SetSize(8, 16)
    ind:SetPoint("RIGHT", parent, "LEFT", 4, 0)
    ind:SetAlpha(0) -- Hidden by default
    
    local t = ind:CreateTexture(nil, "OVERLAY")
    t:SetAllPoints()
    t:SetTexture("Interface\\AddOns\\ArenaCore\\Media\\Textures\\target_arrow.tga")
    t:SetTexCoord(0.1, 0.9, 0.1, 0.9)
    
    ind.texture = t
    return ind
end

function FrameManager:CreateDRIcon(parent, category)
    -- Create DR icon using round styling like trinkets/racials
    -- CRITICAL FIX: Ensure drContainer exists, or parent directly to frame
    local parentFrame = parent.drContainer or parent
    local dr = CreateFrame("Frame", nil, parentFrame)
    dr:SetSize(22, 22)
    dr.category = category
    dr.severity = 0  -- CRITICAL: Start at 0, TrackDRApplication will increment to 1
    dr.active = false
    dr:Hide() -- Hidden by default
    
    -- SOLID ORANGE BACKGROUND: Create multiple layers of the same rounded texture for opacity
    -- Layer 1: Base orange layer
    local solidBackground1 = dr:CreateTexture(nil, "BACKGROUND")
    solidBackground1:SetAllPoints() -- Same size as frame (22x22)
    solidBackground1:SetTexture("Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\orangeoverlay.tga")
    solidBackground1:SetTexCoord(0, 1, 0, 1)
    solidBackground1:SetVertexColor(1, 0.4, 0, 1) -- Bright orange
    
    -- Layer 2: Second orange layer for more opacity
    local solidBackground2 = dr:CreateTexture(nil, "BACKGROUND", nil, 1)
    solidBackground2:SetAllPoints()
    solidBackground2:SetTexture("Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\orangeoverlay.tga")
    solidBackground2:SetTexCoord(0, 1, 0, 1)
    solidBackground2:SetVertexColor(1, 0.3, 0, 0.8) -- Slightly different orange for layering
    
end
-- ENHANCED: Update DR icon positions with comprehensive test mode safeguards
function AC:UpdateDRPositions(frame)
    -- DEBUG: UpdateDRPositions called
    -- print("|cffFF00FF[DR POSITION]|r UpdateDRPositions called")
    
    -- Position DR icons in both test mode and live arena
    if not frame then 
        -- DEBUG: No frame provided
        -- print("|cffFF0000[DR POSITION]|r No frame provided!")
        return 
    end
    
    local db = (AC.DB and AC.DB.profile and AC.DB.profile.diminishingReturns)
    if not db then 
        -- DEBUG: No DR database
        -- print("|cffFF0000[DR POSITION]|r No DR database!")
        return 
    end
    
    -- Check if DR is enabled (nil or true = enabled, false = disabled)
    local enabled = (db.enabled ~= false)
    if not enabled then 
        -- DEBUG: DR disabled
        -- print("|cffFF0000[DR POSITION]|r DR disabled!")
        return 
    end
    
    -- DEBUG: Positioning DR icons
    -- print("|cff00FF00[DR POSITION]|r Positioning DR icons...")

  local pos = db.positioning or {}
  local sizeCfg = db.sizing or {}
  local rowsCfg = (db.rows or {})
  local mode = rowsCfg.mode or "Straight"
  local spacing = tonumber(pos.spacing) or 5
  local iconSize = tonumber(sizeCfg.size) or 22
  local growth = pos.growthDirection or "Right"

  -- Collect a stable-ordered list of DR icon frames to position
  local drTable = frame.drIcons or {}
  -- If in test mode and we created test icons, also consider them
  local isTest = AC.testModeEnabled and frame.testDRIcons
  if isTest then drTable = frame.testDRIcons end

  -- DYNAMIC POSITIONING: Check if enabled (default true)
  local dynamicPositioning = (rowsCfg.dynamicPositioning ~= false)
  
  local ordered = {}
  for key, f in pairs(drTable) do
    -- CRITICAL FIX: Always include all DR icons for positioning
    -- Dynamic positioning affects ANIMATION, not which icons get positioned
    -- This fixes the bug where icons in real arena don't get positioned at all
    if f then 
      table.insert(ordered, {key=key, frame=f})
    end
  end
  
  -- PRIORITY ORDER: stun, disorient, silence, incap, root, disarm, knockback
  local priorityOrder = {
    stun = 1,
    disorient = 2,
    silence = 3,
    incapacitate = 4,
    root = 5,
    disarm = 6,
    knockback = 7
  }
  
  -- Sort by priority order (stun first, then disorient, etc.)
  table.sort(ordered, function(a,b) 
    local aPriority = priorityOrder[tostring(a.key)] or 999
    local bPriority = priorityOrder[tostring(b.key)] or 999
    if aPriority ~= bPriority then
      return aPriority < bPriority
    end
    return tostring(a.key) < tostring(b.key) -- Alphabetical fallback
  end)

  if #ordered == 0 then return end

  -- Base anchor position with user offsets
  local baseX = (pos.horizontal or 0)
  local baseY = (pos.vertical or 0)

  if mode == "Straight" then
    -- Straight line mode - simple positioning based on growth direction
    for idx, item in ipairs(ordered) do
      local iconFrame = item.frame
      
      local offset = (idx - 1) * (iconSize + spacing)
      local point, relativePoint, x, y

      if growth == "Right" then
        -- Icons extend to the left from the right edge
        point, relativePoint, x, y = "TOPRIGHT", "TOPRIGHT", baseX - offset, baseY
      elseif growth == "Left" then
        -- Icons extend to the right from the left edge
        point, relativePoint, x, y = "TOPLEFT", "TOPLEFT", baseX + offset, baseY
      elseif growth == "Up" then
        -- Icons extend downward from the top
        point, relativePoint, x, y = "TOPRIGHT", "TOPRIGHT", baseX, baseY - offset
      elseif growth == "Down" then
        -- Icons extend upward from the bottom
        point, relativePoint, x, y = "BOTTOMRIGHT", "BOTTOMRIGHT", baseX, baseY + offset
      end
      
      -- CRITICAL FIX: Disable broken animation system that causes drift
      -- The Translation animation is ADDITIVE and was causing icons to move towards center
      -- Always use instant positioning for reliability
      iconFrame:ClearAllPoints()
      iconFrame:SetPoint(point, frame, relativePoint, x, y)
      
      -- FUTURE: If animation is desired, use Alpha fade instead of Translation
      -- Translation animations are buggy with relative positioning
    end
    return
  end

  -- Stacking Rows mode: 4 on top, 3 on bottom
  local topCount = math.min(4, #ordered)
  local bottomCount = math.max(0, math.min(3, #ordered - topCount))

  -- Calculate row widths
  local function rowWidth(n)
    if n <= 0 then return 0 end
    return (n * iconSize) + ((n - 1) * spacing)
  end

  local topRowWidth = rowWidth(topCount)
  local bottomRowWidth = rowWidth(bottomCount)

  -- Position top row (first 4 icons)
  for i = 1, topCount do
    local item = ordered[i]
    local iconFrame = item.frame
    iconFrame:ClearAllPoints()

    -- Position within the top row
    local posInRow = (i - 1) * (iconSize + spacing)

    if growth == "Right" then
      -- Top row extends left from right edge
      iconFrame:SetPoint("TOPRIGHT", frame, "TOPRIGHT", baseX - posInRow, baseY)
    elseif growth == "Left" then
      -- Top row extends right from left edge
      iconFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", baseX + posInRow, baseY)
    elseif growth == "Up" then
      -- Top row extends down from top
      iconFrame:SetPoint("TOPRIGHT", frame, "TOPRIGHT", baseX - posInRow, baseY)
    elseif growth == "Down" then
      -- Top row extends up from bottom
      iconFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", baseX - posInRow, baseY)
    end
  end

  -- Position bottom row (next 3 icons) - aligned under the start of top row
  for j = 1, bottomCount do
    local item = ordered[topCount + j]
    local iconFrame = item.frame
    iconFrame:ClearAllPoints()

    -- Position within the bottom row
    local posInRow = (j - 1) * (iconSize + spacing)
    local rowOffset = iconSize + spacing -- Distance between rows

    if growth == "Right" then
      -- Bottom row starts under the rightmost icon of top row
      iconFrame:SetPoint("TOPRIGHT", frame, "TOPRIGHT", baseX - posInRow, baseY - rowOffset)
    elseif growth == "Left" then
      -- Bottom row starts under the leftmost icon of top row
      iconFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", baseX + posInRow, baseY - rowOffset)
    elseif growth == "Up" then
      -- Bottom row is to the right of top row
      iconFrame:SetPoint("TOPRIGHT", frame, "TOPRIGHT", baseX - posInRow - topRowWidth - spacing, baseY)
    elseif growth == "Down" then
      -- Bottom row is to the right of top row
      iconFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", baseX - posInRow - topRowWidth - spacing, baseY)
    end
  end
end


-- ENHANCED: RefreshMoreGoodiesLayout with test mode awareness
function AC:RefreshMoreGoodiesLayout()
    -- Allow refresh in test mode for real-time preview
    
    -- Get frames from unified system
    local frames = GetArenaFrames()
    if not frames and AC.FrameManager and AC.FrameManager.GetFrames then
        frames = AC.FrameManager:GetFrames()
    end

  -- Check if absorbs feature is enabled (matches Absorbs module logic)
  local moreGoodiesDB = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies
  local absorbsEnabled = true -- Default to true if setting doesn't exist
  if moreGoodiesDB and moreGoodiesDB.absorbs then
    absorbsEnabled = moreGoodiesDB.absorbs.enabled ~= false
  end
  
  -- MoreGoodies refresh
  if AC.Absorbs and AC.Absorbs.RefreshAll then
    AC.Absorbs:RefreshAll()
  end
  
  -- CRITICAL FIX: Refresh immunity glows when absorbs checkbox changes
  if AC.ImmunityTracker then
    if absorbsEnabled then
      -- Absorbs enabled - refresh all immunity glows
      if AC.ImmunityTracker.RefreshAll then
        AC.ImmunityTracker:RefreshAll()
      end
    else
      -- Absorbs disabled - hide all immunity glows
      if AC.ImmunityTracker.HideAll then
        AC.ImmunityTracker:HideAll()
      end
    end
  end
  
  -- CRITICAL FIX: Re-apply test mode data after refresh if in test mode
  -- The refresh above calls UpdateAbsorbBar which hides test absorbs/glows
  -- We need to re-apply the test data to show them again
  if AC.testModeEnabled and absorbsEnabled then
    local MFM = _G.ArenaCore and _G.ArenaCore.MasterFrameManager
    if MFM and MFM.ApplyTestData then
      MFM:ApplyTestData()
    end
  end

  -- 2) Party class indicators
  if AC.UpdatePartyClassIndicators then
    AC:UpdatePartyClassIndicators()
  end
end

-- ENHANCED: Component-specific clear helpers for better maintainability
local function ClearHealthBars(frame)
    if frame.healthBar then
        frame.healthBar:SetMinMaxValues(0, 100)
        frame.healthBar:SetValue(100)
        if frame.healthBar.statusText then
            frame.healthBar.statusText:SetText("")
            frame.healthBar.statusText:Hide()
        end
    end
    
    if frame.manaBar then
        frame.manaBar:SetMinMaxValues(0, 100)
        frame.manaBar:SetValue(100)
    end
end

local function ClearIconsAndIndicators(frame)
    -- Clear class icon and spec chips
    if frame.classIcon then
        if frame.classIcon.icon then frame.classIcon.icon:SetTexture(nil) end
        if frame.classIcon.overlay then frame.classIcon.overlay:SetTexture(nil) end
        if frame.classIcon.background then frame.classIcon.background:SetVertexColor(1, 1, 1, 1) end
        if frame.classIcon.specChip then
            frame.classIcon.specChip:Hide()
            if frame.classIcon.specChip.icon then frame.classIcon.specChip.icon:SetTexture(nil) end
        end
    end
    
    -- Clear trinket and racial indicators
    if frame.trinketIndicator then
        if frame.trinketIndicator.texture then frame.trinketIndicator.texture:SetTexture(nil) end
        if frame.trinketIndicator.cooldown then frame.trinketIndicator.cooldown:Clear() end
        frame.trinketIndicator:Hide()
    end
    
    if frame.racialIndicator then
        if frame.racialIndicator.texture then frame.racialIndicator.texture:SetTexture(nil) end
        if frame.racialIndicator.cooldown then frame.racialIndicator.cooldown:Clear() end
        frame.racialIndicator:Hide()
    end
end

local function ClearDebuffsAndDR(frame)
    -- Clear all debuffs/auras
    if frame.debuffContainer and frame.debuffContainer.debuffs then
        for _, debuff in pairs(frame.debuffContainer.debuffs) do
            if debuff then
                debuff:Hide()
                if debuff.icon then debuff.icon:SetTexture(nil) end
                if debuff.cooldown then debuff.cooldown:Clear() end
            end
        end
        wipe(frame.debuffContainer.debuffs)
    end
    
    -- Clear all DR (Diminishing Returns) icons
    if frame.drIcons then
        for category, dr in pairs(frame.drIcons) do
            if dr then
                dr:Hide()
                if dr.icon then dr.icon:SetTexture(nil) end
                if dr.cooldown then 
                    dr.cooldown.duration = 0
                    dr.cooldown.startTime = 0
                end
                if dr.timerText then dr.timerText:Hide() end
                dr.severity = 1
            end
        end
    end
end

-- REMOVED: Duplicate GetRemainingCD() function (lines 3241-3250)
-- This was an exact duplicate of GetRemainingCD() at line 715
-- Using the original function defined earlier in the file

-- ENHANCED: Function to update cooldown timer text (works in test mode and real arena)
local function UpdateCooldownText(indicator)
    if not indicator then 
        -- DEBUG: Indicator is nil
        -- print("|cffFF0000[UpdateCooldownText]|r Indicator is nil")
        return 
    end
    if not indicator.cooldown then 
        -- DEBUG: No cooldown frame
        -- print("|cffFF0000[UpdateCooldownText]|r No cooldown frame")
        return 
    end
    if not indicator.cooldown.Text then 
        -- DEBUG: No Text object on cooldown
        -- print("|cffFF0000[UpdateCooldownText]|r No Text object on cooldown")
        return 
    end
    
    local start, duration = indicator.cooldown:GetCooldownTimes()
    if start == 0 or duration == 0 then
        indicator.cooldown.Text:SetText("")
        indicator.cooldown.Text:Hide()
        return
    end
    
    local remaining = (start + duration) / 1000 - GetTime()
    if remaining > 0.5 then
        -- Format as M:SS (e.g., "1:59", "0:45")
        local minutes = math.floor(remaining / 60)
        local seconds = math.floor(remaining % 60)
        local timeText = string.format("%d:%02d", minutes, seconds)
        indicator.cooldown.Text:SetText(timeText)
        indicator.cooldown.Text:Show()
    else
        indicator.cooldown.Text:SetText("")
        indicator.cooldown.Text:Hide()
    end
end


-- Hide debuff test - called from DebuffsWindow HIDE button
function FrameManager:HideDebuffTest()
    if not self:FramesExist() then return end
    
    local frames = self:GetFrames()
    for i = 1, #frames do
        if frames[i] and frames[i].debuffContainer then
            frames[i].debuffContainer:Hide()
            -- CRITICAL FIX: Clear the active flag so TEST button works again
            frames[i].debuffContainer.testDebuffsActive = false
        end
    end
end

-- Refresh debuff settings - called when settings change in DebuffsWindow
function FrameManager:RefreshDebuffSettings()
    local debuffModule = GetDebuffModule()
    if debuffModule and debuffModule.RefreshSettings then
        debuffModule:RefreshSettings()
        return
    end

    if not self:FramesExist() then return end
    
    local frames = self:GetFrames()
    local debuffSettings = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies and AC.DB.profile.moreGoodies.debuffs
    
    -- Update positioning for all debuff containers
    for i = 1, #frames do
        if frames[i] and frames[i].debuffContainer then
            local c = frames[i].debuffContainer
            
            -- Clear existing positioning and apply new positioning
            c:ClearAllPoints()
            local horizontal = debuffSettings and debuffSettings.positioning and debuffSettings.positioning.horizontal or 8
            local vertical = debuffSettings and debuffSettings.positioning and debuffSettings.positioning.vertical or 6
            c:SetPoint("BOTTOMLEFT", frames[i], "BOTTOMLEFT", horizontal, vertical)
            
            -- Hide all existing debuff frames
            for j = 1, #c.debuffs do 
                if c.debuffs[j] then
                    c.debuffs[j]:SetAlpha(0)
                    c.debuffs[j]:Hide()
                end
            end
            
            -- Clear the debuffs array to force recreation with new scale
            c.debuffs = {}
            
            -- CRITICAL FIX: Clear the active flag to allow re-setup with new settings
            c.testDebuffsActive = false
            
            -- Hide container if debuffs are disabled
            if not debuffSettings or not debuffSettings.enabled then
                c:Hide()
            end
        end
    end
    
    -- Check if any frame is shown (test mode active)
    local inTestMode = false
    for i = 1, #frames do
        if frames[i] and frames[i]:IsShown() then
            inTestMode = true
            break
        end
    end
    
    -- If in test mode and debuffs are enabled, refresh with new settings
    if inTestMode and debuffSettings and debuffSettings.enabled then
        self:TestDebuffMode()
    end
end

-- Spec-based resource colors for prep room and arena
function FrameManager:ApplySpecBasedResourceColors(frame, classFile, specID)
    if not frame.manaBar or not classFile then return end
    
    -- Use MFM's spec-based resource color system
    local color = MFM:GetSpecResourceColor(classFile, specID)
    frame.manaBar:SetStatusBarColor(color[1], color[2], color[3], color[4] or 1)
end

-- Legacy function for backward compatibility
function FrameManager:ApplyClassBasedResourceColors(frame, classFile)
    self:ApplySpecBasedResourceColors(frame, classFile, nil)
end

-- ============================================================================
-- ABSORBS SYSTEM MOVED TO MODULE
-- See: modules/Absorbs/Absorbs.lua
-- Backward compatibility maintained via global UpdateAbsorbBar in module
-- ============================================================================

-- ============================================================================
-- 7. EVENT SYSTEM & COMBAT LOG
-- ============================================================================
-- Event handling, combat log processing, cast bars, trinket/racial tracking

-- Get user's chosen trinket icon from settings (returns texture path/ID ready for SetTexture)
function FrameManager:GetUserTrinketIcon()
    if AC.TrinketsRacials and AC.TrinketsRacials.GetUserTrinketIcon then
        return AC.TrinketsRacials:GetUserTrinketIcon()
    end
    return 1322720
end

-- PHASE 3: REMOVED - Racial icon logic now in TrinketsRacials module
-- Use AC.TrinketsRacials:RefreshFrame() instead

function FrameManager:RegisterDebuffEvents()
    -- Create event frame if it doesn't exist
    if not self.debuffEventFrame then
        self.debuffEventFrame = CreateFrame("Frame")
        self.debuffEventFrame:SetScript("OnEvent", function(_, event, ...)
            -- Only show critical events (trinkets/racials/cast bars), not UNIT_AURA spam
            if event == "UNIT_SPELLCAST_SUCCEEDED" then
                local arg1 = select(1, ...)
                local spellID = select(3, ...)
                -- DEBUG: Event fired
                -- print("|cffFFFF00[EVENT]|r " .. event .. " - " .. tostring(arg1) .. " spell " .. tostring(spellID))
            end
            self:OnDebuffEvent(event, ...)
        end)
    end
    
    -- Register for aura update events
    -- CRITICAL FIX: Use RegisterUnitEvent to filter for arena units only
    -- This prevents unnecessary event spam from player/party/raid auras
    self.debuffEventFrame:RegisterUnitEvent("UNIT_AURA", "arena1", "arena2", "arena3")
    
    local castModule = GetCastBarModule()
    if castModule and castModule.RegisterEvents then
        castModule:RegisterEvents(self.debuffEventFrame)
    end
    
    -- Trinket/Racial updates are handled by TrinketsRacials module via ArenaTracking events
    self.debuffEventFrame:RegisterEvent("ARENA_CROWD_CONTROL_SPELL_UPDATE")
    
    -- REMOVED: COMBAT_LOG_EVENT_UNFILTERED registration (ArenaTracking already handles this and delegates to us)
    -- self.debuffEventFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") -- For DR tracking
    
    -- DEBUG: Events registered
    -- print("|cff00FF00[FrameManager]|r ✓ Registered ALL events (cast bars, trinkets, racials)")
    -- print("|cff00FF00[FrameManager]|r ✓ UNIT_SPELLCAST_SUCCEEDED registered for arena1, arena2, arena3")
    -- print("|cff00FF00[FrameManager]|r ✓ UNIT_SPELLCAST_START registered for arena1, arena2, arena3")
end

function FrameManager:OnDebuffEvent(event, ...)
    if not self:FramesExist() then 
        return 
    end
    
    -- Handle COMBAT_LOG_EVENT_UNFILTERED for DR tracking and dispels
    if event == "COMBAT_LOG_EVENT_UNFILTERED" then
        self:HandleCombatLogEvent()
        return
    end
    
    -- PHASE 2: Removed duplicate UNIT_SPELLCAST_SUCCEEDED handler
    -- ArenaTracking already handles this event and forwards to TrinketsRacials module
    -- Keeping ARENA_CROWD_CONTROL_SPELL_UPDATE as it's FrameManager-specific
    if event == "ARENA_CROWD_CONTROL_SPELL_UPDATE" then
        local unit, spellID = ...
        if AC.TrinketsRacials and AC.TrinketsRacials.OnCrowdControlSpell then
            AC.TrinketsRacials:OnCrowdControlSpell(unit, spellID)
        end
        return
    end
    
    local unit = ...
    if not unit or not unit:match("^arena%d$") then 
        return 
    end
    
    -- Get arena index from unit
    local arenaIndex = tonumber(unit:match("^arena(%d)$"))
    if not arenaIndex or arenaIndex < 1 or arenaIndex > 3 then 
        return 
    end
    
    -- Get the frame for this arena unit
    local frames = self:GetFrames()
    local frame = frames[arenaIndex]
    if not frame then 
        return 
    end
    
    -- Handle debuff events
    if event == "UNIT_AURA" then
        local debuffModule = GetDebuffModule()
        if debuffModule and debuffModule.Update then
            debuffModule:Update(frame, unit)
        end
        -- Also update dispel tracking
        local dispelModule = GetDispelModule()
        if dispelModule and dispelModule.Update then
            dispelModule:Update(frame, unit)
        else
            self:UpdateDispels(frame, unit)
        end
    -- Handle cast bar events
    elseif event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" or event == "UNIT_SPELLCAST_EMPOWER_START" then
        local castModule = GetCastBarModule()
        if castModule and castModule.Update then
            castModule:Update(frame, unit, event)
        end
    elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" or event == "UNIT_SPELLCAST_CHANNEL_STOP" or event == "UNIT_SPELLCAST_EMPOWER_STOP" then
        local castModule = GetCastBarModule()
        if castModule and castModule.Hide then
            castModule:Hide(frame)
        end
    end
end

function FrameManager:UpdateCastBar(frame, unit, event)
    local castModule = GetCastBarModule()
    if castModule and castModule.Update then
        castModule:Update(frame, unit, event)
    end
end

function FrameManager:HideCastBar(frame)
    local castModule = GetCastBarModule()
    if castModule and castModule.Hide then
        castModule:Hide(frame)
    end
end


-- Handle COMBAT_LOG_EVENT_UNFILTERED for all tracking systems
function FrameManager:HandleCombatLogEvent()
    local _, combatEvent, _, sourceGUID, _, _, _, destGUID, _, _, _, spellID = CombatLogGetCurrentEventInfo()
    
    if combatEvent == "SPELL_AURA_APPLIED" or combatEvent == "SPELL_AURA_REFRESH" then
        -- Call DR tracker from main combat log handler (not separate registration)
        if AC and AC.HandleCombatLogDR then
            AC:HandleCombatLogDR()
        end
        
        -- ====================================================================
        -- TRIBADGES (CLASS PACKS) COMBAT_LOG TRACKING - FALLBACK METHOD
        -- ====================================================================
        -- NOTE: TriBadges now uses AuraUtil.ForEachAura as PRIMARY method (BBP pattern)
        -- This COMBAT_LOG tracking is kept as FALLBACK for reliability
        -- Also needed for: Trinkets, Racials, DR, Dispels, etc.
        -- 
        -- This handles spells like Unholy Assault that apply buffs AFTER hitting a target
        -- destGUID is the unit receiving the buff (the arena enemy)
        if _G.ArenaCore and _G.ArenaCore.TriBadges and _G.ArenaCore.TriBadges.OnSpellCastByGUID then
            _G.ArenaCore.TriBadges:OnSpellCastByGUID(destGUID, spellID, combatEvent)
        end
    elseif combatEvent == "SPELL_CAST_SUCCESS" then
        -- Track dispel casts
        local dispelSpells = {
            [527] = 8,     -- Purify (Priest)
            [4987] = 8,    -- Cleanse (Paladin)
            [77130] = 8,   -- Purify Spirit (Shaman)
            [88423] = 8,   -- Nature's Cure (Druid)
            [115450] = 8,  -- Detox (Monk)
            [2782] = 8,    -- Remove Corruption (Druid)
            [51886] = 8,   -- Cleanse Spirit (Shaman)
            [475] = 8,     -- Remove Curse (Mage)
            [360823] = 8,  -- Naturalize (Evoker)
        }
        
        if dispelSpells[spellID] then
            -- Find which arena unit cast this
            for i = 1, 3 do
                local unitGUID = UnitGUID("arena" .. i)
                if unitGUID == sourceGUID then
                    local frames = self:GetFrames()
                    local frame = frames[i]
                    if frame then
                        self:TrackDispelCooldown(frame, spellID, dispelSpells[spellID], "arena" .. i)
                    end
                    break
                end
            end
        else
            for i = 1, 3 do
                local unitGUID = UnitGUID("arena" .. i)
                if unitGUID == sourceGUID then
                    local unit = "arena" .. i
                    local frame = self:GetFrames()[i]
                    if AC.TrinketsRacials and AC.TrinketsRacials.HandleSpell then
                        AC.TrinketsRacials:HandleSpell(frame, unit, spellID)
                    end
                    -- TriBadges COMBAT_LOG tracking (FALLBACK - primary method is AuraUtil.ForEachAura)
                    if _G.ArenaCore and _G.ArenaCore.TriBadges and _G.ArenaCore.TriBadges.OnSpellCast then
                        _G.ArenaCore.TriBadges:OnSpellCast(unit, spellID, combatEvent)
                    end
                    break
                end
            end
        end
    end
end


-- Update dispel tracking
function FrameManager:UpdateDispels(frame, unit)
    local dispelModule = GetDispelModule()
    if dispelModule and dispelModule.Update then
        dispelModule:Update(frame, unit)
        return
    end
end

-- Track dispel cooldown
function FrameManager:TrackDispelCooldown(frame, spellID, duration, unit)
    local dispelModule = GetDispelModule()
    if dispelModule and dispelModule.TrackCooldown then
        dispelModule:TrackCooldown(frame, spellID, duration)
        return
    end
end

-- Local implementation of UpdateDebuffs (adapted from ArenaTracking.lua)
function FrameManager:UpdateDebuffs(frame, unit)
    local debuffModule = GetDebuffModule()
    if debuffModule and debuffModule.Update then
        return debuffModule:Update(frame, unit)
    end

    if not frame.debuffContainer then return end
    
    -- Check if debuffs are enabled
    local debuffSettings = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies and AC.DB.profile.moreGoodies.debuffs
    if not debuffSettings or not debuffSettings.enabled then
        frame.debuffContainer:Hide()
        return
    end
    
    -- Get max count and scaling from settings (default 6, 100%)
    local maxCount = debuffSettings.maxCount or 6
    local scale = (debuffSettings.sizing and debuffSettings.sizing.scale or 100) / 100
    
    local function Trim(texture)
        if texture and texture.SetTexCoord then 
            texture:SetTexCoord(0.002, 0.998, 0.002, 0.998) 
        end 
    end
    
    local c = frame.debuffContainer
    
    -- Hide existing debuffs
    for i = 1, #c.debuffs do 
        if c.debuffs[i] then
            c.debuffs[i]:SetAlpha(0) 
        end
    end
    
    local idx = 1
    local hasDebuffs = false
    
    -- CRITICAL FIX: Consolidate debuffs by spell ID to combine stacks from multiple sources
    local debuffMap = {}
    
    -- Scan for debuffs (up to 40 slots)
    for i = 1, 40 do
        local auraData = C_UnitAuras.GetDebuffDataByIndex(unit, i)
        if not auraData then break end
        
        local spellID = auraData.spellId
        
        -- FILTER OUT DAMPENING (spell ID 110310)
        if spellID ~= 110310 and auraData.name then
            if not debuffMap[spellID] then
                -- First instance of this spell
                debuffMap[spellID] = {
                    name = auraData.name,
                    icon = auraData.icon,
                    count = 1, -- Count instances, not applications
                    duration = auraData.duration,
                    expirationTime = auraData.expirationTime
                }
            else
                -- Same spell from another source - increment count
                debuffMap[spellID].count = debuffMap[spellID].count + 1
            end
        end
    end
    
    -- Convert map to sorted array and display up to maxCount
    local debuffArray = {}
    for spellID, data in pairs(debuffMap) do
        table.insert(debuffArray, data)
    end
    
    -- Display consolidated debuffs
    for _, debuffData in ipairs(debuffArray) do
        if idx > maxCount then break end
        
        hasDebuffs = true
        local name = debuffData.name
        local texture = debuffData.icon
        local count = debuffData.count
        
        -- Create debuff frame if it doesn't exist
        if not c.debuffs[idx] then 
            local frameSize = 20 * scale
            local iconSize = 16 * scale
            local spacing = 23 * scale
            
            local d = CreateFrame("Frame", nil, c)
            d:SetSize(frameSize, frameSize)
            d:SetPoint("LEFT", (idx-1) * spacing, 0)
            d:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
            d:SetFrameLevel(30) -- Lower level within MEDIUM strata, below trinkets/racials
            
            -- Black square background (like Blackout Aura editor)
            local bg = d:CreateTexture(nil, "BACKGROUND")
            bg:SetAllPoints()
            bg:SetTexture(1, 1, 1, 1) -- White background first
            bg:SetVertexColor(0.1, 0.1, 0.1, 0.8) -- Dark background
            
            -- Black square border
            local border = AC:CreateFlatTexture(d, "BORDER", 1, {0, 0, 0, 1}, 1)
            border:SetAllPoints()
            
            -- Inner border for clean square look
            local innerBg = AC:CreateFlatTexture(d, "BACKGROUND", 2, {0.15, 0.15, 0.15, 1}, 1)
            innerBg:SetPoint("TOPLEFT", 1, -1)
            innerBg:SetPoint("BOTTOMRIGHT", -1, 1)
            
            -- Icon
            local icon = d:CreateTexture(nil, "ARTWORK")
            icon:SetSize(iconSize, iconSize)
            icon:SetPoint("CENTER")
            Trim(icon)
            
            -- Stack count text (scaled)
            local stack = d:CreateFontString(nil, "OVERLAY")
            local fontSize = math.max(6, 7 * scale)
            stack:SetFont("Interface\\\\AddOns\\\\ArenaCore\\\\Media\\\\Fonts\\\\arenacore.ttf", fontSize, "OUTLINE")
            stack:SetPoint("BOTTOMRIGHT", 2, -2)
            stack:SetTextColor(1, 1, 1, 1) -- White text
            
            -- NEW FEATURE: Cooldown frame for countdown spiral (using helper to block OmniCC)
            local cd = AC:CreateCooldown(d, nil, "CooldownFrameTemplate")
            cd:SetAllPoints(d)
            cd:SetHideCountdownNumbers(true)
            cd:SetDrawEdge(false)
            cd.noCooldownCount = true
            -- CRITICAL: Make spiral very faint so icon is visible
            cd:SetSwipeColor(0, 0, 0, 0.3) -- Very faint black (30% opacity)
            
            -- NEW FEATURE: Countdown timer text (center of icon)
            local timerFontSize = (debuffSettings.timerFontSize) or 10
            local timer = cd:CreateFontString(nil, "OVERLAY")
            timer:SetFont("Interface/AddOns/ArenaCore/Media/Fonts/arenacore.ttf", timerFontSize, "OUTLINE")
            timer:SetPoint("CENTER", cd, "CENTER", 0, 0)
            timer:SetTextColor(1, 1, 1, 1)
            cd.Text = timer
            
            d.icon = icon
            d.stack = stack
            d.cooldown = cd
            d.timer = timer
            c.debuffs[idx] = d
        end
        
        -- Update debuff display
        local d = c.debuffs[idx]
        d:SetAlpha(1)
        d.icon:SetTexture(texture)
        
        -- Show/hide stack count
        if count and count > 1 then 
            d.stack:SetText(count)
            d.stack:Show()
        else 
            d.stack:Hide()
        end
        
        -- NEW FEATURE: Countdown timer system (like TriBadges)
        local showTimer = debuffSettings.showTimer
        if showTimer == nil then showTimer = true end -- Default enabled
        
        if showTimer and debuffData.duration and debuffData.duration > 0 and debuffData.expirationTime then
            -- Start cooldown spiral
            local startTime = debuffData.expirationTime - debuffData.duration
            d.cooldown:SetCooldown(startTime, debuffData.duration)
            
            -- Start countdown timer text
            if d.timer then
                -- Cancel existing timer if any
                if d.timerUpdate then
                    d.timerUpdate:Cancel()
                    d.timerUpdate = nil
                end
                
                -- Update timer text every 0.1 seconds
                local expirationTime = debuffData.expirationTime
                local function UpdateTimerText()
                    if not d or not d.timer then return end
                    
                    local remaining = expirationTime - GetTime()
                    if remaining > 0 then
                        -- Format: M:SS for times over 60s, just seconds for under 60s
                        local minutes = math.floor(remaining / 60)
                        local seconds = math.floor(remaining % 60)
                        local timeText
                        if minutes > 0 then
                            timeText = string.format("%d:%02d", minutes, seconds)
                        else
                            timeText = string.format("%.0f", remaining)
                        end
                        d.timer:SetText(timeText)
                        
                        -- Schedule next update
                        d.timerUpdate = C_Timer.NewTimer(0.1, UpdateTimerText)
                    else
                        -- Timer expired
                        d.timer:SetText("")
                        d.timerUpdate = nil
                    end
                end
                
                -- Start the timer
                UpdateTimerText()
            end
        else
            -- Clear cooldown and timer if disabled or no duration
            if d.cooldown then
                d.cooldown:Clear()
            end
            if d.timer then
                d.timer:SetText("")
            end
            if d.timerUpdate then
                d.timerUpdate:Cancel()
                d.timerUpdate = nil
            end
        end
        
        idx = idx + 1
    end
    
    -- Show/hide container based on whether we have debuffs
    if hasDebuffs then
        c:Show()
    else
        c:Hide()
    end
end

-- ============================================================================
-- 8. ARENA STATE MANAGEMENT
-- ============================================================================
-- Arena detection, prep room handling, unit updates, arena entry/exit logic

-- ENHANCED: Helper functions to break down massive UpdateFrameData function
local function UpdateFrameVisibility(frame, unit, arenaIndex)
    local unitExists = UnitExists(unit)
    local _, instanceType = IsInInstance()
    local isArena = instanceType == "arena"
    
    -- If in arena, always show frame
    local shouldShow = isArena
    
    return shouldShow, unitExists
end
local function UpdateFrameIndicators(frame, unit)
    -- PHASE 1: Delegate to TrinketsRacials module for centralized control
    -- Module handles ALL trinket/racial visibility logic
    if AC.TrinketsRacials and AC.TrinketsRacials.RefreshFrame then
        AC.TrinketsRacials:RefreshFrame(frame, unit)
    else
        -- Fallback: Basic visibility control if module not loaded
        local db = AC.DB and AC.DB.profile
        
        if frame.trinketIndicator then
            local trinketEnabled = db and db.trinkets and db.trinkets.enabled
            if trinketEnabled ~= false then
                frame.trinketIndicator:Show()
            else
                frame.trinketIndicator:Hide()
            end
        end
        
        if frame.racialIndicator then
            local racialEnabled = db and db.racials and db.racials.enabled
            if racialEnabled ~= false then
                frame.racialIndicator:Show()
            else
                frame.racialIndicator:Hide()
            end
        end
    end
end

local function UpdateFrameBars(frame, unit)
    -- Update health bar
    if frame.healthBar then
        local health = UnitHealth(unit)
        local maxHealth = UnitHealthMax(unit)
        if maxHealth > 0 then
            frame.healthBar:SetMinMaxValues(0, maxHealth)
            frame.healthBar:SetValue(health)
            
            -- Apply class colors if enabled
            local _, class = UnitClass(unit)
            if AC:GetClassColorsEnabled() and class then
                local classColor = RAID_CLASS_COLORS[class]
                if classColor then
                    frame.healthBar:SetStatusBarColor(classColor.r, classColor.g, classColor.b, 1)
                else
                    frame.healthBar:SetStatusBarColor(0, 1, 0, 1)
                end
            else
                frame.healthBar:SetStatusBarColor(0, 1, 0, 1)
            end
        end
    end
    
    -- Update mana bar
    if frame.manaBar then
        -- CRITICAL FIX: Clear resource bar when unit is dead
        if UnitIsDead(unit) or UnitIsGhost(unit) then
            frame.manaBar:SetMinMaxValues(0, 100)
            frame.manaBar:SetValue(0)
            -- Update text to show 0%
            if frame.manaBar.text then
                frame.manaBar.text:SetText("0%")
            end
        else
            local power = UnitPower(unit)
            local maxPower = UnitPowerMax(unit)
            local powerType = UnitPowerType(unit)
            
            if maxPower > 0 then
                frame.manaBar:SetMinMaxValues(0, maxPower)
                frame.manaBar:SetValue(power)
                
                -- CRITICAL FIX: Cache power type to prevent flickering (Shadow Priest issue)
                -- Initialize cache if it doesn't exist
                if not frame.manaBar.cachedPowerType then
                    frame.manaBar.cachedPowerType = -1
                end
                
                -- Only update color if power type changed (prevents flickering)
                if frame.manaBar.cachedPowerType ~= powerType then
                    frame.manaBar.cachedPowerType = powerType
                    
                    local powerColor = PowerBarColor[powerType]
                    if powerColor then
                        frame.manaBar:SetStatusBarColor(powerColor.r, powerColor.g, powerColor.b, 1)
                    else
                        frame.manaBar:SetStatusBarColor(0, 0, 1, 1)
                    end
                end
            end
        end
    end
end

-- OPTIMIZED: UpdateFrameData function broken into manageable pieces
UpdateFrameData = function(frame, unit)
    if not frame or not unit then return end
    
    -- Extract arena index (1, 2, or 3)
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex then return end
    
    -- CRITICAL: Complete test mode protection
    if AC.testModeEnabled then
        return
    end

    -- Use helper functions for cleaner, maintainable code
    local shouldShow, unitExists = UpdateFrameVisibility(frame, unit, arenaIndex)
    
    if shouldShow then
        -- CRITICAL FIX: Only update indicators if unit actually exists (not in prep room)
        -- In prep room, units don't exist yet, so skip indicator updates
        -- ARENA_OPPONENT_UPDATE will handle indicator updates when gates open
        if unitExists then
            -- Update indicators (trinkets, racials)
            UpdateFrameIndicators(frame, unit)
            
            -- Update health and mana bars
            UpdateFrameBars(frame, unit)
        end
    end
end

-- ENHANCED: Clean UpdateAllFrames function with comprehensive test mode protection
UpdateAllFrames = function()
    -- CRITICAL: Complete test mode protection - ArenaTracking should not interfere
    if AC.testModeEnabled then 
        StopTicker() -- Stop ArenaTracking ticker in test mode
        return 
    end
    
    -- FIXED: Get frames from proper location
    local frames = AC.FrameManager and AC.FrameManager:GetFrames()
    if not frames then
        frames = _G.ArenaCore and _G.ArenaCore.arenaFrames
    end
    if not frames then return end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            UpdateFrameData(frames[i], "arena"..i)
            
            -- Update cooldown timer text for trinket and racial indicators
            if frames[i].trinketIndicator then
                UpdateCooldownText(frames[i].trinketIndicator)
            end
            if frames[i].racialIndicator then
                UpdateCooldownText(frames[i].racialIndicator)
            end
            
            -- Update DR icons for each frame (only in live arena)
            UpdateDRIcons("arena" .. i)
            
            -- Update DR cooldown text for all DR icons
            if frames[i].drFrames then
                for category, drFrame in pairs(frames[i].drFrames) do
                    if drFrame.cooldown and drFrame.cooldown.Text then
                        UpdateCooldownText(drFrame)
                    end
                end
            end
        end
    end
end

local function UpdatePrepRoomUnit(arenaIndex, frame)
    if not frame or not arenaIndex then return end
    
    -- CRITICAL: Always ensure frame is visible in prep room
    if not InCombatLockdown() then
        frame:Show()
    end
    
    -- CRITICAL FIX: Clear test mode names and set prep room names
    if frame.playerName and not AC.testModeEnabled then
        -- Explicitly clear any test mode names that might be lingering
        frame.playerName:SetText("Arena " .. arenaIndex)
    end
    
    -- Get spec information using proper API calls
    local specID = GetArenaOpponentSpec and GetArenaOpponentSpec(arenaIndex)
    if specID and specID > 0 then
        local _, specName, _, specIcon, _, classFile = GetSpecializationInfoByID(specID)
        
        -- Update spec icon
        if frame.specIcon and specIcon and frame.specIcon.icon then
            -- FIXED: Check if spec icons are enabled before showing
            local db = AC.DB and AC.DB.profile
            local specEnabled = db and db.specIcons and db.specIcons.enabled
            if specEnabled ~= false then
                frame.specIcon.icon:SetTexture(specIcon)
                frame.specIcon:Show()
            else
                frame.specIcon:Hide()
            end
        end
        
        -- Update class icon based on spec
        if frame.classIcon and classFile then
            -- FIXED: Check if class icons are enabled before showing
            local db = AC.DB and AC.DB.profile
            local classEnabled = db and db.classIcons and db.classIcons.enabled
            if classEnabled ~= false then
                if frame.classIcon.UpdateClassIcon then
                    frame.classIcon.UpdateClassIcon(classFile)
                    frame.classIcon:Show()
                elseif frame.classIcon.icon then
                    -- CRITICAL FIX: Fallback must also update overlay, not just icon
                    local iconPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\" .. classFile .. ".tga"
                    frame.classIcon.icon:SetTexture(iconPath)
                    
                    -- Update overlay to match the detected class (prep room fix)
                    if frame.classIcon.overlay and classFile then
                        local overlayPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\" .. classFile:lower() .. "overlay.tga"
                        frame.classIcon.overlay:SetTexture(overlayPath, true, true)
                    end
                    
                    frame.classIcon:Show()
                end
            else
                frame.classIcon:Hide()
            end
        end
        
        -- Apply class-based health bar colors in prep room (unique ArenaCore feature!)
        if frame.healthBar and classFile and RAID_CLASS_COLORS[classFile] then
            local classColor = RAID_CLASS_COLORS[classFile]
            frame.healthBar:SetStatusBarColor(classColor.r, classColor.g, classColor.b)
        end
        
        -- REVOLUTIONARY: Apply SPEC-based resource colors in prep room (no other addon does this!)
        if frame.manaBar and classFile and AC.FrameManager and AC.FrameManager.ApplySpecBasedResourceColors then
            AC.FrameManager:ApplySpecBasedResourceColors(frame, classFile, specID)
        end
    else
        -- No spec data available YET - hide spec and class icons but KEEP FRAME VISIBLE
        if frame.specIcon then
            frame.specIcon:Hide()
        end
        if frame.classIcon then
            frame.classIcon:Hide()
        end
        
        -- DEFENSIVE: Even without spec data, keep the frame visible in prep room
        -- Spec data might load later via subsequent events
        if not InCombatLockdown() then
            frame:Show()
        end
    end
    
    -- CRITICAL FIX: Hide cast bar AND all child elements in prep room
    -- This prevents empty spell icon borders from showing after /reload
    if frame.castBar then
        frame.castBar:Hide()
        
        -- Hide spell icon (prevents empty border from showing)
        if frame.castBar.spellIcon then
            frame.castBar.spellIcon:Hide()
            -- Clear icon texture
            if frame.castBar.spellIcon.texture then
                frame.castBar.spellIcon.texture:SetTexture(nil)
            end
            -- CRITICAL FIX: Hide the border textures (top, bottom, left, right)
            -- These stay visible even when spellIcon is hidden, causing empty black borders
            -- Check BOTH border and styledBorder (IconStyling uses styledBorder)
            if frame.castBar.spellIcon.border then
                if frame.castBar.spellIcon.border.top then frame.castBar.spellIcon.border.top:Hide() end
                if frame.castBar.spellIcon.border.bottom then frame.castBar.spellIcon.border.bottom:Hide() end
                if frame.castBar.spellIcon.border.left then frame.castBar.spellIcon.border.left:Hide() end
                if frame.castBar.spellIcon.border.right then frame.castBar.spellIcon.border.right:Hide() end
            end
            if frame.castBar.spellIcon.styledBorder then
                if frame.castBar.spellIcon.styledBorder.top then frame.castBar.spellIcon.styledBorder.top:Hide() end
                if frame.castBar.spellIcon.styledBorder.bottom then frame.castBar.spellIcon.styledBorder.bottom:Hide() end
                if frame.castBar.spellIcon.styledBorder.left then frame.castBar.spellIcon.styledBorder.left:Hide() end
                if frame.castBar.spellIcon.styledBorder.right then frame.castBar.spellIcon.styledBorder.right:Hide() end
            end
        end
        
        -- Hide border frame (prevents empty border from showing)
        if frame.castBar.borderFrame then
            frame.castBar.borderFrame:Hide()
        end
        
        -- Clear and hide text
        if frame.castBar.text then
            frame.castBar.text:SetText("")
            frame.castBar.text:Hide()
        end
        
        -- Reset cast bar value
        frame.castBar:SetValue(0)
    end
    
    -- Keep health/mana at 100% (as requested)
    if frame.healthBar then
        frame.healthBar:SetMinMaxValues(0, 100)
        frame.healthBar:SetValue(100)
        if frame.healthBar.statusText then
            frame.healthBar.statusText:SetText("100%")
        end
    end
    
    if frame.manaBar then
        frame.manaBar:SetMinMaxValues(0, 100)
        frame.manaBar:SetValue(100)
        if frame.manaBar.statusText then
            frame.manaBar.statusText:SetText("100%")
        end
    end
    
    -- PHASE 3: Racial visibility now handled by TrinketsRacials module
    -- Module's RefreshFrame handles prep room hiding automatically
    
    -- Show trinkets (as requested - they can stay)
    if frame.trinketIndicator then
        frame.trinketIndicator:Show()
    end
    
    -- CRITICAL: Hide DR icons in prep room (they should only show in live arena)
    if frame.drIcons then
        for category, drFrame in pairs(frame.drIcons) do
            if drFrame then
                drFrame:Hide()
            end
        end
    end
    
    -- CRITICAL: Hide dispel container in prep room (should only show in live arena)
    if frame.dispelContainer then
        frame.dispelContainer:Hide()
    end
    
    -- REMOVED: Don't force alpha to 1.0 - let stealth system control it
    -- frame:SetAlpha(1.0)  -- This was overriding prep room 0.5 alpha from ArenaFrameStealth
    
    -- CRITICAL: Always show the frame at the end, regardless of spec data availability
    -- This is the final safety net to prevent disappearing frames
    if not InCombatLockdown() then
        frame:Show()
    end
end

-- Arena state update handler - works with FrameLayout frames
function AC:UpdateArenaFrames(inPrepRoom)
    -- CRITICAL: Don't interfere with FrameLayout test mode
    if AC.testModeEnabled then
        -- DEBUG: Skipping UpdateArenaFrames in test mode
        -- print("ArenaCore: Skipping UpdateArenaFrames - test mode active")
        return
    end
    
    -- FIXED: Get frames from proper location
    local frames = AC.FrameManager and AC.FrameManager:GetFrames()
    if not frames then
        frames = _G.ArenaCore and _G.ArenaCore.arenaFrames
    end
    if not frames then 
        print("ArenaCore: No frames available from FrameLayout!")
        return 
    end
    
    -- OLD MISMATCH CODE REMOVED - Bracket size logic deleted
    -- MismatchHandler now handles all frame visibility
    -- This function only updates frame data, not visibility
    
    -- Update frame data based on context
    for i = 1, 3 do
        local frame = frames[i]
        if frame then
            if inPrepRoom then
                -- Prep room: Update if valid spec data exists
                local specID = GetArenaOpponentSpec and GetArenaOpponentSpec(i)
                if specID and specID > 0 then
                    UpdatePrepRoomUnit(i, frame)
                end
            else
                -- Real arena: Update if unit exists
                local unit = "arena" .. i
                if UnitExists(unit) then
                    UpdateArenaUnit(unit, frame)
                end
            end
        end
    end
end

-- Arena unit updates for real matches (global so it can be called from anywhere)
function UpdateArenaUnit(unit, frame)
    if not frame or not unit or not UnitExists(unit) then return end
    
    -- Update unit association
    frame.unit = unit
    
    -- Update name
    local name = GetUnitName(unit, true) or "Unknown"
    
    -- Handle server name display setting (arena only)
    if frame.playerName then
        local displayName = name
        local showArenaServerNames = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.general and AC.DB.profile.arenaFrames.general.showArenaServerNames
        
        -- If setting is OFF (default), remove server name in arena matches only
        if not showArenaServerNames and name then
            local dashPos = name:find("-")
            if dashPos then
                displayName = name:sub(1, dashPos - 1) -- Take only the character name part
            end
        end
        
        frame.playerName:SetText(displayName)
    end
    
    -- Update health/mana
    if frame.healthBar then
        local maxHealth = UnitHealthMax(unit) or 100
        local currentHealth = UnitHealth(unit) or 0
        frame.healthBar:SetMinMaxValues(0, maxHealth)
        frame.healthBar:SetValue(currentHealth)
        
        -- Dynamic class color for health bar (check useClassColors setting)
        local general = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.general
        local useClassColors = general and general.useClassColors ~= false
        
        local _, classFile = UnitClass(unit)
        
        -- CRITICAL FIX: Always set a color, don't leave it undefined
        if useClassColors and classFile then
            -- Use WoW's built-in class colors for consistency
            if RAID_CLASS_COLORS[classFile] then
                local classColor = RAID_CLASS_COLORS[classFile]
                frame.healthBar:SetStatusBarColor(classColor.r, classColor.g, classColor.b, 1)
            else
                -- Fallback to green if class color not found
                frame.healthBar:SetStatusBarColor(0, 1, 0, 1)
            end
        else
            -- Use default green when class colors are disabled OR class not detected
            frame.healthBar:SetStatusBarColor(0, 1, 0, 1)
        end
        
        if frame.healthBar.text then
            -- Check usePercentage setting
            local general = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.general
            local usePercentage = general and general.usePercentage ~= false
            
            if usePercentage then
                local percent = maxHealth > 0 and math.ceil((currentHealth / maxHealth) * 100) or 0
                frame.healthBar.text:SetText(percent .. "%")
            else
                -- Use AbbreviateLargeNumbers like sArena for shorter display
                frame.healthBar.text:SetText(AbbreviateLargeNumbers(currentHealth))
            end
        end
    end
    
    if frame.manaBar then
        local maxPower = UnitPowerMax(unit) or 100
        local currentPower = UnitPower(unit) or 0
        frame.manaBar:SetMinMaxValues(0, maxPower)
        frame.manaBar:SetValue(currentPower)
        
        -- CRITICAL FIX: Cache power type to prevent flickering (Shadow Priest issue)
        -- Only update color when power type actually changes, not on every power update
        local powerType = UnitPowerType(unit)
        
        -- Initialize cache if it doesn't exist
        if not frame.manaBar.cachedPowerType then
            frame.manaBar.cachedPowerType = -1
        end
        
        -- Only update color if power type changed (prevents flickering)
        if frame.manaBar.cachedPowerType ~= powerType then
            frame.manaBar.cachedPowerType = powerType
            
            local powerColors = {
                [0] = {0.00, 0.00, 1.00}, -- Mana (Blue)
                [1] = {1.00, 0.00, 0.00}, -- Rage (Red)
                [2] = {1.00, 0.50, 0.25}, -- Focus (Orange)
                [3] = {1.00, 1.00, 0.00}, -- Energy (Yellow)
                [4] = {0.00, 1.00, 1.00}, -- Combo Points (Cyan)
                [5] = {0.50, 0.50, 1.00}, -- Runes (Light Blue)
                [6] = {0.00, 1.00, 0.00}, -- Runic Power (Green)
                [7] = {0.50, 0.00, 1.00}, -- Soul Shards (Purple)
                [8] = {0.40, 0.80, 1.00}, -- Lunar Power (Light Blue)
                [9] = {0.80, 0.40, 0.80}, -- Holy Power (Pink)
                [11] = {0.50, 1.00, 1.00}, -- Maelstrom (Cyan)
                [12] = {0.00, 0.80, 0.60}, -- Chi (Green-Blue)
                [13] = {0.60, 0.20, 0.80}, -- Insanity (Purple)
                [17] = {1.00, 0.30, 0.30}, -- Fury (Red)
                [18] = {0.80, 0.60, 0.00}, -- Pain (Yellow-Brown)
                [19] = {0.00, 0.60, 1.00}  -- Essence (Blue)
            }
            
            local color = powerColors[powerType] or {0.00, 0.00, 1.00} -- Default to mana blue
            frame.manaBar:SetStatusBarColor(color[1], color[2], color[3], 1)
        end
        
        if frame.manaBar.text then
            -- Check usePercentage setting
            local general = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.general
            local usePercentage = general and general.usePercentage ~= false
            
            if usePercentage then
                local percent = maxPower > 0 and math.ceil((currentPower / maxPower) * 100) or 0
                frame.manaBar.text:SetText(percent .. "%")
            else
                -- Use AbbreviateLargeNumbers like sArena for shorter display
                frame.manaBar.text:SetText(AbbreviateLargeNumbers(currentPower))
            end
        end
    end
    
    -- Update class icon
    if frame.classIcon then
        local _, classFile = UnitClass(unit)
        if classFile then
            -- FIXED: Check if class icons are enabled before showing
            local db = AC.DB and AC.DB.profile
            local classEnabled = db and db.classIcons and db.classIcons.enabled
            if classEnabled ~= false then
                if frame.classIcon.UpdateClassIcon then
                    frame.classIcon.UpdateClassIcon(classFile)
                elseif frame.classIcon.icon then
                    local iconPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\" .. classFile .. ".tga"
                    frame.classIcon.icon:SetTexture(iconPath)
                    
                    -- CRITICAL FIX: Update overlay to match detected class
                    if frame.classIcon.overlay then
                        local overlayPath = "Interface\\AddOns\\ArenaCore\\Media\\Classicons\\Overlays\\" .. classFile:lower() .. "overlay.tga"
                        frame.classIcon.overlay:SetTexture(overlayPath, true, true)
                    end
                end
                frame.classIcon:Show()
            else
                frame.classIcon:Hide()
            end
        end
    end
    
    -- Update spec icon
    if frame.specIcon then
        local arenaIndex = tonumber(unit:match("arena(%d)"))
        if arenaIndex then
            local specID = GetArenaOpponentSpec and GetArenaOpponentSpec(arenaIndex)
            if specID and specID > 0 then
                local _, specName, _, specIcon = GetSpecializationInfoByID(specID)
                if specIcon and frame.specIcon.icon then
                    -- FIXED: Check if spec icons are enabled before showing
                    local db = AC.DB and AC.DB.profile
                    local specEnabled = db and db.specIcons and db.specIcons.enabled
                    if specEnabled ~= false then
                        frame.specIcon.icon:SetTexture(specIcon)
                        -- CRITICAL FIX: Safe Show() call - check if method exists
                        if frame.specIcon.Show then
                            frame.specIcon:Show()
                        end
                        -- CRITICAL FIX: Ensure border is visible when icon shows
                        if frame.specIcon.border and frame.specIcon.border.Show then
                            frame.specIcon.border:Show()
                        end
                    else
                        frame.specIcon:Hide()
                    end
                else
                    -- CRITICAL FIX: No spec icon available - hide icon but keep border structure intact
                    -- This prevents "broken borders" when spec data isn't ready yet
                    if frame.specIcon.icon then
                        frame.specIcon.icon:SetTexture(nil)
                    end
                    frame.specIcon:Hide()
                end
            else
                -- CRITICAL FIX: No spec ID yet (player didn't join or data not ready)
                -- Clear texture but don't break the frame structure
                if frame.specIcon.icon then
                    frame.specIcon.icon:SetTexture(nil)
                end
                frame.specIcon:Hide()
            end
        end
    end
    
    -- Show cast bar (will be updated by cast events)
    if frame.castBar then
        frame.castBar:Show()
    end
    
    -- CRITICAL FIX: Respect user settings for trinkets and racials
    local db = AC.DB and AC.DB.profile
    
    -- Update trinket (show only if enabled)
    if frame.trinketIndicator then
        local trinketEnabled = db and db.trinkets and db.trinkets.enabled
        if trinketEnabled ~= false then
            frame.trinketIndicator:Show()
        else
            frame.trinketIndicator:Hide()
        end
    end
    
    -- CRITICAL FIX: Show racial indicator like trinkets - always visible with border/icon
    if frame.racialIndicator then
        local racialEnabled = db and db.racials and db.racials.enabled
        
        -- CRITICAL FIX: Check if in prep room (same logic as absorbs)
        local _, instanceType = IsInInstance()
        local inPrepRoom = false
        if instanceType == "arena" then
            local numOpponents = GetNumArenaOpponentSpecs and GetNumArenaOpponentSpecs()
            if numOpponents and numOpponents > 0 then
                inPrepRoom = true
            end
        end
        
        -- Show racial indicator if enabled and not in prep room (like trinkets)
        if racialEnabled ~= false and not inPrepRoom then
            frame.racialIndicator:Show()
            -- Icon texture is handled by TrinketsRacials module
        else
            frame.racialIndicator:Hide()
        end
    end
    
    -- REMOVED: Don't force alpha to 1.0 - let stealth system control it
    -- frame:SetAlpha(1.0)  -- This was overriding stealth alpha from ArenaFrameStealth
    -- The "seen" event in ARENA_OPPONENT_UPDATE will set alpha to 1.0 when appropriate
    
    -- Show the frame (use secure method to avoid combat restrictions)
    if not InCombatLockdown() then
        frame:Show()
    end
end

-- ENHANCED: Component-specific clear helpers for better maintainability
local function ClearHealthBars(frame)
    if frame.healthBar then
        frame.healthBar:SetMinMaxValues(0, 100)
        frame.healthBar:SetValue(100)
        if frame.healthBar.statusText then
            frame.healthBar.statusText:SetText("")
            frame.healthBar.statusText:Hide()
        end
    end
    
    if frame.manaBar then
        frame.manaBar:SetMinMaxValues(0, 100)
        frame.manaBar:SetValue(100)
    end
end

local function ClearIconsAndIndicators(frame)
    -- Clear class icon and spec chips
    if frame.classIcon then
        if frame.classIcon.icon then frame.classIcon.icon:SetTexture(nil) end
        if frame.classIcon.overlay then frame.classIcon.overlay:SetTexture(nil) end
        if frame.classIcon.background then frame.classIcon.background:SetVertexColor(1, 1, 1, 1) end
        if frame.classIcon.specChip then
            frame.classIcon.specChip:Hide()
            if frame.classIcon.specChip.icon then frame.classIcon.specChip.icon:SetTexture(nil) end
        end
    end
    
    -- Clear trinket and racial indicators
    if frame.trinketIndicator then
        if frame.trinketIndicator.texture then frame.trinketIndicator.texture:SetTexture(nil) end
        if frame.trinketIndicator.cooldown then frame.trinketIndicator.cooldown:Clear() end
        frame.trinketIndicator:Hide()
    end
    
    if frame.racialIndicator then
        if frame.racialIndicator.texture then frame.racialIndicator.texture:SetTexture(nil) end
        if frame.racialIndicator.cooldown then frame.racialIndicator.cooldown:Clear() end
        frame.racialIndicator:Hide()
    end
end

local function ClearDebuffsAndDR(frame)
    -- Clear all debuffs/auras
    if frame.debuffContainer and frame.debuffContainer.debuffs then
        for _, debuff in pairs(frame.debuffContainer.debuffs) do
            if debuff then
                debuff:Hide()
                if debuff.icon then debuff.icon:SetTexture(nil) end
                if debuff.cooldown then debuff.cooldown:Clear() end
            end
        end
        wipe(frame.debuffContainer.debuffs)
    end
    
    -- Clear all DR (Diminishing Returns) icons
    if frame.drIcons then
        for category, dr in pairs(frame.drIcons) do
            if dr then
                dr:Hide()
                if dr.icon then dr.icon:SetTexture(nil) end
                if dr.cooldown then 
                    dr.cooldown.duration = 0
                    dr.cooldown.startTime = 0
                end
                if dr.timerText then dr.timerText:Hide() end
                dr.severity = 1
            end
        end
    end
end

local function ClearMiscElements(frame)
    -- Clear cast bars
    if frame.castBar then
        if frame.castBar.Icon then frame.castBar.Icon:SetTexture(nil) end
        if frame.castBar.Text then frame.castBar.Text:SetText("") end
        frame.castBar:SetValue(0)
        frame.castBar:Hide()
    end
    
    -- Clear target indicator
    if frame.targetIndicator then
        frame.targetIndicator:SetAlpha(0)
    end
    
    -- Clear player name and stored race data
    if frame.playerName then frame.playerName:SetText("") end
    frame.race = nil
end

-- REMOVED DUPLICATE: AC:ClearArenaData() already defined at line 993
-- ============================================================================
-- CONSOLIDATION: OLD SYSTEM REMOVED (Lines 5131-5309)
-- ============================================================================
-- This OLD arenaEventFrame system has been DELETED and consolidated into
-- MasterFrameManager (MFM) at lines 2644-3078.
--
-- ALL events now handled by MFM.eventFrame:
-- - ARENA_OPPONENT_UPDATE (stealth) → MFM:HandleArenaOpponentUpdate()
-- - GROUP_ROSTER_UPDATE (Solo Shuffle) → MFM:HandleSoloShuffleTransition()
-- - ARENA_PREP_OPPONENT_SPECIALIZATIONS → MFM:HandlePrepRoom()
-- - UNIT_HEALTH, UNIT_POWER, etc. → MFM:UpdateUnitHealth/Power/Name()
--
-- This eliminates the dual system problem that caused recurring bugs.
-- ============================================================================

-- REMOVED: Old arenaEventFrame code (178 lines deleted)
-- If you need to restore, check git history or backup

-- Placeholder to maintain line structure (will be cleaned up later)
local function OLD_SYSTEM_REMOVED_PLACEHOLDER()
    -- This function does nothing - placeholder for deleted OLD arenaEventFrame
    -- The NEW consolidated system is in MFM (MasterFrameManager)
end

-- OLD event handler body completely removed (lines 5153-5315 deleted)
-- All functionality moved to MFM:HandleGlobalEvent() at line 2680

-- ============================================================================
-- PHASE 1.3: Duplicate StartTicker/StopTicker removed (kept only first instance at line ~825)

-- Enhanced UpdateDebuffs function with comprehensive test mode protection
UpdateDebuffs = function(frame, unit)
    -- CRITICAL: Complete test mode protection
    if AC.testModeEnabled then return end
    if not frame or not frame.debuffContainer then return end
  
    local c = frame.debuffContainer
    for i=1,#c.debuffs do c.debuffs[i]:SetAlpha(0) end
    
    -- Get player debuffs only setting
    local db = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies and AC.DB.profile.moreGoodies.debuffs
    -- CRITICAL FIX: Check if playerDebuffsOnly is explicitly false, otherwise respect the actual value
    local playerDebuffsOnly = (db and db.playerDebuffsOnly ~= nil) and db.playerDebuffsOnly or false
    local maxCount = db and db.maxCount or 6
    
    local idx = 1
    for i=1,40 do
        -- Use the modern API approach
        local auraData = C_UnitAuras.GetDebuffDataByIndex(unit, i)
        if not auraData then break end

        local name = auraData.name
        local texture = auraData.icon  
        local count = auraData.applications
        local spellID = auraData.spellId
        local sourceUnit = auraData.sourceUnit
        
        -- FILTER OUT DAMPENING (spell ID 110310)
        if spellID == 110310 then
            -- Skip Dampening debuff entirely
        -- FILTER: Player debuffs only mode
        elseif playerDebuffsOnly and sourceUnit ~= "player" then
            -- Skip debuffs not created by the player
        elseif not name or idx > maxCount then 
            break
        else
            if not c.debuffs[idx] then 
                local d = CreateFrame("Frame", nil, c)
                d:SetSize(20,20)
                d:SetPoint("LEFT", (idx-1)*23, 0)
                
                -- Set aura icons to lower frame level so spec icons appear on top
                d:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
                d:SetFrameLevel(30) -- Lower level within MEDIUM strata, below spec icons
                local bg = d:CreateTexture(nil, "BACKGROUND")
                bg:SetAllPoints()
                bg:SetTexture(TEX.debuff_bg)
                Trim(bg)
                
                local icon = d:CreateTexture(nil, "ARTWORK")
                icon:SetSize(16,16)
                icon:SetPoint("CENTER")
                Trim(icon)
                
                local br = d:CreateTexture(nil, "BORDER")
                br:SetAllPoints()
                br:SetTexture(TEX.debuff_border)
                Trim(br)
                
                local stack = d:CreateFontString(nil, "OVERLAY")
                if AC.SafeSetFont then
                    AC.SafeSetFont(stack, AC.FONT_PATH, 7, "OUTLINE")
                else
                    stack:SetFont("Fonts\\\\FRIZQT__.TTF", 7, "OUTLINE")
                end
                stack:SetPoint("BOTTOMRIGHT", 2, -2)
                
                d.icon, d.stack = icon, stack
                c.debuffs[idx] = d
            end
            
            local d = c.debuffs[idx]
            d:SetAlpha(1)
            d.icon:SetTexture(texture)
            
            if count and count > 1 then 
                d.stack:SetText(count)
                d.stack:Show() 
            else 
                d.stack:Hide() 
            end
            
            idx = idx + 1
        end
    end
end

-- Public API with proper FrameLayout integration
function AC.GetFrameForUnit(unit)
    local idx = unit and unit:match("^arena(%d)$")
    local arenaFrames = GetArenaFrames() -- Use Wave 3 helper function
    return idx and arenaFrames and arenaFrames[tonumber(idx)]
end

-- REMOVED: Duplicate AC:UpdateDispelFrames function that was overriding DispelTracker.lua
-- The working implementation is in Core/DispelTracker.lua (line 443)
-- This duplicate was preventing dispels from showing in real arena

-- ============================================================================
-- CHUNK 9: BRIDGE FUNCTIONS & UI INTEGRATION
-- Functions that connect UI settings pages to frame updates
-- ============================================================================

-- ============================================================================
-- REFRESH LAYOUT FUNCTIONS - Connect UI pages to frame updates
-- ============================================================================

function AC:RefreshTexturesLayout(skipTextureUpdate)
    -- IMPORTANT: Single function handles all texture updates (like RefreshCastBarsLayout)
    -- WARNING: This function ONLY affects health/resource bars - DO NOT touch player names!
    -- Player names have their own independent positioning system in Arena Frames page
    local db = (self.DB and self.DB.profile and self.DB.profile.textures)
    if not db then return end
    
    -- Apply positioning settings
    local pos = db.positioning or {}
    local hPos = pos.horizontal or 56
    local vPos = pos.vertical or 6
    local spacing = pos.spacing or 8

    -- Apply sizing settings
    local sizing = db.sizing or {}
    local healthWidth = sizing.healthWidth or 128
    local healthHeight = sizing.healthHeight or 18
    local resourceWidth = sizing.resourceWidth or 136
    local resourceHeight = sizing.resourceHeight or 8
    -- Debug print removed to prevent console spam

    -- Get frames from unified system (MFM.frames)
    local frames = GetArenaFrames()
    if not frames then
        print("|cffFF0000[REFRESH TEXTURES ERROR]|r No frames found!")
        return
    end
    
    for i = 1, MAX_ARENA_ENEMIES do
        local f = frames[i]
        
        if f and f.healthBar and f.manaBar then
            -- ALWAYS update positioning to ensure spacing changes apply
            -- Position health bar at absolute coordinates
            f.healthBar:ClearAllPoints()
            f.healthBar:SetPoint("TOPLEFT", f, "TOPLEFT", hPos, -vPos)

            -- Position mana bar below health bar with spacing
            f.manaBar:ClearAllPoints()
            f.manaBar:SetPoint("TOPLEFT", f.healthBar, "BOTTOMLEFT", 0, -spacing)
            
            -- CRITICAL: DO NOT re-anchor player name here!
            -- Player names have their own independent positioning system
            -- They should NEVER be affected by health/resource bar positioning

            -- Apply sizing (SetSize doesn't cause flicker)
            f.healthBar:SetSize(healthWidth, healthHeight)
            f.manaBar:SetSize(resourceWidth, resourceHeight)
        end
    end

    -- ONLY update textures if this is a texture change, not positioning/sizing change
    -- This prevents the flash when just moving/resizing bars
    if not skipTextureUpdate then
        -- Apply texture settings ONLY to health and mana bars
        -- Cast bars have their own refresh function and shouldn't be touched here
        local healthTex = db.healthBarTexture or "Interface\\AddOns\\ArenaCore\\Media\\Frametextures\\texture1.tga"
        local powerTex = db.useDifferentPowerBarTexture and db.powerBarTexture or healthTex

        for i = 1, MAX_ARENA_ENEMIES do
            -- Use same frame source as above (unified system)
            local f = frames[i]
            
            if f and f.healthBar then
                f.healthBar:SetStatusBarTexture(healthTex)
                
                -- CRITICAL FIX: Reapply background vertex color after texture refresh
                -- SetStatusBarTexture may reset the background layer, causing white background bug
                -- This ensures the dark gray background (0.3, 0.3, 0.3) is always maintained
                if f.healthBar.bg then
                    f.healthBar.bg:SetVertexColor(0.3, 0.3, 0.3, 1)
                end
            end
            if f and f.manaBar then
                f.manaBar:SetStatusBarTexture(powerTex)
                
                -- CRITICAL FIX: Reapply mana bar background vertex color after texture refresh
                -- Same issue as health bar - prevent white background on mana bar
                if f.manaBar.bg then
                    f.manaBar.bg:SetVertexColor(0.3, 0.3, 0.3, 1)
                end
            end
        end
    end
end

function AC:RefreshBarTextures()
    local db = (self.DB and self.DB.profile and self.DB.profile.textures)
    if not db then return end
    local healthTex = db.healthBarTexture
    local powerTex = db.useDifferentPowerBarTexture and db.powerBarTexture or healthTex
    local castTex = db.useDifferentCastBarTexture and db.castBarTexture or powerTex
    for i = 1, MAX_ARENA_ENEMIES do
        local f = GetArenaFrames()[i]
        if f then
            if f.healthBar and f.healthBar.fill then 
                f.healthBar.fill:SetTexture(healthTex)
                -- CRITICAL FIX: Reapply background vertex color after texture changes
                if f.healthBar.bg then
                    f.healthBar.bg:SetVertexColor(0.3, 0.3, 0.3, 1)
                end
            end
            if f.manaBar and f.manaBar.fill then 
                f.manaBar.fill:SetTexture(powerTex)
                -- CRITICAL FIX: Reapply mana bar background vertex color
                if f.manaBar.bg then
                    f.manaBar.bg:SetVertexColor(0.3, 0.3, 0.3, 1)
                end
            end
            -- Cast bar texture update (uses StatusBar texture)
            if f.castBar then
                if castTex then
                    f.castBar:SetStatusBarTexture(castTex)
                    -- Update the fill reference
                    f.castBar.fill = f.castBar:GetStatusBarTexture()
                end
            end
        end
    end
end

-- Refresh function used by sliders and Edit Mode for Class Packs (TriBadges)
function AC:RefreshClassPacksLayout()
    if self.TriBadges and self.TriBadges.RefreshAll then
        self.TriBadges:RefreshAll()
    end
end

-- ================= Z-ORDER (STRATA/LEVEL) POLICY =================
-- Prevents overlays (DR icons, TriBadges) from bleeding through UI panels
local Z = {}
AC.ZPolicy = Z

-- Tuned so: Game panels (DIALOG) > our overlays (HIGH) > base frame art (MEDIUM)
Z.STRATA_BASE   = "MEDIUM"
Z.STRATA_OVER   = "HIGH"     -- overlays (DR, TriBadges, etc.)
Z.STRATA_PANEL  = "DIALOG"   -- config/edit UI

-- Apply to one arena frame
function Z:ApplyToFrame(f)
    if not f then return end
    local baseLevel = (f:GetFrameLevel() or 10)

    -- DR holder
    if f.DRHolder then
        f.DRHolder:SetToplevel(false)
        f.DRHolder:SetFrameStrata(self.STRATA_OVER)
        f.DRHolder:SetFrameLevel(baseLevel + 30)
    end

    -- TriBadges holder
    if f.TriBadgesHolder then
        f.TriBadgesHolder:SetToplevel(false)
        f.TriBadgesHolder:SetFrameStrata(self.STRATA_OVER)
        f.TriBadgesHolder:SetFrameLevel(baseLevel + 30)
    end
end

-- Apply to ALL frames
function Z:ApplyAll()
    local arenaFrames = AC.FrameManager and AC.FrameManager:GetFrames()
    if not arenaFrames then return end
    for i = 1, #arenaFrames do
        self:ApplyToFrame(arenaFrames[i])
    end
end

-- When config/edit UI opens
function Z:OnPanelShow()
    self:ApplyAll()
end

-- When panel closes
function Z:OnPanelHide()
    self:ApplyAll()
end

function AC:RefreshCastBarsLayout()
    -- IMPORTANT: This function targets the ONE main cast bar (f.castBar) for each arena frame
    -- There are no old/duplicate cast bars - only the main one created in CreateArenaFrame
    
    local db = (self.DB and self.DB.profile and self.DB.profile.castBars)
    if not db then 
        return 
    end
    local pos = db.positioning or {}
    local size = db.sizing or {}
    
    -- Get cast bar texture from textures settings
    local texturesDB = self.DB and self.DB.profile and self.DB.profile.textures
    local castTex = nil
    if texturesDB then
        local healthTex = texturesDB.healthBarTexture or "Interface\\AddOns\\ArenaCore\\Media\\Frametextures\\texture1.tga"
        local powerTex = texturesDB.useDifferentPowerBarTexture and texturesDB.powerBarTexture or healthTex
        castTex = texturesDB.useDifferentCastBarTexture and texturesDB.castBarTexture or powerTex
    end
    
    local castModule = GetCastBarModule()

    for i = 1, MAX_ARENA_ENEMIES do
        local f = GetArenaFrames()[i]
        if f and f.castBar then
            local cb = f.castBar -- This is the ONE main cast bar
            -- Apply positioning settings (works in both test mode and arena mode)
            cb:ClearAllPoints()
            cb:SetPoint("TOP", f, "TOP", pos.horizontal or 2, pos.vertical or -81)
            cb:SetWidth(size.width or 227)
            cb:SetHeight(size.height or 18)
            cb:SetScale((size.scale or 86) / 100)
            
            -- Apply cast bar texture if available
            if castTex then
                cb:SetStatusBarTexture(castTex)
            end
            
            -- Ensure visibility in test mode
            if AC.testModeEnabled then
                cb:SetAlpha(1)
                cb:Show()
                if castModule and castModule.RefreshTestLayout then
                    castModule:RefreshTestLayout(f, db)
                end
            end

            -- Apply Spell Icon settings (affects cb.spellIcon)
            if cb.spellIcon and db.spellIcons then
                local idb = db.spellIcons
                
                -- DEBUG: Check if spell icon has texture
                -- DEBUG: Spell icon check
                -- print("|cffFF00FF[SPELL ICON DEBUG]|r Frame " .. i .. " - spellIcon exists: " .. tostring(cb.spellIcon ~= nil))
                -- print("|cffFF00FF[SPELL ICON DEBUG]|r Frame " .. i .. " - spellIcon.texture exists: " .. tostring(cb.spellIcon.texture ~= nil))
                if cb.spellIcon.texture then
                    local texPath = cb.spellIcon.texture:GetTexture()
                    -- print("|cffFF00FF[SPELL ICON DEBUG]|r Frame " .. i .. " - texture path: " .. tostring(texPath))
                end
                
                -- FIXED: Explicitly check if enabled is true (not just "not false")
                -- This ensures the checkbox properly controls visibility
                local enabled = (idb.enabled == true)
                cb.spellIcon:SetShown(enabled)
                -- DEBUG: Spell icon visibility set
                -- print("|cff00FFFF[RefreshCastBarsLayout]|r Frame " .. i .. " - spellIcon:SetShown(" .. tostring(enabled) .. ") called")
                -- Hide/show styled border if present
                if cb.styledBorder then
                    for _, tex in ipairs(cb.styledBorder) do
                        if tex and tex.SetShown then tex:SetShown(enabled) end
                    end
                end

                -- CRITICAL FIX: Ensure proper table structure (don't reset, just ensure it exists)
                if not idb.sizing or type(idb.sizing) ~= "table" then
                    idb.sizing = {}
                end
                if not idb.sizing.scale then
                    idb.sizing.scale = 100
                end
                
                if not idb.positioning or type(idb.positioning) ~= "table" then
                    idb.positioning = {}
                end
                if not idb.positioning.horizontal then
                    idb.positioning.horizontal = -4
                end
                if not idb.positioning.vertical then
                    idb.positioning.vertical = 0
                end

                -- Size via scale percent relative to base 16
                local scalePct = (idb.sizing and idb.sizing.scale or 100) / 100
                local iconSize = math.max(8, math.floor(16 * scalePct + 0.5))
                cb.spellIcon:SetSize(iconSize, iconSize)

                -- Position relative to the bar
                local ix = (idb.positioning and idb.positioning.horizontal) or -4
                local iy = (idb.positioning and idb.positioning.vertical) or 0
                cb.spellIcon:ClearAllPoints()
                cb.spellIcon:SetPoint("RIGHT", cb, "LEFT", ix, iy)
            end
            
            -- CRITICAL FIX: Update cast bar color when spell school colors setting changes
            -- Only update color in test mode (live mode updates happen during actual casts)
            if AC.testModeEnabled and cb:IsShown() then
                if castModule and castModule.RefreshTestLayout then
                    castModule:RefreshTestLayout(f, db)
                end
            end
        end
    end
end

function AC:RefreshDRLayout()
    local drModule = GetDRModule()
    if drModule and drModule.RefreshLayout then
        drModule:RefreshLayout()
    end
end

-- ============================================================================
-- ELEMENT UPDATE HELPER - Used by trinkets/other layout refreshers
-- ============================================================================

local function UpdateElement(frame, dbPath, fontPath, skipReposition)
    if not frame then return end
    
    -- OPTION 1 FIX: Always read from GLOBAL database to get fresh data
    -- AC.DB.profile might be stale, but _G.ArenaCoreDB.profile is always current
    local globalDB = _G.ArenaCoreDB and _G.ArenaCoreDB.profile
    local db = (globalDB and globalDB[dbPath]) or {}
    
    -- OPTION 1 DEBUG: Show which database we're reading from
    if not globalDB then
        print("|cffFF0000[UpdateElement ERROR]|r _G.ArenaCoreDB.profile is nil!")
    elseif not globalDB[dbPath] then
        print("|cffFF0000[UpdateElement ERROR]|r _G.ArenaCoreDB.profile." .. dbPath .. " is nil!")
    end
    
    -- Handle enabled state (nil or true = enabled, false = disabled)
    local enabled = db.enabled
    if enabled == nil then enabled = true end -- Default to enabled if not set
    
    if not enabled then
        frame:Hide()
        return
    else
        -- COMBAT PROTECTED: Only show frame outside combat
        if not InCombatLockdown() then
            frame:Show()
        end
    end

    local pos = db.positioning or {}
    local size = db.sizing or {}
    
    -- CRITICAL FIX: Skip repositioning if requested (prevents jump after Edit Mode save)
    -- When skipReposition is true, we only update scale/visibility/fonts, not position
    if not skipReposition then
        -- CRITICAL FIX: EDIT MODE + SLIDER INTEGRATION
        -- Final position = Edit Mode base + Slider offset
        -- This allows both systems to work together without jumping
        
        -- Try to get Edit Mode base position first
        local editModeBaseX = pos.draggedBaseX or 0
        local editModeBaseY = pos.draggedBaseY or 0
        
        -- Try to get slider offset
        local sliderOffsetX = pos.sliderOffsetX or 0
        local sliderOffsetY = pos.sliderOffsetY or 0
        
        -- If no Edit Mode base exists, use the main horizontal/vertical as the position
        -- (backward compatibility for elements never moved in Edit Mode)
        local hasEditModeBase = (pos.draggedBaseX ~= nil or pos.draggedBaseY ~= nil)
        
        local finalX, finalY
        if hasEditModeBase then
            -- Edit Mode was used: combine base + slider offset
            finalX = editModeBaseX + sliderOffsetX
            finalY = editModeBaseY + sliderOffsetY
        else
            -- Edit Mode never used: use main position directly (legacy behavior)
            finalX = pos.horizontal or 0
            finalY = pos.vertical or 0
        end
        
        frame:ClearAllPoints()
        frame:SetPoint("CENTER", frame:GetParent(), "CENTER", finalX, finalY)
    end
    
    frame:SetScale((size.scale or 100) / 100)

    if fontPath and size.fontSize then
        local textObject = frame[fontPath]
        
        -- CRITICAL FIX: Create text object if it doesn't exist (for old frames)
        if not textObject and frame.cooldown then
            textObject = frame.cooldown:CreateFontString(nil, "OVERLAY")
            textObject:SetFont("Interface\\\\AddOns\\\\ArenaCore\\\\Media\\\\Fonts\\\\arenacore.ttf", size.fontSize, "OUTLINE")
            textObject:SetPoint("CENTER", frame.cooldown, "CENTER", 0, 0) -- Anchor to center of cooldown
            textObject:SetTextColor(1, 1, 1, 1)
            frame.cooldown.Text = textObject
            frame[fontPath] = textObject
            -- Font created
        elseif textObject and textObject.SetFont then
            -- CRITICAL FIX: GetFont() can return nil, use SafeSetFont instead
            if AC.SafeSetFont then
                AC.SafeSetFont(textObject, AC.FONT_PATH, size.fontSize, "OUTLINE")
            else
                local font, _, flags = textObject:GetFont()
                -- If GetFont returns nil, use fallback
                if not font or font == "" then
                    font = "Fonts\\\\FRIZQT__.TTF"
                    flags = "OUTLINE"
                end
                textObject:SetFont(font, size.fontSize, flags)
            end
            -- Font updated
        end
    end
end

function AC:RefreshTrinketsOtherLayout()
    -- Update spec icons using the main Layout system (once for all frames)
    
    -- CRITICAL: Refresh trinket icons when settings change
    self:RefreshTrinketIcons()
    
    local frames = GetArenaFrames()
    -- Refreshing trinkets/other
    
    -- CRITICAL FIX: Check if we should skip repositioning (post-save refresh)
    local skipReposition = self._skipRepositionOnRefresh or false
    
    for i = 1, MAX_ARENA_ENEMIES do
        local f = frames and frames[i]
        -- Processing frame
        if f then
            -- Update trinket, racial, and specIcon - call UpdateElement directly
            -- Pass skipReposition flag to prevent jump after Edit Mode save
            UpdateElement(f.trinketIndicator, "trinkets", "text", skipReposition)
            UpdateElement(f.racialIndicator, "racials", "text", skipReposition)
            UpdateElement(f.specIcon, "specIcons", nil, skipReposition)
            
            -- Apply Class Icons settings WITHOUT hiding the frame so the spec chip remains independent
            if f.classIcon and self.DB and self.DB.profile and self.DB.profile.classIcons then
                local db = self.DB.profile.classIcons
                local pos = db.positioning or {}
                local size = db.sizing or {}
                local enabled = (db.enabled ~= false)

                -- Always keep the classIcon frame shown so specChip can display even if main icon is disabled
                f.classIcon:Show()
                
                -- Apply positioning and scale - use original LEFT anchor with offsets
                f.classIcon:ClearAllPoints()
                local xOffset = 8 + (pos.horizontal or 0)  -- Original LEFT position (8) + user offset
                local yOffset = 0 + (pos.vertical or 0)    -- Original vertical position (0) + user offset
                f.classIcon:SetPoint("LEFT", f, "LEFT", xOffset, yOffset)
                f.classIcon:SetScale((size.scale or 100) / 100)

                -- Toggle only the main class icon visuals; do not affect spec chip
                local alpha = enabled and 1 or 0
                if f.classIcon.background then f.classIcon.background:SetAlpha(alpha) end
                if f.classIcon.icon then f.classIcon.icon:SetAlpha(alpha) end
                if f.classIcon.overlay then f.classIcon.overlay:SetAlpha(alpha) end
            end
        end
    end
end

function AC:RefreshMoreGoodiesLayout()
    -- Allow refresh in test mode for real-time preview
    
    -- Get frames from unified system
    local frames = GetArenaFrames()
    if not frames and self.FrameManager and self.FrameManager.GetFrames then
        frames = self.FrameManager:GetFrames()
    end

    -- Check if absorbs feature is enabled (matches Absorbs module logic)
    local moreGoodiesDB = self.DB and self.DB.profile and self.DB.profile.moreGoodies
    local absorbsEnabled = true -- Default to true if setting doesn't exist
    if moreGoodiesDB and moreGoodiesDB.absorbs then
        absorbsEnabled = moreGoodiesDB.absorbs.enabled ~= false
    end
    
    -- MoreGoodies refresh
    if UpdateAbsorbBar and frames then
        for i = 1, #frames do
            local f = frames[i]
            if f then pcall(UpdateAbsorbBar, f) end
        end
    end
    
    -- CRITICAL FIX: Refresh immunity glows when absorbs checkbox changes
    if self.ImmunityTracker then
        if absorbsEnabled then
            -- Absorbs enabled - refresh all immunity glows
            if self.ImmunityTracker.RefreshAll then
                self.ImmunityTracker:RefreshAll()
            end
        else
            -- Absorbs disabled - hide all immunity glows
            if self.ImmunityTracker.HideAll then
                self.ImmunityTracker:HideAll()
            end
        end
    end
    
    -- CRITICAL FIX: Re-apply test mode data after refresh if in test mode
    -- The refresh above calls UpdateAbsorbBar which hides test absorbs/glows
    -- We need to re-apply the test data to show them again
    if self.testModeEnabled and absorbsEnabled then
        local MFM = _G.ArenaCore and _G.ArenaCore.MasterFrameManager
        if MFM and MFM.ApplyTestData then
            MFM:ApplyTestData()
        end
    end

    -- 2) Party class indicators
    if self.UpdatePartyClassIndicators then
        self:UpdatePartyClassIndicators()
    end
end

function AC:RefreshClassPacksLayout()
    -- Real-time refresh for Class Packs (TriBadges) settings
    -- Called when sliders or checkbox change on Class Packs page
    -- CRITICAL: Only update TriBadges, don't touch main arena frames!
    
    if AC.TriBadges and AC.TriBadges.RefreshAll then
        AC.TriBadges:RefreshAll()
    end
    
    -- NEW FEATURE: Update timer font sizes when font size slider changes
    if AC.TriBadges and AC.TriBadges.UpdateTimerFontSize then
        AC.TriBadges:UpdateTimerFontSize()
    end
end

-- ============================================================================
-- FRAME PROPERTY UPDATERS - Connect frame properties to settings
-- ============================================================================

function AC:UpdateFramePosition()
    local settings = GetFrameSettings()
    if not settings then return end
    
    local positioning = settings.positioning or {}
    local x = tonumber(positioning.horizontal) or 200
    local y = tonumber(positioning.vertical) or -200
    
    -- Enforce WoW TOPLEFT semantics and screen bounds
    if y > 0 then y = -math.abs(y) end
    local w = UIParent:GetWidth() or 1920
    local h = UIParent:GetHeight() or 1080
    if x > w or x < -w then x = 200 end
    if y > h or y < -h then y = -200 end
    
    -- Store in database (avoid undefined config global)
    self:EnsureDB()
    self:SetPath(self.DB.profile, "arenaFrames.positioning.horizontal", x)
    self:SetPath(self.DB.profile, "arenaFrames.positioning.vertical", y)
end

function AC:UpdateFrameSpacing()
    local settings = GetFrameSettings()
    if not settings then return end
    
    local positioning = settings.positioning or {}
    -- Update spacing (avoid undefined FRAME_SPACING global)
    if self.FrameManager and self.FrameManager.UpdateSpacing then
        local spacing = positioning.spacing
        if not spacing or spacing == 0 then spacing = 21 end
        self.FrameManager:UpdateSpacing(spacing)
    end
end

function AC:UpdateFrameScale()
    local settings = GetFrameSettings()
    if not settings then return end
    
    local sizing = settings.sizing or {}
    local scale = (sizing.scale or 100) / 100
    
    -- Apply to FrameLayout frames with null safety
    local arenaFrames = GetArenaFrames()
    if arenaFrames then
        for i = 1, MAX_ARENA_ENEMIES do
            if arenaFrames[i] then
                arenaFrames[i]:SetScale(scale)
            end
        end
    end
end

function AC:UpdateFrameSize()
    local settings = GetFrameSettings()
    if not settings then return end
    
    local sizing = settings.sizing or {}
    local frameWidth = sizing.width or 350
    local frameHeight = sizing.height or 72
    
    -- CRITICAL FIX: Get bar sizes from textures database, NOT calculated from frame width
    -- This respects user's bar size settings from Textures page
    local texDb = self.DB and self.DB.profile and self.DB.profile.textures
    local healthWidth = (texDb and texDb.sizing and texDb.sizing.healthWidth) or 128
    local healthHeight = (texDb and texDb.sizing and texDb.sizing.healthHeight) or 18
    local resourceWidth = (texDb and texDb.sizing and texDb.sizing.resourceWidth) or 136
    local resourceHeight = (texDb and texDb.sizing and texDb.sizing.resourceHeight) or 8
    
    -- DEBUG: Using bar sizes
    -- print("|cffFFFF00[UPDATE FRAME SIZE]|r Using bar sizes - healthWidth: " .. healthWidth .. ", resourceWidth: " .. resourceWidth)
    
    -- Apply to FrameLayout frames with null safety
    local arenaFrames = GetArenaFrames()
    if not arenaFrames then return end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if arenaFrames[i] then
            local f = arenaFrames[i]
            
            -- Update frame dimensions
            f:SetSize(frameWidth, frameHeight)
            
            -- Update health and mana bars using TEXTURES database sizes (NOT calculated from frame width)
            if f.healthBar then
                f.healthBar:SetSize(healthWidth, healthHeight)
            end
            
            if f.manaBar then
                f.manaBar:SetSize(resourceWidth, resourceHeight)
            end
            
            -- Update cast bar sizing - CRITICAL: Use cast bar database settings, NOT calculated from frame width!
            if f.castBar then
                local castBarSettings = self.DB and self.DB.profile and self.DB.profile.castBars and self.DB.profile.castBars.sizing
                -- FIXED: Use user's custom cast bar width from database, don't calculate from frame width
                local castBarWidth = (castBarSettings and castBarSettings.width) or 227
                local castBarHeight = (castBarSettings and castBarSettings.height) or 18
                
                f.castBar:SetSize(castBarWidth, castBarHeight)
                
                -- Test mode visibility (no interference with live arena)
                if self.testModeEnabled then
                    f.castBar:SetAlpha(1)
                    f.castBar:Show()
                end
            end
        end
    end
end

function AC:SetGrowthDirection(direction)
    if not direction or not tContains({"Down", "Up", "Right", "Left"}, direction) then return end
    
    -- Store in database (avoid undefined config global)
    local settings = GetFrameSettings()
    if settings then
        settings.positioning = settings.positioning or {}
        settings.positioning.growthDirection = direction
        
        -- Notify FrameLayout system of growth direction change
        if self.FrameManager and self.FrameManager.UpdateGrowthDirection then
            self.FrameManager:UpdateGrowthDirection(direction)
        end
    end
end

function AC:ResetToDefaultSettings()
    if not self.DB or not self.DB.profile then return end
    local defaults = {
        positioning = { horizontal = 200, vertical = -200, spacing = 12, growthDirection = "Down" },
        sizing = { scale = 100, width = 350, height = 72 }
    }
    self.DB.profile.arenaFrames = self.DB.profile.arenaFrames or {}
    self.DB.profile.arenaFrames.positioning = defaults.positioning
    self.DB.profile.arenaFrames.sizing = defaults.sizing
    config.position.x = defaults.positioning.horizontal
    config.position.y = defaults.positioning.vertical
    config.growthDirection = defaults.positioning.growthDirection
    config.scale = defaults.sizing.scale / 100
    FRAME_SPACING = defaults.positioning.spacing
    FRAME_WIDTH = defaults.sizing.width
    FRAME_HEIGHT = defaults.sizing.height
    self:UpdateFramePosition()
    self:UpdateFrameSpacing()
    self:UpdateFrameScale()
    self:UpdateFrameSize()
    if self.configFrame and self.configFrame:IsShown() and self.RefreshConfigUI then
        self:RefreshConfigUI()
    end
end

-- ============================================================================
-- TRINKET ICON REFRESH SYSTEM - Handle trinket design changes
-- ============================================================================

function AC:RefreshTrinketIcons()
    -- Use the correct frame array that has trinket indicators
    local frames = self.arenaFrames or GetArenaFrames()
    
    -- Also handle new FrameManager frames
    if self.FrameManager and self.FrameManager.GetFrames then
        local fmFrames = self.FrameManager:GetFrames()
        if fmFrames and #fmFrames > 0 then
            frames = fmFrames
        end
    end
    
    -- DEBUG: Refreshing trinket icons
    -- print("|cff00FFFF[RefreshTrinketIcons]|r Refreshing trinket icons for " .. (#frames or 0) .. " frames")
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] and frames[i].trinketIndicator then
            -- Get the custom trinket icon - this now returns a texture ID ready for SetTexture()
            local trinketTexture = GetTrinketIcon()
            
            -- DEBUG: Trinket texture
            -- print("|cff00FFFF[RefreshTrinketIcons]|r Frame " .. i .. " trinket texture: " .. tostring(trinketTexture))
            
            if trinketTexture then
                -- Use .icon property based on CreateTrinket function (FrameManager structure)
                if frames[i].trinketIndicator.icon then
                    frames[i].trinketIndicator.icon:SetTexture(trinketTexture)
                    frames[i].trinketIndicator.icon:SetTexCoord(0.1, 0.9, 0.1, 0.9)
                    -- DEBUG: Icon set
                    -- print("|cff00FF00[RefreshTrinketIcons SUCCESS]|r Set icon for frame " .. i)
                elseif frames[i].trinketIndicator.texture then
                    frames[i].trinketIndicator.texture:SetTexture(trinketTexture)
                    frames[i].trinketIndicator.texture:SetTexCoord(0.1, 0.9, 0.1, 0.9)
                    -- DEBUG: Texture set
                    -- print("|cff00FF00[RefreshTrinketIcons SUCCESS]|r Set texture for frame " .. i)
                end
            end
        end
    end
end

-- ============================================================================
-- CLASS COLORS HELPER - Consistent class color settings access
-- ============================================================================
-- REMOVED DUPLICATE: This function is already defined at line 1208 and line 9927
-- Keeping the simpler version at line 9927 as the authoritative one

-- ============================================================================
-- CHUNK 10: TEST MODE SYSTEM
-- Complete test mode functionality with frame protection and realistic data
-- ============================================================================

-- ============================================================================
-- TEST MODE STATE MANAGEMENT
-- ============================================================================

AC.testModeEnabled = false
AC.framesLocked = true

-- Test mode configuration
local testModeConfig = {
    playerNames = {
        [1] = "Survivable",
        [2] = "Patymorph", 
        [3] = "Easymodex"
    },
    testClasses = {"DEATHKNIGHT", "MAGE", "HUNTER"},
    testSpecs = {
        [1] = 250, -- Death Knight Unholy
        [2] = 62,  -- Mage Arcane  
        [3] = 253  -- Hunter Beast Mastery
    }
}

-- ============================================================================
-- MAIN TEST MODE FUNCTIONS
-- ============================================================================

-- Setup test debuffs for demonstration in test mode
function FrameManager:SetupTestDebuffs(frame, testClass)
    local debuffModule = GetDebuffModule()
    if debuffModule and debuffModule.ApplyTestDebuffs then
        return debuffModule:ApplyTestDebuffs(frame, testClass)
    end

    if not frame.debuffContainer then return end
    
    -- CRITICAL FIX: Prevent re-running if already set up to avoid flickering
    if frame.debuffContainer.testDebuffsActive then return end
    
    -- Check if debuffs are enabled
    local debuffSettings = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies and AC.DB.profile.moreGoodies.debuffs
    if not debuffSettings or not debuffSettings.enabled then
        frame.debuffContainer:Hide()
        frame.debuffContainer.testDebuffsActive = false
        return
    end
    
    -- Get max count and scaling from settings (default 6, 100%)
    local maxCount = debuffSettings.maxCount or 6
    local scale = (debuffSettings.sizing and debuffSettings.sizing.scale or 100) / 100
    
    -- UNIVERSAL TEST DEBUFFS: 8 proper spells that work regardless of user's maxCount setting
    -- These are real debuff spells with proper icons (not class-specific, works for all frames)
    -- NEW FEATURE: Added duration field for countdown timer testing
    local testDebuffs = {
        {spellID = 589, icon = "Interface\\Icons\\spell_shadow_shadowwordpain", count = 1, duration = 18},      -- Shadow Word: Pain (18s)
        {spellID = 30108, icon = "Interface\\Icons\\spell_shadow_unstableaffliction_3", count = 1, duration = 12}, -- Unstable Affliction (12s)
        {spellID = 191587, icon = "Interface\\Icons\\spell_shadow_plaguecloud", count = 1, duration = 8},      -- Virulent Plague (8s)
        {spellID = 703, icon = "Interface\\Icons\\ability_rogue_garrote", count = 1, duration = 15},            -- Garrote (15s)
        {spellID = 164812, icon = "Interface\\Icons\\spell_nature_starfall", count = 2, duration = 10},         -- Moonfire (10s, 2 stacks)
        {spellID = 20271, icon = "Interface\\Icons\\spell_holy_righteousfury", count = 1, duration = 6},       -- Judgment (6s)
        {spellID = 772, icon = "Interface\\Icons\\ability_gouge", count = 1, duration = 12},                    -- Rend (12s)
        {spellID = 1715, icon = "Interface\\Icons\\ability_shockwave", count = 1, duration = 8}                -- Hamstring (8s)
    }
    
    -- No need for class-specific debuffs - these 8 universal debuffs work for all frames
    local debuffs = testDebuffs
    if not debuffs then return end
    
    local function Trim(texture)
        if texture and texture.SetTexCoord then 
            texture:SetTexCoord(0.002, 0.998, 0.002, 0.998) 
        end 
    end
    
    local c = frame.debuffContainer
    c:Show() -- Show container for test mode
    
    -- First, hide ALL existing debuff frames
    for i = 1, #c.debuffs do 
        if c.debuffs[i] then
            c.debuffs[i]:SetAlpha(0)
            c.debuffs[i]:Hide()
        end
    end
    
    -- Limit debuffs to user's max count setting
    local actualDebuffs = {}
    for i = 1, math.min(#debuffs, maxCount) do
        table.insert(actualDebuffs, debuffs[i])
    end
    
    -- Create test debuff icons with new styling and scaling
    for i, debuffData in ipairs(actualDebuffs) do
        if not c.debuffs[i] then 
            local frameSize = 20 * scale
            local iconSize = 16 * scale
            local spacing = 23 * scale
            
            local d = CreateFrame("Frame", nil, c)
            d:SetSize(frameSize, frameSize)
            d:SetPoint("LEFT", (i-1) * spacing, 0)
            d:SetFrameStrata("MEDIUM") -- CRITICAL: Use MEDIUM to stay below bags
            d:SetFrameLevel(30) -- Lower level within MEDIUM strata
            
            -- Black square background (like Blackout Aura editor)
            local bg = d:CreateTexture(nil, "BACKGROUND")
            bg:SetAllPoints()
            bg:SetTexture(1, 1, 1, 1) -- White background first
            bg:SetVertexColor(0.1, 0.1, 0.1, 0.8) -- Dark background
            
            -- Black square border
            local border = AC:CreateFlatTexture(d, "BORDER", 1, {0, 0, 0, 1}, 1)
            border:SetAllPoints()
            
            -- Inner border for clean square look
            local innerBg = AC:CreateFlatTexture(d, "BACKGROUND", 2, {0.15, 0.15, 0.15, 1}, 1)
            innerBg:SetPoint("TOPLEFT", 1, -1)
            innerBg:SetPoint("BOTTOMRIGHT", -1, 1)
            
            -- Icon
            local icon = d:CreateTexture(nil, "ARTWORK")
            icon:SetSize(iconSize, iconSize)
            icon:SetPoint("CENTER")
            Trim(icon)
            
            -- Stack count text (scaled)
            local stack = d:CreateFontString(nil, "OVERLAY")
            local fontSize = math.max(6, 7 * scale)
            stack:SetFont("Interface/AddOns/ArenaCore/Media/Fonts/arenacore.ttf", fontSize, "OUTLINE")
            stack:SetPoint("BOTTOMRIGHT", 2, -2)
            stack:SetTextColor(1, 1, 1, 1) -- White text
            
            -- NEW FEATURE: Cooldown frame for test mode countdown (using helper to block OmniCC)
            local cd = AC:CreateCooldown(d, nil, "CooldownFrameTemplate")
            cd:SetAllPoints(d)
            cd:SetHideCountdownNumbers(true)
            cd:SetDrawEdge(false)
            cd.noCooldownCount = true
            
            -- NEW FEATURE: Countdown timer text for test mode
            local timerFontSize = (debuffSettings.timerFontSize) or 10
            local timer = cd:CreateFontString(nil, "OVERLAY")
            timer:SetFont("Interface/AddOns/ArenaCore/Media/Fonts/arenacore.ttf", timerFontSize, "OUTLINE")
            timer:SetPoint("CENTER", cd, "CENTER", 0, 0)
            timer:SetTextColor(1, 1, 1, 1)
            cd.Text = timer
            
            d.icon = icon
            d.stack = stack
            d.cooldown = cd
            d.timer = timer
            c.debuffs[i] = d
        end
        
        -- Update test debuff display
        local d = c.debuffs[i]
        d:SetAlpha(1)
        d:Show() -- Ensure frame is visible
        d.icon:SetTexture(debuffData.icon)
        
        -- Show stack count
        if debuffData.count and debuffData.count > 1 then 
            d.stack:SetText(debuffData.count)
            d.stack:Show()
        else 
            d.stack:Hide()
        end
        
        -- NEW FEATURE: Test mode countdown timers
        local showTimer = debuffSettings.showTimer
        if showTimer == nil then showTimer = true end
        
        if showTimer and d.cooldown and d.timer and debuffData.duration then
            local duration = debuffData.duration
            
            local function StartTestCountdown()
                if not AC.testModeEnabled then return end
                if not d or not d.cooldown then return end
                
                local startTime = GetTime()
                local expirationTime = startTime + duration
                d.cooldown:SetCooldown(startTime, duration)
                
                local function UpdateTimerText()
                    if not d or not d.timer or not AC.testModeEnabled then return end
                    local remaining = expirationTime - GetTime()
                    if remaining > 0 then
                        local minutes = math.floor(remaining / 60)
                        local seconds = math.floor(remaining % 60)
                        local timeText = minutes > 0 and string.format("%d:%02d", minutes, seconds) or string.format("%.0f", remaining)
                        d.timer:SetText(timeText)
                        d.timerUpdate = C_Timer.NewTimer(0.1, UpdateTimerText)
                    else
                        d.timer:SetText("")
                        if d.timerUpdate then d.timerUpdate:Cancel() d.timerUpdate = nil end
                        C_Timer.After(0.5, function()
                            if AC.testModeEnabled and d and d:IsVisible() then StartTestCountdown() end
                        end)
                    end
                end
                UpdateTimerText()
            end
            StartTestCountdown()
        else
            if d.cooldown then d.cooldown:Clear() end
            if d.timer then d.timer:SetText("") end
            if d.timerUpdate then d.timerUpdate:Cancel() d.timerUpdate = nil end
        end
    end
    
    -- CRITICAL FIX: Mark as active to prevent re-running
    frame.debuffContainer.testDebuffsActive = true
end

-- Test debuff mode - called from DebuffsWindow TEST button
function FrameManager:TestDebuffMode()
    local debuffModule = GetDebuffModule()
    if debuffModule and debuffModule.TestMode then
        debuffModule:TestMode()
        return
    end

    if not self:FramesExist() then
        print("|cffFF6B6BArena Core:|r No frames available for testing")
        return
    end
    
    local frames = self:GetFrames()
    local testClasses = {"Deathknight", "Mage", "Hunter"}
    
    for i = 1, #frames do
        if frames[i] and frames[i].debuffContainer then
            frames[i].debuffContainer.testDebuffsActive = false
            local testClass = testClasses[i] or "Mage"
            self:SetupTestDebuffs(frames[i], testClass)
        end
    end
end

-- Refresh debuff settings - called when settings change in DebuffsWindow
function FrameManager:RefreshDebuffSettings()
    if not self:FramesExist() then return end
    
    local frames = self:GetFrames()
    local debuffSettings = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies and AC.DB.profile.moreGoodies.debuffs
    
    -- Update positioning for all debuff containers
    for i = 1, #frames do
        if frames[i] and frames[i].debuffContainer then
            local c = frames[i].debuffContainer
            
            -- Clear existing positioning and apply new positioning
            c:ClearAllPoints()
            local horizontal = debuffSettings and debuffSettings.positioning and debuffSettings.positioning.horizontal or 8
            local vertical = debuffSettings and debuffSettings.positioning and debuffSettings.positioning.vertical or 6
            c:SetPoint("BOTTOMLEFT", frames[i], "BOTTOMLEFT", horizontal, vertical)
            
            -- Hide all existing debuff frames
            for j = 1, #c.debuffs do 
                if c.debuffs[j] then
                    c.debuffs[j]:SetAlpha(0)
                    c.debuffs[j]:Hide()
                end
            end
            
            -- Clear the debuffs array to force recreation with new scale
            c.debuffs = {}
            
            -- CRITICAL FIX: Clear the active flag to allow re-setup with new settings
            c.testDebuffsActive = false
            
            -- Hide container if debuffs are disabled
            if not debuffSettings or not debuffSettings.enabled then
                c:Hide()
            end
        end
    end
    
    -- Check if any frame is shown (test mode active)
    local inTestMode = false
    for i = 1, #frames do
        if frames[i] and frames[i]:IsShown() then
            inTestMode = true
            break
        end
    end
    
    -- If in test mode and debuffs are enabled, refresh with new settings
    if inTestMode and debuffSettings and debuffSettings.enabled then
        self:TestDebuffMode()
    end
end
-- ============================================================================
-- MASTER FRAME MANAGER TEST MODE FUNCTIONS
-- ============================================================================

function MFM:CreateDragInfoBox()
    -- Only create once
    if self.dragInfoBox then
        self.dragInfoBox:Show()
        -- Restart pulse animation
        if self.dragInfoBox.pulseAnim then
            self.dragInfoBox.pulseAnim:Play()
        end
        return
    end
    
    -- Create small, non-intrusive info box
    local infoBox = CreateFrame("Frame", "ArenaCoreDragInfoBox", UIParent)
    infoBox:SetSize(150, 22)  -- Even more compact!
    infoBox:SetFrameStrata("MEDIUM")  -- Below DIALOG strata (where UI windows are)
    infoBox:SetFrameLevel(50)  -- Lower level so UI windows appear on top
    
    -- Position UPPER LEFT of Arena 1, high and to the left - visible but non-intrusive
    if arenaFrames and arenaFrames[1] then
        infoBox:SetPoint("BOTTOMRIGHT", arenaFrames[1], "TOPLEFT", -15, 40)  -- Higher up for more clearance
    else
        infoBox:SetPoint("CENTER", UIParent, "CENTER", -200, 100)
    end
    
    -- Use frame texture for rounded corners (matches arena frames)
    local bg = infoBox:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints()
    bg:SetTexture(TEX.frame_bg or "Interface\\AddOns\\ArenaCore\\Media\\Frametextures\\texture1.tga")
    bg:SetVertexColor(0.1, 0.1, 0.1, 0.9)  -- Dark tint
    
    -- Thin purple accent line at top
    if AC and AC.CreateFlatTexture then
        local topAccent = AC:CreateFlatTexture(infoBox, "OVERLAY", 3, AC.COLORS.PRIMARY or {0.66, 0.33, 0.94, 1}, 1)
        topAccent:SetPoint("TOPLEFT", 0, 0)
        topAccent:SetPoint("TOPRIGHT", 0, 0)
        topAccent:SetHeight(1)  -- Thinner line
    end
    
    -- Compact info text
    local text = infoBox:CreateFontString(nil, "OVERLAY")
    text:SetFont(AC.FONT_PATH or "Fonts\\FRIZQT__.TTF", 10, "OUTLINE")  -- Smaller font
    text:SetPoint("CENTER", 0, 0)
    text:SetText("|cffFFDD00Ctrl+Alt+Click|r to drag")  -- Shorter, gold color
    text:SetJustifyH("CENTER")
    text:SetShadowOffset(1, -1)
    text:SetShadowColor(0, 0, 0, 1)
    
    -- COOL PULSING ANIMATION like class icons!
    local animGroup = infoBox:CreateAnimationGroup()
    animGroup:SetLooping("REPEAT")
    
    -- Pulse out (scale up + fade out)
    local scaleOut = animGroup:CreateAnimation("Scale")
    scaleOut:SetDuration(1.5)
    scaleOut:SetScale(1.15, 1.15)
    scaleOut:SetSmoothing("IN_OUT")
    
    local fadeOut = animGroup:CreateAnimation("Alpha")
    fadeOut:SetDuration(1.5)
    fadeOut:SetFromAlpha(1.0)
    fadeOut:SetToAlpha(0.6)
    fadeOut:SetSmoothing("IN_OUT")
    
    -- Pulse back (scale down + fade in)
    local scaleIn = animGroup:CreateAnimation("Scale")
    scaleIn:SetDuration(1.5)
    scaleIn:SetScale(0.87, 0.87)  -- Back to original (1.15 * 0.87 ≈ 1.0)
    scaleIn:SetSmoothing("IN_OUT")
    scaleIn:SetOrder(2)
    
    local fadeIn = animGroup:CreateAnimation("Alpha")
    fadeIn:SetDuration(1.5)
    fadeIn:SetFromAlpha(0.6)
    fadeIn:SetToAlpha(1.0)
    fadeIn:SetSmoothing("IN_OUT")
    fadeIn:SetOrder(2)
    
    animGroup:Play()
    infoBox.pulseAnim = animGroup
    
    infoBox:Show()
    self.dragInfoBox = infoBox
    
    -- HIDDEN: Drag info box creation happens silently for cleaner user experience
    -- print("|cff8B45FFArena Core:|r Drag info box created with pulse animation!")
end

function MFM:EnableTestMode()
    if isTestMode then return end
    
    -- CRITICAL FIX: Initialize MFM if not already done (for new users clicking TEST early)
    if not self.frames[1] then
        print("|cffFFAA00ArenaCore:|r Initializing frames for first-time test mode...")
        self:Initialize()
    end
    
    isTestMode = true
    
    -- CRITICAL: Set the global test mode flag for TriBadges system
    if _G.ArenaCore then
        _G.ArenaCore.testModeEnabled = true
    end
    
    if AC and AC.Debug then 
        AC.Debug:Print("[FrameLayout] ===== ENABLE TEST MODE START =====")
        -- Debug what coordinates we have BEFORE creating frames
        local db = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and AC.DB.profile.arenaFrames.positioning
        if db then
            AC.Debug:Print("[FrameLayout] BEFORE CreateFrames - DB coords: (" .. tostring(db.horizontal) .. ", " .. tostring(db.vertical) .. ")")
        else
            AC.Debug:Print("[FrameLayout] BEFORE CreateFrames - NO DB COORDS FOUND")
        end
        
        -- Check if anchor already exists and where it is
        if AC.ArenaFramesAnchor then
            local anchorX, anchorY = AC.ArenaFramesAnchor:GetCenter()
            AC.Debug:Print("[FrameLayout] BEFORE CreateFrames - Existing anchor at: (" .. tostring(anchorX) .. ", " .. tostring(anchorY) .. ")")
        else
            AC.Debug:Print("[FrameLayout] BEFORE CreateFrames - NO ANCHOR EXISTS")
        end
    end
    
    self:CreateFrames()
    
    if AC and AC.Debug then 
        -- Check where anchor ended up AFTER CreateFrames
        if AC.ArenaFramesAnchor then
            local anchorX, anchorY = AC.ArenaFramesAnchor:GetCenter()
            AC.Debug:Print("[FrameLayout] AFTER CreateFrames - Anchor at: (" .. tostring(anchorX) .. ", " .. tostring(anchorY) .. ")")
        end
    end
    
    -- Update arena state (will create frames if needed)
    self:UpdateArenaState()
    
    if AC and AC.Debug then 
        -- Check where anchor is AFTER UpdateArenaState
        if AC.ArenaFramesAnchor then
            local anchorX, anchorY = AC.ArenaFramesAnchor:GetCenter()
            AC.Debug:Print("[FrameLayout] AFTER UpdateArenaState - Anchor at: (" .. tostring(anchorX) .. ", " .. tostring(anchorY) .. ")")
        end
    end
    
    -- Apply test data to frames
    self:ApplyTestData()
    
    -- Create drag info box (test mode only)
    self:CreateDragInfoBox()
    
    -- REMOVED: Alpha protection was blocking stealth system from working
    -- ArenaCore doesn't have alpha protection - it lets stealth system control it
    
    -- Apply final visibility restoration
    C_Timer.After(0.1, function()
        if isTestMode and self:FramesExist() then
            for i = 1, #arenaFrames do
                local frame = arenaFrames[i]
                if frame then
                    -- Don't force alpha - let stealth system control it
                    -- frame:SetAlpha(1.0)  -- REMOVED: Blocks prep room 0.5 alpha
                    -- COMBAT PROTECTED: Only show frame outside combat
                    if not InCombatLockdown() then
                        frame:Show()
                    end
                    -- CRITICAL FIX: Respect user settings even in test mode
                    local db = AC.DB and AC.DB.profile
                    local general = db and db.arenaFrames and db.arenaFrames.general or {}
                    
                    -- Always show health and mana bars
                    if frame.healthBar then frame.healthBar:SetAlpha(1.0); frame.healthBar:Show() end
                    if frame.manaBar then frame.manaBar:SetAlpha(1.0); frame.manaBar:Show() end
                    
                    -- Show player name only if enabled
                    if frame.playerName then
                        frame.playerName:SetAlpha(1.0)
                        if general.showNames ~= false then
                            frame.playerName:Show()
                        else
                            frame.playerName:Hide()
                        end
                    end
                    
                    -- Show class icon only if enabled
                    if frame.classIcon then
                        frame.classIcon:SetAlpha(1.0)
                        local classEnabled = db and db.classIcons and db.classIcons.enabled
                        if classEnabled ~= false then
                            frame.classIcon:Show()
                        else
                            frame.classIcon:Hide()
                        end
                    end
                    
                    -- Show trinket only if enabled
                    if frame.trinketIndicator then
                        frame.trinketIndicator:SetAlpha(1.0)
                        local trinketEnabled = db and db.trinkets and db.trinkets.enabled
                        if trinketEnabled ~= false then
                            frame.trinketIndicator:Show()
                        else
                            frame.trinketIndicator:Hide()
                        end
                    end
                    
                    -- Show racial only if enabled
                    if frame.racialIndicator then
                        frame.racialIndicator:SetAlpha(1.0)
                        local racialEnabled = db and db.racials and db.racials.enabled
                        if racialEnabled ~= false then
                            frame.racialIndicator:Show()
                        else
                            frame.racialIndicator:Hide()
                        end
                    end
                    
                    if frame.castBar then frame.castBar:SetAlpha(1.0) end
                end
            end
            -- DEBUG: Test mode frames visible
            -- print("|cffAAFFAA[SUCCESS]|r Test mode frames fully visible with alpha protection")
        end
        
        -- Show test DR icons after frames are visible
        self:ShowTestDRIcons()
        
        -- CRITICAL FIX: Show test dispel icons if dispels are enabled
        if AC.TestDispelCooldowns then
            local dispelSettings = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies and AC.DB.profile.moreGoodies.dispels
            if dispelSettings and dispelSettings.enabled ~= false then
                AC:TestDispelCooldowns()
            end
        end
    end)
    
    if AC and AC.Debug then 
        AC.Debug:Print("[FrameLayout] ===== ENABLE TEST MODE END =====")
    end
end

-- Show test DR icons in test mode (based on old FrameLayout.lua system)
function MFM:ShowTestDRIcons()
    -- DEBUG: ShowTestDRIcons called
    -- print("|cffFFAA00[TEST DR]|r ShowTestDRIcons called, isTestMode: " .. tostring(isTestMode))
    
    if not isTestMode then 
        -- DEBUG: Not in test mode
        -- print("|cffFF0000[TEST DR]|r Not in test mode, exiting")
        return 
    end
    
    local db = AC.DB and AC.DB.profile and AC.DB.profile.diminishingReturns
    if not db then 
        -- DEBUG: No DR database found
        -- print("|cffFF0000[TEST DR]|r No DR database found")
        return 
    end
    
    -- CRITICAL: Check if DR is enabled (nil or true = enabled, false = disabled)
    local enabled = (db.enabled ~= false)
    -- DEBUG: DR enabled check
    -- print("|cffFFAA00[TEST DR]|r DR enabled: " .. tostring(enabled))
    
    if not enabled then
        -- Hide all DR icons when disabled
        -- DEBUG: DR disabled
        -- print("|cffFFAA00[TEST DR]|r DR disabled, hiding icons")
        self:HideTestDRIcons()
        return
    end
    
    -- Get active DR settings (respects Detailed DR Settings window)
    local activeDB = (AC.GetActiveDRSettingsDB and AC:GetActiveDRSettingsDB()) or db
    activeDB.categories = activeDB.categories or {}
    
    -- Show test DR icons on each frame
    -- DEBUG: Looping through frames
    -- print("|cffFFAA00[TEST DR]|r Looping through " .. MAX_ARENA_ENEMIES .. " frames")
    
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = arenaFrames[i]
        -- DEBUG: Frame check
        -- print("|cffFFAA00[TEST DR]|r Frame " .. i .. ": " .. tostring(frame ~= nil) .. ", drIcons: " .. tostring(frame and frame.drIcons ~= nil))
        
        if frame and frame.drIcons then
            -- Show 7 core PvP DR categories with proper spell icons
            local testCategories = {
                {category = "stun", defaultSpellID = 408},
                {category = "silence", defaultSpellID = 15487},
                {category = "root", defaultSpellID = 339},
                {category = "incapacitate", defaultSpellID = 118},
                {category = "disorient", defaultSpellID = 5782},
                {category = "disarm", defaultSpellID = 207777}, -- Dismantle (Rogue)
                {category = "knockback", defaultSpellID = 51490}
            }
            
            for j, data in ipairs(testCategories) do
                local drFrame = frame.drIcons[data.category]
                -- DEBUG: Category check
                -- print("|cffFFAA00[TEST DR]|r   Category: " .. data.category .. ", drFrame exists: " .. tostring(drFrame ~= nil))
                
                if drFrame then
                    -- CRITICAL FIX: Check if this category is enabled in Detailed DR Settings
                    local categoryEnabled = (activeDB.categories[data.category] ~= false)
                    -- DEBUG: Category enabled check
                    -- print("|cffFFAA00[TEST DR]|r   Category " .. data.category .. " enabled: " .. tostring(categoryEnabled))
                    
                    if categoryEnabled then
                        -- CRITICAL FIX: Use ResolveDRIconSpellID for dynamic/custom icon logic
                        local spellIDToShow = data.defaultSpellID
                        if AC.ResolveDRIconSpellID then
                            -- Pass nil for unitGUID in test mode, use default spell as fallback
                            spellIDToShow = AC:ResolveDRIconSpellID(data.category, nil, data.defaultSpellID)
                        end
                        
                        -- RETAIL-ACCURATE: Set different DR stages per frame (1-3 only, no stage 4)
                        -- Stage 1 = 100%, Stage 2 = 50%, Stage 3 = 25%, Stage 4 = immune (hidden)
                        local stage = ((i - 1) % 3) + 1 -- Cycles through 1, 2, 3
                        if drFrame.UpdateStage then
                            drFrame:UpdateStage(stage)
                        else
                            drFrame.severity = stage
                            if drFrame.stageText then
                                drFrame.stageText:SetText(tostring(stage))
                            end
                        end
                        
                        -- Set spell icon texture using resolved spell ID (same method as dropdown)
                        if drFrame.icon then
                            local spellInfo = C_Spell.GetSpellInfo(spellIDToShow)
                            if spellInfo and spellInfo.iconID then
                                drFrame.icon:SetTexture(spellInfo.iconID)
                            else
                                -- Fallback: Try default spell for this category
                                local fallbackInfo = C_Spell.GetSpellInfo(data.defaultSpellID)
                                if fallbackInfo and fallbackInfo.iconID then
                                    drFrame.icon:SetTexture(fallbackInfo.iconID)
                                end
                            end
                        end
                        
                        drFrame:Show()
                        -- DEBUG: Showing DR icon
                        -- print("|cff00FF00[TEST DR]|r   Showing " .. data.category .. " icon at stage " .. stage)
                        
                        -- Set test cooldown with auto-refresh
                        if drFrame.cooldown then
                            local cooldownTime = 18.5 - (j * 2)
                            drFrame.cooldown:SetCooldown(GetTime(), cooldownTime)
                            drFrame.testCooldownDuration = cooldownTime
                            drFrame.testCooldownCategory = data.category
                        end
                    else
                        -- Category disabled - hide this DR icon
                        drFrame:Hide()
                    end
                end
            end
            
            -- Position the DR icons
            -- DEBUG: Calling UpdateDRPositions
            -- print("|cffFFAA00[TEST DR]|r Calling AC:UpdateDRPositions for frame " .. i)
            if AC and AC.UpdateDRPositions then
                AC:UpdateDRPositions(frame)
                -- DEBUG: UpdateDRPositions called
                -- print("|cff00FF00[TEST DR]|r AC:UpdateDRPositions called successfully")
            else
                -- DEBUG: UpdateDRPositions not found
                -- print("|cffFF0000[TEST DR]|r AC:UpdateDRPositions function NOT FOUND!")
            end
        end
    end
    
    -- Start auto-refresh timer if not already running
    if not self.drTestRefreshTimer then
        self:StartDRTestRefreshTimer()
    end
end

-- Auto-refresh DR test cooldowns when they expire
function MFM:StartDRTestRefreshTimer()
    -- Cancel existing timer if any
    if self.drTestRefreshTimer then
        self.drTestRefreshTimer:Cancel()
    end
    
    -- Create repeating timer to refresh cooldowns
    -- PHASE 3: Reduced from 1s to 2s (50% reduction, test mode only)
    self.drTestRefreshTimer = C_Timer.NewTicker(2, function()
        if not isTestMode then
            -- Stop timer when test mode ends
            if self.drTestRefreshTimer then
                self.drTestRefreshTimer:Cancel()
                self.drTestRefreshTimer = nil
            end
            return
        end
        
        -- Check each frame's DR icons
        for i = 1, MAX_ARENA_ENEMIES do
            local frame = arenaFrames[i]
            if frame and frame.drIcons then
                for category, drFrame in pairs(frame.drIcons) do
                    if drFrame and drFrame:IsShown() and drFrame.cooldown and drFrame.testCooldownDuration then
                        -- Check if cooldown expired
                        local start, duration = drFrame.cooldown:GetCooldownTimes()
                        local currentTime = GetTime() * 1000
                        
                        if start == 0 or currentTime >= (start + duration) then
                            -- Restart cooldown
                            drFrame.cooldown:SetCooldown(GetTime(), drFrame.testCooldownDuration)
                        end
                        
                        -- CRITICAL: Update timer text with remaining time
                        if drFrame.timerText and start > 0 then
                            local remaining = math.ceil((start + duration - currentTime) / 1000)
                            if remaining > 0 then
                                drFrame.timerText:SetText(tostring(remaining))
                            else
                                drFrame.timerText:SetText("")
                            end
                        end
                    end
                end
            end
        end
    end)
end

function MFM:DisableTestMode()
    if not isTestMode then return end
    
    isTestMode = false
    
    -- Stop frame monitoring
    if frameMonitorTimer then
        frameMonitorTimer:Cancel()
        frameMonitorTimer = nil
    end
    
    -- Stop DR test refresh timer
    if self.drTestRefreshTimer then
        self.drTestRefreshTimer:Cancel()
        self.drTestRefreshTimer = nil
    end
    
    -- MEMORY LEAK FIX: Cancel trinket and racial tickers
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = arenaFrames[i]
        if frame then
            -- Cancel trinket ticker
            if frame.trinketIndicator and frame.trinketIndicator.ticker then
                frame.trinketIndicator.ticker:Cancel()
                frame.trinketIndicator.ticker = nil
            end
            -- Cancel racial ticker
            if frame.racialIndicator and frame.racialIndicator.ticker then
                frame.racialIndicator.ticker:Cancel()
                frame.racialIndicator.ticker = nil
            end
        end
    end
    
    -- CRITICAL: Set the global test mode flag
    if _G.ArenaCore then
        _G.ArenaCore.testModeEnabled = false
    end
    
    -- Hide test DR icons
    self:HideTestDRIcons()
    
    -- CRITICAL FIX: Hide cast bar elements when exiting test mode
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = arenaFrames[i]
        if frame and frame.castBar then
            frame.castBar:Hide()
            -- Hide shield overlay
            if frame.castBar.shieldOverlay then
                frame.castBar.shieldOverlay:Hide()
            end
            -- Hide border frame
            if frame.castBar.borderFrame then
                frame.castBar.borderFrame:Hide()
            end
        end
    end
    
    -- Hide drag info box
    if self.dragInfoBox then
        self.dragInfoBox:Hide()
    end
    
    -- Hide frames unless we're in arena
    if not self.isInArena then
        self:HideAllFrames()
    end
    
    -- DEBUG: Test Mode Disabled
    -- print("|cffFFAA00ArenaCore Master:|r Test Mode Disabled")
end

-- Hide test DR icons when exiting test mode
function MFM:HideTestDRIcons()
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = arenaFrames[i]
        if frame and frame.drIcons then
            for category, drFrame in pairs(frame.drIcons) do
                if drFrame then
                    drFrame:Hide()
                    -- Clear cooldown
                    if drFrame.cooldown then
                        drFrame.cooldown:Clear()
                    end
                end
            end
        end
    end
end

-- ============================================================================
-- NOTE: OmniCC exclusion now handled globally in Init.lua via metatable hook
-- This section removed - no longer needed
-- ============================================================================

-- ============================================================================
-- TRINKET AND RACIAL INDICATOR CREATION
-- ============================================================================

function MFM:FramesExist()
    -- Check if arena frames exist and are valid
    return self.frames and self.frames[1] and self.frames[2] and self.frames[3]
end

-- CRITICAL FIX: Add GetFrames() function for module compatibility
function MFM:GetFrames()
    return self.frames or {}
end

function MFM:ApplyTestData()
    if not self:FramesExist() then return end
    
    -- Apply realistic test data to all frames
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = self.frames[i]
        if frame then
            self:ApplyTestDataToFrame(frame, i)
        end
    end
    
    -- Force refresh all layout systems with test data
    self:RefreshAllTestLayouts()
    
    -- DEBUG: Test data applied
    -- print("|cffAAFFAA[SUCCESS]|r Test data applied to all frames")
end

function MFM:ApplyTestDataToFrame(frame, index)
    if not frame then return end
    
    -- DEBUG: print("|cffFFAA00[ApplyTestDataToFrame]|r Called for frame " .. index)
    
    -- Local test configuration
    local config = {
        playerNames = {
            [1] = "Survivable",
            [2] = "Patymorph", 
            [3] = "Easymodex"
        },
        serverNames = {
            [1] = "Stormrage",
            [2] = "Sargeras",
            [3] = "Moonguard"
        },
        testClasses = {"DEATHKNIGHT", "MAGE", "HUNTER"},
        testSpecs = {
            [1] = 250, -- Death Knight Unholy
            [2] = 62,  -- Mage Arcane  
            [3] = 253  -- Hunter Beast Mastery
        }
    }
    
    local testClass = config.testClasses[index] or "MAGE"
    
    -- Store test class on frame for color updates
    frame.testClass = testClass
    
    if frame.playerName then
        -- Check if server names should be shown in test mode
        local showServerNames = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames and 
                                AC.DB.profile.arenaFrames.general and 
                                AC.DB.profile.arenaFrames.general.showArenaServerNames
        
        local playerName = config.playerNames[index] or ("TestPlayer" .. index)
        
        if showServerNames then
            -- Add server name with hyphen separator (WoW format)
            local serverName = config.serverNames[index] or "TestServer"
            frame.playerName:SetText(playerName .. "-" .. serverName)
        else
            -- Just character name
            frame.playerName:SetText(playerName)
        end
        
        -- CRITICAL FIX: Only show if enabled in settings
        local db = AC.DB and AC.DB.profile
        local general = db and db.arenaFrames and db.arenaFrames.general or {}
        if general.showNames ~= false then
            frame.playerName:Show()
        else
            frame.playerName:Hide()
        end
    end
    -- Set health and mana to realistic values
    -- NEW FEATURE: Realistic health values for combat preview (unique to ArenaCore!)
    -- Arena 1: 100% (full health), Arena 2: 73% (damaged), Arena 3: 43% (low health)
    local healthValues = {100, 73, 43}
    local healthValue = healthValues[index] or 100
    
    if frame.healthBar then
        frame.healthBar:SetMinMaxValues(0, 100)
        frame.healthBar:SetValue(healthValue)
        
        -- Apply class colors if enabled
        if AC and AC:GetClassColorsEnabled() and RAID_CLASS_COLORS[testClass] then
            local classColor = RAID_CLASS_COLORS[testClass]
            frame.healthBar:SetStatusBarColor(classColor.r, classColor.g, classColor.b, 1)
            
            -- CRITICAL FIX: Apply class-based border color (matches health bar)
            if frame.border then
                frame.border:SetVertexColor(classColor.r, classColor.g, classColor.b, 1)
            end
        else
            frame.healthBar:SetStatusBarColor(0, 1, 0, 1) -- Default green
            
            -- Default border color when class colors disabled
            if frame.border then
                frame.border:SetVertexColor(1, 1, 1, 1) -- White
            end
        end
        
        if frame.healthBar.statusText then
            frame.healthBar.statusText:SetText(healthValue .. "%")
            frame.healthBar.statusText:Show()
            -- DEBUG: Health text set
            -- print("|cff00FF00[TEST MODE HEALTH]|r Arena " .. index .. " health text set to: " .. healthValue .. "%")
        else
            -- DEBUG: Health text missing
            -- print("|cffFF0000[TEST MODE ERROR]|r Arena " .. index .. " healthBar.statusText does NOT exist!")
        end
    end
    
    -- Get test spec for this frame
    local testSpecID = config.testSpecs and config.testSpecs[index]
    
    -- NEW FEATURE: Realistic resource values for combat preview (unique to ArenaCore!)
    -- Arena 1: 100% (full), Arena 2: 82% (mana), Arena 3: 24% (focus)
    local resourceValues = {100, 82, 24}
    local resourceValue = resourceValues[index] or 100
    
    if frame.manaBar then
        frame.manaBar:SetMinMaxValues(0, 100)
        frame.manaBar:SetValue(resourceValue)
        
        -- REVOLUTIONARY: Set SPEC-appropriate resource color (no other addon does this!)
        local resourceColor = self:GetSpecResourceColor(testClass, testSpecID)
        frame.manaBar:SetStatusBarColor(resourceColor[1], resourceColor[2], resourceColor[3], resourceColor[4] or 1)
        
        if frame.manaBar.statusText then
            frame.manaBar.statusText:SetText(resourceValue .. "%")
            frame.manaBar.statusText:Show()
            -- DEBUG: Resource text set
            -- print("|cff00FF00[TEST MODE RESOURCE]|r Arena " .. index .. " resource text set to: " .. resourceValue .. "%")
        else
            -- DEBUG: Resource text missing
            -- print("|cffFF0000[TEST MODE ERROR]|r Arena " .. index .. " manaBar.statusText does NOT exist!")
        end
    end
    
    -- Set class icon
    if frame.classIcon and frame.classIcon.UpdateClassIcon then
        -- FIXED: Check if class icons are enabled in test mode
        local db = AC.DB and AC.DB.profile
        local classEnabled = db and db.classIcons and db.classIcons.enabled
        if classEnabled ~= false then
            frame.classIcon.UpdateClassIcon(testClass)
            frame.classIcon:Show()
        else
            frame.classIcon:Hide()
        end
    end
    
    -- Set spec icon
    if frame.specIcon and frame.specIcon.icon then
        local _, _, _, specIcon = GetSpecializationInfoByID(testSpecID)
        if specIcon and frame.specIcon.icon then
            -- FIXED: Check if spec icons are enabled in test mode
            local db = AC.DB and AC.DB.profile
            local specEnabled = db and db.specIcons and db.specIcons.enabled
            if specEnabled ~= false then
                frame.specIcon.icon:SetTexture(specIcon)
                frame.specIcon:Show()
            else
                frame.specIcon:Hide()
            end
        end
    end
    
    -- CRITICAL FIX: Set frame.unit BEFORE calling RefreshFrame so racial icons work
    frame.unit = "arena" .. index
    
    -- Show trinket and racial icons with test data
    if AC.TrinketsRacials and AC.TrinketsRacials.RefreshFrame then
        AC.TrinketsRacials:RefreshFrame(frame, frame.unit)
    end
    
    -- SPECIAL: Arena 3 gets looping cooldowns for font testing (trinket + racial)
    if index == 3 then
        -- Trinket looping cooldown
        if frame.trinketIndicator and frame.trinketIndicator.cooldown then
            local function UpdateTrinketTimer()
                if not isTestMode or not frame.trinketIndicator then return end
                
                local start, duration = frame.trinketIndicator.cooldown:GetCooldownTimes()
                if start > 0 and duration > 0 and frame.trinketIndicator.txt then
                    local remaining = (start + duration - GetTime() * 1000) / 1000
                    if remaining > 0 then
                        local minutes = math.floor(remaining / 60)
                        local seconds = math.floor(remaining % 60)
                        local text = string.format("%d:%02d", minutes, seconds)
                        frame.trinketIndicator.txt:SetText(text)
                        frame.trinketIndicator.txt:Show()
                    else
                        frame.trinketIndicator.txt:SetText("")
                    end
                end
            end
            
            local function StartTrinketLoop()
                if not isTestMode then return end
                frame.trinketIndicator.cooldown:SetCooldown(GetTime(), 120)
                
                -- PHASE 3: Update timer text every 0.5 seconds (was 0.1s - 80% reduction)
                local ticker = C_Timer.NewTicker(0.5, UpdateTrinketTimer)
                frame.trinketIndicator.ticker = ticker
                
                -- Restart loop after 120 seconds
                C_Timer.After(120, function()
                    if frame.trinketIndicator.ticker then
                        frame.trinketIndicator.ticker:Cancel()
                    end
                    StartTrinketLoop()
                end)
            end
            StartTrinketLoop()
        end
        
        -- Racial looping cooldown
        if frame.racialIndicator and frame.racialIndicator.cooldown then
        -- Start looping cooldown (30 second loop for easy testing)
        local function UpdateRacialTimer()
            if not isTestMode or not frame.racialIndicator then return end
            
            local start, duration = frame.racialIndicator.cooldown:GetCooldownTimes()
            if start > 0 and duration > 0 and frame.racialIndicator.txt then
                local remaining = (start + duration - GetTime() * 1000) / 1000
                if remaining > 0 then
                    local text = string.format("%.0f", remaining)
                    frame.racialIndicator.txt:SetText(text)
                    frame.racialIndicator.txt:Show()
                else
                    frame.racialIndicator.txt:SetText("")
                end
            end
        end
        
        local function StartRacialLoop()
            if not isTestMode then return end
            frame.racialIndicator.cooldown:SetCooldown(GetTime(), 30)
            
            -- PHASE 3: Update timer text every 0.5 seconds (was 0.1s - 80% reduction)
            local ticker = C_Timer.NewTicker(0.5, UpdateRacialTimer)
            frame.racialIndicator.ticker = ticker
            
            -- Restart loop after 30 seconds
            C_Timer.After(30, function()
                if frame.racialIndicator.ticker then
                    frame.racialIndicator.ticker:Cancel()
                end
                StartRacialLoop()
            end)
        end
        StartRacialLoop()
        end
    end

    -- Set up test cast bar
    if frame.castBar then
        local castModule = GetCastBarModule()
        if castModule and castModule.ApplyTestSpell then
            local spellData = self:GetTestSpellData(testClass)
            castModule:ApplyTestSpell(frame, spellData, {
                isNonInterruptible = (index == 3 and testClass == "HUNTER"),
                progress = 75,
            })
        end
    end
    
    -- Apply test debuffs if enabled
    if AC.FrameManager and AC.FrameManager.SetupTestDebuffs then
        AC.FrameManager:SetupTestDebuffs(frame, testClass)
    end
    
    -- Apply test absorb bars if enabled
    -- CRITICAL FIX: Don't show absorbs in prep room (check instance type)
    local _, instanceType = IsInInstance()
    if AC.Absorbs and AC.Absorbs.UpdateAbsorbBar and instanceType ~= "arena" then
        -- frame.unit already set above for racial icons
        -- Only show test absorbs outside arena (not in prep room)
        AC.Absorbs:UpdateAbsorbBar(frame)
    end
    
    -- Apply test immunity glow (show on frame 2 for demonstration) - ONLY IN TEST MODE
    -- CRITICAL FIX: Check if absorbs feature is enabled (immunity uses same checkbox)
    local moreGoodiesDB = AC.DB and AC.DB.profile and AC.DB.profile.moreGoodies
    local absorbsEnabled = true -- Default to true if setting doesn't exist
    if moreGoodiesDB and moreGoodiesDB.absorbs then
        absorbsEnabled = moreGoodiesDB.absorbs.enabled ~= false
    end
    
    if index == 2 and AC.testModeEnabled and absorbsEnabled then
        if AC.ImmunityTracker then
            AC.ImmunityTracker:CreateImmunityGlow(frame)
            -- Show magic immunity glow on frame 2 in test mode
            if frame.immunityGlow then
                frame.immunityGlow:SetVertexColor(0, 1, 0, 1) -- BRIGHT GREEN for magic immunity
                frame.immunityGlow:Show()
                if frame.immunityGlowAnim and not frame.immunityGlowAnim:IsPlaying() then
                    frame.immunityGlowAnim:Play()
                end
            end
        end
    end
    
    -- Also show white immunity glow on frame 3 for demonstration - ONLY IN TEST MODE
    if index == 3 and AC.testModeEnabled and absorbsEnabled then
        if AC.ImmunityTracker then
            AC.ImmunityTracker:CreateImmunityGlow(frame)
            -- Show total immunity glow on frame 3 in test mode
            if frame.immunityGlow then
                frame.immunityGlow:SetVertexColor(1, 1, 1, 1) -- PURE WHITE for total immunity
                frame.immunityGlow:Show()
                if frame.immunityGlowAnim and not frame.immunityGlowAnim:IsPlaying() then
                    frame.immunityGlowAnim:Play()
                end
            end
        end
    end
end

-- ============================================================================
-- TEST DATA HELPER FUNCTIONS
-- ============================================================================

-- REVOLUTIONARY: Spec-based resource colors (no other addon does this!)
-- Returns resource color based on SPEC, not just class
function MFM:GetSpecResourceColor(className, specID)
    -- Spec-based resource colors for specs with different resources than base class
    local specResourceColors = {
        -- DRUID: Different resources per spec
        [102] = {0.30, 0.52, 0.90, 1}, -- Balance: Astral Power (Royal Blue #4D85E6)
        [103] = {1.00, 1.00, 0.00, 1}, -- Feral: Energy (Yellow #FFFF00)
        [104] = {1.00, 0.00, 0.00, 1}, -- Guardian: Rage (Red #FF0000)
        [105] = {0.00, 0.00, 1.00, 1}, -- Restoration: Mana (Blue #0000FF)
        
        -- MONK: Different resources per spec
        [268] = {1.00, 1.00, 0.00, 1}, -- Brewmaster: Energy (Yellow #FFFF00)
        [270] = {0.00, 0.00, 1.00, 1}, -- Mistweaver: Mana (Blue #0000FF) - FIXED: Was cyan, now proper mana blue
        [269] = {1.00, 1.00, 0.00, 1}, -- Windwalker: Energy (Yellow #FFFF00)
        
        -- PRIEST: Shadow uses Insanity instead of Mana
        [256] = {0.00, 0.00, 1.00, 1}, -- Discipline: Mana (Blue #0000FF)
        [257] = {0.00, 0.00, 1.00, 1}, -- Holy: Mana (Blue #0000FF)
        [258] = {0.40, 0.00, 0.80, 1}, -- Shadow: Insanity (Purple #6600CC)
        
        -- SHAMAN: Elemental uses Maelstrom, others use Mana
        [262] = {0.00, 0.50, 1.00, 1}, -- Elemental: Maelstrom (Azure #0080FF)
        [263] = {0.00, 0.00, 1.00, 1}, -- Enhancement: Mana (Blue #0000FF)
        [264] = {0.00, 0.00, 1.00, 1}, -- Restoration: Mana (Blue #0000FF)
    }
    
    -- If spec has custom color, use it
    if specID and specResourceColors[specID] then
        return specResourceColors[specID]
    end
    
    -- Otherwise use class default resource color
    local classResourceColors = {
        ["DEATHKNIGHT"] = {0.00, 0.82, 1.00, 1}, -- Runic Power (Cyan #00D1FF)
        ["DEMONHUNTER"] = {0.788, 0.259, 0.992, 1}, -- Fury (Heliotrope #C942FD)
        ["DRUID"] = {0.00, 0.00, 1.00, 1},       -- Mana (Blue #0000FF) - default, overridden by spec
        ["EVOKER"] = {0.00, 0.00, 1.00, 1},      -- Mana (Blue #0000FF)
        ["HUNTER"] = {1.00, 0.50, 0.25, 1},      -- Focus (Light Orange #FF8040)
        ["MAGE"] = {0.00, 0.00, 1.00, 1},        -- Mana (Blue #0000FF)
        ["MONK"] = {1.00, 1.00, 0.00, 1},        -- Energy (Yellow #FFFF00) - default, overridden by spec
        ["PALADIN"] = {0.00, 0.00, 1.00, 1},     -- Mana (Blue #0000FF)
        ["PRIEST"] = {0.00, 0.00, 1.00, 1},      -- Mana (Blue #0000FF) - default, overridden by spec
        ["ROGUE"] = {1.00, 1.00, 0.00, 1},       -- Energy (Yellow #FFFF00)
        ["SHAMAN"] = {0.00, 0.00, 1.00, 1},      -- Mana (Blue #0000FF) - default, overridden by spec
        ["WARLOCK"] = {0.00, 0.00, 1.00, 1},     -- Mana (Blue #0000FF)
        ["WARRIOR"] = {1.00, 0.00, 0.00, 1}      -- Rage (Red #FF0000)
    }
    
    return classResourceColors[className] or {0.00, 0.00, 1.00, 1} -- Default to mana blue
end

-- Legacy function for backward compatibility
function MFM:GetClassResourceColor(className)
    return self:GetSpecResourceColor(className, nil)
end

function MFM:GetTestSpellData(className)
    -- Spell school constants: PHYSICAL=1, HOLY=2, FIRE=4, NATURE=8, FROST=16, SHADOW=32, ARCANE=64
    local spellData = {
        ["DEATHKNIGHT"] = {
            name = "Death Coil",
            icon = "Interface\\Icons\\Spell_Shadow_DeathCoil",
            spellSchool = 32 -- Shadow
        },
        ["MAGE"] = {
            name = "Fireball",
            icon = "Interface\\Icons\\Spell_Fire_FlameBolt",
            spellSchool = 4 -- Fire
        },
        ["HUNTER"] = {
            name = "Aimed Shot",
            icon = "Interface\\Icons\\INV_Spear_07",
            spellSchool = 1 -- Physical (non-interruptible in test mode)
        },
        ["WARRIOR"] = {
            name = "Mortal Strike",
            icon = "Interface\\Icons\\Ability_Warrior_SavageBlow",
            spellSchool = 1 -- Physical
        },
        ["PRIEST"] = {
            name = "Greater Heal",
            icon = "Interface\\Icons\\Spell_Holy_GreaterHeal",
            spellSchool = 2 -- Holy
        },
        ["PALADIN"] = {
            name = "Holy Light",
            icon = "Interface\\Icons\\Spell_Holy_HolyBolt",
            spellSchool = 2 -- Holy
        },
        ["ROGUE"] = {
            name = "Shadowstrike",
            icon = "Interface\\Icons\\Ability_Rogue_Shadowstrike",
            spellSchool = 1 -- Physical
        },
        ["WARLOCK"] = {
            name = "Shadow Bolt",
            icon = "Interface\\Icons\\Spell_Shadow_ShadowBolt",
            spellSchool = 32 -- Shadow
        },
        ["SHAMAN"] = {
            name = "Lightning Bolt",
            icon = "Interface\\Icons\\Spell_Nature_Lightning",
            spellSchool = 8 -- Nature
        },
        ["DRUID"] = {
            name = "Wrath",
            icon = "Interface\\Icons\\Spell_Nature_AbolishMagic",
            spellSchool = 8 -- Nature
        },
        ["MONK"] = {
            name = "Vivify",
            icon = "Interface\\Icons\\Ability_Monk_Vivify",
            spellSchool = 8 -- Nature
        },
        ["DEMONHUNTER"] = {
            name = "Chaos Strike",
            icon = "Interface\\Icons\\Ability_DemonHunter_ChaosStrike",
            spellSchool = 1 -- Physical
        },
        ["EVOKER"] = {
            name = "Living Flame",
            icon = "Interface\\Icons\\Ability_Evoker_LivingFlame",
            spellSchool = 4 -- Fire
        }
    }
    
    return spellData[className] or spellData["MAGE"] -- Default to Mage
end

-- ============================================================================
-- TEST MODE LAYOUT REFRESH SYSTEM
-- ============================================================================

function MFM:RefreshAllTestLayouts()
    if not AC then return end
    
    -- Refresh all layout systems to apply test mode settings
    if AC.RefreshTexturesLayout then
        AC:RefreshTexturesLayout()
    end
    
    if AC.RefreshCastBarsLayout then
        AC:RefreshCastBarsLayout()
    end
    
    -- CRITICAL FIX: Delay DR refresh to allow Edit Mode save to complete (0.1s delay)
    -- This prevents DR icons from jumping when TEST/HIDE is clicked after dragging
    if AC.RefreshDRLayout then
        C_Timer.After(0.15, function()
            if AC.RefreshDRLayout then
                AC:RefreshDRLayout()
            end
        end)
    end
    
    if AC.RefreshTrinketsOtherLayout then
        AC:RefreshTrinketsOtherLayout()
    end
    
    -- CRITICAL FIX: Don't refresh absorbs during initial test setup
    -- The absorbs and immunity glows are already set up by ApplyTestDataToFrame
    -- Refreshing them here causes UpdateAbsorbBar to run again and hide them
    -- Only refresh other More Goodies features (party indicators, etc.)
    -- if AC.RefreshMoreGoodiesLayout then
    --     AC:RefreshMoreGoodiesLayout()
    -- end
    
    -- CRITICAL FIX: Refresh TriBadges (Class Packs) in test mode
    if AC.TriBadges and AC.TriBadges.RefreshAll then
        AC.TriBadges:RefreshAll()
        -- DEBUG: TriBadges refreshed
        -- print("|cff00FF00[TRIBADGES]|r Refreshed for test mode")
    end
    
    -- Force update frame positions with current settings
    if AC.FrameManager and AC.FrameManager.ApplyAllSettings then
        AC.FrameManager:ApplyAllSettings()
    end
end

-- ============================================================================
-- TEST MODE PROTECTION SYSTEM
-- ============================================================================

function AC:ProtectTestModeFrames()
    if not self.testModeEnabled then return end
    
    local frames = GetArenaFrames()
    if not frames then return end
    
    -- Protect frames from being hidden or having alpha reduced
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = frames[i]
        if frame then
            -- Force full visibility
            frame:SetAlpha(1.0)
            -- COMBAT PROTECTED: Only show frame outside combat
            if not InCombatLockdown() then
                frame:Show()
            end
            
            -- Protect critical child elements
            local elements = {
                frame.healthBar,
                frame.manaBar,
                frame.playerName,
                frame.classIcon,
                frame.trinketIndicator,
                frame.racialIndicator,
                frame.castBar
            }
            
            for _, element in pairs(elements) do
                if element then
                    element:SetAlpha(1.0)
                    if element.Show then
                        element:Show()
                    end
                end
            end
        end
    end
end

-- ============================================================================
-- TEST MODE CLEAR DATA PROTECTION
-- ============================================================================

-- REMOVED DUPLICATE: AC:ClearArenaData() already defined at line 993
-- The first definition is the authoritative version with memory leak cleanup

function AC:ClearFrameData(frame)
    if not frame then return end
    
    -- Clear health and mana bars
    if frame.healthBar then
        frame.healthBar:SetMinMaxValues(0, 100)
        frame.healthBar:SetValue(100)
        -- CRITICAL: Don't reset to green - preserve class colors or apply them if unit exists
        if frame.unit and UnitExists(frame.unit) then
            local _, classFile = UnitClass(frame.unit)
            if classFile and RAID_CLASS_COLORS[classFile] then
                local classColor = RAID_CLASS_COLORS[classFile]
                frame.healthBar:SetStatusBarColor(classColor.r, classColor.g, classColor.b, 1)
            else
                frame.healthBar:SetStatusBarColor(0, 1, 0, 1) -- Fallback to green only if no class
            end
        else
            frame.healthBar:SetStatusBarColor(0, 1, 0, 1) -- Fallback to green if no unit
        end
        if frame.healthBar.statusText then
            frame.healthBar.statusText:SetText("")
            frame.healthBar.statusText:Hide()
        end
    end
    
    if frame.manaBar then
        frame.manaBar:SetMinMaxValues(0, 100)
        frame.manaBar:SetValue(100)
        frame.manaBar:SetStatusBarColor(0, 0.5, 1, 1) -- Reset to blue
        if frame.manaBar.statusText then
            frame.manaBar.statusText:SetText("")
            frame.manaBar.statusText:Hide()
        end
    end
    
    -- Clear icons and indicators
    if frame.classIcon then
        if frame.classIcon.icon then frame.classIcon.icon:SetTexture(nil) end
        frame.classIcon:Hide()
    end
    
    if frame.specIcon then
        if frame.specIcon.icon then frame.specIcon.icon:SetTexture(nil) end
        frame.specIcon:Hide()
    end
    
    if frame.trinketIndicator then
        if frame.trinketIndicator.cooldown then frame.trinketIndicator.cooldown:Clear() end
        frame.trinketIndicator:Hide()
    end
    
    if frame.racialIndicator then
        if frame.racialIndicator.cooldown then frame.racialIndicator.cooldown:Clear() end
        frame.racialIndicator:Hide()
    end
    
    -- Clear cast bar (but preserve test mode data)
    if frame.castBar and not AC.testModeEnabled then
        frame.castBar:SetValue(0)
        if frame.castBar.Text then frame.castBar.Text:SetText("") end
        if frame.castBar.spellIcon then frame.castBar.spellIcon:Hide() end
        frame.castBar:Hide()
    end
    
    -- Clear player name
    if frame.playerName then
        frame.playerName:SetText("")
    end
    
    -- Clear debuffs
    if frame.debuffContainer then
        frame.debuffContainer:Hide()
    end
    
    -- Clear DR icons
    if frame.drIcons then
        for _, drIcon in pairs(frame.drIcons) do
            if drIcon then
                drIcon:Hide()
                if drIcon.cooldown then drIcon.cooldown:Clear() end
            end
        end
    end
end

-- ============================================================================
-- TEST MODE INTEGRATION WITH EXISTING SYSTEMS
-- ============================================================================

-- Override UpdateFrameData to protect test mode
local originalUpdateFrameData = UpdateFrameData
UpdateFrameData = function(frame, unit)
    -- CRITICAL: Complete test mode protection
    if AC.testModeEnabled then
        return
    end
    
    -- Call original function for live arena
    if originalUpdateFrameData then
        originalUpdateFrameData(frame, unit)
    end
end

-- Override UpdateAllFrames to protect test mode
local originalUpdateAllFrames = UpdateAllFrames
UpdateAllFrames = function()
    -- CRITICAL: Complete test mode protection - ArenaTracking should not interfere
    if AC.testModeEnabled then 
        if StopTicker then StopTicker() end -- Stop ArenaTracking ticker in test mode
        return 
    end
    
    -- Call original function for live arena
    if originalUpdateAllFrames then
        originalUpdateAllFrames()
    end
end

-- Override UpdateDebuffs to protect test mode
local originalUpdateDebuffs = UpdateDebuffs
UpdateDebuffs = function(frame, unit)
    -- CRITICAL: Complete test mode protection
    if AC.testModeEnabled then return end
    
    -- Call original function for live arena
    if originalUpdateDebuffs then
        originalUpdateDebuffs(frame, unit)
    end
end

-- ============================================================================
-- CHUNK 11: UTILITY FUNCTIONS
-- Frame visibility management, positioning utilities, and reset functions
-- ============================================================================

-- ============================================================================
-- FRAME VISIBILITY MANAGEMENT
-- ============================================================================
-- OLD MISMATCH CODE REMOVED - Now handled by MismatchHandler module

function MFM:HideAllFrames()
    if not self.frames then
        return
    end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if self.frames[i] then
            -- CRITICAL: Use both SetAlpha AND Hide when leaving arena
            -- SetAlpha(0) alone leaves frames visible in open world
            -- Only use SetAlpha during combat to avoid taint
            if InCombatLockdown() then
                -- In combat: Only use SetAlpha to avoid taint
                self.frames[i]:SetAlpha(0)
            else
                -- Out of combat: Properly hide frames
                self.frames[i]:Hide()
                self.frames[i]:SetAlpha(1) -- Reset alpha for next arena
            end
            -- STAGE 2: REMOVED hasEverHadData flag clearing
        end
    end
end

-- OLD MISMATCH CODE REMOVED - Now handled by MismatchHandler module
function AC:ShowArenaFrames()
    -- Deprecated - MismatchHandler now controls frame visibility
    -- Kept for backward compatibility but does nothing
end

-- OLD MISMATCH CODE REMOVED - Now handled by MismatchHandler module
function AC:HideArenaFrames()
    -- Deprecated - MismatchHandler now controls frame visibility
    -- Kept for backward compatibility
    if MFM and MFM.HideAllFrames then
        MFM:HideAllFrames()
    end
end

-- ============================================================================
-- FRAME POSITIONING UTILITIES
-- ============================================================================

function MFM:UpdateFramePositions()
    -- DRAG + SLIDER POSITIONING SYSTEM
    -- Formula: Final Position = Dragged Base + Slider Offset
    local db = AC.DB and AC.DB.profile and AC.DB.profile.arenaFrames or {}
    local positioning = db.positioning or {}
    
    -- Get dragged base position (where user dragged frames to)
    local draggedBaseX = positioning.draggedBaseX or 0
    local draggedBaseY = positioning.draggedBaseY or 0
    
    -- Get slider offsets (fine-tuning adjustments from sliders)
    local sliderOffsetX = positioning.sliderOffsetX or 0
    local sliderOffsetY = positioning.sliderOffsetY or 0
    
    -- Calculate final position: base + offset
    local horizontal = draggedBaseX + sliderOffsetX
    local vertical = draggedBaseY + sliderOffsetY
    
    -- Get other settings (use proper default from Init.lua: 21)
    local spacing = positioning.spacing
    if not spacing or spacing == 0 then spacing = 21 end
    local growthDirection = positioning.growthDirection or "Down"
    
    -- CRITICAL FIX: Get base frame height from database, not from frame:GetHeight()
    -- frame:GetHeight() returns SCALED height which changes with frame scale
    local sizing = db.sizing or {}
    local baseFrameHeight = sizing.height or 68
    local baseFrameWidth = sizing.width or 235
    
    -- Position anchor using TOPLEFT to UIParent BOTTOMLEFT with scale-adjusted coordinates
    if AC.ArenaFramesAnchor then
        AC.ArenaFramesAnchor:ClearAllPoints()
        local scale = AC.ArenaFramesAnchor:GetEffectiveScale()
        -- Formula: SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", db.x / scale, db.y / scale)
        AC.ArenaFramesAnchor:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", horizontal / scale, vertical / scale)
    end
    
    -- Position frames relative to anchor
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = self.frames[i]
        if frame then
            frame:ClearAllPoints()
            
            -- CRITICAL FIX: Use base dimensions from database, not frame:GetHeight()/GetWidth()
            -- This prevents spacing from changing when frame scale changes
            local offsetX, offsetY = 0, 0
            if growthDirection == "Down" then
                offsetY = -((i-1) * (baseFrameHeight + spacing))
            elseif growthDirection == "Up" then
                offsetY = ((i-1) * (baseFrameHeight + spacing))
            elseif growthDirection == "Right" then
                offsetX = ((i-1) * (baseFrameWidth + spacing))
            elseif growthDirection == "Left" then
                offsetX = -((i-1) * (baseFrameWidth + spacing))
            end
            
            if AC.ArenaFramesAnchor then
                frame:SetPoint("TOPLEFT", AC.ArenaFramesAnchor, "TOPLEFT", offsetX, offsetY)
            end
        end
    end
end

function AC:UpdateFramePositions()
    -- Use Master Frame Manager if available
    if MFM and MFM.UpdateFramePositions then
        MFM:UpdateFramePositions()
        return
    end
    
    -- Fallback implementation with DRAG + SLIDER system
    local frames = GetArenaFrames()
    if not frames then return end
    
    local settings = GetFrameSettings()
    if not settings or not settings.positioning then return end
    
    local pos = settings.positioning
    
    -- DRAG + SLIDER SYSTEM: Calculate final position
    local draggedBaseX = pos.draggedBaseX or 0
    local draggedBaseY = pos.draggedBaseY or 0
    local sliderOffsetX = pos.sliderOffsetX or 0
    local sliderOffsetY = pos.sliderOffsetY or 0
    
    -- Final position = base + offset
    local horizontal = draggedBaseX + sliderOffsetX
    local vertical = draggedBaseY + sliderOffsetY
    
    -- Use proper default from Init.lua: 21
    local spacing = pos.spacing
    if not spacing or spacing == 0 then spacing = 21 end
    local growthDirection = pos.growthDirection or "Down"
    
    -- CRITICAL FIX: Get base frame dimensions from database, not from frame:GetHeight()/GetWidth()
    -- This prevents spacing from changing when frame scale changes
    local sizing = settings.sizing or {}
    local baseFrameHeight = sizing.height or 68
    local baseFrameWidth = sizing.width or 235
    
    -- Position anchor using TOPLEFT to UIParent BOTTOMLEFT with scale-adjusted coordinates
    if AC.ArenaFramesAnchor then
        AC.ArenaFramesAnchor:ClearAllPoints()
        local scale = AC.ArenaFramesAnchor:GetEffectiveScale()
        -- Formula: SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", db.x / scale, db.y / scale)
        AC.ArenaFramesAnchor:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", horizontal / scale, vertical / scale)
    end
    
    -- Position individual frames
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = frames[i]
        if frame then
            frame:ClearAllPoints()
            
            -- CRITICAL FIX: Use base dimensions from database, not frame:GetHeight()/GetWidth()
            local offsetX, offsetY = 0, 0
            if growthDirection == "Down" then
                offsetY = -((i-1) * (baseFrameHeight + spacing))
            elseif growthDirection == "Up" then
                offsetY = ((i-1) * (baseFrameHeight + spacing))
            elseif growthDirection == "Right" then
                offsetX = ((i-1) * (baseFrameWidth + spacing))
            elseif growthDirection == "Left" then
                offsetX = -((i-1) * (baseFrameWidth + spacing))
            end
            
            if AC.ArenaFramesAnchor then
                frame:SetPoint("TOPLEFT", AC.ArenaFramesAnchor, "TOPLEFT", offsetX, offsetY)
            else
                frame:SetPoint("CENTER", UIParent, "CENTER", horizontal + offsetX, vertical + offsetY)
            end
        end
    end
end

function MFM:CaptureFramePositions()
    if not self:FramesExist() then
        return false
    end
    
    -- Get the position of the first frame as the base position
    local frame1 = self.frames[1]
    if frame1 then
        -- Use GetPoint() to match our positioning system
        local point, relativeTo, relativePoint, xOfs, yOfs = frame1:GetPoint()
        if xOfs and yOfs then
            -- Use the relative coordinates directly
            local horizontal = math.floor(xOfs * 10 + 0.5) / 10
            local vertical = math.floor(yOfs * 10 + 0.5) / 10
            
            -- Save to database with proper structure
            if AC.DB and AC.DB.profile then
                -- Ensure arenaFrames structure exists
                AC.DB.profile.arenaFrames = AC.DB.profile.arenaFrames or {}
                AC.DB.profile.arenaFrames.positioning = AC.DB.profile.arenaFrames.positioning or {}
                
                -- Save the position
                AC.DB.profile.arenaFrames.positioning.horizontal = horizontal
                AC.DB.profile.arenaFrames.positioning.vertical = vertical
                
                -- Force database save
                _G.ArenaCoreDB = AC.DB
                
                print("ArenaCore: Frame positions saved!")
                return true
            end
        end
    end
    
    return false
end

function AC:CaptureFramePositions()
    -- Use Master Frame Manager if available
    if MFM and MFM.CaptureFramePositions then
        return MFM:CaptureFramePositions()
    end
    
    -- Fallback implementation
    local frames = GetArenaFrames()
    if not frames or not frames[1] then return false end
    
    local frame1 = frames[1]
    local point, relativeTo, relativePoint, xOfs, yOfs = frame1:GetPoint()
    if xOfs and yOfs then
        -- Save position to database
        self:EnsureDB()
        self:SetPath(self.DB.profile, "arenaFrames.positioning.horizontal", xOfs)
        self:SetPath(self.DB.profile, "arenaFrames.positioning.vertical", yOfs)
        
        print("ArenaCore: Frame positions captured and saved!")
        return true
    end
    
    return false
end

-- ============================================================================
-- FRAME RESET UTILITIES
-- ============================================================================

function MFM:ResetFrame(frame)
    if not frame then return end
    
    frame.playerName:SetText("Arena " .. frame.id)
    frame.healthBar:SetValue(100)
    frame.manaBar:SetValue(100)
    frame.healthBar.text:SetText("100%")
    frame.manaBar.text:SetText("100%")
    frame.healthBar:SetStatusBarColor(0, 1, 0)  -- Reset to green
    frame.classIcon:Hide()
    frame.castBar:Hide()
    frame:SetAlpha(1)
end

function AC:ResetFrame(frame)
    if not frame then return end
    
    -- Reset basic frame properties
    frame:SetAlpha(1.0)
    
    -- Reset player name
    if frame.playerName then
        local arenaIndex = frame.arenaIndex or frame.id or 1
        frame.playerName:SetText("Arena " .. arenaIndex)
        -- CRITICAL FIX: Only show if enabled in settings
        local db = AC.DB and AC.DB.profile
        local general = db and db.arenaFrames and db.arenaFrames.general or {}
        if general.showNames ~= false then
            frame.playerName:Show()
        else
            frame.playerName:Hide()
        end
    end
    
    -- Reset health bar (with realistic test mode values)
    if frame.healthBar then
        frame.healthBar:SetMinMaxValues(0, 100)
        
        -- NEW FEATURE: Use realistic values in test mode
        local arenaIndex = frame.arenaIndex or frame.id or 1
        local healthValues = {100, 73, 43}
        local healthValue = (AC.testModeEnabled and healthValues[arenaIndex]) or 100
        
        frame.healthBar:SetValue(healthValue)
        -- CRITICAL: Don't reset to green - preserve class colors or apply them if unit exists
        if frame.unit and UnitExists(frame.unit) then
            local _, classFile = UnitClass(frame.unit)
            if classFile and RAID_CLASS_COLORS[classFile] then
                local classColor = RAID_CLASS_COLORS[classFile]
                frame.healthBar:SetStatusBarColor(classColor.r, classColor.g, classColor.b, 1)
            else
                frame.healthBar:SetStatusBarColor(0, 1, 0, 1) -- Fallback to green only if no class
            end
        else
            frame.healthBar:SetStatusBarColor(0, 1, 0, 1) -- Fallback to green if no unit
        end
        if frame.healthBar.statusText then
            frame.healthBar.statusText:SetText(healthValue .. "%")
            if not InCombatLockdown() then
                frame.healthBar.statusText:Show()
            end
        end
    end
    
    -- Reset mana bar (with realistic test mode values)
    if frame.manaBar then
        frame.manaBar:SetMinMaxValues(0, 100)
        
        -- NEW FEATURE: Use realistic values in test mode
        local arenaIndex = frame.arenaIndex or frame.id or 1
        local resourceValues = {100, 82, 24}
        local resourceValue = (AC.testModeEnabled and resourceValues[arenaIndex]) or 100
        
        frame.manaBar:SetValue(resourceValue)
        frame.manaBar:SetStatusBarColor(0, 0.5, 1, 1) -- Blue
        if frame.manaBar.statusText then
            frame.manaBar.statusText:SetText(resourceValue .. "%")
            if not InCombatLockdown() then
                frame.manaBar.statusText:Show()
            end
        end
    end
    
    -- Reset icons and indicators
    if frame.classIcon then
        frame.classIcon:Hide()
    end
    
    if frame.specIcon then
        frame.specIcon:Hide()
    end
    
    if frame.trinketIndicator then
        if frame.trinketIndicator.cooldown then
            frame.trinketIndicator.cooldown:Clear()
        end
        frame.trinketIndicator:Hide()
    end
    
    if frame.racialIndicator then
        if frame.racialIndicator.cooldown then
            frame.racialIndicator.cooldown:Clear()
        end
        frame.racialIndicator:Hide()
    end
    
    -- Reset cast bar
    if frame.castBar then
        frame.castBar:SetValue(0)
        if frame.castBar.Text then
            frame.castBar.Text:SetText("")
        end
        frame.castBar:Hide()
    end
    
    -- Reset debuffs
    if frame.debuffContainer then
        frame.debuffContainer:Hide()
    end
    
    -- Reset DR icons
    if frame.drIcons then
        for _, drIcon in pairs(frame.drIcons) do
            if drIcon then
                drIcon:Hide()
                if drIcon.cooldown then
                    drIcon.cooldown:Clear()
                end
            end
        end
    end
end

function AC:ResetAllFrames()
    local frames = GetArenaFrames()
    if not frames then return end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            self:ResetFrame(frames[i])
        end
    end
    
    print("ArenaCore: All frames reset to default state")
end

-- ============================================================================
-- BETA PRESET & DEFAULTS SYSTEM
-- ============================================================================
-- Apply your personal beta preset settings to current profile
-- This overwrites ALL settings with your COMPLETE configuration from Init.lua
function AC:ApplyBetaPreset()
    print("|cffFFAA00[DEBUG]|r ApplyBetaPreset() called!")
    
    if not self.GetBetaDefaults then
        print("|cffFF0000ArenaCore:|r GetBetaDefaults function not found in Init.lua!")
        return
    end
    
    if not _G.ArenaCoreDB or not _G.ArenaCoreDB.profile then
        print("|cffFF0000ArenaCore:|r Database not initialized!")
        return
    end
    
    -- Deep copy function (local to avoid conflicts)
    local function DeepCopy(tbl)
        if type(tbl) ~= "table" then return tbl end
        local out = {}
        for k, v in pairs(tbl) do out[k] = DeepCopy(v) end
        return out
    end
    
    -- Get COMPLETE beta defaults (includes Blackout, debuffs, everything)
    local betaDefaults = self:GetBetaDefaults()
    if not betaDefaults then
        print("|cffFF0000ArenaCore:|r Failed to get beta defaults!")
        return
    end
    
    -- CRITICAL: Deep copy the defaults FIRST to avoid corrupting the source
    local defaultsCopy = DeepCopy(betaDefaults)
    
    -- Apply ALL defaults to current profile (using the copy, not the original)
    for key, value in pairs(defaultsCopy) do
        _G.ArenaCoreDB.profile[key] = DeepCopy(value)
    end
    
    -- CRITICAL: Force overwrite texture settings (dropdown values don't update otherwise)
    if defaultsCopy.textures then
        _G.ArenaCoreDB.profile.textures = DeepCopy(defaultsCopy.textures)
        print("|cffFFAA00[DEBUG]|r Forced texture settings update")
        
        -- DEBUG: Show what textures were applied
        local tex = _G.ArenaCoreDB.profile.textures
        if tex then
            print("|cffFFAA00[DEBUG]|r Health texture: " .. tostring(tex.healthBarTexture))
            print("|cffFFAA00[DEBUG]|r Power texture: " .. tostring(tex.powerBarTexture))
            print("|cffFFAA00[DEBUG]|r Cast texture: " .. tostring(tex.castBarTexture))
        end
    end
    
    -- DEBUG: Show what positioning values were applied
    if betaDefaults.arenaFrames and betaDefaults.arenaFrames.positioning then
        local pos = betaDefaults.arenaFrames.positioning
        print("|cffFFAA00[DEBUG]|r Applied positioning: horizontal=" .. tostring(pos.horizontal) .. ", vertical=" .. tostring(pos.vertical))
    end
    
    -- DEBUG: Verify what's actually in the database now
    if _G.ArenaCoreDB.profile.arenaFrames and _G.ArenaCoreDB.profile.arenaFrames.positioning then
        local dbPos = _G.ArenaCoreDB.profile.arenaFrames.positioning
        print("|cffFFAA00[DEBUG]|r Database now has: horizontal=" .. tostring(dbPos.horizontal) .. ", vertical=" .. tostring(dbPos.vertical))
    end
    
    -- CRITICAL FIX: Actually apply the settings to move frames immediately
    if self.UpdateFramePositions then
        self:UpdateFramePositions()
        print("|cffFFAA00[DEBUG]|r Called UpdateFramePositions()")
    end
    
    -- Also refresh trinkets, racials, class packs, etc.
    if self.RefreshTrinketsOtherLayout then
        self:RefreshTrinketsOtherLayout()
        print("|cffFFAA00[DEBUG]|r Called RefreshTrinketsOtherLayout()")
    end
    
    if self.TriBadges and self.TriBadges.RefreshAll then
        self.TriBadges:RefreshAll()
        print("|cffFFAA00[DEBUG]|r Called TriBadges:RefreshAll()")
    end
    
    -- CRITICAL: Refresh textures to apply new bar textures
    print("|cffFFAA00[DEBUG]|r Test mode enabled: " .. tostring(self.testModeEnabled))
    if self.RefreshTexturesLayout then
        self:RefreshTexturesLayout()
        print("|cffFFAA00[DEBUG]|r Called RefreshTexturesLayout()")
    end
    
    -- CRITICAL: Refresh cast bars to apply new cast bar texture and colors
    if self.RefreshCastBarsLayout then
        self:RefreshCastBarsLayout()
        print("|cffFFAA00[DEBUG]|r Called RefreshCastBarsLayout()")
    end
    
    -- If not in test mode, warn user to enable it
    if not self.testModeEnabled then
        print("|cffFFCC00[WARNING]|r Textures updated in database but not visible. Type |cffffff00/arena test|r to see changes!")
    end
    
    print("|cff8B45FFArena Core:|r Beta preset applied! Complete configuration restored.")
    print("|cffFFAA00Includes:|r Blackout spells, debuffs, all settings, and features!")
    print("|cffFFAA00Note:|r Frames should move immediately. If not, type |cffffff00/reload|r")
end

-- Hard reset to defaults (wipes everything and reapplies COMPLETE beta defaults)
function AC:ResetToDefaults()
    if not self.GetBetaDefaults then
        print("|cffFF0000ArenaCore:|r GetBetaDefaults function not found in Init.lua!")
        return
    end
    
    if not _G.ArenaCoreDB then
        print("|cffFF0000ArenaCore:|r Database not initialized!")
        return
    end
    
    -- Deep copy function (local to avoid conflicts)
    local function DeepCopy(tbl)
        if type(tbl) ~= "table" then return tbl end
        local out = {}
        for k, v in pairs(tbl) do out[k] = DeepCopy(v) end
        return out
    end
    
    -- Get COMPLETE beta defaults
    local betaDefaults = self:GetBetaDefaults()
    if not betaDefaults then
        print("|cffFF0000ArenaCore:|r Failed to get beta defaults!")
        return
    end
    
    -- CRITICAL: Deep copy the defaults FIRST to avoid corrupting the source
    local defaultsCopy = DeepCopy(betaDefaults)
    
    -- Wipe profile and reapply COMPLETE defaults (using the copy, not the original)
    _G.ArenaCoreDB.profile = {}
    for key, value in pairs(defaultsCopy) do
        _G.ArenaCoreDB.profile[key] = DeepCopy(value)
    end
    
    -- Reset version tracking
    _G.ArenaCoreDB.__version = self.DEFAULTS_VERSION
    _G.ArenaCoreDB.__firstInstall = false
    
    print("|cff8B45FFArena Core:|r Hard reset complete! Complete configuration restored.")
    print("|cffFFAA00Includes:|r Blackout spells, debuffs, all settings, and features!")
    print("|cffFFAA00Note:|r Type |cffffff00/reload|r to see all changes take effect.")
end

-- ============================================================================
-- FRAME DRAGGING UTILITIES
-- ============================================================================

function AC:EnableFrameDragging()
    local frames = GetArenaFrames()
    if not frames then return end
    
    self.isDragging = true
    self.framesLocked = false
    
    -- Enable dragging for the first frame (anchor frame)
    local anchorFrame = frames[1]
    if anchorFrame then
        anchorFrame:SetMovable(true)
        anchorFrame:EnableMouse(true)
        anchorFrame:RegisterForDrag("LeftButton")
        
        anchorFrame:SetScript("OnDragStart", function(self)
            self:StartMoving()
            AC.isDragging = true
        end)
        
        anchorFrame:SetScript("OnDragStop", function(self)
            self:StopMovingOrSizing()
            AC.isDragging = false
            
            -- Capture new position
            C_Timer.After(0.1, function()
                AC:CaptureFramePositions()
                AC:UpdateFramePositions() -- Update all frame positions
            end)
        end)
        
        print("ArenaCore: Frame dragging enabled - drag the first frame to move all frames")
    end
end

function AC:DisableFrameDragging()
    local frames = GetArenaFrames()
    if not frames then return end
    
    self.isDragging = false
    self.framesLocked = true
    
    -- Disable dragging for the first frame
    local anchorFrame = frames[1]
    if anchorFrame then
        anchorFrame:SetMovable(false)
        anchorFrame:EnableMouse(true) -- Keep mouse enabled for clicking
        anchorFrame:RegisterForDrag() -- Clear drag registration
        
        anchorFrame:SetScript("OnDragStart", nil)
        anchorFrame:SetScript("OnDragStop", nil)
        
        print("ArenaCore: Frame dragging disabled - frames are now locked")
    end
end

function AC:ToggleFrameDragging()
    if self.framesLocked then
        self:EnableFrameDragging()
    else
        self:DisableFrameDragging()
    end
end

-- ============================================================================
-- ADD DEFAULT BLACKOUT SPELLS - Simple command to add creator's spell list
-- ============================================================================

function AC:AddDefaultBlackoutSpells()
    if not _G.ArenaCoreDB or not _G.ArenaCoreDB.profile then
        print("|cffFF0000ArenaCore:|r Database not initialized!")
        return
    end
    
    -- Ensure blackout table exists
    if not _G.ArenaCoreDB.profile.blackout then
        _G.ArenaCoreDB.profile.blackout = { enabled = true, spells = {} }
    end
    
    if not _G.ArenaCoreDB.profile.blackout.spells then
        _G.ArenaCoreDB.profile.blackout.spells = {}
    end
    
    -- Default blackout spells from Init.lua (43 spells)
    local defaultSpells = {
        107574, 31884, 190319, 22812, 19574, 114050, 114051, 359844,
        167105, 231895, 384352, 343721, 12472, 102543, 102560, 123904,
        191427, 51271, 10060, 375982, 196770, 121471, 185313, 137639,
        207289, 1719, 391109, 228260, 357715, 266779, 378957, 203415,
        205180, 205179, 265187, 111898, 267217, 1122, 446285, 288613,
        205320, 365350, 257044
    }
    
    local currentSpells = _G.ArenaCoreDB.profile.blackout.spells
    local addedCount = 0
    local skippedCount = 0
    
    -- Add spells that don't already exist
    for _, spellID in ipairs(defaultSpells) do
        local exists = false
        for _, existingID in ipairs(currentSpells) do
            if existingID == spellID then
                exists = true
                break
            end
        end
        
        if not exists then
            table.insert(currentSpells, spellID)
            addedCount = addedCount + 1
        else
            skippedCount = skippedCount + 1
        end
    end
    
    -- Report results
    print("|cff22AA44ArenaCore:|r Blackout Spells Update Complete!")
    print("|cffFFAA00→|r Added: " .. addedCount .. " new spells")
    print("|cffFFAA00→|r Skipped: " .. skippedCount .. " (already in your list)")
    print("|cffFFAA00→|r Total: " .. #currentSpells .. " spells in your Blackout list")
    
    if addedCount > 0 then
        print("|cff00FF00Tip:|r Open Blackout Editor to see the updated spell list!")
    end
end

-- ============================================================================
-- EXPORT BLACKOUT SPELLS TO DEFAULTS - Update GetBetaDefaults with current list
-- ============================================================================

function AC:ExportBlackoutToDefaults()
    if not _G.ArenaCoreDB or not _G.ArenaCoreDB.profile or not _G.ArenaCoreDB.profile.blackout then
        print("|cffFF0000ArenaCore:|r Blackout database not found!")
        return
    end
    
    local spells = _G.ArenaCoreDB.profile.blackout.spells
    if not spells or #spells == 0 then
        print("|cffFF0000ArenaCore:|r No Blackout spells to export!")
        return
    end
    
    -- Generate the code to paste into Init.lua
    local code = "    spells = {\n"
    
    for i, spellID in ipairs(spells) do
        code = code .. "      [" .. i .. "] = " .. spellID .. ",\n"
    end
    
    code = code .. "    }"
    
    -- Use the existing export window from Init.lua
    if AC.ShowExportWindow then
        AC:ShowExportWindow(code)
    else
        print("|cffFF0000ArenaCore:|r Export window not available!")
        print("|cffFFAA00Code to paste:|r")
        print(code)
        return
    end
    
    print("|cff8B45FFArenaCore:|r Blackout spells exported! (" .. #spells .. " spells)")
    print("|cffFFAA00Instructions:|r")
    print("1. Press Ctrl+A, then Ctrl+C to copy from the window")
    print("2. Open Core/Init.lua and find the GetBetaDefaults() function (line 58)")
    print("3. Replace lines 69-96 (the blackout.spells table) with the copied text")
    print("4. Save Init.lua")
    print("|cffFFAA00Result:|r New users will get your " .. #spells .. " Blackout spells as defaults!")
end

-- ============================================================================
-- FRAME STATE UTILITIES
-- ============================================================================

function AC:GetFrameVisibilityState()
    local frames = GetArenaFrames()
    if not frames then return "no_frames" end
    
    local visibleCount = 0
    local totalCount = 0
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            totalCount = totalCount + 1
            if frames[i]:IsShown() then
                visibleCount = visibleCount + 1
            end
        end
    end
    
    if visibleCount == 0 then
        return "all_hidden"
    elseif visibleCount == totalCount then
        return "all_visible"
    else
        return "partial_visible"
    end
end

function AC:GetFrameCount()
    local frames = GetArenaFrames()
    if not frames then return 0 end
    
    local count = 0
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            count = count + 1
        end
    end
    
    return count
end

function AC:AreFramesInTestMode()
    return self.testModeEnabled or false
end

function AC:AreFramesLocked()
    return self.framesLocked or true
end

-- ============================================================================
-- FRAME SCALING UTILITIES
-- ============================================================================

function AC:ScaleAllFrames(scaleFactor)
    if not scaleFactor or scaleFactor <= 0 then
        scaleFactor = 1.0
    end
    
    -- Clamp scale to reasonable values
    scaleFactor = math.max(0.5, math.min(2.0, scaleFactor))
    
    local frames = GetArenaFrames()
    if not frames then return end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            frames[i]:SetScale(scaleFactor)
        end
    end
    
    -- Save scale to database
    self:EnsureDB()
    self:SetPath(self.DB.profile, "arenaFrames.sizing.scale", scaleFactor * 100)
    
    print("ArenaCore: All frames scaled to " .. (scaleFactor * 100) .. "%")
end

function AC:ResetFrameScale()
    self:ScaleAllFrames(1.0)
end

-- ============================================================================
-- FRAME ALPHA UTILITIES
-- ============================================================================

function AC:SetFrameAlpha(alpha)
    if not alpha then alpha = 1.0 end
    
    -- Clamp alpha to valid range
    alpha = math.max(0.0, math.min(1.0, alpha))
    
    local frames = GetArenaFrames()
    if not frames then return end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            frames[i]:SetAlpha(alpha)
        end
    end
    
    print("ArenaCore: Frame alpha set to " .. (alpha * 100) .. "%")
end

function AC:FadeFramesIn(duration)
    duration = duration or 0.5
    
    local frames = GetArenaFrames()
    if not frames then return end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            frames[i]:SetAlpha(0)
            frames[i]:Show()
            
            -- Use WoW's fade in function if available
            if UIFrameFadeIn then
                UIFrameFadeIn(frames[i], duration, 0, 1)
            else
                -- Fallback manual fade
                local startTime = GetTime()
                -- PHASE 3: Reduced from 0.02s to 0.05s (60% reduction, still smooth at 20 FPS)
                local ticker = C_Timer.NewTicker(0.05, function()
                    local elapsed = GetTime() - startTime
                    local progress = math.min(1, elapsed / duration)
                    frames[i]:SetAlpha(progress)
                    
                    if progress >= 1 then
                        ticker:Cancel()
                    end
                end)
            end
        end
    end
end

function AC:FadeFramesOut(duration)
    duration = duration or 0.5
    
    local frames = GetArenaFrames()
    if not frames then return end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            -- Use WoW's fade out function if available
            if UIFrameFadeOut then
                UIFrameFadeOut(frames[i], duration, 1, 0)
            else
                -- Fallback manual fade
                local startTime = GetTime()
                local startAlpha = frames[i]:GetAlpha()
                -- PHASE 3: Reduced from 0.02s to 0.05s (60% reduction, still smooth at 20 FPS)
                local ticker = C_Timer.NewTicker(0.05, function()
                    local elapsed = GetTime() - startTime
                    local progress = math.min(1, elapsed / duration)
                    local currentAlpha = startAlpha * (1 - progress)
                    frames[i]:SetAlpha(currentAlpha)
                    
                    if progress >= 1 then
                        frames[i]:Hide()
                        ticker:Cancel()
                    end
                end)
            end
        end
    end
end

-- ============================================================================
-- FRAME SIZE UTILITIES
-- ============================================================================

function AC:ResizeAllFrames(width, height)
    if not width or not height then return end
    
    -- Clamp sizes to reasonable values
    width = math.max(100, math.min(800, width))
    height = math.max(50, math.min(200, height))
    
    local frames = GetArenaFrames()
    if not frames then return end
    
    for i = 1, MAX_ARENA_ENEMIES do
        if frames[i] then
            frames[i]:SetSize(width, height)
        end
    end
    
    -- Save size to database
    self:EnsureDB()
    self:SetPath(self.DB.profile, "arenaFrames.sizing.width", width)
    self:SetPath(self.DB.profile, "arenaFrames.sizing.height", height)
    
    print("ArenaCore: All frames resized to " .. width .. "x" .. height)
end

function AC:ResetFrameSize()
    self:ResizeAllFrames(350, 72) -- Default ArenaCore frame size
end

-- ============================================================================
-- FRAME VALIDATION UTILITIES
-- ============================================================================

function AC:ValidateFrames()
    local frames = GetArenaFrames()
    if not frames then
        print("ArenaCore: No frames found - frames may not be initialized")
        return false
    end
    
    local issues = {}
    local validFrames = 0
    
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = frames[i]
        if frame then
            validFrames = validFrames + 1
            
            -- Check essential components
            if not frame.healthBar then
                table.insert(issues, "Frame " .. i .. " missing health bar")
            end
            
            if not frame.manaBar then
                table.insert(issues, "Frame " .. i .. " missing mana bar")
            end
            
            if not frame.playerName then
                table.insert(issues, "Frame " .. i .. " missing player name")
            end
            
            if not frame.classIcon then
                table.insert(issues, "Frame " .. i .. " missing class icon")
            end
            
            if not frame.trinketIndicator then
                table.insert(issues, "Frame " .. i .. " missing trinket indicator")
            end
        else
            table.insert(issues, "Frame " .. i .. " is nil")
        end
    end
    
    print("ArenaCore: Frame validation complete")
    print("Valid frames: " .. validFrames .. "/" .. MAX_ARENA_ENEMIES)
    
    if #issues > 0 then
        print("Issues found:")
        for _, issue in ipairs(issues) do
            print("  - " .. issue)
        end
        return false
    else
        print("All frames validated successfully!")
        return true
    end
end

-- ============================================================================
-- FRAME REFRESH UTILITIES
-- ============================================================================

function AC:RefreshAllFrames()
    -- Refresh all frame layouts and settings
    if self.RefreshTexturesLayout then
        self:RefreshTexturesLayout()
    end
    
    if self.RefreshCastBarsLayout then
        self:RefreshCastBarsLayout()
    end
    
    if self.RefreshDRLayout then
        self:RefreshDRLayout()
    end
    
    if self.RefreshTrinketsOtherLayout then
        self:RefreshTrinketsOtherLayout()
    end
    
    if self.RefreshMoreGoodiesLayout then
        self:RefreshMoreGoodiesLayout()
    end
    
    -- Update frame positions and properties using MasterFrameManager
    if MFM and MFM.UpdateFramePositions then
        MFM:UpdateFramePositions()
    end
    
    -- Apply general settings (NEW!)
    if self.ApplyGeneralSettings then
        self:ApplyGeneralSettings()
    end
    
    -- CRITICAL: Force health bar color updates (for class colors toggle)
    self:ForceHealthBarColorUpdate()
    
    -- Apply settings via ProfileManager
    if self.ProfileManager and self.ProfileManager.ApplyArenaFramesSettings then
        self.ProfileManager:ApplyArenaFramesSettings()
    end
    
    -- CRITICAL: Refresh test mode player names if in test mode
    if self.testModeEnabled and MFM and MFM.frames then
        for i = 1, MAX_ARENA_ENEMIES do
            if MFM.frames[i] then
                MFM:ApplyTestDataToFrame(MFM.frames[i], i)
            end
        end
    end
    
    -- DEBUG: All frames refreshed
    -- print("ArenaCore: All frames refreshed with current settings")
end

-- Force update health bar colors based on current class colors setting
function AC:ForceHealthBarColorUpdate()
    local frames = GetArenaFrames()
    if not frames then 
        -- DEBUG: No frames found
        -- print("|cffFF0000[FORCE COLOR UPDATE]|r No frames found!")
        return 
    end
    
    local general = self.DB and self.DB.profile and self.DB.profile.arenaFrames and self.DB.profile.arenaFrames.general
    local useClassColors = general and general.useClassColors ~= false
    
    -- DEBUG: useClassColors
    -- print("|cffFFFF00[FORCE COLOR UPDATE]|r useClassColors: " .. tostring(useClassColors))
    
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = frames[i]
        if frame and frame.healthBar then
            if useClassColors then
                -- Get class and apply class color
                local unit = frame.unit or ("arena" .. i)
                local _, classFile = UnitClass(unit)
                
                -- In test mode, use stored test class if unit doesn't exist
                if not classFile and self.testModeEnabled and frame.testClass then
                    classFile = frame.testClass
                    -- DEBUG: Using test class
                    -- print("|cffFFFF00[FORCE COLOR UPDATE]|r Frame " .. i .. " - Using test class: " .. tostring(classFile))
                end
                
                -- DEBUG: Frame unit and class
                -- print("|cffFFFF00[FORCE COLOR UPDATE]|r Frame " .. i .. " - unit: " .. tostring(unit) .. ", class: " .. tostring(classFile))
                if classFile then
                    local classColors = {
                        ["DEATHKNIGHT"] = {0.77, 0.12, 0.23},
                        ["DEMONHUNTER"] = {0.64, 0.19, 0.79},
                        ["DRUID"] = {1.00, 0.49, 0.04},
                        ["EVOKER"] = {0.20, 0.58, 0.50},
                        ["HUNTER"] = {0.67, 0.83, 0.45},
                        ["MAGE"] = {0.25, 0.78, 0.92},
                        ["MONK"] = {0.00, 1.00, 0.59},
                        ["PALADIN"] = {0.96, 0.55, 0.73},
                        ["PRIEST"] = {1.00, 1.00, 1.00},
                        ["ROGUE"] = {1.00, 0.96, 0.41},
                        ["SHAMAN"] = {0.00, 0.44, 0.87},
                        ["WARLOCK"] = {0.53, 0.53, 0.93},
                        ["WARRIOR"] = {0.78, 0.61, 0.43}
                    }
                    local color = classColors[classFile]
                    if color then
                        frame.healthBar:SetStatusBarColor(color[1], color[2], color[3], 1)
                        -- DEBUG: Applied class color
                        -- print("|cff00FF00[FORCE COLOR UPDATE]|r Applied class color for " .. classFile)
                    else
                        -- DEBUG: No color found
                        -- print("|cffFF0000[FORCE COLOR UPDATE]|r No color found for class: " .. tostring(classFile))
                    end
                else
                    -- DEBUG: No class found
                    -- print("|cffFF0000[FORCE COLOR UPDATE]|r No class found for unit: " .. tostring(unit))
                end
            else
                -- Use default green when class colors are disabled
                frame.healthBar:SetStatusBarColor(0, 1, 0, 1)
                -- DEBUG: Set to green
                -- print("|cffFFFF00[FORCE COLOR UPDATE]|r Set to green (class colors disabled)")
            end
        end
    end
end

-- ============================================================================
-- GENERAL SETTINGS APPLICATION - Connect Arena Frames UI to Master Frame System
-- ============================================================================

function AC:ApplyGeneralSettings()
    local frames = GetArenaFrames()
    
    if not frames then 
        -- Try alternative frame sources
        if MFM and MFM.frames then
            frames = MFM.frames
        elseif AC.arenaFrames then
            frames = AC.arenaFrames
        end
        
        if not frames then
            print("|cffFF0000[ERROR]|r No frames available for general settings!")
            return
        end
    end
    
    -- Get general settings from database
    local general = self.DB and self.DB.profile and self.DB.profile.arenaFrames and self.DB.profile.arenaFrames.general
    if not general then 
        print("|cffFF0000[ERROR]|r No general settings found in database")
        return 
    end
    
    -- DEBUG: Applying general settings
    -- print("|cff00FF00ArenaCore:|r Applying general settings to frames...")
    
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = frames[i]
        if frame then
            self:ApplyFrameGeneralSettings(frame, general)
        end
    end
    
    -- CRITICAL: Force health bar color update after applying settings
    -- This ensures class colors toggle works immediately
    self:ForceHealthBarColorUpdate()
end

function AC:ApplyFrameGeneralSettings(frame, general)
    if not frame or not general then return end
    
    -- ==========================================
    -- TEXT DISPLAY SETTINGS (Checkboxes)
    -- ==========================================
    
    -- Status Text (Health and Resource percentages)
    local statusTextEnabled = general.statusText ~= false
    local usePercentage = general.usePercentage ~= false
    
    if frame.healthBar and frame.healthBar.text then
        if statusTextEnabled then
            if not InCombatLockdown() then
                frame.healthBar.text:Show()
            end
            -- Update text format based on percentage setting
            if usePercentage then
                -- Will be updated by UpdateArenaUnit with percentage format
                frame.healthBar.text:SetText("100%")
            else
                -- Will be updated by UpdateArenaUnit with actual numbers
                frame.healthBar.text:SetText("100")
            end
        else
            frame.healthBar.text:Hide()
        end
    end
    
    if frame.manaBar and frame.manaBar.text then
        if statusTextEnabled then
            if not InCombatLockdown() then
                frame.manaBar.text:Show()
            end
            if usePercentage then
                frame.manaBar.text:SetText("100%")
            else
                frame.manaBar.text:SetText("100")
            end
        else
            frame.manaBar.text:Hide()
        end
    end
    
    -- Use Class Colors (for health bars)
    local useClassColors = general.useClassColors ~= false
    if frame.healthBar and not useClassColors then
        -- Reset to default green if class colors disabled
        frame.healthBar:SetStatusBarColor(0, 1, 0, 1)
    end
    -- Class colors will be applied in UpdateArenaUnit when enabled
    
    -- Show Names
    local showNames = general.showNames ~= false
    if frame.playerName then
        if showNames then
            frame.playerName:Show()
        else
            frame.playerName:Hide()
        end
        
        -- CRITICAL: Handle server names in test mode (don't override player names)
        if AC.testModeEnabled then
            local showServerNames = general.showArenaServerNames
            local currentText = frame.playerName:GetText() or ""
            
            -- Only modify if we have a valid name
            if currentText ~= "" then
                local baseName = currentText:match("^([^%-]+)") or currentText -- Get name before dash
                
                if showServerNames then
                    -- Always set server name format (don't check if already exists)
                    local testServerNames = {"Stormrage", "Sargeras", "Moonguard"}
                    local frameIndex = tonumber(frame:GetName():match("(%d+)")) or 1
                    local serverName = testServerNames[frameIndex] or "TestServer"
                    frame.playerName:SetText(baseName .. "-" .. serverName)
                else
                    -- Remove server name if present
                    frame.playerName:SetText(baseName)
                end
            end
        end
    end
    
    -- Arena Numbers
    local showArenaNumbers = general.showArenaNumbers ~= false
    if frame.arenaNumber then
        if showArenaNumbers then
            frame.arenaNumber:Show()
        else
            frame.arenaNumber:Hide()
        end
    end
    
    -- ==========================================
    -- NAME POSITIONING (Sliders)
    -- ==========================================
    
    if frame.playerName and general.playerNameX and general.playerNameY then
        local nameX = general.playerNameX or 0
        local nameY = general.playerNameY or -6
        
        frame.playerName:ClearAllPoints()
        -- CRITICAL: Anchor to frame, not healthBar, for independent positioning
        frame.playerName:SetPoint("TOPLEFT", frame, "TOPLEFT", nameX, nameY)
    end
    
    -- ==========================================
    -- ARENA NUMBER POSITIONING (Sliders)
    -- ==========================================
    
    if frame.arenaNumber and general.arenaNumberX and general.arenaNumberY then
        local numberX = general.arenaNumberX or -5
        local numberY = general.arenaNumberY or 0
        
        frame.arenaNumber:ClearAllPoints()
        frame.arenaNumber:SetPoint("RIGHT", frame, "LEFT", numberX, numberY)
    end
    
    -- ==========================================
    -- TEXT SCALING & FONTS (Sliders)
    -- ==========================================
    
    local fontPath = "Interface\\AddOns\\ArenaCore\\Media\\Fonts\\arenacore.ttf"
    
    -- Player Name Scale
    if frame.playerName and general.playerNameScale then
        local scale = (general.playerNameScale or 100) / 100
        frame.playerName:SetScale(scale)
    end
    
    -- Arena Number Scale  
    if frame.arenaNumber and general.arenaNumberScale then
        local scale = (general.arenaNumberScale or 100) / 100
        frame.arenaNumber:SetScale(scale)
    end
    
    -- Health Text Scale
    if frame.healthBar and frame.healthBar.text and general.healthTextScale then
        local scale = (general.healthTextScale or 100) / 100
        frame.healthBar.text:SetScale(scale)
    end
    
    -- Resource Text Scale
    if frame.manaBar and frame.manaBar.text and general.resourceTextScale then
        local scale = (general.resourceTextScale or 100) / 100
        frame.manaBar.text:SetScale(scale)
    end
    
    -- Spell Text Scale (Cast Bar Text)
    if frame.castBar and frame.castBar.text and general.spellTextScale then
        local scale = (general.spellTextScale or 100) / 100
        frame.castBar.text:SetScale(scale)
    end
end

-- Helper function to check if class colors are enabled
function AC:GetClassColorsEnabled()
    local general = self.DB and self.DB.profile and self.DB.profile.arenaFrames and self.DB.profile.arenaFrames.general
    return general and general.useClassColors ~= false
end

-- ============================================================================
-- FRAME DEBUGGING UTILITIES
-- ============================================================================

function AC:PrintFrameDebugInfo()
    local frames = MFM and MFM.frames or {}
    if not frames or not next(frames) then
        print("ArenaCore Debug: No frames available")
        return
    end
    
    print("=== ArenaCore Frame Debug Info ===")
    print("Test Mode: " .. tostring(self.testModeEnabled))
    print("Frames Locked: " .. tostring(self.framesLocked))
    print("Is Dragging: " .. tostring(self.isDragging))
    
    for i = 1, MAX_ARENA_ENEMIES do
        local frame = frames[i]
        if frame then
            local x, y = frame:GetCenter()
            local width, height = frame:GetSize()
            local scale = frame:GetScale()
            local alpha = frame:GetAlpha()
            local visible = frame:IsShown()
            
            print("Frame " .. i .. ":")
            print("  Position: (" .. (x or "nil") .. ", " .. (y or "nil") .. ")")
            print("  Size: " .. width .. "x" .. height)
            print("  Scale: " .. scale)
            print("  Alpha: " .. alpha)
            print("  Visible: " .. tostring(visible))
            
            if frame.unit then
                print("  Unit: " .. frame.unit)
            end
            
            if frame.playerName and frame.playerName:GetText() then
                print("  Player: " .. frame.playerName:GetText())
            end
        else
            print("Frame " .. i .. ": nil")
        end
    end
    print("=== End Debug Info ===")
end

-- ============================================================================
-- CHUNK 12: INITIALIZATION & EVENT REGISTRATION
-- Final initialization, slash commands, startup code, and event handlers
-- ============================================================================

-- ============================================================================
-- SLASH COMMANDS AND INTEGRATION
-- ============================================================================

-- Master system slash commands
SLASH_ARENAMASTERCORE1 = "/acmaster"
SLASH_ARENAMASTERCORE2 = "/acm"
SlashCmdList["ARENAMASTERCORE"] = function(msg)
    msg = msg:lower()
    
    if msg == "test" then
        if MFM.isTestMode then
            MFM:DisableTestMode()
        else
            MFM:EnableTestMode()
        end
    elseif msg == "show" then
        MFM:ShowArenaFrames()
    elseif msg == "hide" then
        MFM:HideAllFrames()
    elseif msg == "reset" then
        MFM:UpdateFramePositions()
        print("|cffFFAA00ArenaCore Master:|r Frames reset to saved positions")
    elseif msg == "status" then
        print("|cffFFAA00ArenaCore Master Status:|r")
        print("  isInArena: " .. tostring(MFM.isInArena))
        print("  isTestMode: " .. tostring(MFM.isTestMode))
        print("  instanceType: " .. tostring(MFM.instanceType))
        print("  Frames created: " .. (#MFM.frames))
    elseif msg == "init" then
        MFM:Initialize()
    elseif msg == "validate" then
        AC:ValidateFrames()
    elseif msg == "debug" then
        AC:PrintFrameDebugInfo()
    elseif msg == "drag" then
        AC:ToggleFrameDragging()
    elseif msg == "refresh" then
        AC:RefreshAllFrames()
    elseif msg == "scale reset" then
        AC:ResetFrameScale()
    elseif msg == "size reset" then
        AC:ResetFrameSize()
    elseif msg == "settings" then
        -- Test the general settings application
        if AC.ApplyGeneralSettings then
            AC:ApplyGeneralSettings()
        else
            print("|cffFF0000ArenaCore:|r ApplyGeneralSettings function not found!")
        end
    elseif msg == "create" then
        -- Force create frames
        if MFM and MFM.Initialize then
            MFM:Initialize()
        else
            print("|cffFF0000ArenaCore:|r Master Frame Manager not available!")
        end
    else
        print("|cffFFAA00ArenaCore Master Commands:|r")
        print("  /acmaster test - Toggle test mode")
        print("  /acmaster show - Show frames")
        print("  /acmaster hide - Hide frames")
        print("  /acmaster reset - Reset positions")
        print("  /acmaster status - Show system status")
        print("  /acmaster init - Reinitialize system")
        print("  /acmaster validate - Validate frame integrity")
        print("  /acmaster debug - Print debug information")
        print("  /acmaster drag - Toggle frame dragging")
        print("  /acmaster refresh - Refresh all frame layouts")
        print("  /acmaster scale reset - Reset frame scale")
        print("  /acmaster size reset - Reset frame size")
        print("  /acmaster settings - Test apply general settings")
        print("  /acmaster create - Force create frames")
    end
end

-- Debug slash commands (separate to avoid UI conflict)
SLASH_ARENACOREDEBUG1 = "/acdebug"
SLASH_ARENACOREDEBUG2 = "/acdbg"
SlashCmdList["ARENACOREDEBUG"] = function(msg)
    local AC = _G.ArenaCore
    if not AC then
        print("|cffFF0000ArenaCore:|r Addon not loaded!")
        return
    end
    
    msg = msg:lower()
    
    if msg == "absorb" or msg == "absorbs" then
        -- Toggle absorb system debug mode
        if not AC.ABSORB_DEBUG then
            AC.ABSORB_DEBUG = true
            print("|cffFFAA00ArenaCore:|r Absorb debug ENABLED")
            print("|cff00FF00[AbsorbDebug]|r Will show detailed absorb detection and display info")
        else
            AC.ABSORB_DEBUG = false
            print("|cffFFAA00ArenaCore:|r Absorb debug DISABLED")
        end
    elseif msg == "immunity" or msg == "immunities" then
        -- Toggle immunity tracker debug mode
        if AC.ImmunityTracker then
            AC.ImmunityTracker.DEBUG_ENABLED = not AC.ImmunityTracker.DEBUG_ENABLED
            print("|cffFFAA00ArenaCore:|r Immunity debug " .. (AC.ImmunityTracker.DEBUG_ENABLED and "ENABLED" or "DISABLED"))
        else
            print("|cffFF0000ArenaCore:|r ImmunityTracker not available")
        end
    elseif msg == "blackout" then
        -- Toggle blackout system debug mode
        if not AC.BLACKOUT_DEBUG then
            AC.BLACKOUT_DEBUG = true
            print("|cffFFAA00ArenaCore:|r Blackout debug ENABLED")
            print("|cff00FF00[Blackout]|r Will show spell list loading and aura detection")
        else
            AC.BLACKOUT_DEBUG = false
            print("|cffFFAA00ArenaCore:|r Blackout debug DISABLED")
        end
    elseif msg == "tribadges" or msg == "classpacks" or msg == "priority" then
        -- Toggle TriBadges (Class Packs) debug mode
        if not AC.TRIBADGES_DEBUG then
            AC.TRIBADGES_DEBUG = true
            print("|cffFFAA00ArenaCore:|r TriBadges debug ENABLED")
            print("|cff00FF00[TriBadges]|r Will show:")
            print("  - ALL buffs detected on arena units")
            print("  - Priority comparison logic (why each spell wins/loses)")
            print("  - Special Unholy Assault (207289) detection")
            print("  - BBP vs OLD scanning method tracking")
        else
            AC.TRIBADGES_DEBUG = false
            print("|cffFFAA00ArenaCore:|r TriBadges debug DISABLED")
        end
    elseif msg == "all" then
        -- Enable all debug modes
        AC.ABSORB_DEBUG = true
        AC.BLACKOUT_DEBUG = true
        AC.TRIBADGES_DEBUG = true
        if AC.ImmunityTracker then AC.ImmunityTracker.DEBUG_ENABLED = true end
        print("|cffFFAA00ArenaCore:|r ALL debug modes ENABLED")
    elseif msg == "off" or msg == "disable" then
        -- Disable all debug modes
        AC.ABSORB_DEBUG = false
        AC.BLACKOUT_DEBUG = false
        AC.TRIBADGES_DEBUG = false
        if AC.ImmunityTracker then AC.ImmunityTracker.DEBUG_ENABLED = false end
        print("|cffFFAA00ArenaCore:|r ALL debug modes DISABLED")
    else
        print("|cffFFAA00ArenaCore Debug Commands:|r")
        print("  /acdebug absorb - Toggle absorb debug")
        print("  /acdebug immunity - Toggle immunity debug")
        print("  /acdebug blackout - Toggle blackout debug")
        print("  /acdebug tribadges - Toggle Class Pack priority debug")
        print("  /acdebug all - Enable all debug modes")
        print("  /acdebug off - Disable all debug modes")
    end
end

-- Slash commands for ArenaCore
SLASH_ARENACORE1 = "/arena"
SLASH_ARENACORE2 = "/arenacore"
SLASH_ARENACORE3 = "/ac"
SlashCmdList["ARENACORE"] = function(msg)
    local AC = _G.ArenaCore  -- Get global ArenaCore reference
    if not AC then
        print("|cffFF0000ArenaCore:|r Addon not loaded!")
        return
    end
    
    -- Trim spaces manually (WoW Lua doesn't have :trim())
    msg = msg:match("^%s*(.-)%s*$"):lower()
    
    -- DEBUG: Show what command was received
    print("|cffFFAA00[DEBUG]|r Received command: '" .. msg .. "' (length: " .. #msg .. ")")
    
    if msg == "test" then
        if AC.testModeEnabled then
            AC:HideTestFrames()
        else
            AC:ShowTestFrames()
        end
    elseif msg == "show" then
        AC:ShowArenaFrames()
    elseif msg == "hide" then
        AC:HideArenaFrames()
    elseif msg == "config" then
        if AC.OpenConfigFrame then
            AC:OpenConfigFrame()
        else
            print("ArenaCore: Config frame not available")
        end
    elseif msg == "reset" then
        AC:ResetAllFrames()
    elseif msg == "betapreset" or msg == "preset" then
        print("|cffFFAA00[DEBUG]|r Slash command 'betapreset' detected, calling AC:ApplyBetaPreset()")
        AC:ApplyBetaPreset()
    elseif msg == "resetdefaults" or msg == "resetall" then
        AC:ResetToDefaults()
    elseif msg == "addblackoutspells" or msg == "blackoutspells" then
        AC:AddDefaultBlackoutSpells()
    elseif msg == "exportblackout" or msg == "blackoutexport" then
        AC:ExportBlackoutToDefaults()
    elseif msg == "tickers" or msg == "checktickers" then
        -- PHASE 1.2: Check active tickers
        AC:CheckActiveTickers()
    elseif msg == "events" or msg == "checkevents" then
        -- PHASE 2.3: Check active event frames
        AC:CheckActiveEventFrames()
    elseif msg == "cleanup" then
        -- PHASE 2.3: Manual cleanup
        AC:Cleanup()
    elseif msg == "cache" or msg == "refreshcache" then
        -- PHASE 3.2: Refresh settings cache
        AC:RefreshSettingsCache()
        local count = AC:CountCachedSettings()
        print("|cff00FF00[ArenaCore]|r Settings cache refreshed! (" .. count .. " settings cached)")
    -- REMOVED: blackoutdebug command - backend removed, will be rebuilt
    elseif msg == "immunitydebug" or msg == "debugimmunity" then
        -- Toggle immunity tracker debug mode
        if AC.ImmunityTracker then
            AC.ImmunityTracker.DEBUG_ENABLED = not AC.ImmunityTracker.DEBUG_ENABLED
            print("|cffFFAA00ArenaCore:|r Immunity debug " .. (AC.ImmunityTracker.DEBUG_ENABLED and "ENABLED" or "DISABLED"))
        else
            print("|cffFF0000ArenaCore:|r ImmunityTracker not available")
        end
    elseif msg == "absorbdebug" or msg == "debugabsorb" then
        -- Toggle absorb system debug mode
        if not AC.ABSORB_DEBUG then
            AC.ABSORB_DEBUG = true
            print("|cffFFAA00ArenaCore:|r Absorb debug ENABLED")
            print("|cff00FF00[AbsorbDebug]|r Will show detailed absorb detection and display info")
        else
            AC.ABSORB_DEBUG = false
            print("|cffFFAA00ArenaCore:|r Absorb debug DISABLED")
        end
    elseif msg == "blackoutdebug" or msg == "debugblackout" then
        -- Toggle blackout system debug mode
        if not AC.BLACKOUT_DEBUG then
            AC.BLACKOUT_DEBUG = true
            print("|cffFFAA00ArenaCore:|r Blackout debug ENABLED")
            print("|cff00FF00[Blackout]|r Will show spell list loading and aura detection")
        else
            AC.BLACKOUT_DEBUG = false
            print("|cffFFAA00ArenaCore:|r Blackout debug DISABLED")
        end
    elseif msg == "tribadgesdebug" or msg == "debugtribadges" or msg == "classpacksdebug" then
        -- Toggle TriBadges (Class Packs) debug mode
        if not AC.TRIBADGES_DEBUG then
            AC.TRIBADGES_DEBUG = true
            print("|cffFFAA00ArenaCore:|r TriBadges debug ENABLED")
            print("|cff00FF00[TriBadges]|r Will show which method (BBP/OLD) detects each spell")
        else
            AC.TRIBADGES_DEBUG = false
            print("|cffFFAA00ArenaCore:|r TriBadges debug DISABLED")
        end
    elseif msg == "help" then
        print("|cffFFAA00ArenaCore Commands:|r")
        print("  /arena - Open ArenaCore configuration")
        print("  /ac test - Toggle test mode")
        print("  /ac show - Show arena frames")
        print("  /ac hide - Hide arena frames")
        print("  /ac config - Open configuration")
        print("  /ac reset - Reset all frames")
        print("  /ac tickers - Check active tickers (Phase 1.2)")
        print("  /ac events - Check active event frames (Phase 2.3)")
        print("  /ac cleanup - Manual cleanup (tickers + events)")
        print("  /ac cache - Refresh settings cache (Phase 3.2)")
        print("  ")
        print("|cffFFAA00Debug Commands:|r (Use /acdebug to avoid UI conflict)")
        print("  /acdebug absorb - Toggle absorb debug")
        print("  /acdebug immunity - Toggle immunity debug")
        print("  /acdebug blackout - Toggle blackout debug")
        print("  /acdebug all - Enable all debug modes")
        print("  ")
        print("  /ac betapreset - Apply beta tester preset (your defaults)")
        print("  /ac addblackoutspells - Add default Blackout spells to your list")
        print("  /ac exportblackout - Export your Blackout spells to become defaults")
        print("  /ac resetdefaults - Hard reset to defaults")
        print("  /ac help - Show this help")
    else
        -- Temporarily disabled config panel due to UI consolidation
        print("|cffFFAA00ArenaCore:|r Available commands:")
        print("  /ac test - Toggle test mode")
        print("  /ac show - Show arena frames") 
        print("  /ac hide - Hide arena frames")
        print("  /ac help - Show all commands")
        print("|cffFFCC00Note:|r Config UI being updated for consolidated files")
    end
end

-- ============================================================================
-- GLOBAL EVENT FRAME SETUP
-- ============================================================================

-- Create global event frame for system initialization
local ArenaCore_InitFrame = CreateFrame("Frame", "ArenaCore_InitializationFrame", UIParent)
ArenaCore_InitFrame:RegisterEvent("ADDON_LOADED")
ArenaCore_InitFrame:RegisterEvent("PLAYER_LOGIN")
ArenaCore_InitFrame:RegisterEvent("PLAYER_ENTERING_WORLD")

-- Main initialization event handler
ArenaCore_InitFrame:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" then
        local addonName = ...
        if addonName == "ArenaCore" then
            -- DEBUG: Addon loaded
            -- print("|cffFFAA00ArenaCore:|r Addon loaded, initializing database...")
            
            -- Initialize database first
            AC:EnsureDB()
            
            -- PHASE 3.2: Initialize settings cache
            AC:RefreshSettingsCache()
            
            -- Initialize icon styling system
            if AC.IconStyling then
                -- DEBUG: Icon styling system initialized
                -- print("|cffFFAA00ArenaCore:|r Icon styling system initialized")
            end
            
            -- Initialize DR system
            if InitializeDRSystem and InitializeDRSystem() then
                -- DEBUG: DR system initialized
                -- print("|cffFFAA00ArenaCore:|r DR system initialized")
            end
            
            -- NOTE: Module initialization happens in Core.lua via AC:_EnableModules() on PLAYER_LOGIN
            -- Don't duplicate it here - let the proper system handle it
            
            -- Mark addon as loaded
            AC.addonLoaded = true
        end
        
    elseif event == "PLAYER_LOGIN" then
        -- DEBUG: Player logged in
        -- print("|cffFFAA00ArenaCore:|r Player logged in, initializing frame systems...")
        
        -- CRITICAL: Register slash commands on PLAYER_LOGIN to override any conflicts
        C_Timer.After(0.5, function()
            SLASH_ARENACORE1 = "/arena"
            SLASH_ARENACORE2 = "/arenacore"
            SLASH_ARENACORE3 = "/ac"
            -- Force re-register the handler
            SlashCmdList["ARENACORE"] = SlashCmdList["ARENACORE"]
            -- HIDDEN: Slash commands register silently for cleaner user experience
            -- print("|cffFFAA00[DEBUG]|r ArenaCore slash commands registered!")
        end)
        
        -- Initialize Master Frame Manager if not already done
        if MFM and not MFM.frames[1] then
            MFM:Initialize()
        end
        
        -- Initialize FrameManager if available
        if AC.FrameManager and AC.FrameManager.Initialize then
            AC.FrameManager:Initialize()
        end
        
        -- Initialize TriBadges system if available
        if AC.TriBadges and AC.TriBadges.Initialize then
            AC.TriBadges:Initialize()
        end
        
        -- Register for trinket icon refreshes
        if AC.RefreshTrinketIcons then
            -- Refresh trinket icons with current settings
            C_Timer.After(1, function()
                AC:RefreshTrinketIcons()
            end)
        end
        
        -- CRITICAL: Apply /gg surrender command if enabled
        if AC.MoreFeatures and AC.MoreFeatures.ApplySurrenderSetting then
            AC.MoreFeatures:ApplySurrenderSetting()
        end
        
        -- DEBUG: Systems initialized
        -- print("|cffFFAA00ArenaCore:|r Systems initialized successfully")
        
    elseif event == "PLAYER_ENTERING_WORLD" then
        local isInitialLogin, isReloadingUi = ...
        
        if isInitialLogin or isReloadingUi then
            -- DEBUG: Entering world
            -- print("|cffFFAA00ArenaCore:|r Entering world, checking arena state...")
            
            -- Check arena state after entering world
            C_Timer.After(1, function()
                if MFM and MFM.CheckArenaState then
                    MFM:CheckArenaState()
                end
            end)
            
            -- Apply saved frame settings
            C_Timer.After(2, function()
                if AC.RefreshAllFrames then
                    AC:RefreshAllFrames()
                end
            end)
        end
    end
end)

-- ============================================================================
-- CONSOLIDATION: SECOND OLD SYSTEM REMOVED (Lines 9295-9410)
-- ============================================================================
-- This was the SECOND duplicate arenaEventFrame - also deleted!
-- ALL arena event handling is now in ONE place: MFM (MasterFrameManager)
-- See lines 2644-3078 for the consolidated system.
-- ============================================================================

-- ============================================================================
-- ARENA TRACKING CONTAINER SETUP
-- ============================================================================

-- Initialize ArenaTracking container
local ArenaTracking = CreateFrame("Frame", "ArenaCore_ArenaTrackingContainer", UIParent)
AC.ArenaTracking = ArenaTracking

-- PHASE 2.3: Register main event frame for cleanup
AC:RegisterEventFrame(ArenaTracking, "ArenaTracking_Main")

-- ============================================================================
-- PHASE 1.1: CENTRALIZED COMBAT_LOG HANDLER
-- Consolidates 4 separate COMBAT_LOG registrations into one efficient system
-- Performance gain: 60-75% reduction in COMBAT_LOG processing overhead
-- ============================================================================

local function ProcessCentralizedCombatLog()
    local timestamp, combatEvent, _, sourceGUID, sourceName, sourceFlags, sourceRaidFlags,
          destGUID, destName, destFlags, destRaidFlags, spellID, spellName = CombatLogGetCurrentEventInfo()
    
    -- Early exit for irrelevant events
    if not combatEvent then return end
    
    -- ========================================================================
    -- DELEGATION TO SUBSYSTEMS (each processes only what it needs)
    -- ========================================================================
    
    -- 1. AuraTracker: Interrupts (SPELL_INTERRUPT, SPELL_CAST_SUCCESS)
    if (combatEvent == "SPELL_INTERRUPT" or combatEvent == "SPELL_CAST_SUCCESS") then
        if AC.AuraTracker and AC.AuraTracker.ProcessCombatLogEvent then
            AC.AuraTracker:ProcessCombatLogEvent(timestamp, combatEvent, sourceGUID, destGUID, spellID)
        end
    end
    
    -- 2. DispelTracker: Dispels (SPELL_CAST_SUCCESS, SPELL_DISPEL)
    if (combatEvent == "SPELL_CAST_SUCCESS" or combatEvent == "SPELL_DISPEL") then
        if AC.DispelTracker and AC.DispelTracker.ProcessCombatLogEvent then
            AC.DispelTracker.ProcessCombatLogEvent(timestamp, combatEvent, sourceGUID, destGUID, spellID)
        end
    end
    
    -- 3. FeignDeathDetector: Death events (UNIT_DIED, UNIT_DESTROYED)
    if (combatEvent == "UNIT_DIED" or combatEvent == "UNIT_DESTROYED") then
        if AC.FeignDeathDetector and AC.FeignDeathDetector.ProcessCombatLogEvent then
            AC.FeignDeathDetector:ProcessCombatLogEvent(timestamp, combatEvent, sourceGUID, destGUID, spellID)
        end
    end
    
    -- 4. KickBar: Interrupt cooldown tracking (SPELL_CAST_SUCCESS)
    if combatEvent == "SPELL_CAST_SUCCESS" then
        if AC.KickBar and AC.KickBar.ProcessCombatLogEvent then
            AC.KickBar:ProcessCombatLogEvent(timestamp, combatEvent, sourceGUID, sourceName, sourceFlags, destGUID, spellID)
        end
    end
    
    -- 5. TrinketsRacials: Trinket and racial cooldown tracking (SPELL_CAST_SUCCESS)
    -- CRITICAL FIX: This was missing, causing racials to never work in real arena
    if combatEvent == "SPELL_CAST_SUCCESS" then
        if AC.TrinketsRacials and AC.TrinketsRacials.OnCombatLogSpell then
            AC.TrinketsRacials:OnCombatLogSpell(sourceGUID, spellID)
        end
    end
    
    -- 6. Main System: FrameManager for DR tracking and other systems
    if AC.FrameManager and AC.FrameManager.HandleCombatLogEvent then
        AC.FrameManager:HandleCombatLogEvent()
    end
end

-- Register events for ArenaTracking
ArenaTracking:RegisterEvent("ADDON_LOADED")
ArenaTracking:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") -- PHASE 1.1: Now centralized
ArenaTracking:RegisterEvent("PLAYER_LOGOUT")
-- Power events for mana/energy/rage percentage
ArenaTracking:RegisterUnitEvent("UNIT_POWER_FREQUENT", "arena1", "arena2", "arena3")
ArenaTracking:RegisterUnitEvent("UNIT_DISPLAYPOWER", "arena1", "arena2", "arena3")
-- Spell cast events for trinkets and racials
ArenaTracking:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "arena1", "arena2", "arena3")
-- Cast bar events for all arena units
ArenaTracking:RegisterUnitEvent("UNIT_SPELLCAST_START", "arena1", "arena2", "arena3")
ArenaTracking:RegisterUnitEvent("UNIT_SPELLCAST_STOP", "arena1", "arena2", "arena3")
ArenaTracking:RegisterUnitEvent("UNIT_SPELLCAST_FAILED", "arena1", "arena2", "arena3")
ArenaTracking:RegisterUnitEvent("UNIT_SPELLCAST_INTERRUPTED", "arena1", "arena2", "arena3")
ArenaTracking:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_START", "arena1", "arena2", "arena3")
ArenaTracking:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", "arena1", "arena2", "arena3")
-- CRITICAL: Evoker empowered spells (Spirit Bloom, Fire Breath, etc.)
--- REMOVED: UNIT_AURA, UNIT_ABSORB events now handled by MFM individual frames (lines 2308-2310)
--- This prevents duplicate event handling and ensures proper frame-specific updates
-- ArenaTracking:RegisterUnitEvent("UNIT_AURA", "arena1", "arena2", "arena3")
-- ArenaTracking:RegisterUnitEvent("UNIT_ABSORB_AMOUNT_CHANGED", "arena1", "arena2", "arena3")
-- ArenaTracking:RegisterUnitEvent("UNIT_HEAL_ABSORB_AMOUNT_CHANGED", "arena1", "arena2", "arena3")

--- ArenaTracking event handler
ArenaTracking:SetScript("OnEvent", function(self, event, ...)
    if event == "PLAYER_ENTERING_WORLD" then
        local addonName = ...
        if addonName == "ArenaCore" then
            AC:EnsureDB()
        end
        
    elseif event == "COMBAT_LOG_EVENT_UNFILTERED" then
        -- PHASE 1.1: Use centralized COMBAT_LOG handler
        ProcessCentralizedCombatLog()
        
    elseif event == "UNIT_POWER_FREQUENT" or event == "UNIT_DISPLAYPOWER" then
        local unit = ...
        if unit and string.match(unit, "^arena%d$") and not AC.testModeEnabled then
            local arenaIndex = tonumber(string.match(unit, "^arena(%d)$"))
            local arenaFrames = GetArenaFrames()
            if arenaIndex and arenaFrames and arenaFrames[arenaIndex] then
                -- Update power bars
                if UpdateFrameBars then
                    UpdateFrameBars(arenaFrames[arenaIndex], unit)
                end
            end
        end
        
    elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
        if AC.TrinketsRacials and AC.TrinketsRacials.OnUnitSpellCast then
            local unit, _, spellID = ...
            AC.TrinketsRacials:OnUnitSpellCast(unit, spellID)
        end
        
    elseif event:match("^UNIT_SPELLCAST") then
        local unit = ...
        -- Cast bar event
        if unit and string.match(unit, "^arena%d$") and not AC.testModeEnabled then
            local arenaIndex = tonumber(string.match(unit, "^arena(%d)$"))
            local arenaFrames = GetArenaFrames()
            -- Arena index check
            if arenaIndex and arenaFrames and arenaFrames[arenaIndex] then
                -- Handle cast bar updates through FrameManager
                if AC.FrameManager then
                    if event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" or event == "UNIT_SPELLCAST_EMPOWER_START" then
                        -- Update cast bar
                        if AC.FrameManager.UpdateCastBar then
                            AC.FrameManager:UpdateCastBar(arenaFrames[arenaIndex], unit, event)
                        end
                    else
                        -- Hide cast bar
                        if AC.FrameManager.HideCastBar then
                            AC.FrameManager:HideCastBar(arenaFrames[arenaIndex])
                        end
                    end
                end
            end
        end
        
    -- REMOVED: UNIT_AURA and UNIT_ABSORB handlers - now handled by MFM individual frames (lines 2344-2351)
    -- This prevents duplicate event handling and ensures proper frame-specific updates
    -- Debuffs, AuraTracker, Absorbs, and Immunities are all updated via MFM:HandleFrameEvent
    
    elseif event == "UNIT_AURA" then
        -- LEGACY: Keep debuff and aura tracker calls for compatibility
        local unit = ...
        local _, instanceType = IsInInstance()
        
        if instanceType == "arena" and unit and unit:match("^arena%d$") and not AC.testModeEnabled then
            -- CRITICAL: Call debuff module directly (still needed for compatibility)
            local arenaIndex = tonumber(unit:match("^arena(%d)$"))
            local arenaFrames = GetArenaFrames()
            
            if arenaIndex and arenaFrames and arenaFrames[arenaIndex] then
                local frame = arenaFrames[arenaIndex]
                
                local debuffModule = GetDebuffModule()
                if debuffModule and debuffModule.Update then
                    debuffModule:Update(frame, unit)
                end
                
                -- Update auras via AuraTracker system if available
                if AC.AuraTracker and AC.AuraTracker.UpdateAuras then
                    AC.AuraTracker:UpdateAuras(unit)
                end
                
                -- NOTE: Absorbs and Immunities now handled by MFM (lines 2346-2351)
            end
        end
        
    elseif event == "PLAYER_LOGOUT" then
        -- PHASE 2.3: Comprehensive cleanup on logout
        AC:Cleanup()
    end
end)
-- ============================================================================
-- INTEGRATION WITH EXISTING SYSTEMS
-- ============================================================================

-- REMOVED: Duplicate/conflicting test mode functions
-- ============================================================================
-- AUTO-INITIALIZATION AND STARTUP
-- ============================================================================

-- Auto-initialize on file load
-- DEBUG: Master System loaded
-- print("|cffFFAA00ArenaCore Master System:|r Loaded - use /acmaster test")
-- print("|cffFFAA00ArenaCore Master System:|r Will auto-initialize on PLAYER_LOGIN")

-- For immediate testing, initialize right away if possible
if IsLoggedIn() then
    -- DEBUG: Player already logged in
    -- print("|cffFFAA00ArenaCore Master System:|r Player already logged in - initializing immediately")
    C_Timer.After(1, function()
        AC.InitMasterSystem()
    end)
end
function AC.InitMasterSystem()
    if not MFM.frames[1] then
        MFM:Initialize()
        return true
    end
    return false
end

-- ============================================================================
-- MODULE INITIALIZATION CALLS
-- ============================================================================

-- Initialize all available modules
local function InitializeAllModules()
    -- Initialize Master Frame Manager
    if MFM and not MFM.initialized then
        MFM:Initialize()
        MFM.initialized = true
    end
    
    -- Initialize FrameManager if available
    if AC.FrameManager and not AC.FrameManager.initialized then
        if AC.FrameManager.Initialize then
            AC.FrameManager:Initialize()
        end
        AC.FrameManager.initialized = true
    end
    
    -- Initialize TriBadges if available
    if AC.TriBadges and not AC.TriBadges.initialized then
        if AC.TriBadges.Initialize then
            AC.TriBadges:Initialize()
        end
        AC.TriBadges.initialized = true
    end
    
    -- Initialize AuraTracker if available
    if AC.AuraTracker and not AC.AuraTracker.initialized then
        if AC.AuraTracker.Initialize then
            AC.AuraTracker:Initialize()
        end
        AC.AuraTracker.initialized = true
    end
    
    -- Initialize DispelTracker if available
    if AC.DispelTracker and not AC.DispelTracker.initialized then
        if AC.DispelTracker.Initialize then
            AC.DispelTracker:Initialize()
        end
        AC.DispelTracker.initialized = true
    end
    
    -- CRITICAL FIX: Initialize KickBar if available
    if AC.KickBar and not AC.KickBar.initialized then
        if AC.KickBar.Initialize then
            AC.KickBar:Initialize()
        end
        AC.KickBar.initialized = true
    end
    
    -- DEBUG: Modules initialized
    -- print("|cffAAFFAA[SUCCESS]|r All ArenaCore modules initialized")
end

-- Schedule module initialization
C_Timer.After(3, InitializeAllModules)

-- ============================================================================
-- FINAL SYSTEM VALIDATION
-- ============================================================================

-- Final validation function to ensure everything is working
local function PerformSystemValidation()
    local issues = {}
    
    -- Check Master Frame Manager
    if not MFM or not MFM.frames then
        table.insert(issues, "Master Frame Manager not available")
    elseif not MFM.frames[1] then
        table.insert(issues, "Master Frame Manager frames not created")
    end
    
    -- Check database
    if not AC.DB or not AC.DB.profile then
        table.insert(issues, "Database not initialized")
    end
    
    -- Check slash commands
    if not SlashCmdList["ARENAMASTERCORE"] then
        table.insert(issues, "Master slash commands not registered")
    end
    
    -- Check event frames
    if not ArenaCore_InitFrame then
        table.insert(issues, "Initialization event frame not created")
    end
    
    if not ArenaTracking then
        table.insert(issues, "ArenaTracking event frame not created")
    end
    
    -- Report validation results
    if #issues > 0 then
        print("|cffFF6B6BArenaCore System Validation:|r Issues found:")
        for _, issue in ipairs(issues) do
            print("  - " .. issue)
        end
    else
        -- DEBUG: System validation passed
        -- print("|cffAAFFAA[SUCCESS]|r ArenaCore system validation passed!")
        -- print("|cffAAFFAA[SUCCESS]|r All systems operational and ready!")
    end
end

-- Schedule final validation
C_Timer.After(5, PerformSystemValidation)

-- ============================================================================
-- COMPLETION MESSAGE
-- ============================================================================

-- ============================================================================
-- UI SYSTEM INTEGRATION - Connect Core/UI.lua to Master System
-- ============================================================================

-- Note: AC:OpenConfigPanel() is defined in Core/UI.lua, not here.
-- This ensures Core/UI.lua slash commands work properly.

-- ============================================================================
-- MASTER SYSTEM UI BRIDGE FUNCTIONS
-- ============================================================================

-- Bridge function for UI pages to control Master Frame Manager
function AC:ShowTestFrames()
    if MFM and MFM.EnableTestMode then
        -- CRITICAL FIX: Clear stale aura data BEFORE enabling test mode frames
        -- This prevents real arena auras from persisting into test mode
        if AC.AuraTracker and AC.AuraTracker.EnableTestMode then
            AC.AuraTracker:EnableTestMode()
        end
        
        -- Now enable test mode frames (which will apply test data)
        MFM:EnableTestMode()
        
        return true
    end
    print("|cffFF6B6BArenaCore:|r Master Frame Manager not available!")
    return false
end

function AC:HideTestFrames()
    if MFM and MFM.DisableTestMode then
        MFM:DisableTestMode()
        
        -- CRITICAL: Disable aura tracking test mode
        if AC.AuraTracker and AC.AuraTracker.DisableTestMode then
            AC.AuraTracker:DisableTestMode()
        end
        
        -- CRITICAL: Hide immunity glows when exiting test mode
        if AC.ImmunityTracker and AC.ImmunityTracker.HideAll then
            AC.ImmunityTracker:HideAll()
        end
        
        return true
    end
    print("|cffFF6B6BArenaCore:|r Master Frame Manager not available!")
    return false
end

-- Bridge function to get test mode status
function AC:IsTestModeEnabled()
    return MFM and MFM.isTestMode or false
end

-- Bridge function for UI sliders to update frame positioning
function AC:UpdateFramePositioning()
    if MFM and MFM.UpdateFramePositions then
        MFM:UpdateFramePositions()
    elseif self.UpdateFramePositions then
        self:UpdateFramePositions()
    end
end


-- DEBUG: Master System fully loaded
-- print("|cffAAFFAA=== ArenaCore Master System Fully Loaded ===|r")
-- print("|cffAAFFAAAll 12 chunks successfully integrated!|r")
-- print("|cffAAFFAAUI Bridge functions added for /arena command|r")
-- print("|cffAAFFAAUse /arena to open config or /acmaster for commands|r")
-- DEBUG: Separator line
-- print("|cffAAFFAA=======================================|r")