import Foundation
#if arch(arm64)
import Virtualization
#endif

/// macOS 27 **declarative first-boot provisioning** (docs/MACOS_VM.md §4.4): when host AND guest are
/// macOS 27+, `VZMacGuestProvisioningOptions` lets the very first boot after install create the
/// `agent` account and enable auto-login — unattended, no Setup Assistant. **Remote Login is NOT
/// enabled**: the host↔guest control plane is vsock-only, so the base never runs sshd. Everything
/// after the account exists (dev toolchain + the native agent + TCC grants) is driven host-side via
/// the display surface + a VirtioFS share (`MacVMEngine+Provision.swift`) — no network at any point.
///
/// The framework evaluates the options ONLY on the first boot after restore, so the provisioning pass
/// carries them exactly once. Failure there is non-fatal: the freshly installed base is still
/// published (a multi-GB install must not be discarded).
extension MacVMEngine {
    /// Where the generated agent-account password persists (0600) when `MacVMSettings.agentPassword`
    /// isn't configured — so an unattended build needs no config and the operator can still find the
    /// credential (it's what the provisioning bootstrap uses to prime sudo).
    var agentPasswordURL: URL { credentialsDir.appendingPathComponent("agent-password") }

    /// The password for the declaratively-created guest account: the configured setting, else the
    /// previously generated one, else a fresh random one persisted for reuse.
    func resolveAgentPassword() throws -> String {
        if let configured = MacVMSettings.agentPassword { return configured }
        if let stored = try? String(contentsOf: agentPasswordURL, encoding: .utf8)
            .trimmingCharacters(in: .whitespacesAndNewlines), !stored.isEmpty
        {
            return stored
        }
        let generated = Self.randomPassword()
        let fm = FileManager.default
        try fm.createDirectory(
            at: agentPasswordURL.deletingLastPathComponent(), withIntermediateDirectories: true)
        try generated.write(to: agentPasswordURL, atomically: true, encoding: .utf8)
        try fm.setAttributes([.posixPermissions: 0o600], ofItemAtPath: agentPasswordURL.path)
        return generated
    }

    /// A 20-hex-char random password — comfortably past any password policy, shell-quote-safe.
    static func randomPassword() -> String {
        (0..<10).map { _ in String(format: "%02x", UInt8.random(in: 0...255)) }.joined()
    }
}

#if arch(arm64)
extension MacVMInstance {
    /// Start the VM with macOS-27 declarative guest provisioning attached (account + auto-login,
    /// evaluated by the guest on its first boot after restore). **Remote Login is deliberately left
    /// off** — the control plane is vsock. Runs on the VM queue like every VZ call; validation failures
    /// (bad username/password per VZError 40001–40003) surface as thrown errors.
    @available(macOS 27.0, *)
    func startWithProvisioning(fullName: String, username: String, password: String) async throws {
        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
            queue.async { [self] in
                let provisioning = VZMacGuestProvisioningOptions()
                provisioning.fullName = fullName
                provisioning.username = username
                provisioning.password = password
                provisioning.logsInAutomatically = true
                provisioning.enablesRemoteLogin = false
                let options = VZMacOSVirtualMachineStartOptions()
                do {
                    // Swift imports the ObjC `setGuestProvisioningOptions:error:` as this throwing
                    // form; it validates (VZError 40001–40003 on bad name/username/password).
                    try options.setGuestProvisioning(provisioning)
                } catch {
                    cont.resume(throwing: error)
                    return
                }
                vm.start(options: options) { error in
                    if let error { cont.resume(throwing: error) } else { cont.resume() }
                }
            }
        }
    }

    /// Start the guest **into macOS Recovery** (`startUpFromMacOSRecovery`) instead of the normal
    /// system volume — the Lane B (docs/MACOS_VM_POLICY_ALTERNATIVE.md) entry point for toggling SIP.
    /// recoveryOS is where `csrutil disable`/`enable` must run; the host then drives Recovery's Terminal
    /// through the display surface (there is no vsock agent in recoveryOS). Runs on the VM queue like
    /// every VZ call; a start failure surfaces as a thrown error.
    @available(macOS 13.0, *)
    func startInRecovery() async throws {
        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
            queue.async { [self] in
                let options = VZMacOSVirtualMachineStartOptions()
                options.startUpFromMacOSRecovery = true
                vm.start(options: options) { error in
                    if let error { cont.resume(throwing: error) } else { cont.resume() }
                }
            }
        }
    }

    /// Wait (bounded) for the guest to power itself off after an in-guest `shutdown -h`, then
    /// force-stop if it's still up — the base publish needs a quiesced disk either way.
    func awaitStopped(timeout: TimeInterval) async {
        let deadline = Date().addingTimeInterval(timeout)
        while Date() < deadline {
            let stopped = await withCheckedContinuation { (cont: CheckedContinuation<Bool, Never>) in
                queue.async { [self] in cont.resume(returning: vm.state == .stopped) }
            }
            if stopped { return }
            try? await Task.sleep(nanoseconds: 2_000_000_000)
        }
        await stop()
    }
}
#endif
