using System;
using System.Collections.Generic;
using Animators.Leonid_Animator.Player;
using Unity.MLAgents;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    private static GameManager _instance;
    public static GameManager Instance => _instance;

    public static SimpleMultiAgentGroup _defendersTeam = new SimpleMultiAgentGroup();
    public static SimpleMultiAgentGroup _attackersTeam = new SimpleMultiAgentGroup();

    private void Awake()
    {
        if (_instance is null)
            _instance = this;
        else
        {
            Destroy(gameObject);
            Debug.LogError("Only 1 Instance");
        }
    }

    private void Start()
    {
        Academy.Instance.OnEnvironmentReset += ResetScene;
        GlobalEventManager.OnCaptureFlag += FlagCaptured;
        GlobalEventManager.OnTimeLeft += TimeOut;
    }

    private static SimpleMultiAgentGroup GetAgentList(Team team)
    {
        if (team == Team.Attackers)
            return _attackersTeam;
        else
            return _defendersTeam;
    }

    public static bool IsCloserToEnemyThanToNextNavPoint(NavPoint navPoint, Vector3 currentTransform, Team oppositeTeam)
    {
        var agentGroup = GetAgentList(oppositeTeam);      

        var distToNavPoint = (currentTransform - navPoint.Position).magnitude;
        foreach (var agent in agentGroup.GetRegisteredAgents())
            if (distToNavPoint > (currentTransform - agent.transform.position).magnitude)
                return true;
        if ((SettingsReader.Instance.GetSettings.HasHumanAttacker == true && oppositeTeam == Team.Attackers) ||
            (SettingsReader.Instance.GetSettings.HasHumanDefender == true && oppositeTeam == Team.Defenders))
        {           
            if (distToNavPoint > (currentTransform - CharacterFactory.Instance.Player.transform.position).magnitude)
                return true;
        }
        return false;
    }

    public static bool IsEnemyNearby(Vector3 currentTransform, Team oppositeTeam)
    {
        var agentGroup = GetAgentList(oppositeTeam);

        foreach (var agent in agentGroup.GetRegisteredAgents())
            if ((currentTransform - agent.transform.position).magnitude < SettingsReader.Instance.GetSettings.ViewDistance)
                return true;
        if ((SettingsReader.Instance.GetSettings.HasHumanAttacker == true && oppositeTeam == Team.Attackers) ||
            (SettingsReader.Instance.GetSettings.HasHumanDefender == true && oppositeTeam == Team.Defenders))
        {
            if ((currentTransform - CharacterFactory.Instance.Player.transform.position).magnitude < SettingsReader.Instance.GetSettings.ViewDistance)
                return true;
        }
        return false;
    }

    public static bool IsCloserToFlagFromNextNavPoint(NavPoint navPoint, Vector3 currentTransform)
        => navPoint.FlagDistance < (currentTransform - GameObject.FindGameObjectWithTag("Flag").transform.position).magnitude;

    public static bool IsHaveSeenByEnemy(Team oppositeTeam, Vector3 position)
    {
        return GetVisibleEnemies(oppositeTeam, position).Count > 0 ? true : false;
    }

    public static List<GameObject> GetVisibleEnemies(Team oppositeTeam, Vector3 position)
    {
        var agentGroup = GetAgentList(oppositeTeam);
        var enemies = new List<GameObject>();
        
        RaycastHit rayHit = new RaycastHit();
        foreach (var agent in agentGroup.GetRegisteredAgents() )
        {
            var npc = agent as NPC;
            if (npc != null && Physics.Raycast(position,
                    (npc.NpcBodyState.GetPointToHit(npc.gameObject) - position).normalized,
                    out rayHit,
                    SettingsReader.Instance.GetSettings.ViewDistance))
            {
                if (rayHit.collider.gameObject.GetComponent<ICharacter>() != null)
                    enemies.Add(rayHit.collider.gameObject);
            }
        }
        if ((SettingsReader.Instance.GetSettings.HasHumanAttacker == true && oppositeTeam == Team.Attackers) ||
            (SettingsReader.Instance.GetSettings.HasHumanDefender == true && oppositeTeam == Team.Defenders))
        {
            var player = CharacterFactory.Instance.Player;
            if (Physics.Raycast(position,
                    (player.GetComponent<CharacterLocomotion>().GetMeshCenter() - position).normalized,
                    out rayHit,
                    SettingsReader.Instance.GetSettings.ViewDistance))
            {
                if (rayHit.collider.gameObject.GetComponent<ICharacter>() != null)
                    enemies.Add(rayHit.collider.gameObject);
            }
        }
        return enemies;
    }

    private void FlagCaptured(Team team)
    {
        switch (team)
        {
            case Team.Attackers:
                Debug.Log("Attackers Win");
                _attackersTeam.AddGroupReward(1f);
                _defendersTeam.AddGroupReward(-1f);
                ResetScene();
                break;
            case Team.Defenders:
                Debug.Log("Defenders Win");
                _defendersTeam.AddGroupReward(1f);
                _attackersTeam.AddGroupReward(-1f);
                ResetScene();
                break;
        }
    }

    private void TimeOut()
    {
        _attackersTeam.AddGroupReward(-1f);
        _defendersTeam.AddGroupReward(-1f);
        ResetScene();
    }

    private void OnDestroy()
    {
        GlobalEventManager.OnCaptureFlag -= FlagCaptured;
        GlobalEventManager.OnTimeLeft -= TimeOut;
    }

    public static event Action OnResetScene;
    private void ResetScene()
    {
        Debug.Log("Scene Reset");
        OnResetScene?.Invoke();
    }
}
