import { randomUUID } from "crypto";
import { logger } from "../logging/logger";

interface Event {
    id: string
    type: string
    occurredAt: string
    payload?: Record<string, any>
}

type EventHandler = (event: Event) => void | Promise<void>;

class EventBus {
    private handlers: Map<string, EventHandler[]> = new Map();

    /**
     * Register event listener
     * @param type 
     * @param handler 
     */
    on(type: string, handler: EventHandler) {
        const handlers = this.handlers.get(type) ?? [];
        handlers.push(handler);
        this.handlers.set(type, handlers);
    }

    /**
     * Emit event of given type
     * @param type
     * @param payload 
     */
    async emit(type: string, payload?: Record<string, any>) {
        const event: Event = {
            id: randomUUID(),
            type,
            occurredAt: new Date().toISOString(),
            payload
        }

        const handlers = this.handlers.get(type) ?? []

        for (const h of handlers) {
            try {
                await h(event)
            } catch (error) {
                logger.error('app', 'Event handler failed', {
                    type: event.type,
                    id: event.id,
                    error: error instanceof Error ? error.message : String(error),
                })
            }
        }
    }
}

export const bus = new EventBus();