// src/engine/oauth/local-server.ts — Temporary HTTP server for OAuth callback (SPEC-440)
import { createServer } from 'node:http';
import { createConnection } from 'node:net';
const CALLBACK_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
const DEFAULT_START_PORT = 9876;
const SUCCESS_HTML = `<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Authorization complete</title></head>
<body style="font-family:sans-serif;text-align:center;padding:2rem">
<h1>&#10003; Autorización completada</h1>
<p>Puedes cerrar esta pestaña.</p>
</body>
</html>`;
const ERROR_HTML = `<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Authorization error</title></head>
<body style="font-family:sans-serif;text-align:center;padding:2rem">
<h1>Error en autorización</h1>
<p>Puedes cerrar esta pestaña.</p>
</body>
</html>`;
/**
 * Start a local HTTP server on the given port that waits for the OAuth callback.
 * Resolves with the extracted code + state, or rejects on error/timeout.
 */
export function startCallbackServer(port) {
    return new Promise((resolveServer, rejectServer) => {
        const waitForCallback = new Promise((resolve, reject) => {
            const server = createServer((req, res) => {
                if (!req.url) {
                    res.writeHead(400);
                    res.end(ERROR_HTML);
                    reject(new Error('OAuth callback: no URL in request'));
                    return;
                }
                const url = new URL(req.url, `http://localhost:${port}`);
                if (url.pathname !== '/callback') {
                    res.writeHead(404);
                    res.end('Not found');
                    return;
                }
                const error = url.searchParams.get('error');
                if (error) {
                    const description = url.searchParams.get('error_description') ?? error;
                    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
                    res.end(ERROR_HTML);
                    reject(new Error(`OAuth error: ${description}`));
                    return;
                }
                const code = url.searchParams.get('code');
                const state = url.searchParams.get('state');
                if (!code || !state) {
                    res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
                    res.end(ERROR_HTML);
                    reject(new Error('OAuth callback: missing code or state parameter'));
                    return;
                }
                res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
                res.end(SUCCESS_HTML);
                resolve({ code, state });
            });
            const timeout = setTimeout(() => {
                server.close();
                reject(new Error('OAuth callback timed out after 5 minutes'));
            }, CALLBACK_TIMEOUT_MS);
            server.on('error', (err) => {
                clearTimeout(timeout);
                rejectServer(err);
            });
            server.listen(port, '127.0.0.1', () => {
                resolveServer({
                    server,
                    waitForCallback: waitForCallback.finally(() => {
                        clearTimeout(timeout);
                    }),
                });
            });
        });
    });
}
/**
 * Find the first available TCP port starting from startPort (default 9876).
 */
export async function findFreePort(startPort = DEFAULT_START_PORT) {
    for (let port = startPort; port < startPort + 100; port++) {
        const free = await isPortFree(port);
        if (free) {
            return port;
        }
    }
    throw new Error(`No free port found in range ${startPort}-${startPort + 99}`);
}
function isPortFree(port) {
    return new Promise((resolve) => {
        const socket = createConnection({ port, host: '127.0.0.1' });
        socket.on('connect', () => {
            socket.destroy();
            resolve(false); // port is in use
        });
        socket.on('error', () => {
            resolve(true); // connection refused → port is free
        });
    });
}
//# sourceMappingURL=local-server.js.map