"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWebhookServer = createWebhookServer;
const http_1 = require("http");
const url_1 = require("url");
const integration_foundation_1 = require("@dbis/integration-foundation");
const inboundParser_1 = require("../parsers/inboundParser");
const idempotencyStore = new integration_foundation_1.IdempotencyStore();
function readBody(req) {
    return new Promise((resolve, reject) => {
        const chunks = [];
        req.on('data', (c) => chunks.push(c));
        req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
        req.on('error', reject);
    });
}
function unauthorized(res, reason = 'unauthorized') {
    res.statusCode = 401;
    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify({ error: reason }));
}
function conflict(res) {
    res.statusCode = 409;
    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify({ error: 'duplicate idempotency key' }));
}
function createWebhookServer(options) {
    const healthPath = options.healthPath ?? '/health';
    const sigHeader = options.webhookSignatureHeader ?? 'x-hybx-signature';
    return (0, http_1.createServer)((req, res) => {
        void (async () => {
            try {
                const url = new url_1.URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
                if (req.method === 'GET' && url.pathname === healthPath) {
                    res.statusCode = 200;
                    res.setHeader('Content-Type', 'application/json');
                    res.end(JSON.stringify({ status: 'ok', service: 'swift-listener' }));
                    return;
                }
                if (url.pathname !== options.path) {
                    res.statusCode = 404;
                    res.end(JSON.stringify({ error: 'not found' }));
                    return;
                }
                if (req.method !== 'POST') {
                    res.statusCode = 405;
                    res.end(JSON.stringify({ error: 'method not allowed' }));
                    return;
                }
                const raw = await readBody(req);
                if (options.webhookSecret) {
                    const sig = String(req.headers[sigHeader] ?? req.headers['x-webhook-signature'] ?? '');
                    if (!(0, integration_foundation_1.verifyWebhookSignature)({ secret: options.webhookSecret, payload: raw, signature: sig })) {
                        unauthorized(res, 'invalid webhook signature');
                        return;
                    }
                }
                else if (options.authBearer) {
                    const auth = String(req.headers.authorization ?? '');
                    const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
                    if (token !== options.authBearer) {
                        unauthorized(res);
                        return;
                    }
                }
                const idempotencyHeader = String(req.headers['x-idempotency-key'] ?? '');
                if (idempotencyHeader) {
                    const key = (0, integration_foundation_1.deriveIdempotencyKey)({
                        tenantId: 'swift-listener',
                        entityId: 'webhook',
                        operation: 'inbound',
                        resourceId: idempotencyHeader,
                    });
                    if (!idempotencyStore.record(key)) {
                        conflict(res);
                        return;
                    }
                }
                const correlation = (0, integration_foundation_1.createCorrelationContext)({
                    correlationId: String(req.headers[integration_foundation_1.CORRELATION_ID_HEADER] ?? ''),
                    requestId: String(req.headers[integration_foundation_1.REQUEST_ID_HEADER] ?? req.headers['x-request-id'] ?? ''),
                });
                const contentType = String(req.headers['content-type'] ?? '');
                let format = (0, inboundParser_1.detectInboundFormat)(raw);
                if (contentType.includes('xml'))
                    format = 'iso20022';
                if (contentType.includes('json') && format !== 'swift_fin')
                    format = 'json_broadcast';
                await options.onMessage({
                    source: `webhook:${correlation.requestId}`,
                    format,
                    payload: raw,
                    metadata: {
                        contentType,
                        path: url.pathname,
                        correlationId: correlation.correlationId,
                        requestId: correlation.requestId,
                    },
                });
                res.statusCode = 202;
                res.setHeader('Content-Type', 'application/json');
                res.setHeader(integration_foundation_1.CORRELATION_ID_HEADER, correlation.correlationId);
                res.setHeader(integration_foundation_1.REQUEST_ID_HEADER, correlation.requestId);
                res.end(JSON.stringify({ accepted: true, correlationId: correlation.correlationId }));
            }
            catch (e) {
                res.statusCode = 500;
                res.setHeader('Content-Type', 'application/json');
                res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
            }
        })();
    }).listen(options.port);
}
//# sourceMappingURL=webhookServer.js.map