using DG.Tweening;
using ScrambleKit;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AIGhost : MonoBehaviour
{
    public FXTrigger wakeFX;
    public FXTrigger lungeFX;

    public float wakeupDistance = 3.8f;
    public float lungeDistance = 3.25f;
    public float stalkingDistance = 5f;
    public float telegraphTime = 0.6f;
    public float recoverTime = 0.8f;

    public float tauntTime = 4f;

    public Collider2D bodyCollider;
    public Collider2D triggerCollider;
    private Cargo cargo;


    public float baseSpeed = 4f;
    public float lungeSpeed = 6f;
    public float teleSpeed = 0.5f;
    public float tauntSpeed = 20f;

    private float telegraphUntil;
    private float tauntUntil;
    private float lastTargetTime;

    private TopDownMovement move;
    private FlipTowardPlayer flipper;
    
    private Vector2 moveToPos;
    private Vector2 recoverPos;

    private SpriteRenderer rend;

    public enum State
    {
        Asleep,
        Stalking,
        Telegraphing,
        Lunge,
        Wander,
        Recovering,
        Taunting
    }
    private State state;

    void Start()
    {
        move = GetComponent<TopDownMovement>();
        rend = GetComponent<SpriteRenderer>();
        flipper = GetComponent<FlipTowardPlayer>();
        cargo = GetComponent<Cargo>();
        EnterState(State.Asleep);
    }

    private void ChooseLungeTarget()
    {
        lastTargetTime = Time.time;
        Vector2 dir = (Player.playerTransform.position - transform.position).normalized;
        moveToPos = (Vector2)transform.position + dir * (1.5f * lungeDistance); // overshoot by the lunge threshold by 20% to create some stalking
        recoverPos = (Vector2)transform.position + dir * (3f * lungeDistance); // recovery overshoots the lunge but slowly
    }

    private void ChooseRecoverTarget()
    {
        lastTargetTime = Time.time;
        moveToPos = recoverPos;
    }

    private void ChooseWanderTarget()
    {
        lastTargetTime = Time.time;
        //  if (Random.value > 0.15f)
        moveToPos = new Vector2(Random.Range(-9f, 9f), Random.Range(-5f, 5f));
        // else
        //     moveToPos = (Vector2)Player.playerTransform.position + Random.insideUnitCircle * 2f;
    }

    private void WakeUp()
    {
        rend.enabled = true;
        wakeFX.Trigger(transform);
    }

    private bool PlayerClose()
    {
        return Vector2.Distance(transform.position, Player.playerTransform.position) < wakeupDistance;
    }

    public void EnterState(State newState)
    {
        switch (newState)
        {
            case State.Asleep:
                rend.enabled = false;
                flipper.enabled = true;
                break;

            case State.Stalking:
                flipper.enabled = true;
                move.speedLimit = baseSpeed;
                break;

            case State.Telegraphing:
                flipper.Flip();
                flipper.enabled = false;
                move.speedLimit = teleSpeed;
                transform.DOScale(new Vector3(1.5f, 0.5f, 1), telegraphTime * 0.8f).SetEase(Ease.InQuad);
                telegraphUntil = Time.time + telegraphTime;
                ChooseLungeTarget();
                break;

            case State.Lunge:
                flipper.enabled = false;
                move.speedLimit = lungeSpeed;
                transform.DOScale(Vector3.one, 0.06f).SetEase(Ease.InCubic);
                lungeFX.Trigger(transform);
                break;

            case State.Wander:
                flipper.enabled = true;
                move.speedLimit = baseSpeed;
                ChooseWanderTarget();
                break;

            case State.Recovering:
                flipper.enabled = false;
                move.speedLimit = teleSpeed;
                transform.DOScale(new Vector3(0.75f, 1.25f, 1), 0.2f * recoverTime).SetEase(Ease.OutQuad);
                int dir = rend.flipX ? -1 : 1;
                transform.DORotate(new Vector3(0, 0, dir*10), 0.1f).SetEase(Ease.InQuad);
                ChooseRecoverTarget();
                break;

            case State.Taunting:
                flipper.enabled = true;
                bodyCollider.enabled = false;
                triggerCollider.enabled = false;
                move.speedLimit = tauntSpeed;
                move.moveAcceleration = tauntSpeed * 50f;
                move.stopDeceleration = tauntSpeed * 50f;
                tauntUntil = Time.time + tauntTime;
                transform.DOScale(new Vector3(1.3f, 0.7f, 1), 0.175f).SetEase(Ease.InCubic).SetLoops(18, LoopType.Yoyo);
                break; 

        }
        state = newState;
    }

    public void Update()
    {
        // A ghost that has stolen will ignore other state factors and immediately taunt the player
        if(cargo.type != Cargo.ItemType.None && state != State.Taunting)
        {
            EnterState(State.Taunting);
            return;
        }

        switch (state)
        {
            case State.Asleep:
                if (PlayerClose())
                {
                    WakeUp();
                    EnterState(State.Stalking);
                }
                move.moveDir = Vector2.zero;
                break;

            case State.Stalking:
                if (Vector2.Distance(transform.position, Player.playerTransform.position) < lungeDistance)
                {
                    EnterState(State.Telegraphing);
                }
                else if (Vector2.Distance(transform.position, Player.playerTransform.position) > stalkingDistance)
                {
                    EnterState(State.Wander);
                }
                else
                {
                    move.moveDir = (Player.playerTransform.position - transform.position).normalized;
                }
                break;

            case State.Telegraphing:
                if (Time.time > telegraphUntil)
                {
                    EnterState(State.Lunge);
                }
                move.moveDir = -(moveToPos - (Vector2)transform.position).normalized;
                break;

            case State.Lunge:
                if (Time.time > lastTargetTime + 2.5f)  // abort the lunge, we must be stuck on barrier around truck
                {
                    EnterState(State.Stalking);
                }
                if (Vector2.Distance(transform.position, moveToPos) < 0.1f)  // completed lunge, recover
                {
                    EnterState(State.Recovering);
                }
                else
                {
                    move.moveDir = (moveToPos - (Vector2)transform.position).normalized;
                }
                break;

            case State.Wander:
                if (Vector2.Distance(transform.position, Player.playerTransform.position) < stalkingDistance || Time.time > lastTargetTime + 9f)
                {
                    EnterState(State.Stalking);
                }
                else if (Vector2.Distance(transform.position, moveToPos) < 0.1f)
                {
                    ChooseWanderTarget();
                }
                else
                {
                    move.moveDir = (moveToPos - (Vector2)transform.position).normalized;
                }
                break;

            case State.Recovering:
                if (Time.time > lastTargetTime + recoverTime)
                {
                    transform.DOScale(Vector3.one, 0.04f).SetEase(Ease.InCubic);
                    transform.DORotate(new Vector3(0, 0, 0), 0.05f).SetEase(Ease.OutQuad);
                    EnterState(State.Stalking);
                }
                else
                {
                    move.moveDir = (moveToPos - (Vector2)transform.position).normalized;
                }
                break;

            case State.Taunting:
                if (Time.time > tauntUntil)
                {
                    float dist = Vector2.Distance(transform.position, Player.playerTransform.position);
                    if(dist > 10f)
                    {
                        Destroy(gameObject);
                    }

                    Vector2 away = (transform.position - Player.playerTransform.position).normalized;
                    move.moveDir = away;
                }
                else
                {
                    float dist = Vector2.Distance(transform.position, Player.playerTransform.position);
                    Vector2 away = (transform.position - Player.playerTransform.position).normalized;
                    Vector2 tangent = new Vector2(-away.y, away.x);

                    float goalDist = 0.8f * lungeDistance;
                    float gap = goalDist - dist;

                    Vector2 dir = (away * gap + tangent).normalized;
                    move.moveDir = dir;
                }
                break;
        }
    }
}
