using System.Collections;
using Movement;
using Pausable;
using UnityEngine;
using UnityEngine.Events;

namespace AI
{
    public class ShootingEnemyAI : MonoBehaviour, IPausable
    {
        [SerializeField] protected float focusingTime = .5f;
        [SerializeField] protected float attacksPerSecond = 1f;
        [SerializeField] protected float attackRange = 5f;
        [SerializeField] protected float timeForProjectileToHit = .25f;

        protected float cooldownPerShoot = 0f;
        protected float remainingCooldown = 0f;

        protected float attackRangeSquared = 5f;
        protected Transform target = null;
        protected bool isShooting = false;
        protected IMovement movement = null;

        protected bool canShoot => (target.transform.position - transform.position).sqrMagnitude < attackRangeSquared && target != null;

        public UnityEvent OnShoot { get; protected set; } = null;

        #region IPausable
        public bool IsPaused { get; protected set; } = false;
        public virtual void Pause() => IsPaused = true;
        public virtual void Resume() => IsPaused = false;
        #endregion

        protected virtual void Awake()
        {
            cooldownPerShoot = 1f / attacksPerSecond;
            attackRangeSquared = attackRange * attackRange;
            OnShoot = new UnityEvent();
            UpdateTarget(FindObjectOfType<Player.PlayerController>()?.transform);
        }

        protected virtual void Start()
            => movement = transform.GetComponentInParent<IMovement>();

        protected virtual void Update()
        {
            remainingCooldown -= Time.deltaTime;

            if (canShoot && !isShooting)
                StartCoroutine(ShootCoroutine());
        }

        protected IEnumerator ShootCoroutine()
        {
            isShooting = true;
            float movementBaseSpeed = movement.BaseSpeed;
            movement.BaseSpeed = 0f;

            yield return new WaitForSeconds(focusingTime);

            while (canShoot)
            {
                if (remainingCooldown <= 0f)
                    Shoot();
                yield return null;
            }
            movement.BaseSpeed = movementBaseSpeed;
            isShooting = false;
        }

        protected virtual void Shoot()
        {
            Projectile projectile = ProjectilePool.Instance.Get();
            projectile.transform.position = transform.position;

            Vector3 velocity = GetVelocityForProjectile(timeForProjectileToHit);

            RaycastHit2D raycastHit2D = Physics2D.Raycast(transform.position, target.position - transform.position, attackRange);
            if (raycastHit2D.transform != target)
                velocity = GetVelocityForProjectile(timeForProjectileToHit * 2);

            projectile.SetVelocity(velocity);

            remainingCooldown = cooldownPerShoot;
            OnShoot?.Invoke();
        }

        protected Vector3 GetVelocityForProjectile(float time)
        {
            Vector3 vector3 = target.position - transform.position;
            vector3.z = 0f;
            vector3.y -= (Physics2D.gravity.y / 2) * (time * time);
            vector3 /= time;
            return vector3;
        }

        public virtual void UpdateTarget(Transform transform) => target = transform;

        protected virtual void OnDrawGizmosSelected()
        {
            Gizmos.color = Color.yellow;
            Gizmos.DrawWireSphere(transform.position, 0.125f);
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(transform.position, attackRange);
        }
    }
}