using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Objects.Unit.Events;
using VOID.Generic.Projectile;
using VOID.ScriptableObjects;

namespace VOID.Generic.Status.Effect.Positional
{
    [TypeRegistryItem("Jump")]
    public class JumpEffect : BasicEffect
    {
        private enum JumpEffectState
        {
            Pre,
            Waiting,
            Jump,
            End
        }

        private JumpEffectState _state;
        private ProjectileObject _projectile;
        private UnitObject _forUnit;
        
        public override void OnApply()
        {
            if (forObject is not UnitObject forUnit) return;
            _forUnit = forUnit;

            if (_state == JumpEffectState.Pre)
            {
                // "PRE" is firing from ability, but this will fire instant effect
                // Here we will create new status+effect for jump that will handle whole jump
                var newJumpEffect = new JumpEffect();
                newJumpEffect._state = JumpEffectState.Waiting;
                
                var newJumpStatus = ScriptableObject.CreateInstance<BasicStatus>();
                newJumpStatus.effects = new List<BasicEffect> { newJumpEffect };
                newJumpStatus.hidden = true;
                newJumpStatus.permanent = true;
                _forUnit.statusManager.AddStatus(newJumpStatus, _forUnit, false);
            }
            else if (_state == JumpEffectState.Waiting)
            {
                // "WAITING" this state is waiting for projectile to fire, after that unit can attach to projectile
                _forUnit.unitEvents.onAbilityProjectileStart.AddListener(OnProjectileStart);
                _forUnit.unitEvents.onAbilityProjectileEnd.AddListener(OnProjectileEnd);
            }
        }

        public override void OnEnd()
        {
            if (!_forUnit) return;
            
            // Just to be sure - remove all listeners after jump is finished
            _forUnit.basicEvents.onFixedUpdate.RemoveListener(MoveWithProjectile);
            _forUnit.unitEvents.onAbilityProjectileStart.RemoveListener(OnProjectileStart);
            _forUnit.unitEvents.onAbilityProjectileEnd.RemoveListener(OnProjectileEnd);
        }
        
        public override void OnTurnSelfEnd() {}
        public override void OnTurnSelfStart() {}
        public override void OnCancel() => OnEnd();
        
        public sealed override string GetSimpleDescription() => "Unit jump like a projectile";

        private void OnProjectileStart(AbilityProjectileEvent projectileEvent)
        {
            // just to be sure that unit stick only to first projectile, if somehow there is more
            if (projectileEvent.projectileIndex != 0) return;
            
            _projectile = projectileEvent.projectile;
            _state = JumpEffectState.Jump;
            
            _forUnit.basicEvents.onFixedUpdate.AddListener(MoveWithProjectile);
            
            _forUnit.unitEvents.onAbilityProjectileStart.RemoveListener(OnProjectileStart);
        }
        
        private void OnProjectileEnd(AbilityProjectileEvent projectileEvent)
        {
            // just to be sure that unit stick only to first projectile, if somehow there is more
            if (projectileEvent.projectileIndex != 0) return;
            
            _state = JumpEffectState.End;
            
            _forUnit.basicEvents.onFixedUpdate.RemoveListener(MoveWithProjectile);
            
            _forUnit.unitEvents.onAbilityProjectileEnd.RemoveListener(OnProjectileEnd);
            
            // End whole jump status when projectile along with unit end their "jump"
            forStatus.End();
        }

        private void MoveWithProjectile()
        {
            _forUnit.transform.position = _projectile.transform.position;
        }
    }
}