using Jovian.EncounterSystem;
using Nox.Game.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace Nox.Game {
    public class EncounterView : IMenuView {
        private const int MaxAnswers = 4;

        private readonly EncounterPrefabs encounterPrefabs;
        private readonly Dictionary<Type, EncounterReference> kindToReference = new();
        private readonly Dictionary<Type, List<AnswerReference>> kindToAnswerPool = new();

        private IEncounter currentEncounter;
        private EncounterReference currentReference;
        private List<AnswerReference> currentAnswerPool;

        public event Action<int> OptionSelected;

        public EncounterView(EncounterPrefabs encounterPrefabs) {
            this.encounterPrefabs = encounterPrefabs;
        }

        public void SetCurrentEncounter(IEncounter encounter) {
            currentEncounter = encounter;
        }

        public void Initialize() { }

        public void Show() {
            if(currentEncounter?.EncounterDefinition?.Kind == null) {
                return;
            }

            if(currentReference) {
                currentReference.gameObject.SetActive(false);
            }

            var kindType = currentEncounter.EncounterDefinition.Kind.GetType();
            var set = encounterPrefabs.encounterSets
                .FirstOrDefault(s => s.encounterKind != null && s.encounterKind.GetType() == kindType);
            if(set == null || !set.encounterReference) {
                return;
            }

            if(!kindToReference.TryGetValue(kindType, out var reference) || !reference) {
                reference = UnityEngine.Object.Instantiate(set.encounterReference);
                kindToReference[kindType] = reference;
            }

            currentReference = reference;
            currentAnswerPool = GetOrBuildAnswerPool(kindType, set);

            PopulateEncounterReference();
            currentReference.gameObject.SetActive(true);
        }

        public void Hide() {
            if(currentReference) {
                currentReference.gameObject.SetActive(false);
            }
            DeactivateAnswers(currentAnswerPool);
        }

        public void Tick() { }

        private List<AnswerReference> GetOrBuildAnswerPool(Type kindType, EncounterSet set) {
            if(kindToAnswerPool.TryGetValue(kindType, out var pool) && pool != null) {
                return pool;
            }

            pool = new List<AnswerReference>(MaxAnswers);
            kindToAnswerPool[kindType] = pool;

            if(!set.answerReference || !currentReference.encounterOptionsContainer) {
                return pool;
            }

            for(var i = 0; i < MaxAnswers; i++) {
                var answer = UnityEngine.Object.Instantiate(set.answerReference, currentReference.encounterOptionsContainer);
                answer.gameObject.SetActive(false);
                pool.Add(answer);
            }
            return pool;
        }

        private void PopulateEncounterReference() {
            var definition = currentEncounter.EncounterDefinition;
            var visuals = currentEncounter.EncounterVisuals;

            if(currentReference.encounterName) {
                currentReference.encounterName.text = definition.name;
            }
            if(currentReference.encounterDescription) {
                currentReference.encounterDescription.text = definition.description;
            }
            if(currentReference.encounterArt && visuals != null) {
                currentReference.encounterArt.sprite = visuals.encounterArt;
            }

            PopulateAnswers();
        }

        private void PopulateAnswers() {
            DeactivateAnswers(currentAnswerPool);

            var optionSet = currentEncounter.EncounterDialogOptionSet;
            if(currentAnswerPool == null || optionSet?.options == null) {
                return;
            }

            var count = Mathf.Min(optionSet.options.Count, currentAnswerPool.Count);
            for(var i = 0; i < count; i++) {
                var option = optionSet.options[i];
                var answer = currentAnswerPool[i];
                if(option == null || !answer) {
                    continue;
                }

                if(answer.number) {
                    answer.number.text = (i + 1).ToString();
                }
                if(answer.dialogText) {
                    answer.dialogText.text = option.text.Resolve(optionSet.library);
                }
                BindAnswerButton(answer, i);
                answer.gameObject.SetActive(true);
            }
        }

        private void BindAnswerButton(AnswerReference answer, int optionIndex) {
            var button = answer.button ? answer.button : answer.GetComponentInChildren<UnityEngine.UI.Button>(true);
            if(!button) {
                return;
            }
            button.onClick.RemoveAllListeners();
            button.onClick.AddListener(() => OptionSelected?.Invoke(optionIndex));
        }

        private static void DeactivateAnswers(List<AnswerReference> pool) {
            if(pool == null) {
                return;
            }
            for(var i = 0; i < pool.Count; i++) {
                if(pool[i]) {
                    pool[i].gameObject.SetActive(false);
                }
            }
        }

        public void Dispose() {
            foreach(var reference in kindToReference.Values) {
                if(reference) {
                    UnityEngine.Object.Destroy(reference.gameObject);
                }
            }
            kindToReference.Clear();
            kindToAnswerPool.Clear();
            currentReference = null;
            currentAnswerPool = null;
            currentEncounter = null;
            OptionSelected = null;
        }
    }
}
