"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildWebhookDispatcher = buildWebhookDispatcher;
/**
 * Webhook Dispatcher — envia eventos para URLs registradas na tabela ext_webhooks.
 *
 * Fluxo:
 *   hookBus emite ext:message.new ou ext:session.status
 *   → dispatcher consulta ext_webhooks do tenant
 *   → para cada webhook ativo que subscreve o evento, faz POST com envelope + assinatura HMAC
 *
 * Envelope enviado ao receptor:
 *   { event, data, timestamp }
 *
 * Header de assinatura:
 *   x-nw-signature: sha256=<hmac-hex>
 *   HMAC-SHA256(secret, JSON.stringify(body))
 *
 * Retry: sem retry automático nesta fase (Fase 5 scope mínimo).
 * Timeout: 10s por requisição para não bloquear o event loop.
 */
const crypto_1 = require("crypto");
const logger_1 = require("../../../backend/src/config/logger");
const logger = logger_1.logger.child({ module: 'webhook-dispatcher' });
const DISPATCH_TIMEOUT_MS = 10000;
// Eventos hookBus → nome de evento no payload enviado ao receptor
const HOOK_TO_EVENT = {
    'ext:message.new': 'message.new',
    'ext:session.status': 'session.status',
};
function sign(secret, body) {
    return 'sha256=' + (0, crypto_1.createHmac)('sha256', secret).update(body).digest('hex');
}
async function dispatch(url, secret, event, data) {
    const body = JSON.stringify({ event, data, timestamp: Date.now() });
    const sig = sign(secret, body);
    const ctrl = new AbortController();
    const timer = setTimeout(() => ctrl.abort(), DISPATCH_TIMEOUT_MS);
    try {
        const res = await fetch(url, {
            method: 'POST',
            headers: {
                'content-type': 'application/json',
                'x-nw-signature': sig,
            },
            body,
            signal: ctrl.signal,
        });
        if (!res.ok) {
            logger.warn({ url, event, status: res.status }, '[webhook] Receptor retornou erro');
        }
    }
    catch (err) {
        logger.warn({ url, event, err: err.message }, '[webhook] Falha ao entregar');
    }
    finally {
        clearTimeout(timer);
    }
}
function buildWebhookDispatcher(prisma, hooks) {
    for (const [hookEvent, wsEvent] of Object.entries(HOOK_TO_EVENT)) {
        hooks.register(hookEvent, async (raw) => {
            const tenantId = raw.tenantId;
            if (!tenantId)
                return;
            // Carregar apenas webhooks ativos que subscrevem este evento
            let webhooks;
            try {
                webhooks = await prisma.extWebhook.findMany({
                    where: {
                        tenantId,
                        active: true,
                        events: { has: wsEvent },
                    },
                    select: { url: true, secret: true },
                });
            }
            catch (err) {
                logger.error({ err, tenantId, event: wsEvent }, '[webhook] Erro ao buscar webhooks');
                return;
            }
            if (webhooks.length === 0)
                return;
            // Retira tenantId do payload antes de enviar ao receptor
            const { tenantId: _tid, ...data } = raw;
            // Dispara em paralelo sem bloquear o hookBus
            Promise.allSettled(webhooks.map(wh => dispatch(wh.url, wh.secret, wsEvent, data))).catch(() => { });
        });
    }
    logger.info('[webhook] Dispatcher ativo para eventos: ' + Object.values(HOOK_TO_EVENT).join(', '));
}
//# sourceMappingURL=webhook-dispatcher.js.map