using System.Collections.Generic;
using System.Numerics;
using UnityEngine;
using Vector2 = UnityEngine.Vector2;
using Vector3 = UnityEngine.Vector3;

public enum EnemyRole
{
    Idle,
    Approach,
    Attack,
    Stunned,
    Scripted
}
[DefaultExecutionOrder(-100)]  // ensure this runs early
public class EnemyCoordinator : MonoBehaviour
{
    public static EnemyCoordinator Instance { get; private set;} // singleton instance

    [Header("References")] 
    [SerializeField] private Transform playerTransform;

    [Header("Attack Management")] 
    [SerializeField] private int maxAttackers = 2;
    [SerializeField] private float ringDistance = 1.5f;
    [SerializeField] private float idleDistance = 6f;
    [SerializeField] private float minYLane = -6f;
    [SerializeField] private float maxYLane = 0f;
    [Header("the List of Controller Enemies")]
    private readonly List<EnemyBrain> _enemies = new();
    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
           
            Destroy(gameObject);
            return;
        }
        
        Instance = this;
        FindPlayerTransform();
    }
    
    void FindPlayerTransform()
    {
        if (playerTransform == null)
        {
            GameObject playerObject = GameObject.FindGameObjectWithTag("Player");
            if(playerObject != null) playerTransform = playerObject.transform;

        }
    }
    
    void Update()
    {
        if (playerTransform == null) return;
        UpdateEnemyAssignments();
    }
    
    public void RegisterEnemy(EnemyBrain brain)
    {
        if (!_enemies.Contains(brain))
        {
            Debug.Log("enemy registered");
            _enemies.Add(brain);
        }
    }
    
    public void DeregisterEnemy(EnemyBrain brain)
    {
        if (_enemies.Contains(brain))
        {
            _enemies.Remove(brain);
            Debug.Log("enemy deregistered");
        }
    }
    
    private void UpdateEnemyAssignments()
    {
        _enemies.RemoveAll(e => e ==null); // remove the dead enemies from the list

        if (_enemies.Count == 0) return; //if there are no enemies, don't do anything
        
        //Sort the enemies according to their distances
        _enemies.Sort((a, b) =>
        {
            Vector2 enemy_a_pos = new Vector2(a.transform.position.x, a.transform.position.y);
            Vector2 enemy_b_pos = new Vector2(b.transform.position.x, b.transform.position.y);
            Vector2 player_pos = new Vector2(playerTransform.position.x, playerTransform.position.y);

            float distance_to_enemy_a = (enemy_a_pos - player_pos).sqrMagnitude;
            float distance_to_enemy_b = (enemy_b_pos - player_pos).sqrMagnitude;

            return distance_to_enemy_a.CompareTo(distance_to_enemy_b);
        });

        int AttackersAssigned = 0;
        int slotIndex = 0;
        int totalSlots = _enemies.Count;

        foreach (EnemyBrain enemy in _enemies)
        {
            if(enemy == null) continue;
            
            Vector2 slotPos = ComputeSlotPosition(slotIndex, totalSlots);
            
            enemy.SetDesiredSlot(slotPos);
            
            float distanceToThePlayer = Vector2.Distance(
                new Vector2(enemy.transform.position.x, enemy.transform.position.y),
                new Vector2(playerTransform.position.x, playerTransform.position.y));
            
            //Decide Role
            if (AttackersAssigned < maxAttackers && distanceToThePlayer <= enemy.GetEngageDistance())
            {
                enemy.SetRole(EnemyRole.Attack);
                AttackersAssigned++;
            }
            else if(distanceToThePlayer < idleDistance)
            {
                enemy.SetRole(EnemyRole.Approach);
            }
            else
            {
                enemy.SetRole(EnemyRole.Idle);
            }

            slotIndex++;
        }
    }

    private Vector3 ComputeSlotPosition(int slotIndex, int totalSlots)
    {
        if(playerTransform == null) return Vector3.zero;
        //Arrange Enemies evenly on a semi circle

        float stepAngle = Mathf.PI / Mathf.Max(1, totalSlots);
        float angle = -Mathf.PI / 2f + stepAngle * slotIndex;

        float offsetX = Mathf.Cos(angle) * ringDistance;
        float targetY = Mathf.Clamp(playerTransform.position.y, minYLane, maxYLane);

        return new Vector2(playerTransform.position.x + offsetX, targetY);
    }

    public Transform GetPlayer() => playerTransform;
}
