UI/debug_overlay.razor
@using System;
@using System.Collections.Generic;
@using System.Linq;
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent

@if ( !_isOpen ) return;

<root>
    <div class="header-bar">
        <div class="title-group">
            <span class="main-title">Gladiator Debugger</span>
            <span class="sub-title">Cible : @(CurrentFighter?.GameObject.Name ?? "Aucune")</span>
        </div>
        <div class="target-nav">
            <button @onclick=@CyclePreviousTarget>◄</button>
            <button @onclick=@CycleNextTarget>►</button>
        </div>
    </div>

    @if ( CurrentFighter != null )
    {
        <div class="section-header">Diagnostic Animgraph</div>
        <DataRow Label="AnimParam [attack]" 
                 Value="@(AnimAttackParam ? "TRUE (EN COURS)" : "FALSE")" 
                 ValueColor="@(AnimAttackParam ? "#f59e0b" : "#ef4444")" />

        <DataRow Label="AnimParam [attack_index]" 
                 Value="@AnimAttackIndex.ToString()" />

        <DataRow Label="AnimParam [is_kicking]" 
                 Value="@(AnimKickParam ? "TRUE" : "FALSE")" 
                 ValueColor="@(AnimKickParam ? "#f59e0b" : "#ef4444")" />

        <div class="section-header">État Fighter</div>
        <DataRow Label="Points de Vie" 
                 Value="@($"{CurrentFighter.CurrentHealth:F0} / {CurrentFighter.MaxHealth:F0}")" 
                 ValueColor="@GetHealthColor()" />

        <DataRow Label="Statut" 
                 Value="@CurrentFighter.Status.ToString()" />

        <DataRow Label="Parade Active" 
                 Value="@(CurrentFighter.IsParrying ? "OUI" : "NON")" 
                 ValueColor="@(CurrentFighter.IsParrying ? "#22c55e" : "#94a3b8")" />

        @if ( Movement != null )
        {
            <div class="section-header">Locomotion</div>
            <DataRow Label="Au Sol (Grounded)" 
                     Value="@(Movement.Controller?.IsOnGround == true ? "OUI" : "NON")" />
            <DataRow Label="Vitesse" 
                     Value="@($"{GetHorizontalSpeed():F1} u/s")" />
        }
    }
</root>

@code {
    [Property] public string ToggleAction { get; set; } = "Score"; // Touche TAB

    private bool _isOpen = false;
    private List<Fighter> _allFighters = new();
    private int _targetIndex = 0;

    private Fighter CurrentFighter => (_allFighters.Count > 0 && _targetIndex >= 0 && _targetIndex < _allFighters.Count) 
        ? _allFighters[_targetIndex] 
        : null;

    private FighterMovement Movement => CurrentFighter?.Components.Get<FighterMovement>();
    private SkinnedModelRenderer MainRenderer => CurrentFighter?.Components.GetInChildren<SkinnedModelRenderer>();

    private bool AnimAttackParam => MainRenderer?.GetBool( "attack" ) ?? false;
    private int AnimAttackIndex => MainRenderer?.GetInt( "attack_index" ) ?? 0;
    private bool AnimKickParam => MainRenderer?.GetBool( "is_kicking" ) ?? false;

    protected override void OnUpdate()
    {
        // 1. Gestion de l'ouverture / fermeture via le gestionnaire d'état
        if ( Input.Pressed( ToggleAction ) )
        {
            SetOpenState( !_isOpen );
        }

        if ( !_isOpen ) return;

        // 2. Scan des combattants présents
        _allFighters = Scene.GetAllComponents<Fighter>().ToList();
        if ( _targetIndex >= _allFighters.Count )
            _targetIndex = Math.Max( 0, _allFighters.Count - 1 );
    }

    private void SetOpenState( bool open )
    {
        _isOpen = open;

        // Enregistrement auprès du gestionnaire central
        if ( _isOpen )
            UIStateManager.RequestFocus( this );
        else
            UIStateManager.ReleaseFocus( this );
    }

    protected override void OnDestroy()
    {
        // Sécurité : si le composant est détruit pendant qu'il est ouvert, il libère son focus
        if ( _isOpen )
            UIStateManager.ReleaseFocus( this );
    }

    private void CycleNextTarget()
    {
        if ( _allFighters.Count == 0 ) return;
        _targetIndex = (_targetIndex + 1) % _allFighters.Count;
    }

    private void CyclePreviousTarget()
    {
        if ( _allFighters.Count == 0 ) return;
        _targetIndex = (_targetIndex - 1 + _allFighters.Count) % _allFighters.Count;
    }

    private float GetHorizontalSpeed() => Movement?.Controller?.Velocity.WithZ( 0 ).Length ?? 0f;

    private string GetHealthColor()
    {
        if ( CurrentFighter == null ) return "#ffffff";
        var ratio = CurrentFighter.CurrentHealth / CurrentFighter.MaxHealth;
        return ratio > 0.5f ? "#22c55e" : (ratio > 0.25f ? "#f59e0b" : "#ef4444");
    }

    protected override int BuildHash()
    {
        return HashCode.Combine( 
            _isOpen,
            AnimAttackParam,
            AnimKickParam,
            CurrentFighter?.CurrentHealth,
            CurrentFighter?.Status,
            _targetIndex 
        );
    }
}