using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Intrepid.Core.UI;
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 TMPro;
using UnityEngine;
using UnityEngine.UI;

namespace Intrepid.Gameplay.Entities.Player.PDA.UI
{
    public class PDAContentsUI : MonoBehaviour
    {
        [SerializeField] private PDALoadingProcessUI loadingProcess;

        [SerializeField] private PDAContentReferenceUIPoolData poolData;
        [SerializeField] private Transform poolSpace;

        [SerializeField] private RectTransform rectCarousel;
        [SerializeField] private TextMeshProUGUI title;
        [SerializeField] private NewsTickerUI description;

        [SerializeField] private int padding = 10;
        [SerializeField] private float spacing = 60f;
        [SerializeField] private float startPosition = -60f;
        [SerializeField] private float contentWidth = 150f;
        
        [SerializeField] private LinearAnimation linearAnimation;
        
        [SerializeField, ReadOnly] private int currentIndex = int.MaxValue;
        [SerializeField, ReadOnly] private int numberOfContents;
        [SerializeField, ReadOnly] private SDictionary<int, PDAContentReferenceUI> contentReferences;

        public int ContentsCapacity => poolData.poolSize;
        public bool AnimationRunning => loadingProcess.AnimationRunning;
        public bool IsLoading => loadingProcess.IsLoading;
        private bool ChangingContentFocus { get; set; }
        private List<PDAContentTag> ContentTagsToLoad { get; set; }
        private GameObjectPool<PDAContentReferenceUI> Pool { get; set; }
        public PDAContentReferenceUI CurrentContentFocus => contentReferences[currentIndex];
        
        private void OnValidate()
        {
            var layoutGroup = rectCarousel.GetComponent<HorizontalLayoutGroup>();
            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<PDAContentReferenceUI>(poolData, poolSpace);
        }
        
        private Func<bool> LoadingProcess()
        {
            CleanupContentStack();
            ContentTagsToLoad?
                .Select(BuildContentReferenceData)
                .OrderBy(contentReference => contentReference.Title)
                .ForEach(AddContentReferenceInstance);
            ResetContentReferenceFocus();
            return () => false;
        }

        private void CleanupContentStack()
        {
            foreach (var contentReference in contentReferences.Values)
            {
                contentReference.gameObject.transform.SetParent(poolSpace);
                Pool.KillNow(contentReference);
            }
            Reset();
        }

        private void ResetContentReferenceFocus()
        {
            if (numberOfContents > 0)
            {
                currentIndex = 0;
                foreach (var contentReference in contentReferences.Values)
                {
                    contentReference.ResetFocus();
                }
                ContentFocus();
            }
            else
            {
                currentIndex = int.MaxValue;
            }
            UpdatePositionWith(UpdatePosition());
        }
        
        public void StartLoading()
        {
            loadingProcess.StartLoading(LoadingProcess, true);
        }

        public void WhenPreparingSystemBoot()
        {
        }

        public void WhenStarting(ParameterStore parameters)
        {
            loadingProcess.StraightBlock();
            loadingProcess.StartLoadingNoAnimation(LoadingProcess, true);
        }

        public void WhenFinishing()
        {
            CleanupContentStack();
        }

        public bool UpdateContents(PDAInputs inputs)
        {
            loadingProcess.UpdateLoadingProcess();
            if (IsLoading || ChangingContentFocus) return false;
            
            if (inputs.DPad.Left())
            {
                GoLeft();
            }
            
            if (inputs.DPad.Right())
            {
                GoRight();
            }

            return true;
        }

        private float UpdatePosition()
        {
            return UpdatePosition(currentIndex);
        }
        
        private float UpdatePosition(int index)
        {
            if (index == int.MaxValue)
            {
                return startPosition + padding + contentWidth / 2f;
            }
            return startPosition + padding + contentWidth / 2f + (numberOfContents - index - 1) * (contentWidth + spacing);
        }

        private void UpdatePositionWith(float x)
        {
            rectCarousel.anchoredPosition = new Vector2(x, rectCarousel.anchoredPosition.y);
        }
        
        private IEnumerator AnimateUpdatePositionLeft(float xFinal)
        {
            return linearAnimation.Animate(rectCarousel.anchoredPosition.x, xFinal,
                di => -di, 
                UpdatePositionWith,
                r =>
                {
                    ChangingContentFocus = r;
                });
        }

        private IEnumerator AnimateUpdatePositionRight(float xFinal)
        {
            return linearAnimation.Animate(rectCarousel.anchoredPosition.x, xFinal,
                di => di,
                UpdatePositionWith,
                r =>
                {
                    ChangingContentFocus = r;
                });
        }

        private void GoLeft()
        {
            if (ChangingContentFocus) return;
            ContentUnFocus();
            if (currentIndex <= 0)
            {
                currentIndex = numberOfContents;
                currentIndex = --currentIndex % numberOfContents;
                var x = UpdatePosition();
                ContentFocus();
                StartCoroutine(AnimateUpdatePositionLeft(x));
            }
            else
            {
                currentIndex = --currentIndex % numberOfContents;
                var x = UpdatePosition();
                ContentFocus();
                StartCoroutine(AnimateUpdatePositionRight(x));
            }
        }

        private void GoRight()
        {
            if (ChangingContentFocus) return;
            ContentUnFocus();
            if (currentIndex >= numberOfContents - 1)
            {
                currentIndex = ++currentIndex % numberOfContents;
                var x = UpdatePosition();
                ContentFocus();
                StartCoroutine(AnimateUpdatePositionRight(x));
            }
            else
            {
                currentIndex = ++currentIndex % numberOfContents;
                var x = UpdatePosition();
                ContentFocus();
                StartCoroutine(AnimateUpdatePositionLeft(x));
            }
        }

        private void ContentFocus()
        {
            var contentReference = CurrentContentFocus;
            title.text = contentReference.Title;
            description.Operational(contentReference.Description);
            contentReference.Focus();
        }
        
        private void ContentUnFocus()
        {
            title.text = "";
            description.NotOperational();
            if (currentIndex < contentReferences.Count)
            {
                CurrentContentFocus.UnFocus();
            }
        }

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

        private void AddContentReferenceInstance(PDAContentReferenceDTO contentReference)
        {
            var goContentReference = Pool.Get(ui =>
            {
                ui.transform.SetParent(rectCarousel.transform);
                ui.transform.localScale = Vector3.one;
                ui.Setup(contentReference);
            });
            contentReferences.Add(numberOfContents++, goContentReference);
        }

        public void PrepareEnvironment(List<PDAContentTag> contentTagsToLoad)
        {
            ContentTagsToLoad = contentTagsToLoad;
        }

        private PDAContentReferenceDTO BuildContentReferenceData(PDAContentTag contentTag)
        {
            var r = Randomizer.Instance.Range(0f, 1f);
            var g = Randomizer.Instance.Range(0f, 1f);
            var b = Randomizer.Instance.Range(0f, 1f);
            return new PDAContentReferenceDTO
            {
                Width = contentWidth,
                ContentTag = contentTag,
                Title = Proxy.Instance.I18N.Localise(contentTag.Localization),
                Description = Proxy.Instance.I18N.Localise(contentTag.Description),
                Color = new Color(r, g, b), 
            };
        }
    }
}