using System;
using AYellowpaper.SerializedCollections;
using UnityEngine;
using UniRx;

namespace SpillMechanic
{
    /// <summary>
    /// 
    /// This class is responsible for the water spill removal interaction.
    /// It requires the player to click on the area of the water spill and scrub at a certain rate (fastSpeedThreshold).
    /// We are using hysteresisFactor to compensate flickering when on the edge of the fastSpeedThreshold.
    ///
    /// The speed of the water spill going away is dictated by waterSpillAnimationSpeedMultiplier.
    /// 
    /// (TODO: find solution for two possible colliders to live at the same time. One for the mouse movement,
    /// so the player does not scrub to far away and one for grandma interaction.) 
    /// </summary>
    [RequireComponent(typeof(Animator))]
    [RequireComponent(typeof(BoxCollider2D))]
    public class WaterSpill : Hazard
    {
        [Header("Water Spill")]
        [SerializeField] private Animator waterSpillAnimator;
        [SerializeField] private float waterSpillAnimationSpeedMultiplier;
        
        [Header("Particle System")]
        [SerializeField] private GameObject bubblesPS;
        public float fastSpeedThreshold = 0.2f;

        [SerializedDictionary("ParticleSystem", "RateOverTime")]
        public SerializedDictionary<ParticleSystem, float> ratesOverTimePS;
        
        // Hysteresis is needed, as otherwise the bool will flicker between true and false, when on the edge. 
        [Header("Hysteresis")]
        [SerializeField, Range(0f, 0.90f)] private float hysteresisFactor = 0.7f; 
        
        private void OnEnable()
        {
            // TODO: This is just stupid, fix it someday...
            bubblesPS = GameObject.Find("NormalBubble_DiscreteSizeChange");
            
            // prefabs seem not to be ok here...so we need to add them by hand, I hate it so much
            ratesOverTimePS.Clear();

            var particleSystems = bubblesPS.GetComponentsInChildren<ParticleSystem>();
         
            ratesOverTimePS.Add(particleSystems[0], 4);
            ratesOverTimePS.Add(particleSystems[1], 4);
        }
        
        private void Start()
{
    waterSpillAnimator.speed = 0;
    
    var mouseMovement = Observable.EveryUpdate()
        .Select(_ => new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")));

    float smoothedSpeed = 0f;
    float smoothing = 0.05f; // Adjust this — lower = smoother but less responsive
    
    bool isFast = false;
    float onThresh = fastSpeedThreshold;
    float offThresh = fastSpeedThreshold * hysteresisFactor;
    
    mouseMovement
        .Where(_ => isDragging)
        .Subscribe(delta =>
        {
            // Smooth the speed instead of using raw values
            float rawSpeed = delta.magnitude;
            smoothedSpeed = Mathf.Lerp(smoothedSpeed, rawSpeed, smoothing);
            
            bool wasFast = isFast;
            
            if (!isFast && smoothedSpeed >= onThresh) isFast = true;
            else if (isFast && smoothedSpeed < offThresh) isFast = false;
            
            if (!wasFast && isFast)
            {
                AudioManager.Instance.PlayCleanPuddle();
            }
            else if (wasFast && !isFast)
            {
                AudioManager.Instance.PauseCleanPuddle();
            }
            
            if (isFast)
            {
                AnimatorStateInfo stateInfo = waterSpillAnimator.GetCurrentAnimatorStateInfo(0);
                var progress = stateInfo.normalizedTime;
                if (progress >= 0.99)
                {
                    AudioManager.Instance.PauseCleanPuddle();
                    if (PlayerInteraction.Instance != null)
                    {
                        PlayerInteraction.Instance.StopInteraction();
                    }
                    SetEmissionRatesOverTime(true);
                    Destroy(gameObject);
                    return;
                }
                
                SetEmissionRatesOverTime(false);
                bubblesPS.transform.position = transform.position;
                waterSpillAnimator.speed = 1 * waterSpillAnimationSpeedMultiplier;
            }
            else
            {
                SetEmissionRatesOverTime(true);
                waterSpillAnimator.speed = 0;
            }

        }).AddTo(this);
    }   


        #region MouseColliderInteraction
        private void OnMouseDown()
        {
            if (PlayerInteraction.Instance != null && !isDragging)
            {
                PlayerInteraction.Instance.StartInteraction();
            }
            isDragging = true;
        }
        private void OnMouseExit()  { if (isDragging) { isDragging = false; OnDragEnd(); } }
        private void OnMouseUp()    { if (isDragging) { isDragging = false; OnDragEnd(); } }

        /// <summary>
        /// As unity does not offer a OnDragEnd/Exit we are doing this with the update call via a flag.
        /// </summary>
        public override void OnDragEnd()
        {
            waterSpillAnimator.speed = 0;
            SetEmissionRatesOverTime(true);
            AudioManager.Instance.PauseCleanPuddle();

            if (PlayerInteraction.Instance != null)
            {
                PlayerInteraction.Instance.StopInteraction();
            }
        }
        #endregion

        #region ParticleEffectsHelperFunctions
        /// <summary>
        /// Sets the rateOverTime value of a given ParticleSystem emission module.
        /// </summary>
        /// <param name="ps">The ParticleSystem in question.</param>
        /// <param name="rate">The rateOverTime of the emission module to be set.</param>
        private void SetEmissionRateOverTime(ParticleSystem ps, float rate)
        {
            ParticleSystem.EmissionModule emissionModule = ps.emission;
            emissionModule.rateOverTime = rate;
        }

        /// <summary>
        /// Sets the rateOverTime value of the ParticleSystems emission modules.
        /// If stop is set to true the values are set to 0, otherwise the values from the dictionary are used.
        /// </summary>
        /// <param name="stop">If set to true, the particles emission module is set to 0, i.e. no emission.</param>
        private void SetEmissionRatesOverTime(bool stop)
        {
            foreach (var psRateOverTime in ratesOverTimePS)
            {
                float rate = !stop ? psRateOverTime.Value : 0;
                SetEmissionRateOverTime(psRateOverTime.Key, rate);
            }
        }
        #endregion
        
    }
}
