-- ============================================================================
-- MismatchHandler
-- Keeps frame visibility sane in weird brackets (2v3, 1v2, etc.)
-- Preserves correct class data and prevents incorrect data from being applied
-- when Blizzard's arena APIs return stale or inconsistent information.
-- ============================================================================

local AddonName, AC = ...

-- Create module
local MismatchHandler = {}
AC.MismatchHandler = MismatchHandler

-- Local references for performance
local GetArenaOpponentSpec = GetArenaOpponentSpec
local GetNumArenaOpponentSpecs = GetNumArenaOpponentSpecs
local GetSpecializationInfoByID = GetSpecializationInfoByID
local UnitClass = UnitClass
local UnitExists = UnitExists
local UnitGUID = UnitGUID
local IsActiveBattlefieldArena = IsActiveBattlefieldArena
local IsInInstance = IsInInstance
local InCombatLockdown = InCombatLockdown
local C_Timer = C_Timer
local wipe = wipe
local MAX_ARENA_ENEMIES = 5

-- Event frame
local eventFrame = CreateFrame("Frame")

-- ============================================================================
-- CLASS DATA PRESERVATION
-- ============================================================================

-- Track which arena units have valid class data detected
local seenValidClassData = {}

-- Track which units have been seen (for older clients where UnitExists may return false)
local seenArenaUnits = {}

-- Store last known valid class for each arena unit
local lastKnownClass = {}

-- Store last known valid spec for each arena unit
local lastKnownSpec = {}

-- Store last known valid spec texture for each arena unit
local lastKnownSpecTexture = {}

-- Store last known valid spec ID for each arena unit
local lastKnownSpecID = {}

-- Track if we're in a match (for proper state management)
local isInArenaMatch = false

-- Track if we're in preparation room
local isInPrepRoom = false

-- ============================================================================
-- UNIT VALIDATION
-- ============================================================================

local function IsValidUnit(unit)
    if not unit then
        return false
    end
    
    -- Validate it's an arena unit (arena1-arena5)
    local unitID = unit:match("arena(%d+)")
    return unitID and tonumber(unitID) <= 5
end

-- ============================================================================
-- STATE MANAGEMENT
-- ============================================================================

local function ResetState()
    -- Reset all tracking tables when leaving arena
    wipe(seenValidClassData)
    wipe(seenArenaUnits)
    wipe(lastKnownClass)
    wipe(lastKnownSpec)
    wipe(lastKnownSpecTexture)
    wipe(lastKnownSpecID)
    
    isInArenaMatch = false
    isInPrepRoom = false
end

-- ============================================================================
-- CLASS DATA TRACKING
-- ============================================================================

-- Update tracking for a specific arena unit
local function UpdateClassDataTracking(arenaIndex, unit)
    if not unit then return end
    
    local unitExists = UnitExists(unit)
    
    -- For older clients, track seen units even when UnitExists returns false
    if AC.isTBC or AC.isWrath then
        if unitExists then
            seenArenaUnits[arenaIndex] = true
        end
    end
    
    -- Get current class data
    local currentClass, currentClassFile = UnitClass(unit)
    local currentSpecID = GetArenaOpponentSpec and GetArenaOpponentSpec(arenaIndex) or 0
    
    -- Only update our stored data if we have valid new information
    -- This is the key: we DON'T overwrite valid data with invalid data
    if unitExists and currentClassFile then
        -- Check if spec data is valid
        if currentSpecID and currentSpecID > 0 then
            local id, name, description, icon, role, class = GetSpecializationInfoByID(currentSpecID)
            
            if class then
                -- Store valid class data
                lastKnownClass[arenaIndex] = class
                lastKnownSpec[arenaIndex] = name
                lastKnownSpecTexture[arenaIndex] = icon
                lastKnownSpecID[arenaIndex] = id
                seenValidClassData[arenaIndex] = true
            end
        elseif not seenValidClassData[arenaIndex] then
            -- No spec data yet, but we have class data - store it as fallback
            lastKnownClass[arenaIndex] = currentClassFile
            seenValidClassData[arenaIndex] = true
        end
    end
end

-- ============================================================================
-- PUBLIC API - Get preserved class data
-- ============================================================================

-- Get the last known valid class for an arena unit
function MismatchHandler:GetKnownClass(arenaIndex)
    return lastKnownClass[arenaIndex]
end

-- Get the last known valid spec for an arena unit
function MismatchHandler:GetKnownSpec(arenaIndex)
    return lastKnownSpec[arenaIndex]
end

-- Get the last known valid spec texture for an arena unit
function MismatchHandler:GetKnownSpecTexture(arenaIndex)
    return lastKnownSpecTexture[arenaIndex]
end

-- Get the last known valid spec ID for an arena unit
function MismatchHandler:GetKnownSpecID(arenaIndex)
    return lastKnownSpecID[arenaIndex]
end

-- Check if we have valid class data for an arena unit
function MismatchHandler:HasValidClassData(arenaIndex)
    return seenValidClassData[arenaIndex] == true
end

-- Apply preserved class data to a frame
function MismatchHandler:ApplyPreservedClassData(frame, arenaIndex)
    if not frame then return end
    
    -- Only apply if we have valid data
    if seenValidClassData[arenaIndex] then
        -- Preserve existing class data if frame already has it
        if not frame.class or frame.class == "" then
            frame.class = lastKnownClass[arenaIndex]
        end
        
        if not frame.specID or frame.specID == 0 then
            frame.specID = lastKnownSpecID[arenaIndex]
        end
        
        if not frame.specIconTexture or frame.specIconTexture == "" then
            frame.specIconTexture = lastKnownSpecTexture[arenaIndex]
        end
    end
end

-- ============================================================================
-- ARENA_OPPONENT_UPDATE
-- ============================================================================

function MismatchHandler:ARENA_OPPONENT_UPDATE(event, unit, updateType)
    -- Only care in arena.
    if not IsActiveBattlefieldArena() then
        return
    end
    
    -- Validate unit.
    if not IsValidUnit(unit) then
        return
    end
    
    -- Extract arena index.
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex then
        return
    end
    
    -- Update class data tracking BEFORE applying to frame
    UpdateClassDataTracking(arenaIndex, unit)
    
    -- This is kind of important I guess: create the frame if it doesn't exist yet.
    -- (Ex: stealth player finally shows up.)
    local frame = self:GetFrame(unit)
    if not frame then
        -- Try to get frame from FrameManager first.
        if AC.FrameManager then
            local frames = AC.FrameManager:GetFrames()
            if frames then
                if not frames[arenaIndex] then
                    -- Frame doesn't exist, create it.
                    if AC.FrameManager.CreateFrame then
                        AC.FrameManager:CreateFrame(arenaIndex)
                        frame = self:GetFrame(unit)
                    end
                end
            end
        end
        
        -- Still no frame.
        if not frame then
            return
        end
    end
    
    -- Get current spec data from API
    local specID = GetArenaOpponentSpec and GetArenaOpponentSpec(arenaIndex)
    
    -- KEY LOGIC: Only update frame data if API returns valid data
    -- Otherwise, preserve existing valid data (don't overwrite with 0 or nil)
    if specID and specID > 0 then
        local id, name, description, icon, role, class = GetSpecializationInfoByID(specID)
        if class then
            frame.class = class
            frame.specID = id
            frame.specIconTexture = icon
            
            -- Update our tracking
            lastKnownClass[arenaIndex] = class
            lastKnownSpec[arenaIndex] = name
            lastKnownSpecTexture[arenaIndex] = icon
            lastKnownSpecID[arenaIndex] = id
            seenValidClassData[arenaIndex] = true
            
            if frame.classIcon then
                local db = AC.DB and AC.DB.profile
                local showSpecInstead = db and db.classIcons and db.classIcons.showSpecInstead == true
                if showSpecInstead and icon then
                    if not frame.classIcon.ccActive then
                        local iconTexture = frame.classIcon.classIcon or frame.classIcon.icon
                        if iconTexture and iconTexture.SetTexture then
                            iconTexture:SetTexture(icon)
                            if iconTexture.SetTexCoord then
                                iconTexture:SetTexCoord(0.1, 0.9, 0.1, 0.9)
                            end
                        end
                    end
                elseif frame.classIcon.UpdateClassIcon then
                    frame.classIcon.UpdateClassIcon(class)
                end
            end
        end
    else
        -- API returned invalid data (0 or nil)
        -- PRESERVE existing valid data instead of overwriting
        if seenValidClassData[arenaIndex] then
            -- We have valid stored data, use it
            local classUpdated = false
            if not frame.class or frame.class == "" then
                frame.class = lastKnownClass[arenaIndex]
                classUpdated = true
            end
            if not frame.specID or frame.specID == 0 then
                frame.specID = lastKnownSpecID[arenaIndex]
            end
            if not frame.specIconTexture or frame.specIconTexture == "" then
                frame.specIconTexture = lastKnownSpecTexture[arenaIndex]
            end
            
            if classUpdated and frame.classIcon then
                local db = AC.DB and AC.DB.profile
                local showSpecInstead = db and db.classIcons and db.classIcons.showSpecInstead == true
                if showSpecInstead and frame.specIconTexture then
                    if not frame.classIcon.ccActive then
                        local iconTexture = frame.classIcon.classIcon or frame.classIcon.icon
                        if iconTexture and iconTexture.SetTexture then
                            iconTexture:SetTexture(frame.specIconTexture)
                            if iconTexture.SetTexCoord then
                                iconTexture:SetTexCoord(0.1, 0.9, 0.1, 0.9)
                            end
                        end
                    end
                elseif not showSpecInstead and frame.classIcon.UpdateClassIcon then
                    frame.classIcon.UpdateClassIcon(frame.class)
                end
            end
        else
            -- No valid data yet, try to get class from UnitClass as fallback
            local _, classFile = UnitClass(unit)
            if classFile then
                frame.class = classFile
                lastKnownClass[arenaIndex] = classFile
                seenValidClassData[arenaIndex] = true
                
                if frame.classIcon and frame.classIcon.UpdateClassIcon then
                    local db = AC.DB and AC.DB.profile
                    local showSpecInstead = db and db.classIcons and db.classIcons.showSpecInstead == true
                    if not showSpecInstead then
                        frame.classIcon.UpdateClassIcon(classFile)
                    end
                end
            end
        end
    end
    
    -- Update frame elements (moved from old handler).
    local AC = _G.ArenaCore
    if AC then
        -- Update dispels.
        if AC.UpdateDispelFrames then
            C_Timer.After(0.05, function()
                AC:UpdateDispelFrames()
            end)
        end
        
        -- Update trinkets and racials.
        if AC.TrinketsRacials and AC.TrinketsRacials.RefreshFrame then
            AC.TrinketsRacials:RefreshFrame(frame, unit)
        end
        
        -- Update MFM elements if available.
        local MFM = AC.MasterFrameManager
        if MFM then
            -- Update name (clear test mode names).
            if not AC.testModeEnabled and MFM.UpdateName then
                MFM:UpdateName(frame)
            end
            
            -- Update health and power.
            if MFM.UpdateHealth then MFM.UpdateHealth(frame) end
            if MFM.UpdatePower then MFM.UpdatePower(frame) end
        end
    end
    
    -- This is kind of important I guess: always show the frame.
    -- ArenaFrameStealth.lua handles alpha; we just make sure it's actually shown.
    self:ShowFrame(unit)
end

-- ============================================================================
-- SOLO SHUFFLE ROUND TRANSITION
-- ============================================================================

-- Called by HandleSoloShuffleTransition so stale round data is gone before
-- HandlePrepRoom runs on the next ARENA_PREP_OPPONENT_SPECIALIZATIONS.
function MismatchHandler:ClearRoundData()
    wipe(seenValidClassData)
    wipe(seenArenaUnits)
    wipe(lastKnownClass)
    wipe(lastKnownSpec)
    wipe(lastKnownSpecTexture)
    wipe(lastKnownSpecID)
end

-- ============================================================================
-- ARENA_PREP_OPPONENT_SPECIALIZATIONS
-- ============================================================================

function MismatchHandler:ARENA_PREP_OPPONENT_SPECIALIZATIONS()
    -- We're now in prep room
    isInPrepRoom = true
    isInArenaMatch = false
    
    -- Clear seen valid data when entering prep room for a new match
    -- This ensures we don't carry over stale data from previous match
    wipe(seenValidClassData)
    wipe(seenArenaUnits)
    wipe(lastKnownClass)
    wipe(lastKnownSpec)
    wipe(lastKnownSpecTexture)
    wipe(lastKnownSpecID)
    
    -- Loop through actual opponent count.
    local numOpponents = GetNumArenaOpponentSpecs and GetNumArenaOpponentSpecs() or 0
    
    for i = 1, MAX_ARENA_ENEMIES do
        local unit = "arena" .. i
        local specID = GetArenaOpponentSpec and GetArenaOpponentSpec(i)
        
        if specID and specID > 0 then
            -- Get frame.
            local frame = self:GetFrame(unit)
            if frame then
                -- Update spec/class data.
                local id, name, description, icon, role, class = GetSpecializationInfoByID(specID)
                if class then
                    frame.class = class
                    frame.specID = id
                    frame.specIconTexture = icon
                    
                    -- Store in our tracking
                    lastKnownClass[i] = class
                    lastKnownSpec[i] = name
                    lastKnownSpecTexture[i] = icon
                    lastKnownSpecID[i] = id
                    seenValidClassData[i] = true
                    
                    -- Update the class icon slot, but respect showSpecInstead so we
                    -- don't stomp the spec texture that HandlePrepRoom already applied.
                    if frame.classIcon then
                        local db = AC.DB and AC.DB.profile
                        local showSpecInstead = db and db.classIcons and db.classIcons.showSpecInstead == true
                        if showSpecInstead and icon then
                            -- Place the spec texture on the class icon slot (same as HandlePrepRoom)
                            if not frame.classIcon.ccActive then
                                local iconTexture = frame.classIcon.classIcon or frame.classIcon.icon
                                if iconTexture and iconTexture.SetTexture then
                                    iconTexture:SetTexture(icon)
                                    if iconTexture.SetTexCoord then
                                        iconTexture:SetTexCoord(0.1, 0.9, 0.1, 0.9)
                                    end
                                end
                            end
                        elseif frame.classIcon.UpdateClassIcon then
                            frame.classIcon.UpdateClassIcon(class)
                        end
                    end
                end
                
                -- Show frame (alpha handled by ArenaFrameStealth.lua).
                self:ShowFrame(unit)
            end
        else
            -- CRITICAL FIX: Retry mechanism for Solo Shuffle race condition
            -- GetArenaOpponentSpec sometimes returns 0/nil immediately after GROUP_ROSTER_UPDATE
            -- before the new round's opponent data is available. Schedule a retry.
            C_Timer.After(0.5, function()
                local retrySpecID = GetArenaOpponentSpec and GetArenaOpponentSpec(i)
                if retrySpecID and retrySpecID > 0 then
                    local frame = self:GetFrame("arena" .. i)
                    if frame then
                        local id, name, description, icon, role, class = GetSpecializationInfoByID(retrySpecID)
                        if class then
                            frame.class = class
                            frame.specID = id
                            frame.specIconTexture = icon
                            
                            lastKnownClass[i] = class
                            lastKnownSpec[i] = name
                            lastKnownSpecTexture[i] = icon
                            lastKnownSpecID[i] = id
                            seenValidClassData[i] = true
                            
                            if frame.classIcon then
                                local db = AC.DB and AC.DB.profile
                                local showSpecInstead = db and db.classIcons and db.classIcons.showSpecInstead == true
                                if showSpecInstead and icon then
                                    if not frame.classIcon.ccActive then
                                        local iconTexture = frame.classIcon.classIcon or frame.classIcon.icon
                                        if iconTexture and iconTexture.SetTexture then
                                            iconTexture:SetTexture(icon)
                                            if iconTexture.SetTexCoord then
                                                iconTexture:SetTexCoord(0.1, 0.9, 0.1, 0.9)
                                            end
                                        end
                                    end
                                elseif frame.classIcon.UpdateClassIcon then
                                    frame.classIcon.UpdateClassIcon(class)
                                end
                            end
                            
                            self:ShowFrame("arena" .. i)
                        end
                    end
                end
            end)
        end
    end
    -- Don't explicitly hide extra frames.
    -- Only show frames with data and ignore the rest.
end

-- ============================================================================
-- PLAYER_ENTERING_WORLD
-- ============================================================================

function MismatchHandler:PLAYER_ENTERING_WORLD()
    -- Reset state when entering world
    ResetState()
end

-- ============================================================================
-- ZONE_CHANGED_NEW_AREA
-- ============================================================================

function MismatchHandler:ZONE_CHANGED_NEW_AREA()
    local _, instanceType = IsInInstance()
    
    if instanceType == "arena" then
        isInArenaMatch = true
        isInPrepRoom = false
    else
        -- Left arena
        ResetState()
    end
end

-- ============================================================================
-- FRAME MANAGEMENT
-- ============================================================================

function MismatchHandler:GetFrame(unit)
    -- Get frame from FrameManager.
    if not AC.FrameManager then
        return nil
    end
    
    local frames = AC.FrameManager:GetFrames()
    if not frames then
        return nil
    end
    
    -- Extract arena index.
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex then
        return nil
    end
    
    local frame = frames[arenaIndex]
    return frame
end

function MismatchHandler:ShowFrame(unit)
    local frame = self:GetFrame(unit)
    if not frame then
        return
    end
    
    -- Don't interfere with combat lockdown.
    if InCombatLockdown() then
        return
    end
    
    -- Show the frame.
    frame:Show()
end

function MismatchHandler:HideFrame(unit)
    local frame = self:GetFrame(unit)
    if not frame then
        return
    end
    
    -- Don't interfere with combat lockdown.
    if InCombatLockdown() then
        return
    end
    
    -- Hide the frame.
    frame:Hide()
    frame:SetAlpha(0)
end

function MismatchHandler:UpdateAlpha(unit, alpha)
    local frame = self:GetFrame(unit)
    if not frame then
        return
    end
    
    -- Set alpha.
    frame:SetAlpha(alpha)
end

-- ============================================================================
-- UNIT_NAME_UPDATE
-- ============================================================================

function MismatchHandler:UNIT_NAME_UPDATE(event, unit)
    -- Only care in arena.
    if not IsActiveBattlefieldArena() then
        return
    end
    
    -- Validate unit.
    if not IsValidUnit(unit) then
        return
    end
    
    -- Extract arena index.
    local arenaIndex = tonumber(unit:match("arena(%d)"))
    if not arenaIndex then
        return
    end
    
    -- Update class data tracking
    UpdateClassDataTracking(arenaIndex, unit)
    
    -- Show the frame.
    self:ShowFrame(unit)
end

-- ============================================================================
-- EVENT REGISTRATION
-- ============================================================================

function MismatchHandler:RegisterEvents()
    -- Register arena events.
    eventFrame:RegisterEvent("ARENA_OPPONENT_UPDATE")
    eventFrame:RegisterEvent("ARENA_PREP_OPPONENT_SPECIALIZATIONS")
    eventFrame:RegisterEvent("UNIT_NAME_UPDATE")
    eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
    eventFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
    
    -- Set event handler.
    eventFrame:SetScript("OnEvent", function(self, event, ...)
        if event == "ARENA_OPPONENT_UPDATE" then
            MismatchHandler:ARENA_OPPONENT_UPDATE(event, ...)
        elseif event == "ARENA_PREP_OPPONENT_SPECIALIZATIONS" then
            MismatchHandler:ARENA_PREP_OPPONENT_SPECIALIZATIONS()
        elseif event == "UNIT_NAME_UPDATE" then
            MismatchHandler:UNIT_NAME_UPDATE(event, ...)
        elseif event == "PLAYER_ENTERING_WORLD" then
            MismatchHandler:PLAYER_ENTERING_WORLD()
        elseif event == "ZONE_CHANGED_NEW_AREA" then
            MismatchHandler:ZONE_CHANGED_NEW_AREA()
        end
    end)
end

function MismatchHandler:UnregisterEvents()
    eventFrame:UnregisterEvent("ARENA_OPPONENT_UPDATE")
    eventFrame:UnregisterEvent("ARENA_PREP_OPPONENT_SPECIALIZATIONS")
    eventFrame:UnregisterEvent("UNIT_NAME_UPDATE")
    eventFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
    eventFrame:UnregisterEvent("ZONE_CHANGED_NEW_AREA")
end

-- ============================================================================
-- INITIALIZATION
-- ============================================================================

function MismatchHandler:Initialize()
    -- Register events.
    self:RegisterEvents()
end

-- Auto-initialize.
C_Timer.After(0.1, function()
    if AC.MismatchHandler then
        AC.MismatchHandler:Initialize()
    end
end)

-- ============================================================================
-- DEBUG COMMANDS
-- ============================================================================

SLASH_ACMISMATCH1 = "/acmismatch"
SlashCmdList["ACMISMATCH"] = function(msg)
    if msg == "debug" then
        print("ArenaCore MismatchHandler Debug:")
        print("- Events registered: ARENA_OPPONENT_UPDATE, ARENA_PREP_OPPONENT_SPECIALIZATIONS, UNIT_NAME_UPDATE, PLAYER_ENTERING_WORLD, ZONE_CHANGED_NEW_AREA")
        print("- Frame count: " .. (AC.FrameManager and #AC.FrameManager:GetFrames() or 0))
        print("- In arena: " .. tostring(IsActiveBattlefieldArena()))
        print("- In match: " .. tostring(isInArenaMatch))
        print("- In prep room: " .. tostring(isInPrepRoom))
        
        local numOpps = GetNumArenaOpponentSpecs and GetNumArenaOpponentSpecs() or 0
        print("- Opponent count: " .. numOpps)
        
        for i = 1, MAX_ARENA_ENEMIES do
            local unit = "arena" .. i
            local frame = MismatchHandler:GetFrame(unit)
            if frame then
                local knownClass = MismatchHandler:GetKnownClass(i)
                local knownSpec = MismatchHandler:GetKnownSpec(i)
                local knownSpecID = MismatchHandler:GetKnownSpecID(i)
                local hasValidData = MismatchHandler:HasValidClassData(i)
                print(string.format("  - %s: visible=%s, alpha=%.1f, class=%s, spec=%s (%d), validData=%s", 
                    unit, tostring(frame:IsVisible()), frame:GetAlpha(), 
                    frame.class or "nil", knownSpec or "nil", knownSpecID or 0, tostring(hasValidData)))
            end
        end
    elseif msg == "reset" then
        ResetState()
        print("ArenaCore MismatchHandler: State reset")
    elseif msg == "clear" then
        -- Clear all frames
        for i = 1, MAX_ARENA_ENEMIES do
            local unit = "arena" .. i
            local frame = MismatchHandler:GetFrame(unit)
            if frame then
                frame.class = nil
                frame.specID = nil
                frame.specIconTexture = nil
            end
        end
        ResetState()
        print("ArenaCore MismatchHandler: All data cleared")
    else
        print("ArenaCore MismatchHandler")
        print("Usage: /acmismatch debug - Show debug information")
        print("       /acmismatch reset - Reset internal state")
        print("       /acmismatch clear - Clear all frame data and reset state")
    end
end
