using System;
using DG.Tweening;
using UniRx;
using UnityEngine;
using UnityEngine.VFX;

namespace Sources.Enemies
{
    public class Poring : AggrableEnemy
    {
        [SerializeField] private Rigidbody _rigidbody;
        [SerializeField] private VisualEffect _attackEffect;
        
        [Header("Balance")]
        [SerializeField] private float _attackRadius = 1;
        [SerializeField] private float _jumpDistance = 3;
        [SerializeField] private float _damage = 5f;
        
        private Vector3 _initialPosition;
        private bool _isJumping;
        private bool _isAttacking;
        private CompositeDisposable _disposables = new CompositeDisposable();
        
        protected override void OnUpdate()
        {
            base.OnUpdate();

            if (!IsAlive || _isJumping || _isAttacking)
                return;

            if (TryAttack())
                return;
            
            UpdateMoving();
        }
        
        private void Start()
        {
            base.OnStart();

            if (Physics.Raycast(transform.position, Vector3.down, out var hit, 100, LayerMask.GetMask("Ground")))
            {
                _initialPosition = hit.point;
            }
            else
            {
                _initialPosition = transform.position;
            }
        }
        
        private bool TryAttack()
        {
            if (!IsAttackPossible())
                return false;

            _isAttacking = true;

            var yPos = _rigidbody.position.y;
            _rigidbody.DOMoveY(yPos + 1, 1)
                .OnComplete(() => _rigidbody.DOMoveY(yPos, 1).SetEase(Ease.OutCirc))
                .SetEase(Ease.OutCirc);
            Visual
                .DORotate(new Vector3(0, 3600, 0), 2f, RotateMode.LocalAxisAdd)
                .SetEase(Ease.InOutSine)
                .OnComplete(() => _isAttacking = false);
            
            Observable.Timer(TimeSpan.FromSeconds(1)).Subscribe(_ =>
            {
                _attackEffect.Play();
                
                if (!IsAttackPossible())
                    return;
                
                Target.TakeDamage(_damage, transform, DamageType.Melee);
            }).AddTo(_disposables);

            return true;
        }

        private bool IsAttackPossible()
        {
            if (!IsAlive || !Target)
                return false;
            
            if (Vector3.Distance(transform.position, Target.transform.position) > _attackRadius)
                return false;

            return true;
        }
        
        private void UpdateMoving()
        {
            var position = transform.position;
            if (!Target && Vector3.Distance(position, _initialPosition) < 0.5f)
                return;
            
            Vector3 targetPoint;
            if (Target)
            {
                if (Vector3.Distance(transform.position, Target.transform.position) < _attackRadius)
                {
                    targetPoint = transform.position;
                }
                else
                {
                    var directionToTarget = Target.transform.position - position;
                    directionToTarget = directionToTarget.normalized * _jumpDistance;
                    targetPoint = position + directionToTarget;
                }
            }
            else
            {
                targetPoint = _initialPosition;
            }

            _isJumping = true;
            _rigidbody.DOJump(targetPoint, 1, 1, 1);
            Observable.Timer(TimeSpan.FromSeconds(2)).Subscribe(_ => _isJumping = false);
        }

        private void OnDestroy()
        {
            _disposables?.Dispose();
        }
    }
}