using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// GAME 1303 Tier 2 — Notices the player, runs at them, attacks when close, gives up
/// when they get away.
///
/// SETUP
///  1. Add to an enemy that already has Rigidbody2D + Health. Works with OR without
///     EnemyPatrol (if present, patrol resumes when the chase ends).
///  2. Detect Radius: how close the player must be to be noticed.
///     Lose Radius: bigger — how far they must get before the enemy gives up.
///  3. Require Line Of Sight: raycasts against Ground Layer so walls hide the player.
///  4. Movement:
///       Grounded – moves on X only, gravity applies (thugs, roaches).
///       Free     – moves on X and Y, ignores gravity (ghosts, flying roaches,
///                  top-down monsters in Last Delivery). Set Rigidbody2D gravity to 0.
///  5. Attack: when within Attack Range, calls OnAttack every Attack Cooldown seconds.
///     Wire it to MeleeAttack.Attack or ProjectileShooter.Fire. Leave empty for
///     "just touch to hurt" enemies.
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class EnemyChase : MonoBehaviour
{
    public enum Movement { Grounded, Free }

    [Header("Detection")]
    public float detectRadius = 6f;
    public float loseRadius = 9f;
    public bool requireLineOfSight = true;
    public LayerMask groundLayer;

    [Header("Movement")]
    public Movement movement = Movement.Grounded;
    public float chaseSpeed = 3.5f;
    [Tooltip("Free mode only: how snappy direction changes are.")]
    public float acceleration = 10f;
    [Tooltip("Flip sprite to face the player.")]
    public bool faceTarget = true;
    public bool artFacesRight = true;

    [Header("Attack")]
    public float attackRange = 1.2f;
    public float attackCooldown = 1f;
    [Tooltip("Stop moving while within attack range.")]
    public bool stopInRange = true;
    public UnityEvent OnAttack;

    [Header("Events")]
    public UnityEvent OnNoticedPlayer;
    public UnityEvent OnLostPlayer;

    public bool IsChasing { get; private set; }

    private Rigidbody2D rb;
    private Transform player;
    private EnemyPatrol patrol;
    private Knockback knockback;
    private Health health;
    private PlayerAnimatorBridge animBridge;
    private float attackTimer;
    private bool facingRight;

    void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        patrol = GetComponent<EnemyPatrol>();
        knockback = GetComponent<Knockback>();
        health = GetComponent<Health>();
        animBridge = GetComponent<PlayerAnimatorBridge>();
        facingRight = artFacesRight ? transform.localScale.x > 0 : transform.localScale.x < 0;
    }

    void Start()
    {
        var p = GameObject.FindGameObjectWithTag("Player");
        if (p != null) player = p.transform;
    }

    void FixedUpdate()
    {
        if (player == null || (health != null && health.IsDead)) return;
        attackTimer -= Time.fixedDeltaTime;

        float dist = Vector2.Distance(transform.position, player.position);

        // --- State changes ---
        if (!IsChasing && dist <= detectRadius && CanSee())
        {
            IsChasing = true;
            if (patrol != null) patrol.Active = false;
            OnNoticedPlayer?.Invoke();
        }
        else if (IsChasing && dist > loseRadius)
        {
            IsChasing = false;
            if (patrol != null) patrol.Active = true;
            if (movement == Movement.Free) rb.linearVelocity = Vector2.zero;
            OnLostPlayer?.Invoke();
        }

        if (!IsChasing) return;
        if (knockback != null && knockback.IsStunned) return;

        // --- Face ---
        if (faceTarget)
        {
            bool shouldFaceRight = player.position.x > transform.position.x;
            if (shouldFaceRight != facingRight) Flip();
        }

        // --- Attack ---
        if (dist <= attackRange)
        {
            if (stopInRange)
            {
                rb.linearVelocity = movement == Movement.Grounded
                    ? new Vector2(0f, rb.linearVelocity.y) : Vector2.zero;
                animBridge?.SetSpeed(0f);
            }
            if (attackTimer <= 0f)
            {
                attackTimer = attackCooldown;
                OnAttack?.Invoke();
            }
            if (stopInRange) return;
        }

        // --- Move ---
        if (movement == Movement.Grounded)
        {
            float dir = Mathf.Sign(player.position.x - transform.position.x);
            rb.linearVelocity = new Vector2(dir * chaseSpeed, rb.linearVelocity.y);
            animBridge?.SetSpeed(rb.linearVelocity.x);
        }
        else
        {
            Vector2 desired = ((Vector2)player.position - rb.position).normalized * chaseSpeed;
            rb.linearVelocity = Vector2.Lerp(rb.linearVelocity, desired, acceleration * Time.fixedDeltaTime);
            animBridge?.SetSpeed(rb.linearVelocity.magnitude);
        }
    }

    bool CanSee()
    {
        if (!requireLineOfSight) return true;
        Vector2 dir = player.position - transform.position;
        RaycastHit2D hit = Physics2D.Raycast(transform.position, dir.normalized, dir.magnitude, groundLayer);
        return hit.collider == null;
    }

    void Flip()
    {
        facingRight = !facingRight;
        Vector3 s = transform.localScale;
        s.x *= -1f;
        transform.localScale = s;
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(transform.position, detectRadius);
        Gizmos.color = Color.gray;   Gizmos.DrawWireSphere(transform.position, loseRadius);
        Gizmos.color = Color.red;    Gizmos.DrawWireSphere(transform.position, attackRange);
    }
}