import Foundation
import NucleicProtocol
import Testing

@testable import NucleicCore

/// Unit coverage for the pure/logic surface of the macOS-VM subsystem (docs/MACOS_VM.md). The VZ
/// runtime itself (install / boot / SSH) needs Apple silicon + a provisioned base and is exercised by
/// `macvm-spike`, not here — these tests pin the naming, MAC/lease parsing, SSH command assembly,
/// spec clamping, and settings that must stay correct without a VM.
@Suite struct MacVMTests {

    // MARK: - Naming

    @Test func vmNameIsLowercasedMacSiblingWithChannelSuffix() {
        let sid = SessionID(rawValue: "AB12CD34EF")
        let name = MacVMManager.vmName(for: sid)
        #expect(name.hasPrefix("nucleic-mac-"))
        #expect(name == name.lowercased())
        #expect(name.hasSuffix(ContainerManager.channelSuffix))
        // The short id (lowercased) sits between the prefix and the channel suffix.
        #expect(name.contains(sid.short.lowercased()))
    }

    // MARK: - MAC normalization + lease parsing

    @Test func normalizeMACIsLeadingZeroInsensitive() {
        // The lease file drops leading zeros; VZMACAddress keeps them. Both must normalize equal.
        #expect(MacVMEngine.normalizeMAC("2:34:5:6:7:8") == "02:34:05:06:07:08")
        #expect(MacVMEngine.normalizeMAC("D6:A7:58:8E:78:D4") == "d6:a7:58:8e:78:d4")
        #expect(
            MacVMEngine.normalizeMAC("02:34:05:06:07:08")
                == MacVMEngine.normalizeMAC("2:34:5:6:7:8"))
    }

    @Test func parseLeasesFindsIPForMACIgnoringLeadingZeros() {
        let leases = """
        {
        \tname=vm-a
        \tip_address=192.168.64.7
        \thw_address=1,d6:a7:58:8e:78:d4
        \tidentifier=1,d6:a7:58:8e:78:d4
        \tlease=0x600000
        }
        {
        \tname=vm-b
        \tip_address=192.168.64.9
        \thw_address=1,2:34:5:6:7:8
        }
        """
        // Query with the zero-padded form; the lease stores the stripped form.
        #expect(MacVMEngine.parseLeases(leases, forMAC: "02:34:05:06:07:08") == "192.168.64.9")
        #expect(MacVMEngine.parseLeases(leases, forMAC: "d6:a7:58:8e:78:d4") == "192.168.64.7")
        #expect(MacVMEngine.parseLeases(leases, forMAC: "aa:bb:cc:dd:ee:ff") == nil)
    }

    @Test func parseLeasesTakesNewestMatchingLease() {
        let leases = """
        {
        \tip_address=192.168.64.2
        \thw_address=1,de:ad:be:ef:00:01
        }
        {
        \tip_address=192.168.64.20
        \thw_address=1,de:ad:be:ef:00:01
        }
        """
        #expect(MacVMEngine.parseLeases(leases, forMAC: "de:ad:be:ef:00:01") == "192.168.64.20")
    }

    // MARK: - SSH command assembly

    @Test func shQuoteWrapsAndEscapesSingleQuotes() {
        #expect(MacVMEngine.shQuote("simple") == "'simple'")
        #expect(MacVMEngine.shQuote("a'b") == "'a'\\''b'")
    }

    @Test func remoteScriptExportsEnvThenCdsThenRunsBody() {
        let script = MacVMEngine.remoteScript(
            workdir: "/Volumes/My Shared Files/workspace",
            env: ["FOO": "bar", "BAZ": "x'y"],  // sorted by key: BAZ before FOO
            body: "xcodebuild test")
        #expect(script.contains("export BAZ='x'\\''y';"))  // single quotes escaped
        #expect(script.contains("export FOO='bar';"))
        // Env exports precede the cd, which precedes the body.
        let bazIdx = script.range(of: "export BAZ=")!.lowerBound
        let cdIdx = script.range(of: "cd '/Volumes/My Shared Files/workspace' || exit 1;")!.lowerBound
        #expect(bazIdx < cdIdx)
        #expect(script.hasSuffix("xcodebuild test"))
    }

    @Test func remoteScriptWithoutWorkdirSkipsCd() {
        let script = MacVMEngine.remoteScript(workdir: nil, env: [:], body: "echo hi")
        #expect(!script.contains("cd "))
        #expect(script == "echo hi")
    }

    // MARK: - vsock exec framing (the SSH exec path was retired for the in-guest agent)

    @Test func execRequestLineCarriesOpAndCommand() throws {
        let line = MacVMAgentWire.requestLine(
            op: MacVMAgentWire.Exec.op,
            fields: [MacVMAgentWire.Exec.commandKey: .string("echo ok")])
        #expect(line.last == 0x0A)  // NDJSON: one object per line
        let obj = try JSONValue(parsing: line.dropLast()).objectValue
        #expect(obj?["op"]?.stringValue == "exec")
        #expect(obj?[MacVMAgentWire.Exec.commandKey]?.stringValue == "echo ok")
    }

    @Test func execFrameLineHasTagButNoOp() throws {
        // Streaming frames carry a `t` tag, not an `op` — `requestLineRaw` must not inject one.
        let frame = MacVMAgentWire.requestLineRaw([
            MacVMAgentWire.Exec.tagKey: .string(MacVMAgentWire.Exec.signal),
            MacVMAgentWire.Exec.signalKey: .number(9),
        ])
        let obj = try JSONValue(parsing: frame.dropLast()).objectValue
        #expect(obj?["op"] == nil)
        #expect(obj?[MacVMAgentWire.Exec.tagKey]?.stringValue == "sig")
        #expect(obj?[MacVMAgentWire.Exec.signalKey]?.intValue == 9)  // whole number renders as int
    }

    // MARK: - Spec

    @Test func specClampsResourcesToAtLeastOne() {
        let spec = MacVMSpec(name: "nucleic-mac-x", cpus: 0, memoryGiB: -4)
        #expect(spec.cpus == 1)
        #expect(spec.memoryGiB == 1)
    }

    @Test func specRenamedKeepsEverythingButName() {
        let spec = MacVMSpec(
            name: "a", cpus: 6, memoryGiB: 12,
            mounts: [MacVMSpec.Mount(host: "/repo", name: "workspace", readOnly: false)],
            workdir: "/w", env: ["K": "V"], sshUser: "agent")
        let renamed = spec.renamed("b")
        #expect(renamed.name == "b")
        #expect(renamed.cpus == 6)
        #expect(renamed.memoryGiB == 12)
        #expect(renamed.mounts == spec.mounts)
        #expect(renamed.workdir == "/w")
        #expect(renamed.env == ["K": "V"])
    }

    // MARK: - Restore image versioning

    @Test func restoreCacheNameTracksImageIdentity() {
        // The IPSW filename encodes the macOS version/build, so it IS the cache key — different
        // versions land in different files (the fix that lets the fetch path move up to macOS 27).
        let v26 = URL(string: "https://updates.cdn-apple.com/x/UniversalMac_26.0_25A123_Restore.ipsw")!
        let v27 = URL(string: "https://updates.cdn-apple.com/y/UniversalMac_27.0_26A456_Restore.ipsw")!
        #expect(MacVMEngine.restoreCacheName(for: v26) == "UniversalMac_26.0_25A123_Restore.ipsw")
        #expect(MacVMEngine.restoreCacheName(for: v27) == "UniversalMac_27.0_26A456_Restore.ipsw")
        #expect(MacVMEngine.restoreCacheName(for: v26) != MacVMEngine.restoreCacheName(for: v27))
    }

    @Test func restoreCacheNameAppendsIpswWhenMissing() {
        let noExt = URL(string: "https://example.com/downloads/some-restore")!
        #expect(MacVMEngine.restoreCacheName(for: noExt).hasSuffix(".ipsw"))
    }

    @Test func restoreImageURLSettingReads() async throws {
        let suite = UserDefaults(suiteName: "macvm-url-\(UUID().uuidString)")!
        try await ContainerServiceSettings.withDefaults(suite) {
            #expect(MacVMSettings.restoreImageURL == nil)
            suite.set("https://example.com/UniversalMac_27.0.ipsw", forKey: MacVMSettings.restoreImageURLKey)
            #expect(MacVMSettings.restoreImageURL == "https://example.com/UniversalMac_27.0.ipsw")
        }
    }

    // MARK: - Capability

    @Test func supportedIffNoUnsupportedReason() {
        #expect(MacVMEngine.isSupported == (MacVMEngine.unsupportedReason == nil))
        #expect(MacVMManager.isSupported == MacVMEngine.isSupported)
    }

    // MARK: - Base progress labels

    @Test func baseProgressLabelsAreDistinctAndNonEmpty() {
        let phases: [MacVMBaseProgress.Phase] = [
            .downloadingRestoreImage, .installing, .firstBootSetup, .provisioning,
            .installingAgent, .finalizing, .ready,
        ]
        let labels = phases.map { MacVMBaseProgress(phase: $0).label }
        #expect(Set(labels).count == labels.count)
        #expect(labels.allSatisfy { !$0.isEmpty })
    }

    // MARK: - Base status (bundle.json ⇄ MacVMBaseStatus, provisioning readback)

    @Test func baseStatusJSONRoundTripsAndDefaultsMissingKeys() {
        let full = MacVMBaseStatus(
            installed: true, accountProvisioned: true, provisioned: true, agentInstalled: true,
            sipDisabled: true, axAgentReady: true, osVersion: "27.0", buildVersion: "26A1")
        // Round-trip through the dictionary the bundle.json is (de)serialized as.
        let back = MacVMBaseStatus(json: full.dictionary)
        #expect(back == full)
        // Computer use is host-side, so "ready" == installed (no provisioning / SIP).
        #expect(MacVMBaseStatus(installed: true).computerUseReady)
        #expect(!MacVMBaseStatus().computerUseReady)
        // A legacy base's `computerUseReady` key maps onto the AX-agent readiness (its old meaning).
        let legacy = MacVMBaseStatus(json: ["installed": true, "computerUseReady": true, "osVersion": "26.0"])
        #expect(legacy.installed)
        #expect(legacy.axAgentReady)
        #expect(legacy.osVersion == "26.0")
    }

    @Test func baseStatusSummaryReflectsReadiness() {
        #expect(MacVMBaseStatus().summary.contains("No base"))
        #expect(MacVMBaseStatus(installed: true).summary.contains("account"))  // installed, no account yet
        // A provisioned base: computer use works host-side (no SIP mentioned).
        let provisioned = MacVMBaseStatus(installed: true, accountProvisioned: true, provisioned: true)
        #expect(provisioned.summary.contains("computer use"))
        #expect(!provisioned.summary.contains("SIP"))
        #expect(MacVMBaseStatus(
            installed: true, accountProvisioned: true, provisioned: true, agentInstalled: true,
            sipDisabled: true, axAgentReady: true).summary.contains("AX"))
    }

    // MARK: - Host-side input keymap + scroll

    @Test func keyMapParsesChordsWithModifiers() {
        let s = MacVMKeyMap.stroke(forChord: "cmd+shift+s")
        #expect(s?.keyCode == 0x01)  // 's'
        #expect(s?.command == true)
        #expect(s?.shift == true)
        #expect(MacVMKeyMap.stroke(forChord: "return")?.keyCode == 0x24)
        #expect(MacVMKeyMap.stroke(forChord: "enter")?.keyCode == 0x24)  // alias
        #expect(MacVMKeyMap.stroke(forChord: "boguskey") == nil)
    }

    @Test func keyMapTypesCharactersWithShiftForUppercaseAndSymbols() {
        let a = MacVMKeyMap.stroke(forCharacter: "a")
        #expect(a?.keyCode == 0x00)
        #expect(a?.shift == false)
        #expect(MacVMKeyMap.stroke(forCharacter: "A")?.shift == true)
        #expect(MacVMKeyMap.stroke(forCharacter: "A")?.keyCode == 0x00)
        // Shifted symbol: '!' is shift+'1'.
        #expect(MacVMKeyMap.stroke(forCharacter: "!")?.shift == true)
        #expect(MacVMKeyMap.stroke(forCharacter: "!")?.keyCode == 0x12)
    }

    @Test func scrollDeltasMapDirectionToSignedLines() {
        #expect(MacVMEngine.scrollDeltas(direction: "down", amount: 3) == (0, -3))
        #expect(MacVMEngine.scrollDeltas(direction: "up", amount: 5) == (0, 5))
        #expect(MacVMEngine.scrollDeltas(direction: "left", amount: 2) == (2, 0))
        #expect(MacVMEngine.scrollDeltas(direction: "right", amount: 2) == (-2, 0))
    }

    // MARK: - Resource probe parsing (Control panel VM meters)

    @Test func parseResourceProbeComputesUsedMemoryAndCPUFromIdle() {
        // vm_stat (16 KiB pages) + a `top` "CPU usage" line, as the guest emits them.
        let probe = """
        Mach Virtual Memory Statistics: (page size of 16384 bytes)
        Pages free:                                1000.
        Pages active:                              2000.
        Pages inactive:                             500.
        Pages wired down:                          1000.
        Pages occupied by compressor:               500.
        __CPU__
        CPU usage: 4.76% user, 9.52% sys, 85.71% idle
        """
        let total: UInt64 = 8 * 1_073_741_824  // 8 GiB ceiling
        let sample = MacVMEngine.parseResourceProbe(probe, memoryTotal: total)
        // used = (active + wired + compressed) × page size = 3500 × 16384.
        #expect(sample.memoryUsedBytes == 3500 * 16384)
        #expect(sample.memoryTotalBytes == total)
        // CPU = 100 − idle, within float tolerance.
        #expect(abs(sample.cpuPercent - (100 - 85.71)) < 0.01)
    }

    @Test func parseResourceProbeDegradesToCeilingOnBlankOutput() {
        // A missing/garbled probe must not throw — it yields a ceiling-only sample (zeros).
        let total: UInt64 = 4 * 1_073_741_824
        let sample = MacVMEngine.parseResourceProbe("", memoryTotal: total)
        #expect(sample.memoryUsedBytes == 0)
        #expect(sample.cpuPercent == 0)
        #expect(sample.memoryTotalBytes == total)
        #expect(sample.memoryPercent == 0)
    }

    @Test func parseProvisionStatusReadsGuestReadbackLine() {
        // The guest bootstrap writes `CODE=… SIP=… APP=… LA=… TCC=…` to STATUS on the share.
        let flags = MacVMEngine.parseProvisionStatus("CODE=0 SIP=1 APP=1 LA=1 TCC=3")
        #expect(flags.exitCode == 0)
        #expect(flags.sipDisabled)
        #expect(flags.agentApp)
        #expect(flags.launchAgent)
        #expect(flags.tccCount == 3)
        // Order-independent and tolerant of surrounding noise / SIP-on / partial grants / bad code.
        let partial = MacVMEngine.parseProvisionStatus("noise\nCODE=1 TCC=0 APP=1 SIP=0 LA=0\nmore")
        #expect(partial.exitCode == 1)
        #expect(!partial.sipDisabled)
        #expect(partial.agentApp)
        #expect(!partial.launchAgent)
        #expect(partial.tccCount == 0)
        // A missing CODE defaults to -1 (never mistaken for success).
        #expect(MacVMEngine.parseProvisionStatus("SIP=0").exitCode == -1)
    }

    // MARK: - Settings (isolated task-local defaults suite)

    @Test func settingsDefaultsAndOverrides() async throws {
        let suite = UserDefaults(suiteName: "macvm-test-\(UUID().uuidString)")!
        try await ContainerServiceSettings.withDefaults(suite) {
            // Defaults with an empty suite.
            #expect(MacVMSettings.serviceEnabled == false)
            #expect(MacVMSettings.exposeByDefault == false)  // false while service is off
            #expect(MacVMSettings.vmCPUs == MacVMSettings.defaultVMCPUs)
            #expect(MacVMSettings.vmMemoryGiB == MacVMSettings.defaultVMMemoryGiB)
            #expect(MacVMSettings.maxConcurrentVMs == MacVMSettings.defaultMaxConcurrentVMs)
            #expect(MacVMSettings.sshUser == MacVMSettings.defaultSSHUser)
            #expect(MacVMSettings.basePrebuiltPath == nil)
            #expect(MacVMSettings.restoreImagePath == nil)

            // Overrides take effect.
            suite.set(true, forKey: MacVMSettings.serviceEnabledKey)
            suite.set(true, forKey: MacVMSettings.exposeByDefaultKey)
            suite.set(16, forKey: MacVMSettings.vmCPUsKey)
            suite.set("   ", forKey: MacVMSettings.sshUserKey)  // blank falls back to default
            suite.set("/base/bundle", forKey: MacVMSettings.basePrebuiltPathKey)
            #expect(MacVMSettings.serviceEnabled == true)
            #expect(MacVMSettings.exposeByDefault == true)  // now that the service is on
            #expect(MacVMSettings.vmCPUs == 16)
            #expect(MacVMSettings.sshUser == MacVMSettings.defaultSSHUser)
            #expect(MacVMSettings.basePrebuiltPath == "/base/bundle")
        }
    }

    // MARK: - Guidance

    @Test func macVMGuidanceOnlyWhenEnabled() {
        let off = SessionController.sandboxBuildGuidance(allowHostExec: true, allowMacVMExec: false)
        let on = SessionController.sandboxBuildGuidance(allowHostExec: true, allowMacVMExec: true)
        #expect(!off.contains("mac_vm_exec"))
        #expect(on.contains("mac_vm_exec"))
        #expect(on.contains("/Volumes/My Shared Files/workspace"))
    }

    @Test func computerUseGuidanceOnlyWhenEnabled() {
        let off = SessionController.sandboxBuildGuidance(
            allowHostExec: true, allowMacVMExec: true, allowMacVMComputer: false)
        let on = SessionController.sandboxBuildGuidance(
            allowHostExec: true, allowMacVMExec: true, allowMacVMComputer: true)
        #expect(!off.contains("mac_vm_computer"))
        #expect(on.contains("mac_vm_computer"))
    }

    // MARK: - Computer-use command builders

    @Test func asuserWrapsForAquaSession() {
        #expect(MacVMEngine.asuser("/usr/sbin/screencapture -x")
            == "launchctl asuser $(id -u) /usr/sbin/screencapture -x")
    }

    @Test func screenshotCommandCapturesAndBase64s() {
        let cmd = MacVMEngine.screenshotCommand()
        #expect(cmd.contains("launchctl asuser $(id -u) /usr/sbin/screencapture -x -t jpg"))
        #expect(cmd.contains("base64 -i"))
        #expect(cmd.contains("rm -f"))
    }

    @Test func pointerCommandsMapToCliclickVerbs() {
        #expect(MacVMEngine.pointerCommand(action: "left_click", x: 10, y: 20)
            == "launchctl asuser $(id -u) /usr/local/bin/cliclick c:10,20")
        #expect(MacVMEngine.pointerCommand(action: "right_click", x: 1, y: 2).hasSuffix("rc:1,2"))
        #expect(MacVMEngine.pointerCommand(action: "double_click", x: 1, y: 2).hasSuffix("dc:1,2"))
        #expect(MacVMEngine.pointerCommand(action: "mouse_move", x: 1, y: 2).hasSuffix("m:1,2"))
    }

    @Test func dragCommandUsesExplicitStartAndTarget() {
        // dd:/du: need real coordinates — no `.` placeholder.
        #expect(MacVMEngine.dragCommand(fromX: 3, fromY: 4, toX: 5, toY: 6).hasSuffix("dd:3,4 du:5,6"))
    }

    @Test func parseCursorReadsCliclickPointOutput() {
        #expect(MacVMEngine.parseCursor("640,400\n").map { "\($0.0),\($0.1)" } == "640,400")
        #expect(MacVMEngine.parseCursor("garbage") == nil)
    }

    @Test func typeCommandShellQuotesText() {
        let cmd = MacVMEngine.typeCommand("hello 'world'")
        #expect(cmd.contains("/usr/local/bin/cliclick -w 20 t:'hello '\\''world'\\'''"))
    }

    @Test func keyCommandBuildsChordsAndSpecials() {
        // Letter with a modifier → hold cmd, type the letter.
        #expect(MacVMEngine.keyCommand(chord: "cmd+s").hasSuffix("kd:cmd t:'s' ku:cmd"))
        // Multiple modifiers + digit (screenshot shortcut).
        #expect(MacVMEngine.keyCommand(chord: "cmd+shift+4").hasSuffix("kd:cmd,shift t:'4' ku:cmd,shift"))
        // A special key with a modifier.
        #expect(MacVMEngine.keyCommand(chord: "cmd+tab").hasSuffix("kd:cmd kp:tab ku:cmd"))
        // Bare special key, no modifiers.
        #expect(MacVMEngine.keyCommand(chord: "return").hasSuffix("cliclick kp:return"))
        #expect(MacVMEngine.keyCommand(chord: "escape").hasSuffix("cliclick kp:esc"))
    }

    @Test func scrollCommandApproximatesWithArrowKeys() {
        let cmd = MacVMEngine.scrollCommand(direction: "down", amount: 3)
        #expect(cmd.hasSuffix("kp:arrow-down kp:arrow-down kp:arrow-down"))
        #expect(MacVMEngine.scrollCommand(direction: "up", amount: 1).hasSuffix("kp:arrow-up"))
    }

    @Test func launchAppCommandQuotesName() {
        #expect(MacVMEngine.launchAppCommand("Xcode").hasSuffix("/usr/bin/open -a 'Xcode'"))
    }

    // MARK: - Native in-guest agent (docs/MACOS_VM_NATIVE_AGENT.md)

    @Test func agentRequestLineIsOneJSONObjectPerLine() throws {
        let line = MacVMAgentWire.requestLine(
            op: "click", fields: ["x": .number(10), "y": .number(20)])
        #expect(line.last == 0x0A)  // newline-terminated (NDJSON)
        #expect(!line.dropLast().contains(0x0A))  // and exactly ONE line
        let parsed = try JSONValue(parsing: line.dropLast())
        #expect(parsed["op"]?.stringValue == "click")
        #expect(parsed["x"]?.intValue == 10)
        #expect(parsed["y"]?.intValue == 20)
    }

    @Test func agentRequestLineOpWinsOverFieldCollision() throws {
        // `op` is the dispatcher's field; a caller-supplied collision must not hijack it.
        let line = MacVMAgentWire.requestLine(op: "ping", fields: ["op": .string("evil")])
        let parsed = try JSONValue(parsing: line.dropLast())
        #expect(parsed["op"]?.stringValue == "ping")
    }

    @Test func agentReplyParsingAcceptsObjectsRejectsGarbage() throws {
        let reply = try MacVMAgentWire.parseReply(Data(#"{"ok":true,"version":1}"#.utf8))
        #expect(reply["ok"]?.boolValue == true)
        #expect(reply["version"]?.intValue == 1)
        #expect(throws: (any Error).self) {
            try MacVMAgentWire.parseReply(Data("not json".utf8))
        }
        #expect(throws: (any Error).self) {
            try MacVMAgentWire.parseReply(Data(#"[1,2,3]"#.utf8))  // an array is not a reply
        }
    }

    @Test func agentErrorSeparatesTransportFromOpFailures() {
        // Op-level failures keep the channel; transport failures discard it (re-probe next action).
        #expect(!MacVMAgentError.agentError("no such ref").isTransportFailure)
        #expect(MacVMAgentError.ioFailed("EOF").isTransportFailure)
        #expect(MacVMAgentError.timeout.isTransportFailure)
        #expect(MacVMAgentError.protocolError("bad line").isTransportFailure)
        #expect(MacVMAgentError.noSocketDevice.isTransportFailure)
    }

    @Test func scrollDeltaMapsDirectionToWheelLines() {
        // CGEvent wheel conventions: wheel1 > 0 scrolls content up, wheel2 > 0 scrolls left.
        #expect(MacVMEngine.scrollDelta(direction: "up", amount: 3) == (0, 3))
        #expect(MacVMEngine.scrollDelta(direction: "down", amount: 3) == (0, -3))
        #expect(MacVMEngine.scrollDelta(direction: "left", amount: 2) == (2, 0))
        #expect(MacVMEngine.scrollDelta(direction: "right", amount: 2) == (-2, 0))
        #expect(MacVMEngine.scrollDelta(direction: "diagonal??", amount: 1) == (0, -1))  // → down
    }

    @Test func axActionsAreAgentOnlyVocabulary() {
        #expect(MacVMEngine.axActions == [
            "ax_dump", "ax_element_at", "ax_press", "ax_set_value", "ax_focus",
        ])
        // The pixel vocabulary must stay disjoint — routing switches on membership.
        for pixel in ["screenshot", "left_click", "type", "key", "scroll", "launch_app", "wait"] {
            #expect(!MacVMEngine.axActions.contains(pixel))
        }
    }

    @Test func computerUseGuidanceTeachesAXFirst() {
        let on = SessionController.sandboxBuildGuidance(
            allowHostExec: true, allowMacVMExec: true, allowMacVMComputer: true)
        #expect(on.contains("ax_dump"))
        #expect(on.contains("ax_press"))
    }

    // MARK: - macOS 27 declarative first-boot provisioning (docs/MACOS_VM.md §4.4)

    @Test func provisionBootstrapScriptIsNetworkFreeAndReportsStatus() {
        // The host-composed bootstrap the guest runs via HID: reads the password from the share,
        // runs the provisioner, and writes a CODE/SIP/APP/LA/TCC readback + powers off. No network.
        let script = MacVMEngine.provisionBootstrapScript(user: "agent")
        #expect(script.contains("touch \"$SHARE/STARTED\""))              // "running" sentinel
        #expect(script.contains("cat \"$SHARE/password\""))               // no typed password
        #expect(script.contains("provision-macos-guest.sh"))              // runs the provisioner
        #expect(script.contains("agent ALL=(ALL) NOPASSWD: ALL"))         // primes sudo for the user
        #expect(script.contains("> \"$SHARE/STATUS\""))                   // writes the readback
        #expect(script.contains("shutdown -h now"))                       // powers off when done
        #expect(!script.contains("ssh"))                                  // never touches SSH
    }

    @Test func randomPasswordIsHexAndLongEnough() {
        let a = MacVMEngine.randomPassword()
        let b = MacVMEngine.randomPassword()
        #expect(a.count == 20)
        #expect(a.allSatisfy { $0.isHexDigit })
        #expect(a != b)  // 80 bits of entropy — a collision means the generator is broken
    }

    @Test func agentPasswordSettingReads() async throws {
        let suite = UserDefaults(suiteName: "macvm-pw-\(UUID().uuidString)")!
        try await ContainerServiceSettings.withDefaults(suite) {
            #expect(MacVMSettings.agentPassword == nil)
            suite.set("  hunter2  ", forKey: MacVMSettings.agentPasswordKey)
            #expect(MacVMSettings.agentPassword == "hunter2")
        }
    }

    @Test func firstBootSetupPhaseHasDistinctLabel() {
        let phases: [MacVMBaseProgress.Phase] = [
            .downloadingRestoreImage, .installing, .firstBootSetup, .provisioning, .ready,
        ]
        let labels = phases.map { MacVMBaseProgress(phase: $0).label }
        #expect(Set(labels).count == labels.count)
        #expect(MacVMBaseProgress(phase: .firstBootSetup).label.contains("unattended"))
    }

    @Test func computerUseSettingGatedOnService() async throws {
        let suite = UserDefaults(suiteName: "macvm-cu-\(UUID().uuidString)")!
        try await ContainerServiceSettings.withDefaults(suite) {
            suite.set(true, forKey: MacVMSettings.computerUseByDefaultKey)
            #expect(MacVMSettings.computerUseByDefault == false)  // service off ⇒ false
            suite.set(true, forKey: MacVMSettings.serviceEnabledKey)
            #expect(MacVMSettings.computerUseByDefault == true)
        }
    }

    // MARK: - Linux guests

    @Test func specDefaultsToMacOSAndRenamePreservesOS() {
        #expect(MacVMSpec(name: "a").os == .macOS)
        let linux = MacVMSpec(name: "a", os: .linux)
        #expect(linux.os == .linux)
        #expect(linux.renamed("b").os == .linux)  // renamed() must not lose the guest OS
        #expect(linux.renamed("b").name == "b")
    }

    @Test func linuxVMNameIsDistinctLowercasedSibling() {
        let sid = SessionID(rawValue: "AB12CD34EF")
        let mac = MacVMManager.vmName(for: sid)
        let lin = MacVMManager.linuxVMName(for: sid)
        #expect(lin.hasPrefix("nucleic-lin-"))
        #expect(lin == lin.lowercased())
        #expect(lin.hasSuffix(ContainerManager.channelSuffix))
        #expect(lin != mac)  // a session's Linux + macOS clones never collide
    }

    @Test func bundleCompletenessIsOSAware() throws {
        let fm = FileManager.default
        let root = fm.temporaryDirectory.appendingPathComponent("linbundle-\(UUID().uuidString)")
        try fm.createDirectory(at: root, withIntermediateDirectories: true)
        defer { try? fm.removeItem(at: root) }
        let bundle = MacVMBundle(root: root)
        // Lay down only the Linux artifacts (kernel + initrd + rootfs disk).
        for url in [bundle.kernelURL, bundle.initrdURL, bundle.diskImageURL] {
            fm.createFile(atPath: url.path, contents: Data("x".utf8))
        }
        #expect(bundle.isComplete(for: .linux) == true)
        #expect(bundle.isComplete(for: .macOS) == false)  // no Mac platform identity/NVRAM
        #expect(bundle.isComplete == false)  // the macOS-flavored convenience accessor
    }

    @Test func baseStatusRoundTripsGuestOS() {
        // A Linux status serializes `os` and reads back as Linux.
        let linux = MacVMBaseStatus(installed: true, provisioned: true, osVersion: "24.04", os: .linux)
        let back = MacVMBaseStatus(json: linux.dictionary)
        #expect(back.os == .linux)
        #expect(back.provisioned == true)
        #expect(back.summary.contains("Linux"))
        // A legacy bundle.json without the `os` key defaults to macOS.
        #expect(MacVMBaseStatus(json: ["installed": true]).os == .macOS)
    }

    @Test func linuxSettingsDefaultsAndOverrides() async throws {
        let suite = UserDefaults(suiteName: "linuxvm-test-\(UUID().uuidString)")!
        try await ContainerServiceSettings.withDefaults(suite) {
            #expect(MacVMSettings.linuxServiceEnabled == false)
            #expect(MacVMSettings.linuxExposeByDefault == false)  // false while service off
            #expect(MacVMSettings.linuxBasePrebuiltPath == nil)
            // Pinned Ubuntu defaults when unset.
            #expect(MacVMSettings.linuxKernelURL == MacVMSettings.defaultLinuxKernelURL)
            #expect(MacVMSettings.linuxRootfsURL.contains("ubuntu"))

            suite.set(true, forKey: MacVMSettings.linuxServiceEnabledKey)
            suite.set("https://example.test/vmlinux", forKey: MacVMSettings.linuxKernelURLKey)
            #expect(MacVMSettings.linuxServiceEnabled == true)
            #expect(MacVMSettings.linuxExposeByDefault == true)  // defaults true once service on
            #expect(MacVMSettings.linuxKernelURL == "https://example.test/vmlinux")
        }
    }

    #if arch(arm64)
    @Test func defaultLinuxCommandLineBootsConsoleAndRoot() {
        let cmd = MacVMEngine.defaultLinuxCommandLine
        #expect(cmd.contains("console=hvc0"))
        #expect(cmd.contains("root=/dev/vda"))
    }

    @Test func linuxCacheNameTracksFilenameWithoutForcingIpsw() {
        let url = URL(string: "https://cloud-images.ubuntu.com/x/ubuntu-24.04-arm64-root.tar.xz")!
        #expect(MacVMEngine.linuxCacheName(for: url) == "ubuntu-24.04-arm64-root.tar.xz")
        // Unlike restoreCacheName, it must NOT append .ipsw.
        #expect(!MacVMEngine.linuxCacheName(for: url).hasSuffix(".ipsw"))
    }
    #endif
}
