using System;
using System.Collections.Generic;
using System.Linq;
using Intrepid.Gameplay.Codex.UI.Components;
using Intrepid.Gameplay.Codex.Visual;
using Intrepid.SimpleEvents;
using Intrepid.Utilities;

namespace Intrepid.Gameplay.Codex.UI.Controllers
{
    public abstract class CodexControllerUI : Identification, IWindowLifecycle
    {
        private List<CodexComponentUI> Components { get; set; }
        private bool IsOpenSubscribed { get; set; }
        private bool IsInputSubscribed { get; set; }
        private CodexWindowContext WindowContext { get; set; }
        private EventBus LocalBus => WindowContext?.LocalBus;

        protected string OpenSubscriptionId => $"codex.ui.open.{GetType().Name}.{Id}";
        protected string InputSubscriptionId => $"codex.ui.input.{GetType().Name}.{Id}";

        protected override void Awake()
        {
            base.Awake();
            Components = GetComponentsInChildren<CodexComponentUI>(true)
                .Where(component =>
                {
                    var owner = component.GetComponentInParent<CodexControllerUI>();
                    return owner == this;
                })
                .ToList();
        }

        public void BindToWindow(CodexWindowContext ctx)
        {
            WindowContext = ctx;
            OnBindToWindow();
        }

        public virtual void BeforeOpen()
        {
            if (!IsOpenSubscribed && LocalBus is not null)
            {
                OnOpenSubscribe(new CodexSubscriber(LocalBus, OpenSubscriptionId));
                IsOpenSubscribed = true;
            }
            
            foreach (var component in Components)
            {
                if (component.IsNull()) continue;
                component.BeforeOpen();
            }
        }

        public virtual void AfterOpen()
        {
            foreach (var component in Components)
            {
                if (component.IsNull()) continue;
                component.AfterOpen();
            }
        }

        public virtual void BeforeClose()
        {
            foreach (var component in Components)
            {
                if (component.IsNull()) continue;
                component.BeforeClose();
            }
            
            if (IsOpenSubscribed && LocalBus is not null)
            {
                LocalBus.UnSubscribe(OpenSubscriptionId);
                IsOpenSubscribed = false;
            }
        }

        public virtual void AfterClose()
        {
            foreach (var component in Components)
            {
                if (component.IsNull()) continue;
                component.AfterClose();
            }
        }

        public void EnableInput()
        {
            if (!IsInputSubscribed && LocalBus is not null)
            {
                OnInputSubscribe(new CodexSubscriber(LocalBus, InputSubscriptionId));
                IsInputSubscribed = true;
            }
            
            foreach (var component in Components)
            {
                if (component.IsNull()) continue;
                component.EnableInput();
            }
        }

        public void DisableInput()
        {
            foreach (var component in Components)
            {
                if (component.IsNull()) continue;
                component.DisableInput();
            }
            
            if (IsInputSubscribed && LocalBus is not null)
            {
                LocalBus.UnSubscribe(InputSubscriptionId);
                IsInputSubscribed = false;
            }
        }
        
        protected void Raise<T>(T anEvent) where T : IEventBase
        {
            LocalBus.Raise(anEvent);
        }
    
        public void ApplyCodexVisuals(CodexVisualContext visualContext)
        {
            foreach (var component in Components)
            {
                if (component.IsNull()) continue;
                component.ApplyCodexVisuals(visualContext);
            }
        }

        protected virtual void OnBindToWindow()
        {
        }

        protected virtual void OnOpenSubscribe(CodexSubscriber subscriber)
        {
        }

        protected virtual void OnInputSubscribe(CodexSubscriber subscriber)
        {
        }
    }
}