import { ErrorFactory } from '../exceptions/ZintrustError.js';
import { createServer, } from '../node-singletons/http.js';
const readBody = async (req, maxBodyBytes) => {
    const chunks = [];
    let size = 0;
    for await (const chunk of req) {
        const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
        size += buffer.length;
        if (size > maxBodyBytes) {
            throw ErrorFactory.createValidationError('Body too large');
        }
        chunks.push(buffer);
    }
    return Buffer.concat(chunks).toString('utf8');
};
const respond = (res, response) => {
    res.writeHead(response.status, {
        'Content-Type': 'application/json; charset=utf-8',
        'Cache-Control': 'no-store',
        ...response.headers,
    });
    res.end(JSON.stringify(response.body));
};
const toProxyRequest = (req, body) => {
    const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
    const headers = {};
    for (const [key, value] of Object.entries(req.headers)) {
        headers[key.toLowerCase()] = Array.isArray(value) ? value.join(',') : value;
    }
    return {
        method: req.method ?? 'POST',
        path: url.pathname,
        headers,
        body,
    };
};
export const createProxyServer = (options) => {
    const server = createServer(async (req, res) => {
        try {
            const body = await readBody(req, options.maxBodyBytes);
            const rawUrl = req.url ?? '';
            if (rawUrl.startsWith('/containerstarthealthcheck') ||
                rawUrl.startsWith('containerstarthealthcheck')) {
                respond(res, {
                    status: 200,
                    body: { status: 'ok' },
                });
                return;
            }
            if ((req.url ?? '').startsWith('/health')) {
                const response = await options.backend.health();
                respond(res, response);
                return;
            }
            if (options.verify) {
                const verified = await options.verify(req, body);
                if (!verified.ok) {
                    respond(res, {
                        status: verified.status,
                        body: { code: 'UNAUTHORIZED', message: verified.message },
                    });
                    return;
                }
            }
            const request = toProxyRequest(req, body);
            const response = await options.backend.handle(request);
            respond(res, response);
        }
        catch (error) {
            respond(res, {
                status: 500,
                body: { code: 'PROXY_ERROR', message: String(error) },
            });
        }
    });
    const start = async () => new Promise((resolve) => {
        server.listen(options.port, options.host, () => resolve());
    });
    const stop = async () => new Promise((resolve, reject) => {
        server.close((error) => {
            if (error) {
                reject(error);
                return;
            }
            resolve();
        });
    });
    return Object.freeze({
        start,
        stop,
        server,
    });
};
