/**
 * Network proxy orchestrator for the sandbox.
 *
 * Starts HTTP + SOCKS5 proxy servers in the host Node.js process,
 * then spawns socat bridges in WSL to create Unix sockets accessible
 * from inside bwrap.
 *
 * Domain approval is handled via direct async callbacks — no IPC.
 */
import { spawn, spawnSync } from 'child_process';
import { isDomainAllowed, normalizeDomain } from './domain-filter.js';
import { startHttpProxy } from './http-proxy.js';
import { startSocksProxy } from './socks-proxy.js';
/**
 * Get the Windows host IP as seen from inside WSL.
 * This is the default gateway for the WSL virtual network.
 */
function getWslHostIp(distro) {
    try {
        const result = spawnSync('wsl', ['-d', distro, '--', 'ip', 'route', 'show', 'default'], {
            timeout: 5000,
            encoding: 'utf-8',
            windowsHide: true
        });
        // Output: "default via 172.29.208.1 dev eth0"
        const match = (result.stdout ?? '').match(/default via (\d+\.\d+\.\d+\.\d+)/);
        return match?.[1] ?? null;
    }
    catch {
        return null;
    }
}
/**
 * Spawn a socat bridge in WSL that creates a Unix socket and forwards
 * to a Windows TCP port. Returns the child process.
 */
function spawnSocatBridge(distro, socketPath, hostIp, tcpPort) {
    // Clean up stale socket first
    spawnSync('wsl', ['-d', distro, '--', 'rm', '-f', socketPath], {
        timeout: 3000,
        windowsHide: true
    });
    return spawn('wsl', [
        '-d',
        distro,
        '--',
        'socat',
        `UNIX-LISTEN:${socketPath},fork,mode=777`,
        `TCP:${hostIp}:${tcpPort}`
    ], { stdio: ['ignore', 'ignore', 'ignore'], windowsHide: true });
}
/**
 * Wait for a Unix socket to appear in WSL (created by socat).
 */
function waitForSocket(distro, socketPath, timeoutMs = 5000) {
    return new Promise((resolve) => {
        const start = Date.now();
        const check = () => {
            const result = spawnSync('wsl', ['-d', distro, '--', 'test', '-e', socketPath], {
                timeout: 2000,
                windowsHide: true
            });
            if (result.status === 0) {
                resolve(true);
                return;
            }
            if (Date.now() - start > timeoutMs) {
                resolve(false);
                return;
            }
            setTimeout(check, 100);
        };
        check();
    });
}
/**
 * Start the dual network proxy (HTTP + SOCKS5).
 *
 * 1. Starts Node.js HTTP and SOCKS5 servers on random TCP ports
 * 2. Spawns socat bridges in WSL to create Unix sockets
 * 3. Returns socket paths for bwrap bind-mounting
 *
 * Domain checks are direct function calls — no IPC.
 */
export async function startNetworkProxy(config) {
    const allowedSet = new Set(config.allowedDomains);
    // Build the domain check function that both proxies share.
    // isDomainAllowed handles normalization internally.
    const checkDomain = async (domain) => {
        if (isDomainAllowed(domain, allowedSet))
            return true;
        const choice = await config.onDomainCheck(domain);
        if (choice === false)
            return false;
        if (choice === 'always')
            allowedSet.add(normalizeDomain(domain));
        return true;
    };
    // Start both proxy servers (listen on 127.0.0.1 only — socat bridge is local)
    const [httpProxy, socksProxy] = await Promise.all([
        startHttpProxy({ checkDomain }),
        startSocksProxy({ checkDomain })
    ]);
    // Get WSL host IP for socat bridges
    const hostIp = getWslHostIp(config.distro);
    if (!hostIp) {
        httpProxy.stop();
        socksProxy.stop();
        throw new Error('Failed to determine WSL host IP');
    }
    const pid = process.pid;
    const httpSocketPath = `/tmp/shortcut-http-${pid}.sock`;
    const socksSocketPath = `/tmp/shortcut-socks-${pid}.sock`;
    // Spawn socat bridges in WSL
    const httpBridge = spawnSocatBridge(config.distro, httpSocketPath, hostIp, httpProxy.port);
    const socksBridge = spawnSocatBridge(config.distro, socksSocketPath, hostIp, socksProxy.port);
    // Wait for both sockets to appear
    const [httpReady, socksReady] = await Promise.all([
        waitForSocket(config.distro, httpSocketPath),
        waitForSocket(config.distro, socksSocketPath)
    ]);
    if (!httpReady || !socksReady) {
        httpProxy.stop();
        socksProxy.stop();
        httpBridge.kill();
        socksBridge.kill();
        throw new Error('socat bridges failed to create Unix sockets');
    }
    return {
        httpSocketPath,
        socksSocketPath,
        stop: () => {
            httpProxy.stop();
            socksProxy.stop();
            httpBridge.kill();
            socksBridge.kill();
            // Clean up sockets
            spawnSync('wsl', ['-d', config.distro, '--', 'rm', '-f', httpSocketPath, socksSocketPath], {
                timeout: 3000,
                windowsHide: true
            });
        }
    };
}
//# sourceMappingURL=proxy.js.map