#nullable enable
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;

public class AIEntity : Entity
{
    [FormerlySerializedAs("stats")] [SerializeField] AIStats AIStats = null!;
    BaseState currentState = null!;
    public string[] enemies {get; protected set; }
    override protected void Awake(){
        base.Awake();
		currentState = new FindTargetState(this);
	} 

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

	override protected void Update() {
		if (currentState.UpdateState() is {} newState)
			SwitchState(newState);
	}

	override protected void FixedUpdate() {
		if (currentState.FixedUpdateState() is {} newState)
			SwitchState(newState);
	}

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

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

    //Looks into enemy name list to see if the other is targetable
    virtual protected bool IsTargetable(Entity other){
        foreach (string name in enemies){
            if(other.entityName == name && other.IsAlive()){
                return true;
            }
        }
        
        return false;
    }
    
    abstract class BaseStateAI : BaseState{
		protected AIEntity entity;
		public BaseStateAI(AIEntity entity){
			this.entity = entity;
		}
	}

    /*//Basically a copy of JumpingMovementState
    class NonPhysicThrownState : BaseStateAI {
	    readonly Vector3 target;

	    Vector3 startPosition;
	    float duration;
	    float startTime;

	    public NonPhysicThrownState(AIEntity entity, Vector3 target) : base(entity) {
		    this.target = target;
	    }

	    public override void EnterState() {
		    base.EnterState();
		    
		    duration = entity.AIStats.ThrownDurationPerMeter * Vector3.Distance(entity.transform.position, target);
		    startPosition = entity.transform.position;
		    startTime = Time.time;
		    entity.rb.SetEnabled(false);
	    }

	    public override void LeaveState() {
		    base.LeaveState();
		    entity.rb.SetEnabled(true);
	    }

	    public override BaseState? FixedUpdateState() {
		    float currentTime = Time.time - startTime;
		    if (currentTime >= duration)
			    return new FindTargetState(entity);

		    entity.rb.MovePosition(Vector3.Lerp(
			    startPosition,
			    target,
			    entity.AIStats.ThrownCurve.Evaluate(currentTime / duration)
		    ));

		    return null;
	    }
    }*/
    
    class ThrownState : BaseStateAI {
	    public ThrownState(AIEntity entity) : base(entity) {}

	    public override void EnterState() {
		    base.EnterState();
		    
		    entity.rb.SetEnabled(false);
	    }

	    public override void LeaveState() {
		    base.LeaveState();
		    entity.rb.SetEnabled(true);
	    }

	    public override BaseState? FixedUpdateState()
		    => entity.rb.velocity.magnitude < entity.stats.MinVelocityWhenThrown
		    ? new FindTargetState(entity)
		    : null;
    }

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

        }

        public override BaseState? UpdateState(){
            if(!entity.IsAlive()){
                return new DeadState(entity);
            }
            Entity targetEntity = entity.GetTarget().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(){
            Transform target = entity.GetTarget();
            entity.direction = Vector3.RotateTowards(entity.direction, (target.position - entity.transform.position), entity.rotSpeed*Time.fixedDeltaTime, 0.0f);
            if(entity.IsTargetable(entity.GetTarget().GetComponent<Entity>())){
                if(!entity.IsInAttackRange()){
                    entity.rb.MovePosition(entity.transform.position + entity.direction * entity.movementSpeed * Time.fixedDeltaTime);
                }else{
                    return new AttackState(entity);
                }
            }else{
                return new FindTargetState(entity);
            }
            return null;
        }
    }

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

        public override BaseState? UpdateState(){
            if(!entity.IsAlive()){
                return new DeadState(entity);
            }
            Entity[] entities = FindObjectsOfType<Entity>();
            float lastDist = float.MaxValue;
            Entity chosenEntity = null!;
            foreach (Entity other in entities){// Find the closest entity
                if(entity.IsTargetable(other)){
                    float distance = Vector2.Distance(other.transform.position, entity.transform.position);
                    if(distance < lastDist){
                        lastDist = distance;
                        chosenEntity = other;
                        if(lastDist <= entity.AIStats.closeEnough)break;
                    }
                }
            }
            if(chosenEntity != null){
                entity.SetTarget(chosenEntity.transform);
                return new SeekState(entity);
            }else{
                if(roamPosition == new Vector3()) roamPosition = entity.AIStats.getRandomRoamPositon();
                return null;
            }
        }

        public override BaseState? FixedUpdateState(){
            if(roamPosition == new Vector3()){
                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();
            }
            return null;
        }

    }

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

        public override BaseState? UpdateState(){
            if(!entity.IsAlive()){
                return new DeadState(entity);
            }
            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.GetTarget().GetComponent<Entity>();
                if(targetEntity != null){
                    targetEntity.TakeDamage(entity.attackDmg);
                    bool isTargetAlive = targetEntity.IsAlive();
                    if(!isTargetAlive){
                        return new FindTargetState(entity);
                    }
                }
            return null;
        }
    }

    class DeadState : BaseStateAI{
        public DeadState(AIEntity entity) : base(entity){
            Debug.Log("Dead!");
        }

        public override BaseState? UpdateState(){
            

            return null;
        }
    }
}
