import { spawnSync } from 'node:child_process';
import { existsSync, statSync } from 'node:fs';
import { resolve } from 'node:path';
import { detectSandboxHostStatus, getSandboxConfigSnapshot } from './manager.js';
function shellQuote(value) {
    return `'${value.replace(/'/g, `'\"'\"'`)}'`;
}
function hasCommand(command, args = ['--version']) {
    const result = spawnSync(command, [...args], {
        stdio: 'ignore',
        timeout: 1500,
        windowsHide: true,
    });
    return result.status === 0 || result.status === 1;
}
function qemuBinaryFor(manager) {
    const configured = `${manager.get('sandbox.qemuBinary') ?? ''}`.trim();
    return configured || 'qemu-system-x86_64';
}
function qemuImageFor(manager) {
    return `${manager.get('sandbox.qemuImagePath') ?? ''}`.trim();
}
function qemuExecWrapperFor(manager) {
    return `${manager.get('sandbox.qemuExecWrapper') ?? ''}`.trim();
}
function qemuGuestHostFor(manager) {
    return `${manager.get('sandbox.qemuGuestHost') ?? ''}`.trim();
}
function qemuGuestPortFor(manager) {
    const raw = manager.get('sandbox.qemuGuestPort');
    return `${typeof raw === 'number' ? raw : Number.parseInt(`${raw ?? ''}`, 10) || 2222}`;
}
function qemuGuestUserFor(manager) {
    return `${manager.get('sandbox.qemuGuestUser') ?? ''}`.trim();
}
function qemuWorkspacePathFor(manager) {
    return `${manager.get('sandbox.qemuWorkspacePath') ?? ''}`.trim();
}
function qemuSessionModeFor(manager) {
    const configured = `${manager.get('sandbox.qemuSessionMode') ?? ''}`.trim();
    return configured === 'launch-per-command' ? 'launch-per-command' : 'attach';
}
function isExecutableFile(path) {
    try {
        const stat = statSync(path);
        return stat.isFile() && (stat.mode & 0o111) !== 0;
    }
    catch {
        return false;
    }
}
function buildAvailability(id, available, detail) {
    return Object.freeze({ id, available, detail });
}
export function probeSandboxBackends(manager) {
    const host = detectSandboxHostStatus(manager);
    const config = getSandboxConfigSnapshot(manager);
    const qemuBinary = qemuBinaryFor(manager);
    const qemuImage = qemuImageFor(manager);
    const qemuExecWrapper = qemuExecWrapperFor(manager);
    const qemuGuestHost = qemuGuestHostFor(manager);
    const backends = [
        buildAvailability('local', true, 'host-local process isolation fallback is always available'),
        buildAvailability('qemu', hasCommand(qemuBinary), `requires ${qemuBinary} on PATH`),
    ];
    const requested = config.vmBackend;
    let resolved = 'local';
    if (config.vmBackend === 'qemu') {
        resolved = backends.find((entry) => entry.id === 'qemu')?.available ? 'qemu' : 'local';
    }
    const warnings = [];
    if (config.vmBackend === 'qemu' && resolved === 'local') {
        warnings.push(`Requested sandbox backend "${config.vmBackend}" is unavailable; falling back to local process isolation.`);
    }
    if (config.vmBackend === 'qemu' && !qemuImage) {
        warnings.push('QEMU backend selected without sandbox.qemuImagePath; sessions can be planned and reviewed, but guest execution remains disabled.');
    }
    if (config.vmBackend === 'qemu' && qemuImage && !qemuExecWrapper) {
        warnings.push('QEMU image is configured without sandbox.qemuExecWrapper; guest launch planning is wired, but command execution remains disabled until a host bridge is configured.');
    }
    if (config.vmBackend === 'qemu' && qemuExecWrapper && !qemuGuestHost) {
        warnings.push('QEMU wrapper is configured without sandbox.qemuGuestHost; host bridge mode is available, but real guest SSH transport remains disabled until the guest host is configured.');
    }
    if (config.vmBackend === 'qemu' && qemuExecWrapper && !existsSync(qemuExecWrapper)) {
        warnings.push(`Configured sandbox.qemuExecWrapper does not exist: ${qemuExecWrapper}`);
    }
    if (config.vmBackend === 'qemu' && qemuExecWrapper && existsSync(qemuExecWrapper) && !isExecutableFile(qemuExecWrapper)) {
        warnings.push(`Configured sandbox.qemuExecWrapper is not executable: ${qemuExecWrapper}`);
    }
    if (host.windows && !host.runningInWsl && config.vmBackend === 'qemu') {
        warnings.push('QEMU sandboxing on Windows requires running GoodVibes inside WSL.');
    }
    return Object.freeze({
        requestedBackend: requested,
        resolvedBackend: resolved,
        backends,
        warnings,
    });
}
function buildCommandSummary(command, args) {
    return [command, ...args].join(' ').trim();
}
export function buildSandboxLaunchPlan(profile, label, manager, workspaceRoot) {
    const backendProbe = probeSandboxBackends(manager);
    const safeWorkspaceRoot = resolve(workspaceRoot);
    if (backendProbe.resolvedBackend === 'qemu') {
        const qemuBinary = qemuBinaryFor(manager);
        const qemuImage = qemuImageFor(manager);
        const guestPort = qemuGuestPortFor(manager);
        const args = [
            '-display', 'none',
            '-nodefaults',
            '-name', `gv-${profile.id}`,
            '-snapshot',
            '-m', '512',
            '-nic', `user,hostfwd=tcp::${guestPort}-:22`,
        ];
        if (qemuImage)
            args.push('-drive', `file=${qemuImage},if=virtio,format=qcow2`);
        return Object.freeze({
            backend: 'qemu',
            command: qemuBinary,
            args,
            workspaceRoot: safeWorkspaceRoot,
            summary: buildCommandSummary(qemuBinary, args),
            imagePath: qemuImage || undefined,
        });
    }
    const args = ['-lc', `echo "goodvibes sandbox ${profile.id}: ${label}"`];
    return Object.freeze({
        backend: 'local',
        command: process.env.SHELL || 'bash',
        args,
        workspaceRoot: safeWorkspaceRoot,
        summary: buildCommandSummary(process.env.SHELL || 'bash', args),
    });
}
export function resolveSandboxCommandPlan(launchPlan, command, args, manager) {
    if (launchPlan.backend === 'qemu') {
        if (!launchPlan.imagePath) {
            throw new Error('QEMU-backed sandbox execution requires sandbox.qemuImagePath; guest launch planning is available, but command execution stays disabled until an image is configured.');
        }
        const wrapper = manager ? qemuExecWrapperFor(manager) : '';
        if (!wrapper) {
            throw new Error('QEMU-backed sandbox execution requires sandbox.qemuExecWrapper; image-backed launch planning is wired, but guest command execution stays disabled until a wrapper is configured.');
        }
        if (!existsSync(wrapper)) {
            throw new Error(`QEMU-backed sandbox execution requires an existing sandbox.qemuExecWrapper; missing: ${wrapper}`);
        }
        if (!isExecutableFile(wrapper)) {
            throw new Error(`QEMU-backed sandbox execution requires an executable sandbox.qemuExecWrapper; not executable: ${wrapper}`);
        }
        const guestHost = manager ? qemuGuestHostFor(manager) : '';
        const sessionMode = manager ? qemuSessionModeFor(manager) : 'attach';
        return Object.freeze({
            command: wrapper,
            args: [],
            env: {
                GV_SANDBOX_QEMU_BINARY: launchPlan.command,
                GV_SANDBOX_QEMU_ARGS: JSON.stringify(launchPlan.args),
                GV_SANDBOX_QEMU_IMAGE: launchPlan.imagePath,
                GV_SANDBOX_WORKSPACE_ROOT: launchPlan.workspaceRoot,
                GV_SANDBOX_GUEST_COMMAND: command,
                GV_SANDBOX_GUEST_ARGS: JSON.stringify(args),
                GV_SANDBOX_GUEST_HOST: guestHost,
                GV_SANDBOX_GUEST_PORT: manager ? qemuGuestPortFor(manager) : '2222',
                GV_SANDBOX_GUEST_USER: manager ? qemuGuestUserFor(manager) : '',
                GV_SANDBOX_GUEST_WORKSPACE: manager ? qemuWorkspacePathFor(manager) : '',
                GV_SANDBOX_WRAPPER_MODE: guestHost ? (sessionMode === 'launch-per-command' ? 'launch-qemu-ssh' : 'ssh-guest') : '',
                GV_SANDBOX_EXEC_MODE: 'wrapper',
            },
            summary: buildCommandSummary(wrapper, []),
        });
    }
    return Object.freeze({
        command,
        args,
        summary: buildCommandSummary(command, args),
    });
}
function runSandboxCommand(launchPlan, command, args, managerLike, options = {}) {
    const resolved = resolveSandboxCommandPlan(launchPlan, command, args, managerLike);
    const result = spawnSync(resolved.command, [...resolved.args], {
        cwd: options.cwd ?? launchPlan.workspaceRoot,
        env: { ...process.env, ...resolved.env, ...options.env },
        encoding: 'utf-8',
        timeout: options.timeoutMs ?? 5000,
        input: options.input,
        windowsHide: true,
    });
    return Object.freeze({
        status: result.status,
        stdout: result.stdout ?? '',
        stderr: result.stderr ?? '',
    });
}
export function executeSandboxCommand(launchPlan, command, args, options = {}) {
    return runSandboxCommand(launchPlan, command, args, undefined, options);
}
export function executeSandboxManagedCommand(launchPlan, command, args, manager, options = {}) {
    return runSandboxCommand(launchPlan, command, args, manager, options);
}
