using System;
using Cybe;
using DG.Tweening;
using UniRx;
using UnityEngine;
using Zenject;

namespace Sources.Enemies
{
    public class Bulldozer : AggrableEnemy
    {
        [SerializeField] private Rigidbody _rigidbody;
        [SerializeField] private GameObject _attackEffect;
        
        [Header("Balance")]
        [SerializeField] private float _attackRadius = 10f;
        [SerializeField] private float _damage = 50f;
        [SerializeField] private float _usualMoveSpeed = 5f;
        [SerializeField] private float _punchForce = 5000f;

        [Inject] private AudioManager _audioManager;
        
        private Vector3 _initialPosition;
        private Vector3 _targetVelocity;
        private Vector3 _prevTargetPosition;
        private bool _isAttacking;
        private bool _isDamageCooldown;
        private bool _isTeleportingBack;
        private CompositeDisposable _disposables = new CompositeDisposable();
        
        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;
            }
            
            transform.rotation = Quaternion.identity;
        }
        
        protected override void OnUpdate()
        {
            base.OnUpdate();

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

            ProcessReturningToGround();

            if (Target)
            {
                var curTargetPosition = Target.transform.position;
                _targetVelocity = curTargetPosition - _prevTargetPosition;
                _prevTargetPosition = curTargetPosition;
            }

            if (TryAttack())
                return;
            
            UpdateMoving();
        }

        private void ProcessReturningToGround()
        {
            if (transform.position.y > -2)
                return;

            _isTeleportingBack = true;

            transform.DOScale(0, 1f).OnComplete(() =>
            {
                transform.position = _initialPosition;
                transform.DOScale(1, 1f).OnComplete(() => _isTeleportingBack = false);
            });
        }

        private bool TryAttack()
        {
            if (!IsAttackPossible())
                return false;

            _isAttacking = true;
            
            var direction = Target.transform.position - _rigidbody.position;
            var distance = direction.magnitude;

            var rotation = Quaternion.LookRotation(direction, Vector3.up);
            transform.rotation = rotation;

            var chargeDuration = distance / 20f;
            var target = Target.transform.position + _targetVelocity * (chargeDuration + 0.5f);
            
            _audioManager.PlayFx(SoundType.BulldozerCharging);
            
            Observable.Timer(TimeSpan.FromSeconds(0.6f)).Subscribe(_ =>
                {
                    _audioManager.PlayFx(SoundType.BulldozerAttack);
                    _rigidbody.DOMove(target, chargeDuration).OnComplete(() =>
                        {
                            Observable.Timer(TimeSpan.FromSeconds(1f)).Subscribe(__ => _isAttacking = false).AddTo(_disposables);
                        });
                }).AddTo(_disposables);

            return true;
        }
        
        private void UpdateMoving()
        {
            var position = transform.position;
            if (!Target && Vector3.Distance(position, _initialPosition) < 1f)
                return;

            var targetPoint = Target ? Target.transform.position : _initialPosition;
            var direction = targetPoint - _rigidbody.position;
            
            var rotation = Quaternion.LookRotation(direction, Vector3.up);
            rotation.eulerAngles = new Vector3(0, rotation.eulerAngles.y, 0);
            transform.rotation = rotation;
            
            _rigidbody.MovePosition(_rigidbody.position + Time.deltaTime * _usualMoveSpeed * direction.normalized);
        }

        private void OnCollisionStay(Collision other)
        {
            if (_isDamageCooldown || !IsAlive)
                return;
            
            var player = other.gameObject.GetComponentInParent<Player>();
            if (!player)
                return;

            _isDamageCooldown = true;
            Observable.Timer(TimeSpan.FromSeconds(1f)).Subscribe(_ => _isDamageCooldown = false).AddTo(_disposables);
            
            player.TakeDamage(_damage, transform, DamageType.Melee);

            if (_isAttacking)
            {
                player.Rigidbody.AddForce(transform.forward * _punchForce);
            }
        }

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

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