"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NATS_WS_PROXY_PATH = void 0;
exports.deriveWsUrlFromRequest = deriveWsUrlFromRequest;
exports.attachNatsWsProxy = attachNatsWsProxy;
const ws_1 = require("ws");
const logger_1 = require("../logger");
/** Default path for the NATS WebSocket proxy endpoint. */
exports.NATS_WS_PROXY_PATH = '/nats-ws';
/**
 * Derive the public NATS WebSocket URL from an HTTP request's headers.
 *
 * Respects `X-Forwarded-Proto` and `X-Forwarded-Host` so the correct
 * `wss://` scheme is used behind TLS-terminating load balancers.
 */
function deriveWsUrlFromRequest(req, basePath = '') {
    const proto = req.headers['x-forwarded-proto'] || 'ws';
    const scheme = proto === 'https' ? 'wss' : 'ws';
    const host = req.headers['x-forwarded-host'] || req.headers.host || 'localhost';
    return `${scheme}://${host}${basePath}${exports.NATS_WS_PROXY_PATH}`;
}
/**
 * Attach a WebSocket proxy to an HTTP server that bridges browser
 * connections to an internal NATS WebSocket endpoint.
 *
 * The browser connects to `wss://domain.com{basePath}/nats-ws` (port 443,
 * through an ALB or reverse proxy). This handler upgrades the connection
 * and bridges bidirectionally to the internal NATS WS target.
 *
 * The proxy auto-derives the public `wsUrl` from the first incoming
 * request's headers, respecting `X-Forwarded-Proto` and `X-Forwarded-Host`
 * so the correct `wss://` scheme is used behind TLS-terminating load balancers.
 */
function attachNatsWsProxy(server, target, options = {}) {
    const basePath = options.basePath || '';
    const proxyPath = `${basePath}${exports.NATS_WS_PROXY_PATH}`;
    const wss = new ws_1.WebSocketServer({ noServer: true });
    let derived = false;
    server.on('upgrade', (req, socket, head) => {
        if (req.url !== proxyPath)
            return;
        // Derive the public wsUrl from the first request's headers
        if (!derived && options.onWsUrlDerived) {
            options.onWsUrlDerived(deriveWsUrlFromRequest(req, basePath));
            derived = true;
        }
        wss.handleUpgrade(req, socket, head, (clientWs) => {
            const upstream = new ws_1.WebSocket(target);
            upstream.on('open', () => {
                clientWs.on('message', (data) => {
                    if (upstream.readyState === ws_1.WebSocket.OPEN)
                        upstream.send(data);
                });
                upstream.on('message', (data) => {
                    if (clientWs.readyState === ws_1.WebSocket.OPEN)
                        clientWs.send(data);
                });
            });
            const close = () => {
                if (clientWs.readyState === ws_1.WebSocket.OPEN)
                    clientWs.close();
                if (upstream.readyState === ws_1.WebSocket.OPEN)
                    upstream.close();
            };
            clientWs.on('close', close);
            upstream.on('close', close);
            upstream.on('error', (err) => {
                logger_1.loggerRegistry.error(`[lt-nats-ws-proxy] upstream error: ${err.message}`);
                close();
            });
            clientWs.on('error', (err) => {
                logger_1.loggerRegistry.error(`[lt-nats-ws-proxy] client error: ${err.message}`);
                close();
            });
        });
    });
    logger_1.loggerRegistry.info(`[lt-nats-ws-proxy] active: ${proxyPath} -> ${target}`);
}
