using System;
using System.Collections.Generic;
using VContainer;

namespace Darkmatter.Libs.Observer
{
    public class EventBus : IEventBus
    {
        [Inject]
        public EventBus() { }

        private readonly Dictionary<Type, object> _map = new();
        public void Publish<T>(T evt) where T : struct
        {
            if (_map.TryGetValue(typeof(T), out var handlerObj))
            {
                var action = (Action<T>)handlerObj;
                action?.Invoke(evt);
            }
        }

        public IDisposable Subscribe<T>(Action<T> handler) where T : struct
        {
            var t = typeof(T);

            if (_map.TryGetValue(t, out var existingHandlers))
            {
                var action = (Action<T>)existingHandlers;
                _map[t] = action + handler;
            }
            else
            {
                _map[t] = handler;
            }

            return new Unsub<T>(this, handler);
        }

        internal void Unsubscribe<T>(Action<T> handler) where T : struct
        {
            var t = typeof(T);
            if (_map.TryGetValue(t, out var existingHandlers))
            {
                var action = (Action<T>)existingHandlers;
                action -= handler;

                if (action == null)
                    _map.Remove(t);
                else
                    _map[t] = action;
            }
        }

        private sealed class Unsub<T> : IDisposable where T : struct
        {
            private readonly EventBus _bus;
            private Action<T> _handler;

            public Unsub(EventBus bus, Action<T> handler)
            {
                _bus = bus;
                _handler = handler;
            }

            public void Dispose()
            {
                if (_handler != null)
                {
                    _bus.Unsubscribe(_handler);
                    _handler = null;
                }
            }
        }
    }
}