﻿using UnityEngine;

/// <summary>
/// Gère la physique, le déplacement et la gravité du joueur.
/// Reçoit les ordres de PlayerController et applique les forces via CharacterController.
/// </summary>
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(PlayerStat))]
public class PlayerMotor : MonoBehaviour
{
    // ── Références ────────────────────────────────────────────────────────────
    private CharacterController _cc;
    private PlayerStat _stat;
    private Transform _cameraTransform;

    // ── État interne ──────────────────────────────────────────────────────────
    private Vector3 _velocity;       // Vélocité verticale accumulée (gravité/saut)
    private Vector2 _moveInput;      // Stocké par PlayerController, consommé ici
    private bool _jumpRequested;  // Flag posé par Jump(), consommé dans Update()
    private bool _isGrounded;
    private float _cameraPitch;

    // ─────────────────────────────────────────────────────────────────────────
    // Init
    // ─────────────────────────────────────────────────────────────────────────
    private void Awake()
    {
        _cc = GetComponent<CharacterController>();
        _stat = GetComponent<PlayerStat>();
    }

    private void Start()
    {
        Camera cam = GetComponentInChildren<Camera>();
        if (cam != null)
            _cameraTransform = cam.transform;
        else
            Debug.LogError("[PlayerMotor] Aucune Camera trouvée en enfant du joueur !");
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Update — toute la physique centralisée ici, dans le bon ordre
    // ─────────────────────────────────────────────────────────────────────────
    private void Update()
    {
        // 1. Détection sol
        CheckGround();

        // 2. Saut — traité avant la gravité pour que _velocity.y soit positif ce frame
        if (_jumpRequested)
        {
            if (_isGrounded)
                _velocity.y = Mathf.Sqrt(2f * GetGravity() * _stat.jumpForce);

            _jumpRequested = false;
        }

        // 3. Gravité
        if (_isGrounded && _velocity.y < 0f)
            _velocity.y = -2f;          // Plaque au sol, évite l'accumulation
        else
            _velocity.y -= GetGravity() * Time.deltaTime;

        // 4. Déplacement horizontal + vertical dans un seul cc.Move()
        Vector3 horizontal = (transform.forward * _moveInput.y
                            + transform.right * _moveInput.x * _stat.strafeMultiplier);

        if (horizontal.sqrMagnitude > 1f)
            horizontal.Normalize();

        horizontal *= _stat.moveSpeed;

        _cc.Move((horizontal + Vector3.up * _velocity.y) * Time.deltaTime);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // API publique — appelée par PlayerController
    // ─────────────────────────────────────────────────────────────────────────

    /// <summary>Stocke l'input de déplacement (consommé dans Update).</summary>
    public void Move(Vector2 input) => _moveInput = input;

    /// <summary>Pose un flag de saut (traité dans Update au bon moment).</summary>
    public void Jump() => _jumpRequested = true;

    /// <summary>Rotation horizontale du corps et verticale de la caméra.</summary>
    public void RotateCamera(Vector2 mouseDelta)
    {
        transform.Rotate(Vector3.up * mouseDelta.x * _stat.mouseSensitivityX);

        _cameraPitch -= mouseDelta.y * _stat.mouseSensitivityY;
        _cameraPitch = Mathf.Clamp(_cameraPitch, -_stat.lookUpLimit, _stat.lookDownLimit);

        if (_cameraTransform != null)
            _cameraTransform.localEulerAngles = new Vector3(_cameraPitch, 0f, 0f);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Privé
    // ─────────────────────────────────────────────────────────────────────────

    private void CheckGround()
    {
        // CheckSphere au bas de la capsule + petite marge vers le bas
        Vector3 sphereCenter = transform.position
                             + _cc.center
                             - Vector3.up * (_cc.height * 0.5f - _cc.radius);

        _isGrounded = Physics.CheckSphere(
            sphereCenter,
            _cc.radius + _stat.groundCheckDistance,
            _stat.groundMask,
            QueryTriggerInteraction.Ignore
        );
    }

    private float GetGravity() => Mathf.Abs(Physics.gravity.y) * _stat.gravityMultiplier;

    // ─────────────────────────────────────────────────────────────────────────
    // Accesseurs
    // ─────────────────────────────────────────────────────────────────────────
    public bool IsGrounded => _isGrounded;
    public bool IsMoving => _moveInput.sqrMagnitude > 0.01f;
    public float VerticalVelocity => _velocity.y;
}