using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    // [Header("运动")]
    [SerializeField]
    private float speed = 5f;

    [SerializeField]
    private float jumpForce = 12f;

    // [Header("碰撞")]
    [SerializeField]
    private LayerMask groundLayer;

    [SerializeField]
    private float wallCheckDistance = 0.5f;

    private Rigidbody2D body;

    private Animator anim;

    private CapsuleCollider2D capsuleCollider;

    private float horizontalInput;

    private bool isWallDetected;

    private bool canWallSlide;

    private bool isWallSliding;

    [HideInInspector]
    public bool ledgeDetected;

    // [Header("ledge信息")]
    [SerializeField]
    private Vector2 offset1;

    [SerializeField]
    private Vector2 offset2;

    private Vector2 climbBegunPosition;

    private Vector2 climbOverPosition;

    private bool canGrabLedge = true;

    private bool canClimb;

    private void Awake()
    {
        body = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        capsuleCollider = GetComponent<CapsuleCollider2D>();
    }

    private void Update()
    {
        // 输入
        horizontalInput = Input.GetAxis("Horizontal");

        // 翻转控制器
        FlipController();

        // 检查墙壁
        CollisionCheck();

        // 如果在墙壁滑行状态则只保留垂直速度
        if (isWallSliding)
        {
            // 只保留垂直速度
            body.linearVelocity = new Vector2(0, body.linearVelocity.y);
        }
        else
        {
            // 在水平方向上进行正常移动
            body.linearVelocity = new Vector2(horizontalInput * speed, body.linearVelocity.y);
        }

        // 跳跃
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Jump();
        }

        CheckForLedge();

        // 处理动画
        HandleAnimations();
    }

    private void FixedUpdate()
    {
        // wallslide
        if (isWallDetected && canWallSlide)
        {
            isWallSliding = true;

            // 限制垂直速度
            body.linearVelocity = new Vector2(body.linearVelocity.x, Mathf.Clamp(body.linearVelocity.y, -2f, float.MaxValue));
        }
        else
        {
            isWallSliding = false;
        }
    }

    private void FlipController()
    {
        if (horizontalInput > 0.01f)
        {
            transform.localScale = new Vector3(3, 3, 1);
        }
        else if (horizontalInput < -0.01f)
        {
            transform.localScale = new Vector3(-3, 3, 1);
        }
    }

    private void Jump()
    {
        if (isGrounded())
        {
            body.linearVelocity = new Vector2(body.linearVelocity.x, jumpForce);
            anim.SetTrigger("jump");
        }
    }

    private void CollisionCheck()
    {
        Vector2 direction = new Vector2(Mathf.Sign(transform.localScale.x), 0f);

        RaycastHit2D hit = Physics2D.BoxCast(
            capsuleCollider.bounds.center,
            capsuleCollider.bounds.size,
            0f,
            direction,
            wallCheckDistance,
            groundLayer
        );

        isWallDetected = hit.collider != null;

        canWallSlide = isWallDetected && !isGrounded() && body.linearVelocity.y < 0;
    }

    private void CheckForLedge()
    {
        if (ledgeDetected && canGrabLedge)
        {
            // 
            canGrabLedge = false;
            Vector2 LedgePosition = GetComponentInChildren<LedgeDetection>().transform.position;
            climbBegunPosition = LedgePosition + offset1;
            climbOverPosition = LedgePosition + offset2;
            canClimb = true;
        }

        // 
        if (canClimb)
        {
            Vector2 target = climbBegunPosition;
            body.MovePosition(Vector2.Lerp(body.position, target, Time.fixedDeltaTime * 5f));

            if (Vector2.Distance(body.position, target) < 0.05f)
            {
                LedgeClimbOver();
            }
        }
    }

    private void LedgeClimbOver()
    {
        canClimb = false;
        transform.position = climbOverPosition;
        Invoke("AllowLedgeGrab", 0.1f);
    }

    private void AllowLedgeGrab() => canGrabLedge = true;

    private void HandleAnimations()
    {
        if (isWallSliding)
        {
            anim.SetBool("Run", false);
            anim.SetBool("fall", false);
            anim.SetBool("isWallSliding", true);
        }
        else
        {
            anim.SetBool("Run", horizontalInput != 0);
            anim.SetBool("grounded", isGrounded());
            anim.SetBool("fall", body.linearVelocity.y < -0.1f && !isGrounded());
            anim.SetBool("isWallSliding", false);
            anim.SetBool("canClimb", canClimb);
        }
    }

    private bool isGrounded()
    {
        RaycastHit2D raycastHit = Physics2D.BoxCast(
            capsuleCollider.bounds.center,
            capsuleCollider.bounds.size,
            0f,
            Vector2.down,
            0.1f,
            groundLayer
        );
        return raycastHit.collider != null;
    }

    private void OnDrawGizmos()
    {
        if (capsuleCollider == null) return;
        Gizmos.color = Color.red;
        Vector3 direction3 = Application.isPlaying ? new Vector3(Mathf.Sign(transform.localScale.x), 0, 0) : Vector3.right;
        Gizmos.DrawLine(capsuleCollider.bounds.center, capsuleCollider.bounds.center + direction3 * wallCheckDistance);
    }
}