using Characters.Enemy.States;
using Koptilnya.StateMachine;
using Mirror;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Animations.Rigging;
using UnityEngine.Serialization;

namespace Characters.Enemy
{
    public class Enemy : Pawn
    {
        public NavMeshAgent agent;
        public Animator animator;
        public Rig aimRig;
        public AudioSource audioSource;
        public AudioSource footstepAudioSource;
        public ParticleSystem hitVFX;
        public Transform aimTransform;
        public float aggressionDistance = 5f;

        [SyncVar] public EnemyState state = EnemyState.Idle;
        [SyncVar, HideInInspector] public float aimRigWeight;

        public readonly StateMachine<EnemyState> stateMachine = new StateMachine<EnemyState>();

        [FormerlySerializedAs("target")] public Transform targetTransform;
        public NetworkAnimator networkAnimator;

        public bool HasTarget => targetTransform != null;
        public float DistanceToTarget => Vector3.Distance(targetTransform.transform.position, transform.position);
        
        private CustomNetworkManager _networkManager;
        private NetworkGameManager _networkGameManager;
        private float _targetAimRigWeight;
        private float _stepCycle;
        private AudioClip _footstepClip;
        private Vector3 _footstepPrevPosition;
        private NavMeshPath _navMeshPath;

        private const float StepDelay = 0.12f;
        private const float RunSpeed = 4.974f;
        private static readonly int SpeedAnimHash = Animator.StringToHash("speed");

        void Start()
        {
            _networkManager = NetworkManager.singleton.GetComponent<CustomNetworkManager>();
            _networkGameManager = NetworkGameManager.singleton;
            _footstepClip = Resources.Load<AudioClip>("Audio/EnemySounds/Steps/monsterStep1");
            _navMeshPath = new();
            
            networkAnimator = GetComponent<NetworkAnimator>();
            
            stateMachine.Add(new IdleState(this));
            stateMachine.Add(new PatrolState(this));
            stateMachine.Add(new ChaseState(this));
            stateMachine.Add(new StunnedState(this));
            stateMachine.Add(new AttackState(this));
            
            stateMachine.SetCurrentState(state);
        }
        
        void Update()
        {
            Debug.DrawLine(transform.position, transform.forward * aggressionDistance, Color.green, 0f);
            
            if (isClient)
            {
                Footsteps();
            }
            
            if (isServer)
            {
                agent.speed = _speedMul * RunSpeed;
                animator.SetFloat(SpeedAnimHash, agent.velocity.magnitude);
            }
            
            aimRigWeight = isServer ? Mathf.Lerp(aimRigWeight, _targetAimRigWeight, Time.deltaTime) : aimRigWeight;
            aimRig.weight = aimRigWeight;
            
            stateMachine.Update();
        }

        void FixedUpdate()
        {
            stateMachine.FixedUpdate();
        }

        public void SetAimRigWeight(float value)
        {
            _targetAimRigWeight = value;
        }
        
        void Footsteps()
        {
            float velocity = ((_footstepPrevPosition - transform.position) / Time.deltaTime).magnitude;
            float clampedVelocity = Mathf.Clamp(RunSpeed / velocity + 1, 1, RunSpeed);

            if (Time.time > _stepCycle && velocity > 0)
            {
                footstepAudioSource.pitch = 1f + Random.Range(-0.1f, 0.1f);
                footstepAudioSource.PlayOneShot(_footstepClip);

                _stepCycle = Time.time + (clampedVelocity * StepDelay);
            }

            _footstepPrevPosition = transform.position;
        }

        [ClientRpc]
        public void RpcKillTarget(Transform target)
        {
            Interactions interactions = target.GetComponent<Interactions>();

            if (interactions != null) interactions.DropProp();

            target.GetComponent<Pawn>().Die();
        }

        [ServerCallback]
        public override void TakeDamage()
        {
            base.TakeDamage();
            
            ChangeState(EnemyState.Stunned);
        }

        [Server]
        public void ChangeState(EnemyState newState)
        {
            state = newState;
            
            RpcChangeState(newState);
        }

        [ClientRpc]
        private void RpcChangeState(EnemyState newState)
        {
            stateMachine.SetCurrentState(newState);
        }

        public Transform GetClosestTarget()
        {
            if (!agent.isOnNavMesh) return null;
            
            var alivePlayers = _networkManager.alive;
            
            if (HasTarget && alivePlayers.Count == 1 && alivePlayers[0].transform == targetTransform)
            {
                return targetTransform;
            }
            
            Transform closestTarget = null;
            
            var sortDistance = float.MaxValue;
            foreach (var player in _networkManager.alive)
            {
                var targetPosition = player.transform.position;
                var distance = Vector3.Distance(targetPosition, transform.position);
                var canReachTarget = agent.CalculatePath(targetPosition, _navMeshPath) && _navMeshPath.status == NavMeshPathStatus.PathComplete;
                
                if (distance <= aggressionDistance && distance < sortDistance && canReachTarget)
                {
                    sortDistance = distance;
                    closestTarget = player.transform;
                }
            }

            return closestTarget;
        }
    }
}