using System;
using Intrepid.Utilities;
using UnityEngine;

namespace Intrepid.Gameplay.Entities.Player.Sentinel.Cadence
{
    [Serializable]
    [CreateAssetMenu(fileName = "Core Stability Service", menuName = "GON/Services/Sentinel/Core Stability Service")]
    public class CoreStabilityService : SingletonScriptableObject<CoreStabilityService>
    {
        [GroupConfigurations, SerializeField, Min(1f)] private float maxStability = 100f;
        [GroupConfigurations, SerializeField, Range(0f, 1f)] private float unstableThresholdP = 0.7f;
        [GroupConfigurations, SerializeField, Range(0f, 1f)] private float criticalThresholdP = 0.35f;
        [GroupConfigurations, SerializeField, Min(0f)] private float collapsedRecoveryStability = 25f;
        [GroupConfigurations, SerializeField, Min(0f)] private float naturalRecoveryPerSecond = 3f;
        
        public float MaxStability => maxStability;

        public float EffectiveStability { get; private set; }
        public float ProjectedStability { get; private set; }
        public float EffectiveStabilityP => maxStability <= 0f
            ? 0f
            : EffectiveStability / maxStability;
        public float ProjectedStabilityP => maxStability <= 0f
            ? 0f
            : ProjectedStability / maxStability;

        private bool _hasProjectedCost;

        public CoreStabilityState State
        {
            get
            {
                if (EffectiveStability <= 0f)
                {
                    return CoreStabilityState.Collapsed;
                }

                var p = EffectiveStabilityP;

                if (p <= criticalThresholdP)
                {
                    return CoreStabilityState.Critical;
                }

                if (p <= unstableThresholdP)
                {
                    return CoreStabilityState.Unstable;
                }

                return CoreStabilityState.Stable;
            }
        }

        public bool IsCollapsed => State == CoreStabilityState.Collapsed;

        protected override void WhenInstantiate(CoreStabilityService s)
        {
            base.WhenInstantiate(s);
            ResetCore();
        }

        public void ResetCore()
        {
            EffectiveStability = maxStability;
            ProjectedStability = maxStability;

            _hasProjectedCost = false;
        }

        public void ProjectCost(float maxCost)
        {
            maxCost = Mathf.Max(0f, maxCost);

            var baseStability = _hasProjectedCost
                ? Mathf.Min(EffectiveStability, ProjectedStability)
                : EffectiveStability;

            ProjectedStability = Mathf.Clamp(
                baseStability - maxCost,
                0f,
                maxStability
            );

            _hasProjectedCost = true;
        }

        public void Settle(CadenceEvaluation evaluation)
        {
            var settlementFactor = Mathf.Clamp01(evaluation.SettlementFactor);
            var recovery = Mathf.Max(0f, evaluation.Recovery);

            EffectiveStability = Mathf.Lerp(
                EffectiveStability,
                ProjectedStability,
                settlementFactor
            );

            if (recovery > 0f)
            {
                EffectiveStability = Mathf.Clamp(
                    EffectiveStability + recovery,
                    0f,
                    maxStability
                );
            }

            ProjectedStability = EffectiveStability;
            _hasProjectedCost = false;
        }

        public void ApplyDamage(float damage)
        {
            damage = Mathf.Max(0f, damage);

            EffectiveStability = Mathf.Clamp(
                EffectiveStability - damage,
                0f,
                maxStability
            );

            ProjectedStability = Mathf.Min(ProjectedStability, EffectiveStability);
        }

        public void Recover(float amount)
        {
            amount = Mathf.Max(0f, amount);

            EffectiveStability = Mathf.Clamp(
                EffectiveStability + amount,
                0f,
                maxStability
            );

            if (!_hasProjectedCost)
            {
                ProjectedStability = EffectiveStability;
            }
        }

        public void RecoverFromCollapse()
        {
            EffectiveStability = Mathf.Clamp(
                collapsedRecoveryStability,
                0f,
                maxStability
            );

            ProjectedStability = EffectiveStability;
            _hasProjectedCost = false;
        }

        public void UpdateCore(float deltaTime)
        {
            if (deltaTime <= 0f)
            {
                return;
            }

            if (IsCollapsed)
            {
                return;
            }

            RecoverNaturally(deltaTime);

            if (!_hasProjectedCost)
            {
                ProjectedStability = EffectiveStability;
            }
        }

        private void RecoverNaturally(float deltaTime)
        {
            if (EffectiveStability >= maxStability)
            {
                return;
            }

            EffectiveStability = Mathf.Clamp(
                EffectiveStability + naturalRecoveryPerSecond * deltaTime,
                0f,
                maxStability
            );
        }

        private void OnValidate()
        {
            maxStability = Mathf.Max(1f, maxStability);

            unstableThresholdP = Mathf.Clamp01(unstableThresholdP);
            criticalThresholdP = Mathf.Clamp01(criticalThresholdP);

            if (criticalThresholdP > unstableThresholdP)
            {
                criticalThresholdP = unstableThresholdP;
            }

            collapsedRecoveryStability = Mathf.Clamp(
                collapsedRecoveryStability,
                0f,
                maxStability
            );

            naturalRecoveryPerSecond = Mathf.Max(0f, naturalRecoveryPerSecond);
        }
    }
}