using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;

/// <summary>
/// GAME 1303 Tier 3 — Burst of speed in a direction. Zap Attack, Stardust, Apep's Trial,
/// Spark of Corruption.
///
/// SETUP
///  1. Add to the player (needs PlayerController2D v1.1 and PlayerInput = Send Messages).
///  2. Add a "Dash" action to your Input Actions asset (Player map): Button type,
///     bind Left Shift and gamepad East/B. This script listens for OnDash.
///     (Or tick Use Sprint Action to reuse the built-in "Sprint" binding.)
///  3. Direction: Facing = horizontal only (classic). EightWay = uses the stick (Celeste).
///  4. Air Dashes: how many before landing resets it. 0 = ground only.
///  5. Invulnerable During Dash: sets Health invulnerability for the dash duration.
///  6. Optional: an "Dash" Animator trigger, a "dash" sound, a ghost-trail prefab.
///
/// Zap Attack's "dash through enemies": tick Invulnerable and add a child Hitbox that
/// OnDashStarted enables and OnDashEnded disables.
/// </summary>
public class Dash : MonoBehaviour
{
    public enum DirectionMode { Facing, EightWay }

    public float dashSpeed = 22f;
    public float dashDuration = 0.15f;
    public float cooldown = 0.5f;
    public DirectionMode direction = DirectionMode.Facing;
    [Tooltip("Dashes allowed in the air before touching ground.")]
    public int airDashes = 1;
    public bool invulnerableDuringDash = true;
    [Tooltip("Keep some upward/downward momentum after a horizontal dash? 0 = stop dead.")]
    [Range(0f, 1f)] public float keepVelocityAfter = 0f;

    [Header("Input")]
    public bool useSprintAction = false;

    [Header("Feel")]
    public string animatorTrigger = "Dash";
    public string sfxName = "dash";

    [Header("Events")]
    public UnityEvent OnDashStarted;
    public UnityEvent OnDashEnded;

    public bool IsDashing { get; private set; }

    private PlayerController2D controller;
    private Rigidbody2D rb;
    private Animator anim;
    private Health health;
    private float cooldownTimer;
    private int airDashesLeft;

    void Awake()
    {
        controller = GetComponent<PlayerController2D>();
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        health = GetComponent<Health>();
        airDashesLeft = airDashes;
    }

    void OnEnable()  { controller.OnLanded.AddListener(ResetAirDashes); }
    void OnDisable() { controller.OnLanded.RemoveListener(ResetAirDashes); }

    void Update() { cooldownTimer -= Time.deltaTime; }

    void OnDash(InputValue v)   { if (!useSprintAction && v.isPressed) TryDash(); }
    void OnSprint(InputValue v) { if (useSprintAction && v.isPressed) TryDash(); }

    public bool TryDash()
    {
        if (IsDashing || cooldownTimer > 0f || !controller.InputEnabled || controller.MovementLocked) return false;
        if (!controller.IsGrounded)
        {
            if (airDashesLeft <= 0) return false;
            airDashesLeft--;
        }

        Vector2 dir = controller.Facing;
        if (direction == DirectionMode.EightWay && controller.MoveVector.sqrMagnitude > 0.1f)
        {
            Vector2 mv = controller.MoveVector;
            dir = new Vector2(Mathf.Round(mv.x), Mathf.Round(mv.y)).normalized;
            if (dir.x != 0) controller.FaceDirection(dir.x);
        }

        StartCoroutine(DashRoutine(dir));
        return true;
    }

    IEnumerator DashRoutine(Vector2 dir)
    {
        IsDashing = true;
        cooldownTimer = cooldown;
        controller.MovementLocked = true;
        rb.gravityScale = 0f;

        if (anim != null && !string.IsNullOrEmpty(animatorTrigger)) anim.SetTrigger(animatorTrigger);
        if (!string.IsNullOrEmpty(sfxName)) AudioManager.Instance?.PlaySFX(sfxName);
        if (invulnerableDuringDash && health != null) health.SetInvulnerable(dashDuration);
        OnDashStarted?.Invoke();

        float t = 0f;
        while (t < dashDuration)
        {
            rb.linearVelocity = dir * dashSpeed;
            t += Time.fixedDeltaTime;
            yield return new WaitForFixedUpdate();
        }

        rb.linearVelocity = rb.linearVelocity * keepVelocityAfter;
        rb.gravityScale = controller.BaseGravity;
        controller.MovementLocked = false;
        IsDashing = false;
        OnDashEnded?.Invoke();
    }

    void ResetAirDashes() => airDashesLeft = airDashes;
}