using System.Collections.Generic;
using System.Linq;
using Intrepid.GameManagers;
using Intrepid.Gameplay.Entities.Player.ActionBelt.Events;
using Intrepid.Gameplay.Entities.Player.ActionBelt.UI;
using Intrepid.Gameplay.Entities.Player.Events;
using Intrepid.Gameplay.Entities.Player.PDA.UI;
using Intrepid.Gameplay.Entities.Player.Sentinel.Cadence.UI;
using Intrepid.Gameplay.Entities.Player.UI;
using Intrepid.SimpleEvents;
using Intrepid.Utilities;
using Intrepid.Window;
using Intrepid.Window.Data;
using Intrepid.Window.Events;
using UnityEngine;

namespace Intrepid.Gameplay.Codex.UI
{
    public class CodexWindowManager : EventListener
    {
        [GroupConfigurations, SerializeField] private CadenceUI cadenceUI;
        [GroupConfigurations, SerializeField] private LifeShieldMeterUI lifeShieldUI;
        [GroupConfigurations, SerializeField] private GadgetsUI gadgetsUI;
        [GroupConfigurations, SerializeField] private CodexActionBeltUI actionBeltUI;
        [GroupConfigurations, SerializeField] private PDADeviceUI pdaDeviceUI;

        private Dictionary<WindowData, Window.Window> WindowsMap { get; } = new();
        private Stack<List<Window.Window>> StackWindows { get; } = new();

        private void Add(ManagedWindowUI mwUI) => WindowsMap.Add(mwUI.WindowData, mwUI.ManagedWindow);
        
        protected override void InitializeVariables()
        {
            StackWindows.Clear();
            WindowsMap.Clear();
            Add(cadenceUI);
            Add(lifeShieldUI);
            Add(gadgetsUI);
            Add(actionBeltUI);
            Add(pdaDeviceUI);
            DisableAllWindows();
        }

        protected override void OnBootstrap()
        {
        }

        protected override void Subscriptions()
        {
            Subscribe<WindowOpenEvent>(WhenWindowOpen);
            Subscribe<WindowCloseEvent>(WhenWindowClose);
            
            Subscribe<ActionBeltUIOpenEvent>(WhenActionBeltUIOpen);
            Subscribe<ActionBeltUICloseEvent>(WhenActionBeltUIClose);
            
            Subscribe<PlayerCommandGrantedEvent>(_ =>
            {
                WindowsMap[cadenceUI.WindowData].Open();
                WindowsMap[lifeShieldUI.WindowData].Open();
                WindowsMap[gadgetsUI.WindowData].Open();
            });
            Subscribe<PlayerCommandRevokedEvent>(_ =>
            {
                WindowsMap[cadenceUI.WindowData].Close();
                WindowsMap[lifeShieldUI.WindowData].Close();
                WindowsMap[gadgetsUI.WindowData].Close();
            });
        }

        private void WhenWindowOpen(WindowOpenEvent e)
        {
            if (e.ShouldStack)
            {
                var openedWindows = WindowsMap
                    .Values
                    .Where(w => w.ConditionType == WindowConditionType.Opened)
                    .ToList();
                StackWindows.Push(openedWindows);
                openedWindows.ForEach(window =>
                {
                    window.Close();
                });
            }
            foreach (var windowData in e.WindowDataList)
            {
                if (WindowsMap.TryGetValue(windowData, out var window))
                {
                    window.Open();
                }
            }
        }

        private void WhenWindowClose(WindowCloseEvent e)
        {
            foreach (var windowData in e.WindowDataList)
            {
                if (WindowsMap.TryGetValue(windowData, out var window))
                {
                    window.Close();
                }
            }

            UnStackWindows();

            return;

            void UnStackWindows()
            {
                if (StackWindows.Count <= 0) return;
                var stackedWindows = StackWindows.Pop();
                foreach (var window in stackedWindows)
                {
                    window.Open();
                }
            }
        }
        
        private void WhenActionBeltUIOpen(ActionBeltUIOpenEvent e)
        {
            Proxy.Instance.EventBus.Raise(new WindowOpenEvent
            {
                ShouldStack = true,
                WindowDataList = new[] { actionBeltUI.WindowData },
            });
        }
        
        private void WhenActionBeltUIClose(ActionBeltUICloseEvent e)
        {
            Proxy.Instance.EventBus.Raise(new WindowCloseEvent
            {
                WindowDataList = new[] { actionBeltUI.WindowData },
            });
        }
        
        private void DisableAllWindows()
        {
            foreach (var window in WindowsMap.Values)
            {
                if (window.WindowData.ShouldBeKeptAlwaysEnabled)
                {
                    window.Close();
                }
                else
                {
                    window.gameObject.SetActive(false);
                }
            }
        }
    }
}