using System;
using System.Collections;
using JetBrains.Annotations;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;
#if ODIN_INSPECTOR
#endif

namespace JohnsonUtils.Canvases
{
    public class SliderUIComponent : UISelectableComponentBase
    {
        [UsedImplicitly] public event Action<float> OnValueChanged;

#if ODIN_INSPECTOR
        [Required]
#endif
        [Header("Association"), SerializeField]
        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;

        [UsedImplicitly]
        public float Value
        {
            get => _currentValue;
            set
            {
                if (isChangeValueAnimated)
                {
                    _targetValue = value;
                    StartAnimation();
                }
                else
                {
                    _currentValue = value;
                    slider.value = value;
                }
            }
        }
        
        [UsedImplicitly]
        public float MaxValue
        {
            get => slider.maxValue;
            set => slider.maxValue = value;
        }
        
        [UsedImplicitly]
        public Color BackgroundColor
        {
            set
            {
                if (backgroundImage != null)
                {
                    backgroundImage.color = value;
                }
            }
        }

        [UsedImplicitly]
        public Color FillColor
        {
            set
            {
                if (fillImage != null)
                {
                    _initialColor = value;
                    fillImage.color = _initialColor;
                }
            }
        }

        [UsedImplicitly]
        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>();
        }
    }
}