using System;
using System.Collections;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;

namespace Canvases.Components
{
    // ReSharper disable once InconsistentNaming Reason: UI should be capitalized
    public class SliderUIComponent : UISelectableComponentBase
    {
        public event Action<float> OnValueChanged;

        [Header("Association"), SerializeField, Required]
        private Slider slider;

        [SerializeField] private Image backgroundImage;
        [SerializeField] private Image fillImage;
        [SerializeField] private Image frame;

        [Header("Animation"), SerializeField] private bool isChangeValueAnimated;
        [SerializeField] [Range(0f, 1f)] private float animationSpeed = 0.05f;
        [SerializeField] private Color valueIncreaseColor = Color.green;
        [SerializeField] private Color valueDecreaseColor = Color.red;

        private float targetValue;
        private float currentValue;

        private Coroutine animationRoutine;
        private Color initialColor = Color.black;

        public float Value
        {
            get => currentValue;
            set
            {
                if (isChangeValueAnimated)
                {
                    targetValue = value;
                    StartAnimation();
                }
                else
                {
                    currentValue = value;
                    slider.value = value;
                }
            }
        }

        public float MaxValue
        {
            get => slider.maxValue;
            set => slider.maxValue = value;
        }

        public Color BackgroundColor
        {
            set
            {
                if (backgroundImage != null)
                {
                    backgroundImage.color = value;
                }
            }
        }

        public Color FillColor
        {
            set
            {
                if (fillImage != null)
                {
                    initialColor = value;
                    fillImage.color = initialColor;
                }
            }
        }

        public Color FrameColor
        {
            set
            {
                if (frame != null)
                {
                    frame.color = value;
                }
            }
        }

        private void Start()
        {
            Debug.Assert(slider, $"A {nameof(slider)} must be assigned to a {nameof(SliderUIComponent)}");
            slider.onValueChanged.AddListener(OnSliderChanged);
        }

        protected override void SetSelectableGameObject() => Selectable = slider;

        protected override void OnDestroy()
        {
            slider.onValueChanged.RemoveListener(OnSliderChanged);

            base.OnDestroy();
        }

        private void StartAnimation()
        {
            if (animationRoutine != null) 
                StopCoroutine(animationRoutine);
            
            animationRoutine = StartCoroutine(AnimateValueChange(targetValue > currentValue));
        }

        private IEnumerator AnimateValueChange(bool isValueChangePositive)
        {
            if (isValueChangePositive)
            {
                fillImage.color = valueIncreaseColor;

                while (currentValue < targetValue)
                {
                    currentValue += animationSpeed * Time.deltaTime;
                    slider.value = currentValue;
                    yield return null;
                }
            }
            else
            {
                fillImage.color = valueDecreaseColor;

                while (currentValue > targetValue)
                {
                    currentValue -= animationSpeed * Time.deltaTime;
                    slider.value = currentValue;
                    yield return null;
                }
            }

            currentValue = targetValue;
            slider.value = currentValue;
            fillImage.color = initialColor;
        }

        private void OnSliderChanged(float value) => OnValueChanged?.Invoke(value);

        private void OnValidate()
        {
            if (!slider)
                slider = GetComponent<Slider>();
        }
    }
}