using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerMovement : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb; 
    [SerializeField] private SpriteRenderer spriteRenderer; 
    [SerializeField] private Animator animator;

    [Header("Debug")]
    public float horizontal = 0f; 
    public float vertical = 0f;
    
    public float currentSpeed;
    public Text currentSpeedText;

    [Header("Movement")]
    public float maxSpeed = 45;
    public float speed;
    public float accel;
    public float decel;

    [SerializeField] private float turnAccel = 40f;

    [Header("Jump")]
    public float jumpForce;
    [SerializeField] private float airAccel = 10f;

    [Header("Ground Checks")]
    [SerializeField] private Transform groundCheck;
    [SerializeField] private float groundCheckRadius = 0.2f;
    [SerializeField] private LayerMask groundLayer;

    private bool isGrounded = false;

    [Header("Coyote Time")]
    [SerializeField] private float coyoteTime;
    private float coyoteTimeCounter;

    [Header("Jump Buffering")]
    [SerializeField] private float jumpBufferTime;
    private float jumpBufferCounter;

    [Header("Jump Cut")]
    [SerializeField] private float jumpCutMultiplier = 0.5f;

    [Header("Fall Multiplier")]
    [SerializeField] private float fallMultiplier;

    [Header("BHop")]
    [SerializeField] private float bhopWindow = 0.05f; 
    [SerializeField] private float bhopBoost = 1.05f;    
    [SerializeField] private float maxBhopMultiplier = 2.0f; 

    private int bhopChain = 0; 
    private float bhopMultiplier = 1f;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {
        if (horizontal > 0)
        {
            spriteRenderer.flipX = false;
        }
        else if (horizontal < 0)
        {
            spriteRenderer.flipX = true;
        }  
    
        horizontal = Input.GetAxisRaw("Horizontal");
        vertical = Input.GetAxisRaw("Vertical");

        currentSpeed = rb.velocity.magnitude;

        currentSpeedText.text = "CurrentSpeed: " + currentSpeed;

        if (rb.velocity.magnitude > maxSpeed)
        {
            rb.velocity = rb.velocity.normalized * maxSpeed;
        }

        isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
    
        // coyote time check
        if (isGrounded)
        {
            coyoteTimeCounter = coyoteTime;

            rb.velocity = new Vector2(rb.velocity.x, Mathf.Max(rb.velocity.y, -2f));
        }
        else
        {
            coyoteTimeCounter -= Time.deltaTime;
        }

        // jump buffer time check
        if (Input.GetButtonDown("Jump"))
        {
            jumpBufferCounter = jumpBufferTime;
        }
        else
        {
            jumpBufferCounter -= Time.deltaTime;
        }

        // jump buffer time check
        if (Input.GetButtonUp("Jump") && rb.velocity.y > 0f)
        {
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * jumpCutMultiplier);
        }

        // does the juump
        if (jumpBufferCounter > 0f && coyoteTimeCounter > 0f)
        {
            Jump();
            
            jumpBufferCounter = 0f;
        }

        // more gravity when fall
        if (rb.velocity.y < 0) 
        {
            rb.velocity += Vector2.up * Physics2D.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
        }

        // Update horizontal input
        horizontal = Input.GetAxisRaw("Horizontal");

        // Pass speed to animator
        animator.SetFloat("Speed", Mathf.Abs(rb.velocity.x));

        // Adjust animation speed based on movement
        float animSpeed = Mathf.Abs(rb.velocity.x) / speed; 
        animator.speed = Mathf.Clamp(animSpeed, 0.5f, 2f);
    }

    void FixedUpdate()
    {
        float targetX = horizontal * speed * bhopMultiplier;
        float accelRate;

        if (Mathf.Abs(horizontal) > 0.01f)
        {
            if (isGrounded)
            {
                // Stronger turning accel on ground
                if (Mathf.Sign(horizontal) != Mathf.Sign(rb.velocity.x) && Mathf.Abs(rb.velocity.x) > 0.1f)
                    accelRate = turnAccel;
                else
                    accelRate = accel;
            }
            else
            {
                // In air → weaker accel
                accelRate = airAccel;
            }

            float newX = Mathf.MoveTowards(rb.velocity.x, targetX, accelRate * Time.fixedDeltaTime);
            rb.velocity = new Vector2(newX, rb.velocity.y);
        }
        else if (isGrounded)
        {
            float newX = Mathf.MoveTowards(rb.velocity.x, 0, decel * Time.fixedDeltaTime);
            rb.velocity = new Vector2(newX, rb.velocity.y);
        }
    }

    public void Jump()
    {
        bool perfectBhop = (coyoteTimeCounter < bhopWindow && rb.velocity.magnitude > speed * 0.8f);

        if (perfectBhop)
        {
            bhopChain++; 
            bhopMultiplier = Mathf.Min(bhopMultiplier * bhopBoost, maxBhopMultiplier); 
            
            rb.velocity = new Vector2(rb.velocity.x * bhopMultiplier, jumpForce);
        }
        else
        {
            bhopChain = 0; 
            bhopMultiplier = 1f;

            rb.velocity = new Vector2(rb.velocity.x, jumpForce);
        }
    }

}