import Containerization
import Foundation
import NucleicCore

// Standalone de-risking spike for the container rework. NOT part of the app. Drives the REAL
// `ContainerEngine` end-to-end to prove the rework's runtime path on a signed build:
//   1. ensureRunning → pull vminitd initfs (ghcr) + pull the sandbox image (registry) + boot VM.
//   2. exec a process and stream its stdout through `ContainerizedProcessHandle` (the agent path).
//   3. teardown.
//
// This mirrors exactly how the app acquires + runs containers (pull, don't build on-device).
//
// Build + run (reusing Apple `container` CLI's kernel; any public image works as a stand-in for the
// not-yet-published nucleic-sandbox):
//   swift build --product container-spike
//   codesign --force --sign - --entitlements signing/spike.entitlements .build/.../container-spike
//   NUCLEIC_KERNEL_PATH="$HOME/Library/Application Support/com.apple.container/kernels/vmlinux-6.18.15-186" \
//     NUCLEIC_SPIKE_IMAGE=docker.io/library/debian:bookworm-slim .build/.../container-spike

func log(_ s: String) { FileHandle.standardError.write(Data((s + "\n").utf8)) }
func die(_ s: String) -> Never { log("✗ SPIKE FAIL: \(s)"); exit(1) }

func run() async throws {
    guard ContainerEngine.isSupported else {
        die("ContainerEngine.isSupported == false (need arm64 + a kernel; set NUCLEIC_KERNEL_PATH)")
    }
    let image = ProcessInfo.processInfo.environment["NUCLEIC_SPIKE_IMAGE"]
        ?? "docker.io/library/debian:bookworm-slim"

    let work = FileManager.default.temporaryDirectory.appending(path: "nucleic-engine-spike-\(UUID().uuidString)")
    try FileManager.default.createDirectory(at: work, withIntermediateDirectories: true)
    defer { try? FileManager.default.removeItem(at: work) }

    let engine = ContainerEngine(storageRoot: work.appending(path: "engine"))
    let spec = ContainerSpec(
        name: "nucleic-enginespike",
        image: image,
        mounts: [],
        workdir: "/tmp",
        env: [:],
        idleTimeout: 300,
        claudeHomeStaging: "/tmp",
        claudeHomeWritable: "/tmp")

    log("[1/3] ContainerEngine.ensureRunning — pull vminitd + pull \(image) + boot …")
    let (name, gateway) = try await engine.ensureRunning(spec)
    log("      up: name=\(name) gateway=\(gateway)")

    log("[2/3] ContainerEngine.exec — stream stdout via ContainerizedProcessHandle …")
    let handle = try await engine.exec(
        name: name, workdir: "/tmp", env: [:], argv: ["/bin/echo", "engine-stdio-ok"])
    var lines: [String] = []
    for try await line in handle.stdoutLines { lines.append(String(decoding: line, as: UTF8.self)) }
    let exitCode = await handle.wait()
    log("      exit=\(exitCode) stdout=\(lines)")

    log("[3/3] ContainerEngine.remove (teardown) …")
    _ = await engine.remove(name: name)

    let ok = exitCode == 0 && lines.contains("engine-stdio-ok")
    log("")
    log("─── RESULTS ───")
    log("  pull image + boot + exec + stdout (full ContainerEngine path) : \(ok ? "PASS ✓" : "FAIL ✗")")
    if ok {
        log("✓ SPIKE PASS — the app's pull-and-run path works end-to-end on this machine.")
        exit(0)
    }
    die("engine path check failed (see above)")
}

// Virtualization.framework drives VM callbacks on the main queue, so the main thread must run the
// dispatch loop (not block) or create()/start() deadlock. run() exits the process.
setbuf(stderr, nil)
log("spike: starting")
Task {
    do { try await run() } catch { die("threw: \(error)") }
}
dispatchMain()
