using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections.Generic;

/// <summary>
/// TeamMember - Defines which team a character belongs to and who controls them.
/// This is the foundation for the unified character control system.
/// </summary>
public class TeamMember : MonoBehaviour
{
    #region Static Registry
    
    /// <summary>
    /// Zero-allocation registry of all active TeamMembers.
    /// Use this instead of FindObjectsOfType.
    /// </summary>
    private static readonly List<TeamMember> _allMembers = new List<TeamMember>(16);
    public static IReadOnlyList<TeamMember> AllMembers => _allMembers;
    
    #endregion
    /// <summary>
    /// Team affiliation enum
    /// </summary>
    public enum Team
    {
        PlayerTeam,  // The player's team (human + AI teammates)
        EnemyTeam    // The opposing team (all AI)
    }
    
    /// <summary>
    /// Control type - determines who controls this character
    /// </summary>
    public enum ControlType
    {
        HumanControlled,  // Currently controlled by human player (reads input)
        PlayerAI,         // AI teammate on player's team
        EnemyAI           // AI enemy on opposing team
    }
    
    [Header("Character Data")]
    [SerializeField] private CharacterStats _characterStats;
    
    [Header("Team Settings")]
    [SerializeField] private Team _team = Team.PlayerTeam;
    
    [Header("Control Settings")]
    [SerializeField] private ControlType _controlType = ControlType.EnemyAI;
    
    /// <summary>
    /// The team this character belongs to
    /// </summary>
    public Team CurrentTeam
    {
        get => _team;
        set
        {
            _team = value;
            UpdateLayerAndTag();
        }
    }
    
    /// <summary>
    /// The control type for this character
    /// </summary>
    public ControlType CurrentControlType
    {
        get => _controlType;
        set => SetControlType(value);
    }
    
    /// <summary>
    /// Whether this character is currently controlled by the human player
    /// (Backward compatible property - derives from ControlType)
    /// </summary>
    public bool IsPlayerControlled
    {
        get => _controlType == ControlType.HumanControlled;
        set
        {
            if (value)
            {
                SetControlType(ControlType.HumanControlled);
            }
            else
            {
                // If turning off player control, make them AI based on team
                SetControlType(_team == Team.PlayerTeam ? ControlType.PlayerAI : ControlType.EnemyAI);
            }
        }
    }
    
    /// <summary>
    /// Event fired when control type changes (useful for camera, UI, input routing)
    /// </summary>
    public event System.Action<ControlType> OnControlTypeChanged;
    
    /// <summary>
    /// Event fired when control status changes (backward compatible)
    /// </summary>
    public event System.Action<bool> OnControlChanged;
    
    /// <summary>
    /// Event fired when team changes
    /// </summary>
    public event System.Action<Team> OnTeamChanged;
    
    private void Awake()
    {
        UpdateLayerAndTag();
    }
    
    private void OnEnable()
    {
        if (!_allMembers.Contains(this))
            _allMembers.Add(this);
    }
    
    private void OnDisable()
    {
        _allMembers.Remove(this);
    }
    
    // Track if we've done initial configuration
    private bool _hasBeenConfigured = false;
    
    /// <summary>
    /// Set the control type and configure all relevant components
    /// </summary>
    public void SetControlType(ControlType newType)
    {
        // Skip if already this type AND already configured
        if (_controlType == newType && _hasBeenConfigured) 
        {
            Debug.Log($"[TeamMember] {gameObject.name}: Already configured as {newType}, skipping");
            return;
        }
        
        ControlType oldType = _controlType;
        _controlType = newType;
        _hasBeenConfigured = true;
        
        Debug.Log($"[TeamMember] {gameObject.name}: Control changed from {oldType} to {newType}");
        
        // Configure components based on control type
        ConfigureControlComponents(newType);
        
        // Update layer and tag
        UpdateLayerAndTag();
        
        // Fire events
        OnControlTypeChanged?.Invoke(newType);
        OnControlChanged?.Invoke(newType == ControlType.HumanControlled);
    }
    
    /// <summary>
    /// Force reconfiguration even if type matches (useful for debugging)
    /// </summary>
    public void ForceReconfigure()
    {
        _hasBeenConfigured = false;
        SetControlType(_controlType);
    }
    
    /// <summary>
    /// Configure all components based on control type
    /// </summary>
    private void ConfigureControlComponents(ControlType controlType)
    {
        // Get components
        PlayerScript playerScript = GetComponent<PlayerScript>();
        PlayerInput playerInput = GetComponent<PlayerInput>();
        CharacterInputHandler inputHandler = GetComponent<CharacterInputHandler>();
        CharacterAIController aiController = GetComponent<CharacterAIController>();
        CharacterMovement charMovement = GetComponent<CharacterMovement>();
        UnityEngine.AI.NavMeshAgent navAgent = GetComponent<UnityEngine.AI.NavMeshAgent>();
        AudioListener audioListener = GetComponent<AudioListener>();
        
        switch (controlType)
        {
            case ControlType.HumanControlled:
                // Enable player input components
                if (playerScript != null) playerScript.enabled = true;
                if (playerInput != null) 
                {
                    playerInput.enabled = true;
                    try { playerInput.SwitchCurrentActionMap("Player Controls"); } catch { }
                }
                if (inputHandler != null) inputHandler.enabled = true;
                if (charMovement != null) charMovement.enabled = true;
                
                // Disable AI components
                if (aiController != null) aiController.enabled = false;
                if (navAgent != null) navAgent.enabled = false;
                
                // Enable audio listener only on player-controlled
                if (audioListener != null) audioListener.enabled = true;
                
                // Disable embedded cameras (main camera will follow)
                DisableEmbeddedCameras();
                
                Debug.Log($"[TeamMember] {gameObject.name}: Configured for HUMAN control");
                break;
                
            case ControlType.PlayerAI:
            case ControlType.EnemyAI:
                // Disable player input components
                if (playerScript != null) playerScript.enabled = false;
                if (playerInput != null) playerInput.enabled = false;
                if (inputHandler != null) inputHandler.enabled = false;
                if (charMovement != null) charMovement.enabled = false;  // AI uses NavMesh
                
                // Enable AI components
                if (aiController != null) aiController.enabled = true;
                if (navAgent != null)
                {
                    navAgent.enabled = true;
                    
                    // CRITICAL: Warp agent to NavMesh IMMEDIATELY after enabling.
                    // Without this, there is a 1-frame gap where the agent is enabled 
                    // but not on NavMesh, causing floating. CharacterAIController.Start()
                    // also warps, but it runs on the NEXT frame — too late.
                    UnityEngine.AI.NavMeshHit navHit;
                    if (UnityEngine.AI.NavMesh.SamplePosition(transform.position, out navHit, 20f, UnityEngine.AI.NavMesh.AllAreas))
                    {
                        navAgent.Warp(navHit.position);
                        Debug.Log($"[TeamMember] {gameObject.name}: Warped to NavMesh at Y={navHit.position.y}");
                    }
                    else
                    {
                        Debug.LogWarning($"[TeamMember] {gameObject.name}: No NavMesh found within 20m at {transform.position}!");
                    }
                }
                
                // Disable audio listener on AI
                if (audioListener != null) audioListener.enabled = false;
                
                // Disable embedded cameras
                DisableEmbeddedCameras();
                
                Debug.Log($"[TeamMember] {gameObject.name}: Configured for AI control ({controlType})");
                break;
        }
    }
    
    /// <summary>
    /// Disable all cameras embedded in this character prefab
    /// </summary>
    private void DisableEmbeddedCameras()
    {
        Camera[] cameras = GetComponentsInChildren<Camera>(true);
        foreach (Camera cam in cameras)
        {
            cam.enabled = false;
        }
        
        // Also disable Cinemachine components
        Cinemachine.CinemachineVirtualCameraBase[] vcams = GetComponentsInChildren<Cinemachine.CinemachineVirtualCameraBase>(true);
        foreach (var vcam in vcams)
        {
            vcam.enabled = false;
        }
    }

    
    /// <summary>
    /// Check if another TeamMember is an enemy
    /// </summary>
    public bool IsEnemy(TeamMember other)
    {
        if (other == null) return false;
        return other.CurrentTeam != this.CurrentTeam;
    }
    
    /// <summary>
    /// Check if another TeamMember is an ally
    /// </summary>
    public bool IsAlly(TeamMember other)
    {
        if (other == null) return false;
        return other.CurrentTeam == this.CurrentTeam;
    }
    
    /// <summary>
    /// Updates the layer and tag based on team
    /// </summary>
    private void UpdateLayerAndTag()
    {
        // Set appropriate tag (for compatibility with existing systems)
        if (_team == Team.PlayerTeam)
        {
            if (_controlType == ControlType.HumanControlled)
            {
                gameObject.tag = "Player";
                // Use Player One layer if available
                int playerLayer = LayerMask.NameToLayer("Player One");
                if (playerLayer >= 0) gameObject.layer = playerLayer;
            }
            else
            {
                // AI teammate - still on player team but different handling
                // Keep "Player" tag for combat targeting but mark as ally
                gameObject.tag = "Player";
                int playerLayer = LayerMask.NameToLayer("Player One");
                if (playerLayer >= 0) gameObject.layer = playerLayer;
            }
        }
        else // Enemy Team
        {
            gameObject.tag = "Enemy";
            int enemyLayer = LayerMask.NameToLayer("Player Two");
            if (enemyLayer >= 0) gameObject.layer = enemyLayer;
        }
        
        OnTeamChanged?.Invoke(_team);
    }
    
    /// <summary>
    /// Static helper to get all team members of a specific team
    /// </summary>
    public static TeamMember[] GetTeamMembers(Team team)
    {
        var result = new List<TeamMember>();
        for (int i = 0; i < _allMembers.Count; i++)
        {
            if (_allMembers[i] != null && _allMembers[i].CurrentTeam == team)
                result.Add(_allMembers[i]);
        }
        return result.ToArray();
    }
    
    /// <summary>
    /// Get the character stats for this character
    /// </summary>
    public CharacterStats GetCharacterStats()
    {
        return _characterStats;
    }
    
    /// <summary>
    /// Set the character stats for this character
    /// </summary>
    public void SetCharacterStats(CharacterStats stats)
    {
        _characterStats = stats;
    }
    
    /// <summary>
    /// Get the team this character belongs to
    /// </summary>
    public Team GetTeam()
    {
        return _team;
    }
    
    /// <summary>
    /// Static helper to get the currently player-controlled character
    /// </summary>
    public static TeamMember GetPlayerControlledMember()
    {
        for (int i = 0; i < _allMembers.Count; i++)
        {
            if (_allMembers[i] != null && _allMembers[i].IsPlayerControlled)
                return _allMembers[i];
        }
        return null;
    }
    
    /// <summary>
    /// Find the nearest enemy to this character
    /// </summary>
    public TeamMember FindNearestEnemy()
    {
        TeamMember nearest = null;
        float nearestDistance = float.MaxValue;
        
        for (int i = 0; i < _allMembers.Count; i++)
        {
            TeamMember other = _allMembers[i];
            if (other == null || other == this || other.CurrentTeam == _team) continue;
            
            float distance = Vector3.Distance(transform.position, other.transform.position);
            if (distance < nearestDistance)
            {
                nearestDistance = distance;
                nearest = other;
            }
        }
        
        return nearest;
    }
    
    /// <summary>
    /// Find the nearest ally to this character
    /// </summary>
    public TeamMember FindNearestAlly()
    {
        TeamMember nearest = null;
        float nearestDistance = float.MaxValue;
        
        for (int i = 0; i < _allMembers.Count; i++)
        {
            TeamMember other = _allMembers[i];
            if (other == null || other == this || other.CurrentTeam != _team) continue;
            
            float distance = Vector3.Distance(transform.position, other.transform.position);
            if (distance < nearestDistance)
            {
                nearestDistance = distance;
                nearest = other;
            }
        }
        
        return nearest;
    }
}
