using System;
using System.Collections;
using UnityEngine;
using Random = UnityEngine.Random;

namespace YachtDice.Dice
{
    /// <summary>
    /// Красиво подбрасывает и раскручивает кубик, ждёт пока он остановится,
    /// плавно доворачивает до ровного положения и сообщает результат.
    /// </summary>
    public class DiceRoller : MonoBehaviour
    {
        [Header("References")]
        [SerializeField] private Dice dice;
        [SerializeField] private Rigidbody rb;

        /// <summary>Определение типа дайса (назначается в инспекторе).</summary>
        [field: SerializeField] public DieDefinitionSO Definition { get; private set; }

        [Header("Throw Settings")]
        [Tooltip("Сила подброса вверх")]
        [SerializeField] private float throwUpForce = 6f;

        [Tooltip("Горизонтальный разброс")]
        [SerializeField] private float throwScatter = 1.5f;

        [Tooltip("Минимальный крутящий момент")]
        [SerializeField] private float torqueMin = 15f;

        [Tooltip("Максимальный крутящий момент")]
        [SerializeField] private float torqueMax = 30f;

        [Header("Settle Detection")]
        [Tooltip("Порог скорости, ниже которого кубик считается остановившимся")]
        [SerializeField] private float settleSpeed = 0.05f;

        [Tooltip("Сколько секунд кубик должен быть неподвижен, чтобы засчитать остановку")]
        [SerializeField] private float settleDelay = 0.3f;

        [Header("Snap Alignment")]
        [Tooltip("Длительность плавного доворота к ровному положению")]
        [SerializeField] private float snapDuration = 0.35f;

        [SerializeField]
        private AnimationCurve snapCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
        
        /// <summary>
        /// Вызывается когда кубик полностью остановился. Аргумент — выпавшее значение.
        /// </summary>
        public event Action<int> OnRollFinished;
        
        /// <summary>
        /// Идёт ли сейчас бросок.
        /// </summary>
        public bool IsRolling { get; private set; }

        private Coroutine _rollRoutine;

        private void Reset()
        {
            dice = GetComponent<Dice>();
            rb   = GetComponent<Rigidbody>();
        }


        /// <summary>
        /// Бросить кубик. Повторный вызов во время броска игнорируется.
        /// </summary>
        public void Roll()
        {
            if (IsRolling) return;

            if (_rollRoutine != null)
                StopCoroutine(_rollRoutine);

            _rollRoutine = StartCoroutine(RollSequence());
        }
        
        private IEnumerator RollSequence()
        {
            IsRolling = true;

            // ── 1. Подготовка ────────────────────────────────────────────
            rb.isKinematic  = false;
            rb.useGravity   = true;
            rb.linearVelocity  = Vector3.zero;
            rb.angularVelocity = Vector3.zero;

            // ── 2. Импульс: подбросить вверх с лёгким разбросом ─────────
            Vector3 force = new Vector3(
                Random.Range(-throwScatter, throwScatter),
                throwUpForce,
                Random.Range(-throwScatter, throwScatter)
            );
            rb.AddForce(force, ForceMode.Impulse);

            // ── 3. Случайный крутящий момент — красивое вращение ────────
            Vector3 torque = Random.onUnitSphere * Random.Range(torqueMin, torqueMax);
            rb.AddTorque(torque, ForceMode.Impulse);

            // Даём кубику взлететь
            yield return new WaitForSeconds(0.2f);

            // ── 4. Ждём пока кубик успокоится ───────────────────────────
            float stillTimer = 0f;
            float sqrThreshold = settleSpeed * settleSpeed;

            while (stillTimer < settleDelay)
            {
                yield return new WaitForFixedUpdate();

                bool isSlow = rb.linearVelocity.sqrMagnitude  < sqrThreshold
                           && rb.angularVelocity.sqrMagnitude < sqrThreshold;

                stillTimer = isSlow ? stillTimer + Time.fixedDeltaTime : 0f;
            }

            // ── 5. Замораживаем физику и читаем верхнюю грань ───────────
            rb.linearVelocity  = Vector3.zero;
            rb.angularVelocity = Vector3.zero;
            rb.isKinematic     = true;

            if (!dice.TryGetTopValue(out int topValue))
            {
                Debug.LogWarning("DiceRoller: не удалось определить верхнюю грань.");
                IsRolling = false;
                yield break;
            }

            // ── 6. Плавный доворот до ровного положения ─────────────────
            Quaternion startRot = transform.rotation;

            // Вычисляем целевой поворот через Dice.AlignToTopByLocalAngles
            dice.AlignToTopByLocalAngles();
            Quaternion targetRot = transform.rotation;

            // Откатываемся обратно — будем интерполировать
            transform.rotation = startRot;

            float elapsed = 0f;
            while (elapsed < snapDuration)
            {
                elapsed += Time.deltaTime;
                float t = snapCurve.Evaluate(Mathf.Clamp01(elapsed / snapDuration));
                transform.rotation = Quaternion.Slerp(startRot, targetRot, t);
                yield return null;
            }
            transform.rotation = targetRot;

            // ── 7. Готово ───────────────────────────────────────────────
            IsRolling = false;
            OnRollFinished?.Invoke(topValue);

            // Debug.Log($"{gameObject.name} | Выпало: <b>{topValue}</b>");
        }
    }
}
