using UnityEngine;
using UnityEngine.InputSystem;

/// <summary>
/// GAME 1303 Tier 3 — Lets the player climb Ladders. Roach Invasion, Spark of Corruption,
/// Detective Liz (five stories).
///
/// SETUP
///  - Add to the player (PlayerController2D v1.1). Needs a Collider2D on the player
///    that can overlap the Ladder's trigger.
///  - Press Up/Down (Move Y) while overlapping a ladder to grab it. Jump lets go.
///  - Animator: bool "IsClimbing" and float "ClimbSpeed" (0 when holding still, so you
///    can pause the climb animation with Animator speed = ClimbSpeed).
/// </summary>
public class LadderClimber : MonoBehaviour
{
    public float climbSpeed = 4f;
    [Tooltip("How far the stick must be pushed to grab.")]
    public float grabThreshold = 0.5f;
    [Tooltip("Jump force when leaving the ladder with Jump.")]
    public float jumpOffForce = 10f;

    public bool IsClimbing { get; private set; }

    private PlayerController2D controller;
    private Rigidbody2D rb;
    private Animator anim;
    private Collider2D myCollider;
    private DoubleJump doubleJump;
    private Ladder currentLadder;
    private static readonly int ClimbHash = Animator.StringToHash("IsClimbing");
    private static readonly int ClimbSpeedHash = Animator.StringToHash("ClimbSpeed");
    private bool hasBool, hasFloat;

    void Awake()
    {
        controller = GetComponent<PlayerController2D>();
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        myCollider = GetComponent<Collider2D>();
        doubleJump = GetComponent<DoubleJump>();
        if (anim != null)
            foreach (var p in anim.parameters)
            {
                if (p.nameHash == ClimbHash) hasBool = true;
                if (p.nameHash == ClimbSpeedHash) hasFloat = true;
            }
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        var l = other.GetComponent<Ladder>();
        if (l != null) currentLadder = l;
    }

    void OnTriggerExit2D(Collider2D other)
    {
        var l = other.GetComponent<Ladder>();
        if (l != null && l == currentLadder)
        {
            if (IsClimbing) StopClimbing();
            currentLadder = null;
        }
    }

    void FixedUpdate()
    {
        float y = controller.MoveVector.y;

        if (!IsClimbing)
        {
            // Grab: overlapping a ladder and pushing up, or pushing down while standing at its top
            if (currentLadder != null && controller.InputEnabled && !controller.MovementLocked
                && Mathf.Abs(y) > grabThreshold)
            {
                // Don't grab downward if we're already on the ground below the ladder
                if (y < 0f && controller.IsGrounded && transform.position.y < currentLadder.TopY - 0.5f) return;
                StartClimbing();
            }
            return;
        }

        // Climbing
        float x = currentLadder.snapToCenter ? 0f : controller.MoveInput * controller.moveSpeed * 0.5f;
        rb.linearVelocity = new Vector2(x, y * climbSpeed);

        if (currentLadder.snapToCenter)
            rb.position = new Vector2(Mathf.Lerp(rb.position.x, currentLadder.CenterX, 0.3f), rb.position.y);

        if (hasFloat) anim.SetFloat(ClimbSpeedHash, Mathf.Abs(y));

        // Reached the ground going down? Let go.
        if (y < 0f && controller.IsGrounded) StopClimbing();
        // Climbed off the top? Let go (grounded on the top platform)
        if (y > 0f && rb.position.y > currentLadder.TopY && controller.IsGrounded) StopClimbing();
    }

    void OnJump(InputValue v)
    {
        if (!v.isPressed || !IsClimbing) return;
        StopClimbing();
        controller.ForceJump(jumpOffForce);
        controller.ClearJumpBuffer();
    }

    void StartClimbing()
    {
        IsClimbing = true;
        controller.MovementLocked = true;
        rb.gravityScale = 0f;
        rb.linearVelocity = Vector2.zero;
        if (currentLadder.topPlatform != null) Physics2D.IgnoreCollision(myCollider, currentLadder.topPlatform, true);
        doubleJump?.Refill();
        if (hasBool) anim.SetBool(ClimbHash, true);
    }

    void StopClimbing()
    {
        IsClimbing = false;
        controller.MovementLocked = false;
        rb.gravityScale = controller.BaseGravity;
        if (currentLadder != null && currentLadder.topPlatform != null)
            Physics2D.IgnoreCollision(myCollider, currentLadder.topPlatform, false);
        if (hasBool) anim.SetBool(ClimbHash, false);
        if (hasFloat) anim.SetFloat(ClimbSpeedHash, 0f);
    }
}