#pragma once

#include <functional>
#include <mutex>
#include <shared_mutex>
#include <typeindex>
#include <unordered_map>
#include <vector>

namespace lsp::service
{
    class EventBus
    {
    public:
        template<typename EventType>
        using Handler = std::function<void(const EventType&)>;

        template<typename EventType>
        void Subscribe(Handler<EventType> handler)
        {
            std::unique_lock<std::shared_mutex> lock(mutex_);
            handlers_[std::type_index(typeid(EventType))].push_back(
                [handler](const void* event) {
                    handler(*static_cast<const EventType*>(event));
                });
        }

        template<typename EventType>
        void Publish(const EventType& event) const
        {
            std::vector<std::function<void(const void*)>> local_handlers;
            {
                std::shared_lock<std::shared_mutex> lock(mutex_);
                auto it = handlers_.find(std::type_index(typeid(EventType)));
                if (it != handlers_.end())
                    local_handlers = it->second;
            }
            for (const auto& handler : local_handlers)
                handler(&event);
        }

        template<typename EventType>
        void Unsubscribe(std::type_index type)
        {
            std::unique_lock<std::shared_mutex> lock(mutex_);
            handlers_.erase(type);
        }

        size_t GetSubscriberCount() const
        {
            std::shared_lock<std::shared_mutex> lock(mutex_);
            size_t count = 0;
            for (const auto& [type, handlers] : handlers_)
                count += handlers.size();
            return count;
        }

    private:
        mutable std::shared_mutex mutex_;
        std::unordered_map<std::type_index, std::vector<std::function<void(const void*)>>> handlers_;
    };
}
