using UnityEngine;

[RequireComponent(typeof(CharacterController))]
public class charecter : MonoBehaviour
{
    [Header("Movement")]
    public float walkSpeed   = 5f;
    public float runSpeed    = 9f;
    public float crouchSpeed = 2.5f;
    public float jumpHeight  = 1.5f;
    public float gravity     = -9.81f;

    [Header("References")]
    [Tooltip("Head BONE of the character.")]
    public Transform head;

    [Header("Look")]
    public float mouseSensitivity = 2f;
    public float maxPitch         = 80f;

    [Header("Body Turn")]
    public float maxHeadYaw     = 60f;
    public float bodyAlignSpeed = 300f;

    // ── private ──────────────────────────────────────────────────────────────
    CharacterController _cc;
    Animator            _anim;

    float _verticalVelocity;
    float _bodyYaw;
    float _headYaw;
    float _pitch;
    Quaternion _headBindPose;
    bool _jumpRequested = false;

    // Animator parameter hashes — no hardcoded strings at runtime
    static readonly int HashSpeed       = Animator.StringToHash("Speed");
    static readonly int HashIsMoving    = Animator.StringToHash("IsMoving");
    static readonly int HashIsCrouching = Animator.StringToHash("IsCrouching");
    static readonly int HashIsGrounded  = Animator.StringToHash("IsGrounded");
    static readonly int HashJump        = Animator.StringToHash("Jump");

    // ── Unity lifecycle ───────────────────────────────────────────────────────
    void Start()
    {
        _cc   = GetComponent<CharacterController>();
        _anim = GetComponentInChildren<Animator>();
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible   = false;

        _bodyYaw      = transform.eulerAngles.y;
        _headYaw      = _bodyYaw;
        _headBindPose = head != null ? head.localRotation : Quaternion.identity;

        // Убедиться что PlayerInput существует
        if (PlayerInput.Instance == null)
        {
            var go = new GameObject("PlayerInput");
            go.AddComponent<PlayerInput>();
        }

        PlayerInput.Instance.OnPickupPressed += TryPickupNearbyItem;
        PlayerInput.Instance.OnJumpPressed   += () => { _jumpRequested = true; };
    }

    void Update()
    {
        HandleLook();
        HandleMovement();
    }

    void LateUpdate()
    {
        ApplyRotations();
    }

    // ── Item Pickup ───────────────────────────────────────────────────────────
    void TryPickupNearbyItem()
    {
        float pickupRadius = 2.5f;
        Collider[] colliders = Physics.OverlapSphere(transform.position, pickupRadius);

        WorldItem closestItem = null;
        float closestDist = float.MaxValue;

        foreach (var col in colliders)
        {
            var worldItem = col.GetComponent<WorldItem>();
            if (worldItem == null) continue;

            float dist = Vector3.Distance(transform.position, worldItem.transform.position);
            if (dist < closestDist && dist <= worldItem.interactRange)
            {
                closestDist  = dist;
                closestItem  = worldItem;
            }
        }

        if (closestItem != null)
            closestItem.TryPickupByButton();
    }

    // ── Mouse look ────────────────────────────────────────────────────────────
    void HandleLook()
    {
        var input = PlayerInput.Instance;
        if (input != null)
        {
            _headYaw += input.LookInput.x * mouseSensitivity;
            _pitch   -= input.LookInput.y * mouseSensitivity;
        }
        _pitch = Mathf.Clamp(_pitch, -maxPitch, maxPitch);
    }

    // ── Movement ──────────────────────────────────────────────────────────────
    void HandleMovement()
    {
        var input = PlayerInput.Instance;
        float h = input != null ? input.MoveInput.x : 0f;
        float v = input != null ? input.MoveInput.y : 0f;

        bool isCrouching = input != null && input.Crouch;
        bool isRunning   = input != null && input.Run && !isCrouching;
        bool isMoving    = (h * h + v * v) > 0.01f;

        float speed = isCrouching ? crouchSpeed : isRunning ? runSpeed : walkSpeed;

        // Body aligns toward camera when moving
        if (isMoving)
            _bodyYaw = Mathf.MoveTowardsAngle(_bodyYaw, _headYaw, bodyAlignSpeed * Time.deltaTime);

        // Head-drag clamp
        float offset        = Mathf.DeltaAngle(_bodyYaw, _headYaw);
        float clampedOffset = Mathf.Clamp(offset, -maxHeadYaw, maxHeadYaw);
        _bodyYaw += offset - clampedOffset;
        _headYaw  = _bodyYaw + clampedOffset;

        // Jump
        if (_cc.isGrounded)
        {
            if (_verticalVelocity < 0f) _verticalVelocity = -2f;

            if (_jumpRequested && !isCrouching)
            {
                _verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity);
                _anim?.SetTrigger(HashJump);
            }
            _jumpRequested = false;
        }

        _verticalVelocity += gravity * Time.deltaTime;

        // Move
        Quaternion headRot = Quaternion.Euler(0f, _headYaw, 0f);
        Vector3 moveDir    = (headRot * Vector3.forward * v +
                              headRot * Vector3.right   * h).normalized;
        moveDir.y = _verticalVelocity;
        _cc.Move(moveDir * speed * Time.deltaTime);

        // Animator
        if (_anim != null)
        {
            float animSpeed = !isMoving ? 0f : isRunning ? 1f : 0.5f;
            _anim.SetFloat(HashSpeed,       animSpeed,   0.1f, Time.deltaTime);
            _anim.SetBool (HashIsMoving,    isMoving);
            _anim.SetBool (HashIsCrouching, isCrouching);
            _anim.SetBool (HashIsGrounded,  _cc.isGrounded);
        }
    }

    // ── Apply bone rotations ──────────────────────────────────────────────────
    void ApplyRotations()
    {
        transform.rotation = Quaternion.Euler(0f, _bodyYaw, 0f);

        if (head == null) return;

        float      headOffset = Mathf.DeltaAngle(_bodyYaw, _headYaw);
        Quaternion yawRot     = Quaternion.Euler(0f, headOffset, 0f);
        Quaternion pitchRot   = Quaternion.Euler(_pitch, 0f, 0f);
        head.localRotation    = _headBindPose * yawRot * pitchRot;
    }
}
