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

/// <summary>
/// GAME 1303 Tier 3 — Slide down walls, jump off them. Zap Attack, Stardust,
/// Spark of Corruption.
///
/// SETUP
///  1. Add to the player (PlayerController2D v1.1). Create an empty child "WallCheck"
///     at chest height on the RIGHT side of the sprite and drag it in. (It flips with
///     the sprite, so one check covers both sides.)
///  2. Wall Layer: usually the same as Ground.
///  3. Slide Speed: how fast you slip down while pressing into the wall.
///  4. Wall Jump Force (x, y): x pushes you AWAY from the wall. Lock Time keeps you from
///     steering straight back into the wall for a few frames.
///  5. Animator: sets bool "IsWallSliding" if the parameter exists.
///
/// Also refills DoubleJump when you touch a wall (feels good; turn off Refill Jumps if not).
/// </summary>
public class WallSlideJump : MonoBehaviour
{
    public Transform wallCheck;
    public float checkDistance = 0.2f;
    public LayerMask wallLayer;

    [Header("Slide")]
    public float slideSpeed = 2f;
    [Tooltip("Slide only when pushing toward the wall (Hollow Knight) vs. whenever touching (Mega Man X).")]
    public bool requirePushingIntoWall = true;

    [Header("Wall jump")]
    public Vector2 wallJumpForce = new Vector2(10f, 14f);
    public float lockTime = 0.15f;
    public bool refillJumps = true;
    public string sfxName = "jump";

    public bool IsWallSliding { get; private set; }
    public bool TouchingWall { get; private set; }

    private PlayerController2D controller;
    private Rigidbody2D rb;
    private Animator anim;
    private DoubleJump doubleJump;
    private static readonly int SlideHash = Animator.StringToHash("IsWallSliding");
    private bool hasParam;
    private bool locked;

    void Awake()
    {
        controller = GetComponent<PlayerController2D>();
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        doubleJump = GetComponent<DoubleJump>();
        if (anim != null)
            foreach (var p in anim.parameters) if (p.nameHash == SlideHash) hasParam = true;
    }

    void FixedUpdate()
    {
        if (wallCheck == null) return;

        TouchingWall = Physics2D.Raycast(wallCheck.position, controller.Facing, checkDistance, wallLayer);

        bool pushing = !requirePushingIntoWall ||
                       (controller.MoveInput > 0.1f && controller.FacingRight) ||
                       (controller.MoveInput < -0.1f && !controller.FacingRight);

        bool wantSlide = TouchingWall && !controller.IsGrounded && pushing && !locked
                         && !controller.MovementLocked && rb.linearVelocity.y < 0f;

        if (wantSlide && !IsWallSliding && refillJumps && doubleJump != null) doubleJump.Refill();
        IsWallSliding = wantSlide;

        if (IsWallSliding && rb.linearVelocity.y < -slideSpeed)
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, -slideSpeed);

        if (hasParam) anim.SetBool(SlideHash, IsWallSliding);
    }

    void OnJump(InputValue v)
    {
        if (!v.isPressed || !controller.InputEnabled || locked) return;
        // Wall jump if sliding, or touching a wall in the air (more forgiving)
        if (!(IsWallSliding || (TouchingWall && !controller.IsGrounded && !controller.CanCoyoteJump))) return;

        float away = controller.FacingRight ? -1f : 1f;
        rb.linearVelocity = new Vector2(away * wallJumpForce.x, wallJumpForce.y);
        controller.ClearJumpBuffer();
        controller.Flip();   // face away from the wall
        controller.OnJumped?.Invoke();
        if (!string.IsNullOrEmpty(sfxName)) AudioManager.Instance?.PlaySFX(sfxName);
        StartCoroutine(Lock());
    }

    IEnumerator Lock()
    {
        locked = true;
        controller.MovementLocked = true;
        yield return new WaitForSeconds(lockTime);
        controller.MovementLocked = false;
        locked = false;
    }

    void OnDrawGizmosSelected()
    {
        if (wallCheck == null) return;
        Gizmos.color = Color.magenta;
        Vector3 dir = Application.isPlaying && controller != null ? (Vector3)controller.Facing : Vector3.right;
        Gizmos.DrawLine(wallCheck.position, wallCheck.position + dir * checkDistance);
    }
}