import Foundation
import os

private let mdmPassLog = Logger(subsystem: "com.nucleic", category: "macvm.mdm")

extension MacVMEngine {
    /// Surface/VM label for the mandatory Mode A enrollment and policy-maintenance boot.
    public static let baseMDMPolicySurfaceName = "base-mdm-policy"

    /// Mode A must reach its staged artifacts through Apple's safe automount. A direct/custom mount
    /// would touch the Network Volumes TCC service before the very policy intended to grant it lands.
    static func mdmPolicyPassMount(hostDirectory: String) -> MacVMSpec.Mount {
        MacVMSpec.Mount(
            host: hostDirectory, name: "nucleic-provision", readOnly: false,
            recreateHostPath: false)
    }

    /// Fold a fully verified MDM result into base metadata. Returning nil for partial results keeps
    /// the caller from accidentally publishing optimistic status bits or stamping a broken recipe.
    static func baseStatus(
        byApplying outcome: MDMOutcome, to status: MacVMBaseStatus
    ) -> MacVMBaseStatus? {
        guard outcome.enrolled,
              outcome.profilesInstalled == builtInMDMPolicyProfileNames.count
        else { return nil }
        var verified = status
        verified.mdmEnrolled = true
        verified.profilesInstalled = true
        return verified
    }

    /// Metadata half of the recipe gate. The following production-shaped probe supplies the
    /// functional half; both must pass before a base can be marked current.
    ///
    /// Lane-aware (docs/MACOS_VM_POLICY_ALTERNATIVE.md): the datastore lane (Lane B, default) requires
    /// the verified TCC grants (``MacVMBaseStatus/policyGrantsVerified``); the MDM lane (Lane A) keeps
    /// the enrollment + profile invariant it always had.
    static func baseStatusReadyForRecipe(
        _ status: MacVMBaseStatus, lane: MacVMPolicyLane = .datastore
    ) -> Bool {
        let common = status.installed && status.accountProvisioned && status.provisioned
            && status.agentInstalled
        switch lane {
        case .datastore: return common && status.policyGrantsVerified
        case .mdm: return common && status.mdmEnrolled && status.profilesInstalled
        }
    }
}

#if arch(arm64)
extension MacVMEngine {
    /// Boot the writable golden base once, enroll-or-update through the direct maintenance instance,
    /// verify acknowledgements and exact guest inventory, then shut down cleanly. The surrounding
    /// build owns the base-busy guard and cannot proceed to full provisioning until this succeeds.
    func mdmPolicyPass(bundle: MacVMBundle) async throws -> MDMOutcome {
        guard let surface = surfaceHost else {
            throw MacVMError.provisionFailed(
                "the MDM validation pass needs the app's display surface for User-Approved enrollment")
        }

        baseMaintenance = MacVMMaintenanceInfo(
            name: Self.baseMDMPolicySurfaceName, os: .macOS,
            activity: "Starting MDM policy validation…", booting: true)
        defer { baseMaintenance = nil }

        let fm = FileManager.default
        let shareDir = fm.temporaryDirectory
            .appendingPathComponent("nucleic-mdm-policy-\(UUID().uuidString)", isDirectory: true)
        try fm.createDirectory(at: shareDir, withIntermediateDirectories: true)
        // Held for inspection on the failure path (below): removing the staged share out from under a
        // guest we deliberately kept running would unmount the artifacts we want to look at.
        var releaseShareDir = true
        defer { if releaseShareDir { try? fm.removeItem(at: shareDir) } }

        let profiles = try resolveBuiltInMDMPolicyProfiles()
        let mount = Self.mdmPolicyPassMount(hostDirectory: shareDir.path)
        let config = try Self.makeConfiguration(
            bundle: bundle, cpus: MacVMSettings.vmCPUs, memoryGiB: MacVMSettings.vmMemoryGiB,
            mac: Self.randomMAC(), mounts: [mount])
        let instance = MacVMInstance(configuration: config, label: Self.baseMDMPolicySurfaceName)
        do {
            try await instance.start()
        } catch {
            throw MacVMError.provisionFailed(
                "could not boot the base for MDM policy validation: \(error)")
        }

        await surface.attach(
            name: Self.baseMDMPolicySurfaceName,
            virtualMachine: UncheckedSendableBox(value: instance.vm as AnyObject))
        // The other two provisioning boots honor the operator's "watch this build" request; this one
        // did not, which meant the pass most likely to need watching was the only invisible one.
        if baseObserverRequested {
            await surface.setObserverVisible(name: Self.baseMDMPolicySurfaceName, visible: true)
        }
        do {
            try await awaitAgentReady(
                onInstance: instance, label: Self.baseMDMPolicySurfaceName)
            baseMaintenance = MacVMMaintenanceInfo(
                name: Self.baseMDMPolicySurfaceName, os: .macOS,
                activity: "Enrolling and verifying MDM policies…", booting: false)
            let outcome = try await applyBaseMDMPolicies(
                instance: instance, name: Self.baseMDMPolicySurfaceName, bundle: bundle,
                shareDir: shareDir, profiles: profiles, surface: surface)
            await surface.detach(name: Self.baseMDMPolicySurfaceName)
            await shutdownBaseGuest(instance)
            return outcome
        } catch {
            if MacVMSettings.holdBaseOnMDMFailure {
                // Keep the guest booted, on-screen, and still attached to its staged share, so the
                // screen that defeated the bounded approval flow can be driven by hand instead of
                // reconstructed from captures. `baseIsBusy` stays true for as long as we hold the
                // instance, so nothing tries to clone the base's live-mutating disk meanwhile.
                releaseShareDir = false
                heldDiagnosticInstances.append(instance)
                await surface.setObserverVisible(
                    name: Self.baseMDMPolicySurfaceName, visible: true)
                mdmPassLog.error(
                    """
                    MDM policy pass failed and is being HELD for inspection \
                    (\(MacVMSettings.holdBaseOnMDMFailureKey, privacy: .public)): the guest is still \
                    running with its monitor visible and its staged share at \
                    \(shareDir.path, privacy: .public). The base is pinned busy until the app quits. \
                    Failure: \(String(describing: error), privacy: .public)
                    """)
                throw error
            }
            await surface.detach(name: Self.baseMDMPolicySurfaceName)
            await instance.stop()
            throw error
        }
    }

    /// Re-run **only** the mandatory MDM enrollment/policy pass against the already-built base, and
    /// fold a verified result into its status — the fast iteration loop for
    /// docs/MACOS_VM_PROVISIONING_REPAIR_PLAN.md's enrollment repair.
    ///
    /// The full build is a ~40-minute experiment that reports one bit, which is the wrong shape for
    /// debugging a GUI flow. This boots the existing base, runs the identical Mode A code path
    /// (same surface, same direct-instance exec channel, same verification), and stops — roughly
    /// three minutes per attempt. It deliberately does **not** stamp the provisioning recipe: only a
    /// complete `provisionAndFinalize` may do that, since the toolchain and readiness passes have not
    /// run. A success here therefore means "enrollment works now", not "this base is publishable".
    @discardableResult
    public func revalidateBaseMDMPolicy() async throws -> MDMOutcome {
        guard Self.isSupported else {
            throw MacVMError.unavailable(Self.unsupportedReason ?? "requires Apple silicon")
        }
        guard !baseIsBusy else {
            throw MacVMError.baseBusy(
                "the base image is already in use (a build, a Recovery window, an injection, or a "
                    + "held diagnostic guest)")
        }
        // Deliberately NOT `ensureBaseBundle()`: that gate refuses a base whose MDM enrollment is
        // missing, which is exactly the base this entry point exists to work on. The requirement here
        // is only that the VM artifacts and the phase-1 control plane exist.
        let bundle = MacVMBundle(root: builtBaseDir)
        guard bundle.isComplete else {
            throw MacVMError.baseImageMissing(
                "no engine-built macOS base is present at \(builtBaseDir.path)")
        }
        let status = readBaseStatus(bundle)
        guard status.accountProvisioned, status.agentInstalled else {
            throw MacVMError.provisionFailed(
                "this base has no account/in-guest agent yet, so there is nothing for the MDM pass to "
                    + "drive. Build the base image first.")
        }
        baseBuilding = true
        baseWorkCancelled = false  // a new, deliberate pass — re-enable bundle writes
        syncBackgroundActivity()
        defer {
            baseBuilding = false
            syncBackgroundActivity()
        }
        let outcome = try await mdmPolicyPass(bundle: bundle)
        if let verified = Self.baseStatus(byApplying: outcome, to: status) {
            writeBaseStatus(bundle, verified)
        }
        return outcome
    }
}
#endif
