import Foundation

/// Builds the enrollment `.mobileconfig` `mdmclient` installs to become User-Approved MDM
/// (docs/MACOS_VM_MDM.md §4). Two payloads:
///
///   1. **PKCS#12** (`com.apple.security.pkcs12`): the host-generated device identity (key+cert) as a
///      password-protected `PKCS12` blob, plus its `Password`. macOS imports it into the keychain and
///      the MDM payload references it as the request-signing identity. (The deferred SCEP path, §9,
///      would swap this payload for `com.apple.security.scep`; the seam is `MDMDeviceIdentity`.)
///   2. **MDM** (`com.apple.mdm`): the management payload — `ServerURL`/`CheckInURL`, the `Topic`,
///      `IdentityCertificateUUID` pointing at the PKCS#12 payload (so check-in/command requests are
///      signed by that identity), `AccessRights`, `SignMessage`, `CheckOutWhenRemoved`.
///
/// The profile is emitted **unsigned** (plain XML plist). An unsigned profile installs with an
/// "unverified" note in System Settings, which is fine for our unattended, computer-use-driven flow;
/// the CA-trust bootstrap (staging + `security add-trusted-cert` before install) handles the TLS
/// trust the *management* traffic needs, which profile signing would not affect.
public enum MDMEnrollmentProfile {
    /// Payload UUIDs. The PKCS#12 payload's UUID is referenced by the MDM payload's
    /// `IdentityCertificateUUID`, so the two must agree. Pass fixed ones in tests.
    public struct UUIDs: Sendable {
        public var profile: UUID
        public var identity: UUID
        public var mdm: UUID
        public var root: UUID
        public init(
            profile: UUID = UUID(), identity: UUID = UUID(), mdm: UUID = UUID(), root: UUID = UUID()
        ) {
            self.profile = profile
            self.identity = identity
            self.mdm = mdm
            self.root = root
        }
    }

    /// Produce the enrollment profile as XML plist data.
    ///
    /// - Parameters:
    ///   - config: the deployment identifiers/endpoints.
    ///   - identity: the device identity to bake (its PKCS#12 + password).
    ///   - caCertificateDER: the CA root, delivered as a trusted `com.apple.security.root` payload so
    ///     installing the profile establishes the TLS trust the MDM check-in needs — instead of a
    ///     separate `security add-trusted-cert`, whose System-keychain trust-settings change raises a
    ///     SecurityAgent prompt that cannot be suppressed on macOS 27 (the blocker that stalled four
    ///     consecutive live runs; docs/MACOS_VM_MDM.md §8.0). A profile-delivered root installed at
    ///     System scope with admin approval is trusted, and the profile install is the single approval
    ///     UAMDM already requires — so this removes a whole prompt rather than adding one.
    ///   - uuids: payload UUIDs (defaulted random; pass fixed ones in tests).
    ///   - profileIdentifier: the top-level `PayloadIdentifier`.
    public static func make(
        config: MDMConfiguration,
        identity: MDMDeviceIdentity,
        caCertificateDER: Data,
        uuids: UUIDs = UUIDs(),
        profileIdentifier: String = "xyz.blakeslee.nucleic.mdm.enroll"
    ) throws -> Data {
        // Delivered first so the root is present before the MDM payload's check-in TLS runs.
        let rootPayload: [String: Any] = [
            "PayloadType": "com.apple.security.root",
            "PayloadVersion": 1,
            "PayloadIdentifier": "\(profileIdentifier).root",
            "PayloadUUID": uuids.root.uuidString,
            "PayloadDisplayName": "Nucleic MDM Root CA",
            "PayloadCertificateFileName": "nucleic-mdm-ca.cer",
            "PayloadContent": caCertificateDER,
        ]

        let identityPayload: [String: Any] = [
            "PayloadType": "com.apple.security.pkcs12",
            "PayloadVersion": 1,
            "PayloadIdentifier": "\(profileIdentifier).identity",
            "PayloadUUID": uuids.identity.uuidString,
            "PayloadDisplayName": "Nucleic Device Identity",
            "PayloadCertificateFileName": "nucleic-device.p12",
            "Password": identity.password,
            "PayloadContent": try identity.pkcs12(),
        ]

        let mdmPayload: [String: Any] = [
            "PayloadType": "com.apple.mdm",
            "PayloadVersion": 1,
            "PayloadIdentifier": "\(profileIdentifier).mdm",
            "PayloadUUID": uuids.mdm.uuidString,
            "PayloadDisplayName": "Nucleic Device Management",
            "AccessRights": 8191,  // all rights
            "CheckInURL": config.checkInURL,
            "CheckOutWhenRemoved": true,
            "IdentityCertificateUUID": uuids.identity.uuidString,
            "ServerURL": config.serverURL,
            "Topic": config.topic,
            "SignMessage": true,
            // `ServerCapabilities` is REQUIRED on macOS 27: a live run failed profile installation with
            // "MDM payload is missing 'ServerCapabilities' key that indicates it supports a user
            // channel." (Finding 7 had it backwards — the key is mandatory, not harmful.) Advertising
            // the per-user channel makes macOS send user-scoped check-ins (UserAuthenticate, a per-user
            // TokenUpdate); the server acks the ones it doesn't model with an empty 200 (Workstream
            // 2.2), which is enough for the device channel to still drain our InstallProfile queue.
            "ServerCapabilities": ["com.apple.mdm.per-user-connections"],
            // ── Certificate pinning: why this profile does not need the CA in the system trust store.
            //
            // Both keys take the PayloadUUID of a certificate payload carried in THIS profile, and
            // name the anchor `mdmclient` evaluates the check-in and command URLs against — Apple's
            // designed mechanism for an MDM server fronted by a private CA (macOS 10.13+). Pointing
            // them at the root payload below means the guest validates our server against the CA it
            // just installed *from this profile*, instead of against the system trust store.
            //
            // That matters because there is no way left to put a self-issued root INTO the macOS 27
            // trust store non-interactively. All five routes were tried and closed by live evidence:
            // a root payload in a manually installed profile has not been auto-trusted since Ventura;
            // `security authorizationdb write … allow` silently no-ops; `add-trusted-cert` blocks
            // forever on a SecurityAgent sheet no automation can focus; and a hand-written
            // /Library/Security/Trust Settings/Admin.plist is ignored, before and after a reboot
            // (docs/MACOS_VM_MDM_DEBUG_LOG.md, runs 10–18). Pinning sidesteps the question entirely
            // rather than answering it — no system trust, no public CA, no domain to own.
            //
            // The ROOT is pinned rather than the TLS leaf on purpose: the leaf is reissued on every
            // `server.start()` (fresh random serial), and a Mode B push to a live clone reissues it
            // again, so pinning the leaf would break the moment the server restarted.
            "CheckInURLPinningCertificateUUIDs": [uuids.root.uuidString],
            "ServerURLPinningCertificateUUIDs": [uuids.root.uuidString],
            // Left at its default (false) deliberately: a private CA publishes no CRL/OCSP responder,
            // so requiring a positive revocation answer would fail every connection.
            "PinningRevocationCheckRequired": false,
        ]

        let profile: [String: Any] = [
            "PayloadType": "Configuration",
            "PayloadVersion": 1,
            "PayloadIdentifier": profileIdentifier,
            "PayloadUUID": uuids.profile.uuidString,
            "PayloadDisplayName": "Nucleic MDM Enrollment",
            "PayloadDescription":
                "Enrolls this macOS guest in the local Nucleic MDM so it can receive privacy "
                + "(PPPC/FDA) and notification-policy profiles with SIP enabled.",
            "PayloadOrganization": config.organization,
            "PayloadScope": "System",
            "PayloadRemovalDisallowed": false,
            "PayloadContent": [rootPayload, identityPayload, mdmPayload],
        ]

        return try MDMPlist.serialize(profile)
    }
}
