using UnityEngine;
using Mirror;
using static Mirror.NetworkIdentity;
using static Mirror.NetworkBehaviour;

[RequireComponent(typeof(Rigidbody))]
public class Movement : NetworkBehaviour
{
    [Header("Movement")] public float walkSpeed = 5f;
    public float sprintSpeed = 8f;
    public float crouchSpeed = 2.5f;
    public float jumpForce = 7f;
    public float airControl = 0.5f;
    public float groundDrag = 5f;
    public float airDrag = 1f;

    [Header("Ground Check")] public Transform groundCheck;
    public float groundDistance = 0.4f;
    public LayerMask groundMask;

    [Header("References")] public PlayerCameraController cameraController;
    public Transform orientation;

    private Rigidbody rb;
    private Vector3 moveDirection;
    private bool isGrounded;
    private float originalHeight;
    private Vector3 originalScale;
    private bool isCursorLocked = true;

    private void Start()
    {
        if (!isLocalPlayer) return;
        
        rb = GetComponent<Rigidbody>();
        rb.freezeRotation = true;
        originalHeight = transform.localScale.y;
        originalScale = transform.localScale;
        
        if (cameraController == null) cameraController = GetComponentInChildren<PlayerCameraController>();
        if (orientation == null) CreateOrientation();
        
        UpdateCursorState();
    }

    private void CreateOrientation()
    {
        orientation = new GameObject("Orientation").transform;
        orientation.parent = transform;
        orientation.localPosition = Vector3.zero;
        orientation.localRotation = Quaternion.identity;
    }

    private void Update()
    {
        if (!isLocalPlayer) return;

        HandleCursorToggle();
        isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
        
        if (cameraController != null) 
            orientation.rotation = Quaternion.Euler(0, cameraController.transform.eulerAngles.y, 0);
        
        HandleJump();
        HandleCrouch();
        SpeedControl();
        ApplyDrag();
    }

    private void FixedUpdate()
    {
        if (!isLocalPlayer) return;
        MovePlayer();
    }

    private void HandleCursorToggle()
    {
        if (Input.GetKeyDown(KeyCode.LeftAlt) || Input.GetKeyDown(KeyCode.RightAlt))
        {
            ToggleCursor();
        }
    }

    private void ToggleCursor()
    {
        isCursorLocked = !isCursorLocked;
        UpdateCursorState();
    }

    private void UpdateCursorState()
    {
        Cursor.lockState = isCursorLocked ? CursorLockMode.Locked : CursorLockMode.None;
        Cursor.visible = !isCursorLocked;
        
        if (cameraController != null)
            cameraController.enabled = isCursorLocked;
    }

    private void MovePlayer()
    {
        float horizontal = Input.GetAxisRaw("Horizontal");
        float vertical = Input.GetAxisRaw("Vertical");
        moveDirection = orientation.forward * vertical + orientation.right * horizontal;
        moveDirection = Vector3.ProjectOnPlane(moveDirection, Vector3.up).normalized;

        float currentSpeed = GetCurrentSpeed();
        float speedMultiplier = isGrounded ? 1f : airControl;
        rb.AddForce(moveDirection * currentSpeed * 10f * speedMultiplier, ForceMode.Force);
    }

    private float GetCurrentSpeed()
    {
        if (Input.GetKey(KeyCode.LeftControl)) return crouchSpeed;
        if (Input.GetKey(KeyCode.LeftShift)) return sprintSpeed;
        return walkSpeed;
    }

    private void HandleJump()
    {
        if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
        {
            rb.linearVelocity = new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z);
            rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
            if (cameraController != null) cameraController.JumpEffect();
        }
    }

    private void HandleCrouch()
    {
        bool shouldCrouch = Input.GetKey(KeyCode.LeftControl);
        float targetHeight = shouldCrouch ? originalHeight * 0.5f : originalHeight;
        Vector3 newScale = new Vector3(originalScale.x, targetHeight, originalScale.z);
        transform.localScale = Vector3.Lerp(transform.localScale, newScale, 10f * Time.deltaTime);
        if (cameraController != null) cameraController.SetCrouch(shouldCrouch);
    }

    private void SpeedControl()
    {
        Vector3 flatVel = new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z);
        float currentMaxSpeed = GetCurrentSpeed();
        
        if (flatVel.magnitude > currentMaxSpeed)
        {
            Vector3 limitedVel = flatVel.normalized * currentMaxSpeed;
            rb.linearVelocity = new Vector3(limitedVel.x, rb.linearVelocity.y, limitedVel.z);
        }
        
        if (rb.linearVelocity.y > 10f) rb.linearVelocity = new Vector3(rb.linearVelocity.x, 10f, rb.linearVelocity.z);
    }

    private void ApplyDrag() => rb.linearDamping = isGrounded ? groundDrag : airDrag;

    private void OnDrawGizmosSelected()
    {
        if (groundCheck != null)
        {
            Gizmos.color = Color.yellow;
            Gizmos.DrawWireSphere(groundCheck.position, groundDistance);
        }
    }
}