#nullable enable
using UnityEngine;
using UnityEngine.Serialization;

public class AIEntity : Entity {
    [FormerlySerializedAs("stats")]
    [SerializeField]
    public AIStats AIStats = null!;
    BaseState currentState = null!;
    public EntityFlag enemies { get; protected set; }
    public bool facingRight { get; private set; } = true;

    override protected void Start() {
        base.Start();
        currentState = CreateInitialState();
        currentState.EnterState();
    }

    override protected void Update() {
        base.Update();

        if (gameFlowManager.pauseLevel >= GameFlowManager.PauseLevel.TimeStop)
	        return;

        if (currentState.UpdateState() is { } newState)
            SwitchState(newState);
    }

    override protected void FixedUpdate() {
        base.FixedUpdate();

        if (gameFlowManager.pauseLevel >= GameFlowManager.PauseLevel.TimeStop)
	        return;
        
        if (currentState.FixedUpdateState() is { } newState)
            SwitchState(newState);
        FlipAccordingToInput();
    }

    void OnDrawGizmos() => currentState?.OnDrawGizmos();

    protected override void OnDied() {
        base.OnDied();
        transform.SetParent(arena.graveyard);
    }

    void SwitchState(BaseState newState) {
        currentState.LeaveState();
        currentState = newState;
        newState.EnterState();
    }

    protected virtual BaseState CreateInitialState() => new FindTargetState(this);

    //Looks into enemy name list to see if the other is targetable
    virtual protected bool IsTargetable(Entity other) {
        return enemies.HasFlag(other.entityType) && other.IsAlive();
    }

    override public bool TakeDamage(float amount, Entity other) {
        //TODO Should we warn if target is null here?
        if (target != null && target.GetComponent<VampireEntity>() is { })
            target = other.transform;

        return base.TakeDamage(amount, other);
    }

    #region Flip

    public void Flip() {
        facingRight = !facingRight;
        Vector3 scaler = transform.localScale;
        scaler.x *= -1;
        transform.localScale = scaler;

        healthBar.gameObject.transform.localScale = scaler;
    }

    public void FlipAccordingToInput() {
        Vector3 direction = rb.velocity;
        if (target != null) {
            direction = target.position - transform.position;
        }
        if ((!facingRight && direction.x > 0) || (facingRight && direction.x < 0)) {
            Flip();
        }
    }

    #endregion


    //When ratio is zero, decay is complete. Blood token set to zero
    public void Decay(float ratio){
        if(ratio <= 0){
            bloodTokens = 0;
            OnEmpty();
        }else{
            Color diffColor = deadColor * ratio;
            renderer.color = new Color(diffColor.r, diffColor.g, diffColor.b, renderer.color.a);
        }
    }
    
    protected abstract class BaseStateAI : BaseState{
		protected AIEntity entity;
		public BaseStateAI(AIEntity entity){
			this.entity = entity;
		}
	}

    protected class SeekState : BaseStateAI {
        public SeekState(AIEntity entity) : base(entity) {

        }

        public override void EnterState() {
            if (!entity.animator.GetCurrentAnimatorStateInfo(0).IsName("Attack")) {
                entity.animator.Play("Running");
            }
        }

        public override BaseState? UpdateState() {
            if (!entity.IsAlive()) {
                return new DeadState(entity);
            }
            Entity targetEntity = entity.target.GetComponent<Entity>();
            if (targetEntity != null) {
                if (targetEntity.IsAlive()) {//target is alive, keep chasing it 
                    return null;
                } else {//target is dead, go to findTargetState
                    return new FindTargetState(entity); ;
                }
            }
            return null;
        }

        public override BaseState? FixedUpdateState() {
            entity.direction = Vector3.RotateTowards(entity.direction, (entity.target.position - entity.transform.position), entity.rotSpeed * Time.fixedDeltaTime, 0.0f);
            if (entity.IsTargetable(entity.target.GetComponent<Entity>())) {
                if (!entity.IsInAttackRange()) {
                    entity.rb.MovePosition(entity.transform.position + entity.direction * entity.movementSpeed * Time.fixedDeltaTime);
                    // entity.animator.Play("Running");
                } else {
                    return new AttackState(entity);
                }
            } else {
                return new FindTargetState(entity);
            }
            // entity.animator.Play("Idle");
            return null;
        }
    }

    protected class FindTargetState : BaseStateAI {
        float closeEnough;
        Vector3 roamPosition;
        public FindTargetState(AIEntity entity) : base(entity) {
        }

        public override void EnterState() {
            if (!entity.animator.GetCurrentAnimatorStateInfo(0).IsName("Attack")) {
                entity.animator.Play("Running");
            }
        }

        public override BaseState? UpdateState() {
            if (!entity.IsAlive()) {
                return new DeadState(entity);
            }

            Transform entityParent = entity.entityType == EntityFlag.Gladiator ? entity.arena.minionParent : entity.arena.gladiatorParent;
            float lastDist = float.MaxValue;
            Transform chosenEntity = null!;
            foreach (Transform other in entityParent) {// Find the closest entity
                float distance = Vector2.Distance(other.position, entity.transform.position);
                if (distance < lastDist) {
                    lastDist = distance;
                    chosenEntity = other;
                    if (lastDist <= entity.AIStats.closeEnough) break;
                }
            }

            if (chosenEntity != null) {
                entity.target = chosenEntity;
                return new SeekState(entity);
            } else {
                if (roamPosition == new Vector3()) roamPosition = entity.AIStats.getRandomRoamPositon();
                return null;
            }
        }

        public override BaseState? FixedUpdateState() {
            if (roamPosition == new Vector3()) {
                // entity.animator.Play("Idle");
                return null;
            }
            entity.direction = Vector3.RotateTowards(entity.direction, (roamPosition - entity.transform.position), entity.rotSpeed * Time.fixedDeltaTime, 0.0f);
            if (Vector2.Distance(entity.transform.position, roamPosition) >= entity.attackRange) {
                entity.rb.MovePosition(entity.transform.position + entity.direction * entity.movementSpeed * Time.fixedDeltaTime);
            } else {
                roamPosition = entity.AIStats.getRandomRoamPositon();
            }
            // entity.animator.Play("Running");
            return null;
        }

    }

    protected class AttackState : BaseStateAI {
        public AttackState(AIEntity entity) : base(entity) {
        }

        public override void EnterState() {
            entity.animator.Play("Attack");
        }

        public override BaseState? UpdateState() {
            if (!entity.IsAlive()) {
                return new DeadState(entity);
            }

            if (entity.gameFlowManager.CanDoAction) {
                if (entity.IsInAttackRange()) {
                    if (entity.attackTimer >= entity.attackCooldown) {
                        entity.attackTimer = 0;
                        return Attack();
                    } else {
                        entity.attackTimer += Time.deltaTime;
                    }

                } else
                    return new SeekState(entity);
            }

            return null;
        }

        private BaseState? Attack() {
            Entity targetEntity = entity.target.GetComponent<Entity>();
            if (targetEntity != null) {
                targetEntity.TakeDamage(entity.attackDmg, entity);
                bool isTargetAlive = targetEntity.IsAlive();
                if (!isTargetAlive) {
                    return new FindTargetState(entity);
                }
            }
            return null;
        }
    }

    protected class DeadState : BaseStateAI{
        private float decaytimer;
        public DeadState(AIEntity entity) : base(entity){

        }

        public override void EnterState() {
            entity.animator.Play("Death");
            decaytimer = 0f;
        }

        public override BaseState? UpdateState(){
            if(entity.bloodTokens > 0 && !entity.IsBeingSucked()){
                decaytimer += Time.deltaTime;
                entity.Decay(1 - (decaytimer/entity.AIStats.decayTime));
            }
            
            return null;
        }
    }
}
