using System;
using Intrepid.Core.TimeControl;
using Intrepid.Diagnostics;
using Intrepid.Utilities;
using UnityEngine;

namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
    [Serializable]
    [CreateAssetMenu(fileName = "Cadence Service", menuName = "GON/Services/Cadence/Cadence Service")]
    public class CadenceService : SingletonScriptableObject<CadenceService>
    {
        private Timer CadenceProfileTimer { get; set; } = new();
        private Timer CadenceTotalTimer { get; set; } = new();
        private CadenceProfile CurrentProfile { get; set; }
        private float TargetTime { get; set; }
        private float StartedProfileAt { get; set; }
        
        public bool IsCadenceRunning => CadenceProfileTimer.IsRunning;
        private float CadenceProfileElapsedTime => (float)CadenceProfileTimer.ElapsedTime; // elapsed time since a single cadence began
        public float TotalCadenceElapsedTime => (float)CadenceTotalTimer.ElapsedTime; // elapsed time since first cadence began
        

        protected override void WhenInstantiate(CadenceService s)
        {
            base.WhenInstantiate(s);
            ResetCadenceProfile();
            CadenceTotalTimer.Stop();
        }

        public void BeginCadence(CadenceProfile profile, float targetTime)
        {
            if (profile is null || targetTime <= 0)
            {
                DLogger.LogError($"profile should not be null: (is null? {profile.IsNull()}); targetTime should be > 0: {targetTime}");
                ResetCadenceProfile();
                return;
            }
            
            StartedProfileAt = TimeSimulation.Instance.ScaledElapsedTime;
            CurrentProfile = profile;
            TargetTime = targetTime;
            CadenceProfileTimer.Start();
            if (CadenceTotalTimer.IsNotRunning) CadenceTotalTimer.Start();
        }
        
        public CadenceEvaluation EndCadence()
        {
            CadenceTotalTimer.Stop();
            
            if (!IsCadenceRunning || CurrentProfile is null) // if cadence was already ended
            {
                return CadenceEvaluation.None;
            }
            
            var cadenceEvaluation = CurrentProfile.LateEvaluation();
            ResetCadenceProfile();
            
            return cadenceEvaluation;
        }
        
        public CadenceEvaluation TryEndCadenceProfileWithInputAt(float inputAt)
        {
            if (!IsCadenceRunning || CurrentProfile is null)
            {
                return CadenceEvaluation.None;
            }
            
            var elapsedAtInput = Mathf.Max(0f, inputAt - StartedProfileAt);
            var evaluation = CurrentProfile.Evaluate(elapsedAtInput / TargetTime);
            ResetCadenceProfile();

            return evaluation;
        }
        
        public bool UpdateCadence(float deltaTime)
        {
            if (!IsCadenceRunning || CurrentProfile is null)
            {
                return false;
            }

            CadenceProfileTimer.Update(deltaTime);
            CadenceTotalTimer.Update(deltaTime);

            return true;
        }

        public void ResetCadenceProfile()
        {
            CadenceProfileTimer.Stop();
            CurrentProfile = null;
            TargetTime = 0;
        }
    }
}