using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.AI;
using Random = UnityEngine.Random;

[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(NPC))]
public class MovementController : MonoBehaviour
{
    private Transform _firePointTransform;
    public int PointStartID { get; set; }
    public int PointEndID { get; private set; }
    public float FlagDistance { get; private set; }
    private const float UpdateFlagPositionDelay = 5;
    private const float UpdateReachedDestinationDelay = 5;

    [SerializeField] private NavMeshAgent navMeshAgent;
    [SerializeField] private GameObject flag;
    public float DistanceToGo { get; private set; }
    public float RemainingDistance => navMeshAgent.remainingDistance;
    public Vector3 Velocity => navMeshAgent.velocity;
    private Dictionary<int, NavPoint> _idNavPointDict;
    private NPC _myNpc;
    private Settings _settings;

    private void Awake()
    {
        _settings = SettingsReader.Instance.GetSettings;
        navMeshAgent.speed = _settings.MovementSpeed;
        _idNavPointDict = MapManager.Instance.IDToNavPoint;
        _myNpc = GetComponent<NPC>();
        _firePointTransform = transform.GetChild(0);
        InvokeRepeating(nameof(UpdateFlagPosition), 0, UpdateFlagPositionDelay);
        InvokeRepeating(nameof(ReachedDestination), 0, UpdateReachedDestinationDelay);
    }

    private void OnDestroy()
    {
        CancelInvoke(nameof(UpdateFlagPosition));
        CancelInvoke(nameof(ReachedDestination));
    }

    private void Update()
    {
        var thisPoint = _idNavPointDict[PointStartID];
        if (ReachedDestination())
        {
            _myNpc.ChangePointState(thisPoint.navType == NavPointType.Cover
                ? NpcEnumState.InCover
                : NpcEnumState.InDirectPoint);
            thisPoint.isOcuppied = true;
        }
        else
        {
            thisPoint.isOcuppied = false;
            _idNavPointDict[PointEndID].isOcuppied = false;
           _myNpc.ChangeBodyState(NpcBodyState.Standing); 
            _myNpc.ChangePointState(NpcEnumState.InRunning);
        }

        if (_myNpc.NpcBodyState.State == NpcBodyState.Crouching)
            navMeshAgent.speed = _settings.MovementSpeed / 2;
        else
            navMeshAgent.speed = _settings.MovementSpeed;

    }

    private void UpdateFlagPosition()
    {
        FlagDistance = (flag.transform.position - gameObject.transform.position).magnitude;
    }
    
    [Obsolete]
    public void MoveToRandomPoint()
    {
        GoToNextNavPoint(MapManager.Instance.NavPoints[Random.Range(0, MapManager.Instance.NavPoints.Count)]);
    }

    public List<NavPoint> GetPointsCandidate()
    {
        return MapManager.Instance.NavPoints
            .Where(point => 
            (transform.position - point.Position).magnitude < _settings.MovementDistance && point.isOcuppied == false)
            .ToList();
    }

    public void GoToNextNavPoint(NavPoint destination)
    {
        if (navMeshAgent.isStopped == true) navMeshAgent.isStopped = false;
        PointStartID = PointEndID;
        PointEndID = destination.PointId;
        navMeshAgent.SetDestination(destination.Position);
        DistanceToGo = navMeshAgent.remainingDistance;      
    }

    public void ReturnToStartPoint()
    {
        if (navMeshAgent.isStopped == true) navMeshAgent.isStopped = false;
        navMeshAgent.SetDestination(_idNavPointDict[PointStartID].Position);
        (PointEndID, PointStartID) = (PointStartID, PointEndID);
    }

    public void StopOnPath()
    {
        navMeshAgent.isStopped = true;
    }

    public bool ReachedDestination()
    {
        if (navMeshAgent.remainingDistance < 0.1f)
        {
            PointStartID = PointEndID;
            return true;
        }
        return false;
    }
}
