using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Entities.Player.PDA.Inputs;
using Intrepid.Gameplay.Entities.Player.PDA.UI.Data;
using Intrepid.ObjectPooling;
using Intrepid.Structures;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using Sirenix.Utilities;
using UnityEngine;
using UnityEngine.UI;

namespace Intrepid.Gameplay.Entities.Player.PDA.UI
{
    public class PDASectionsUI : MonoBehaviour
    {
        [SerializeField] private PDAContentsUI contents;
        [SerializeField] private PDASectionReferenceUIPoolData poolData;
        [SerializeField] private Transform poolSpace;
        
        [SerializeField] private RectTransform rectCarousel;
        [SerializeField] private int padding = 10;
        [SerializeField] private float spacing = 75f;
        [SerializeField] private float sectionHeight = 150f;
        
        [SerializeField] private LinearAnimation linearAnimation;
        
        [SerializeField, ReadOnly] private int currentIndex = int.MaxValue;
        [SerializeField, ReadOnly] private int numberOfSections;

        private bool _animationRunning;
        private bool AnimationRunning
        {
            get => _animationRunning || contents.AnimationRunning;
            set => _animationRunning = value;
        }

        private int CurrentIndex => currentIndex; //numberOfSections - currentIndex - 1;
        public bool IsLoadingContents => AnimationRunning || contents.IsLoading;
        private SDictionary<PDASectionTag,List<PDAContentTag>> ContentTagsBySectionTag { get; set; }
        private SDictionary<int, PDASectionReferenceUI> LoadedSectionReferences { get; } = new();
        private GameObjectPool<PDASectionReferenceUI> Pool { get; set; }

        private void OnValidate()
        {
            var layoutGroup = rectCarousel.GetComponent<VerticalLayoutGroup>();
            if (layoutGroup is null) return;
            layoutGroup.padding.top = padding;
            layoutGroup.padding.bottom = padding;
            layoutGroup.padding.left = padding;
            layoutGroup.padding.right = padding;
            layoutGroup.spacing = spacing;
        }

        private void Awake()
        {
            OnValidate();
            Pool = new GameObjectPool<PDASectionReferenceUI>(poolData, poolSpace);
        }

        public void WhenPreparingSystemBoot()
        {
        }

        public void WhenStarting(ParameterStore parameters)
        {
            UpdatePositionWith(UpdatePosition());
            if (ContentTagsBySectionTag.Count <= 0) return;
            contents.PrepareEnvironment(ContentTagsBySectionTag[LoadedSectionReferences[CurrentIndex].SectionTag]);
        }
        
        public void WhenFinishing()
        {
            foreach (var sectionReference in LoadedSectionReferences.Values)
            {
                sectionReference.gameObject.transform.SetParent(poolSpace);
                Pool.KillNow(sectionReference);
            }
            Reset();
        }

        private float UpdatePosition()
        {
            if (currentIndex == int.MaxValue)
            {
                return padding + sectionHeight / 2f;
            }
            return padding + sectionHeight / 2f + CurrentIndex * (sectionHeight + spacing);
        }

        private void UpdatePositionWith(float y)
        {
            rectCarousel.anchoredPosition = new Vector2(rectCarousel.anchoredPosition.x, y);
        }

        private IEnumerator AnimateUpdatePositionUp(float yFinal)
        {
            return linearAnimation.Animate(rectCarousel.anchoredPosition.y, yFinal,
                di => -di, 
                UpdatePositionWith,
                r => AnimationRunning = r);
        }

        private IEnumerator AnimateUpdatePositionDown(float yFinal)
        {
            return linearAnimation.Animate(rectCarousel.anchoredPosition.y, yFinal,
                di => di,
                UpdatePositionWith,
                r => AnimationRunning = r);
        }

        private void GoUp()
        {
            if (LoadedSectionReferences.Count <= 1)
            {
                // play a deny sound?
                return;
            }

            if (AnimationRunning) return;
            var shouldGoDown = false;
            if (currentIndex <= 0)
            {
                currentIndex = numberOfSections;
                shouldGoDown = true;
            }
            currentIndex = --currentIndex % numberOfSections;
            contents.PrepareEnvironment(ContentTagsBySectionTag[LoadedSectionReferences[CurrentIndex].SectionTag]);
            contents.StartLoading();
            var y = UpdatePosition();
            StartCoroutine(shouldGoDown ? AnimateUpdatePositionDown(y) : AnimateUpdatePositionUp(y));
        }
        
        private void GoDown()
        {
            if (LoadedSectionReferences.Count <= 1)
            {
                // play a deny sound?
                return;
            }

            if (AnimationRunning) return;
            var shouldGoUp = currentIndex >= numberOfSections - 1;
            currentIndex = ++currentIndex % numberOfSections;
            contents.PrepareEnvironment(ContentTagsBySectionTag[LoadedSectionReferences[CurrentIndex].SectionTag]);
            contents.StartLoading();
            var y = UpdatePosition();
            StartCoroutine(shouldGoUp ? AnimateUpdatePositionUp(y) : AnimateUpdatePositionDown(y));
        }

        [Button]
        private void Reset()
        {
            currentIndex = int.MaxValue;
            numberOfSections = 0;
            LoadedSectionReferences.Clear();
        }

        private void AddSectionReferenceInstance(PDASectionReferenceDTO sectionReference)
        {
            var goSectionReference = Pool.Get(ui =>
            {
                ui.transform.SetParent(rectCarousel.transform);
                ui.transform.localScale = Vector3.one;
                ui.Setup(sectionReference);
            });
            LoadedSectionReferences.Add(numberOfSections++, goSectionReference);
        }

        public bool UpdateSections(PDAInputs inputs)
        {
            if (AnimationRunning) return false;
            
            if (inputs.DPad.Up())
            {
                GoUp();
            }

            if (inputs.DPad.Down())
            {
                GoDown();
            }

            return true;
        }

        public void PrepareEnvironment(SDictionary<PDASectionTag, List<PDAContentTag>> contentTagsBySectionTag)
        {
            Reset();
            ContentTagsBySectionTag = contentTagsBySectionTag;
            if (contentTagsBySectionTag.Count <= 0) return;
            currentIndex = 0;
            ContentTagsBySectionTag
                .Keys
                .Select(BuildSectionReferenceData)
                .OrderBy(sectionReference => sectionReference.Title)
                .ForEach(AddSectionReferenceInstance);
            UpdatePositionWith(UpdatePosition());
        }

        private PDASectionReferenceDTO BuildSectionReferenceData(PDASectionTag sectionTag)
        {
            var r = Randomizer.Instance.Range(0f, 1f);
            var g = Randomizer.Instance.Range(0f, 1f);
            var b = Randomizer.Instance.Range(0f, 1f);
            return new PDASectionReferenceDTO
            {
                Height = sectionHeight,
                SectionTag = sectionTag,
                Title = Proxy.Instance.I18N.Localise(sectionTag.Localization),
                Color = new Color(r, g, b),
            };
        }
    }
}