#pragma once

#include <QObject>
#include <QMutex>
#include <QMap>
#include <QVector>
#include <functional>
#include <memory>
#include "events.h"

class EventBus : public QObject
{
    Q_OBJECT
public:
    static EventBus& instance();
    ~EventBus() = default;

    // Suscribirse a un tipo de evento específico
    template <typename EventType>
    void subscribe(std::function<void(const EventType&)> callback)
    {
        QMutexLocker locker(&m_mutex);
        // Usamos typeid para identificar el tipo de evento
        auto typeId = typeid(EventType).hash_code();
        m_subscribers[typeId].push_back(
            [callback](const std::shared_ptr<void>& eventPtr) {
                const EventType* event = static_cast<const EventType*>(eventPtr.get());
                if (event) {
                    callback(*event);
                }
            }
        );
    }

    // Publicar un evento
    template <typename EventType>
    void publish(const EventType& event)
    {
        QMutexLocker locker(&m_mutex);
        auto typeId = typeid(EventType).hash_code();
        
        // Crear un shared_ptr para manejar la vida del evento
        auto eventPtr = std::make_shared<EventType>(event);
        
        // Notificar a todos los suscriptores de este tipo
        if (m_subscribers.contains(typeId)) {
            for (auto& callback : m_subscribers[typeId]) {
                callback(eventPtr);
            }
        }
    }

    // Desuscribirse (opcional, para evitar memory leaks en casos complejos)
    // Nota: En una implementación real, necesitaríamos guardar tokens de suscripción
    void unsubscribeAll()
    {
        QMutexLocker locker(&m_mutex);
        m_subscribers.clear();
    }

private:
    EventBus() = default;
    Q_DISABLE_COPY(EventBus)

    QMutex m_mutex;
    // Mapeo de typeid -> vector de callbacks
    QMap<size_t, QVector<std::function<void(const std::shared_ptr<void>&)>>> m_subscribers;
};

// Macros de conveniencia para usar el event bus
#define EVENT_BUS EventBus::instance()
#define SUBSCRIBE(EventType, callback) EVENT_BUS.subscribe<EventType>(callback)
#define PUBLISH(event) EVENT_BUS.publish(event)