using System;
using DG.Tweening;
using Sources.Signals;
using UniRx;
using UnityEngine;
using Zenject;

namespace Sources.Enemies
{
    public class AggrableEnemy : MonoBehaviour, IDamageable
    {
        [SerializeField] private string _shownName;
        [SerializeField] private EnemyHealthBar _healthBar;
        [SerializeField] private Transform _visual;
        [SerializeField] private GameObject _destroyEffect;
    
        [Header("Base Balance")]
        [SerializeField] private float _experience = 10f;
        [SerializeField] private float _aggroRadius = 5f;
        [SerializeField] private float _maxHP = 100;
        [SerializeField] private float _hp = 100;
        [SerializeField] private float _deagroMultiplier = 1.5f;
 
        public Player Target { get; set; }
        public Action<AggrableEnemy> OnDied;
        public float ExpReward => _experience;

        [Inject] private SignalBus _signalBus;
    
        protected bool IsAlive;
        protected Transform Visual => _visual;
    
        private float _deagroRadius => _aggroRadius * _deagroMultiplier;
        private bool _isAttackedByPlayer;
        private IDisposable _attackedTimer;
        private float _healthBarLength;
        private float _prevFrameHp;
        private Collider[] _nearbyObjects;

        private void Reset()
        {
            _healthBar = GetComponentInChildren<EnemyHealthBar>();
        }

        protected virtual void OnUpdate()
        {
            UpdateTarget();
            UpdateHealthBar();
        }

        private void UpdateHealthBar()
        {
            if (!_healthBar)
                return;

            if (Math.Abs(_prevFrameHp - _hp) > 0.001f)
            {
                _healthBar.SetRatio(_hp / _maxHP);
            }

            _prevFrameHp = _hp;
        }

        protected virtual void OnStart()
        {
            _healthBar.SetName(string.IsNullOrEmpty(_shownName) ? name : _shownName);
            _healthBar.SetRatio(_hp / _maxHP);
            _prevFrameHp = _hp;
            
            IsAlive = true;
            _destroyEffect.SetActive(false);
        }

        private void UpdateTarget()
        {
            if (!IsAlive)
                return;
        
            if (!_isAttackedByPlayer && Target)
            {
                var distance = Vector2.Distance(Target.transform.position, transform.position);
                if (distance > _deagroRadius)
                    Target = null;

                return;
            }
        
            Collider[] nearbyObjects = Physics.OverlapSphere(transform.position, _aggroRadius, LayerMask.GetMask("Player"));
            if (nearbyObjects == null)
                return;

            foreach (var nearbyObject in nearbyObjects)
            {
                var player = nearbyObject.GetComponentInParent<Player>();
                if (player)
                {
                    Target = player;
                    return;
                }
            }
        }

        public virtual void TakeDamage(float damage, Transform damageDealer = null, DamageType type = DamageType.Melee)
        {
            if (_hp <= 0 || !IsAlive)
                return;
        
            _hp -= damage;

            if (damageDealer != null)
            {
                var player = damageDealer.GetComponent<Player>();
                if (player)
                {
                    Target = player;
                    _isAttackedByPlayer = true; 
                    _attackedTimer?.Dispose();
                    _attackedTimer = Observable.Timer(TimeSpan.FromSeconds(5f)).Subscribe(_ => _isAttackedByPlayer = false);
                }
            }

            if (_hp <= 0)
            {
                _hp = 0;
                Die();
            }
        }

        protected virtual void Die()
        {
            OnDied?.Invoke(this);
            _signalBus.Fire(new EnemyDiedSignal {Enemy = this});

            IsAlive = false;
            _destroyEffect.SetActive(true);
            _visual.DOScale(0, 0.5f).OnComplete(() => _visual.gameObject.SetActive(false));
            Target = null;
        
            Destroy(gameObject, 5f);
        }
    
        private void Start() => OnStart();

        private void Update() => OnUpdate();

        private void OnDrawGizmos()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(transform.position, _aggroRadius);        
        
            Gizmos.color = Color.green;
            Gizmos.DrawWireSphere(transform.position, _deagroRadius);
        }
    }
}