"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SocketIOEventAdapter = void 0;
const socket_io_1 = require("socket.io");
const logger_1 = require("../logger");
/**
 * Socket.IO event adapter for browser clients.
 *
 * Publishes LTEvent payloads to all connected Socket.IO clients
 * on channels following the pattern: `lt.events.{event.type}`
 *
 * The HTTP server must be attached via `attachServer()` before
 * `connect()` is called. The startup flow handles this automatically
 * when Socket.IO is the active event transport.
 *
 * When an `authenticate` callback is provided, Socket.IO middleware
 * rejects handshakes that do not include a valid `auth.token`.
 *
 * Usage:
 * ```typescript
 * import { eventRegistry } from '@hotmeshio/long-tail';
 * import { SocketIOEventAdapter } from '@hotmeshio/long-tail';
 *
 * const adapter = new SocketIOEventAdapter();
 * eventRegistry.register(adapter);
 * // After HTTP server is created:
 * adapter.attachServer(httpServer);
 * await eventRegistry.connect();
 * ```
 */
class SocketIOEventAdapter {
    constructor(options) {
        this.io = null;
        this.httpServer = null;
        this.socketPath = '/socket.io';
        this.authenticate = options?.authenticate ?? null;
    }
    /** Attach to an HTTP server. Must be called before connect(). */
    attachServer(server) {
        this.httpServer = server;
    }
    /** Override the socket.io path (for subpath-mounted deployments). */
    setPath(socketPath) {
        this.socketPath = socketPath;
    }
    async connect() {
        if (!this.httpServer) {
            logger_1.loggerRegistry.warn('[lt-events:socketio] no HTTP server attached — skipping');
            return;
        }
        this.io = new socket_io_1.Server(this.httpServer, {
            cors: { origin: '*', methods: ['GET', 'POST'] },
            path: this.socketPath,
            transports: ['polling', 'websocket'],
            allowEIO3: true,
        });
        if (this.authenticate) {
            const verify = this.authenticate;
            this.io.use(async (socket, next) => {
                const token = socket.handshake.auth?.token;
                if (!token) {
                    return next(new Error('Authentication required'));
                }
                try {
                    const valid = await verify(token);
                    if (!valid) {
                        return next(new Error('Authentication failed'));
                    }
                    next();
                }
                catch {
                    next(new Error('Authentication failed'));
                }
            });
        }
        this.io.on('connection', (socket) => {
            logger_1.loggerRegistry.info(`[lt-events:socketio] client connected (${socket.id})`);
            socket.on('disconnect', () => {
                logger_1.loggerRegistry.debug(`[lt-events:socketio] client disconnected (${socket.id})`);
            });
        });
        logger_1.loggerRegistry.info('[lt-events:socketio] attached to HTTP server');
    }
    async publish(event) {
        if (!this.io)
            return;
        const channel = `lt.events.${event.type}`;
        this.io.emit(channel, event);
    }
    async disconnect() {
        if (this.io) {
            this.io.close();
            this.io = null;
            logger_1.loggerRegistry.info('[lt-events:socketio] disconnected');
        }
    }
}
exports.SocketIOEventAdapter = SocketIOEventAdapter;
