#if canImport(Network)
import Foundation
import Security
import X509
import XCTest

@testable import NucleicMDM

/// End-to-end over the real TLS server: stand it up on loopback, then drive the exact request
/// sequence `mdmclient` performs — TokenUpdate check-in, then poll `/server` draining an
/// `InstallProfile` — with a `URLSession` that pins the local CA as its trust anchor. This exercises
/// TLS identity import, HTTP parsing, routing, and the command-queue drain together.
final class NucleicMDMServerTests: XCTestCase {
    func testEnrollmentDrainsQueuedProfileWithNoAPNs() async throws {
        let ca = try MDMCertificateAuthority.generate()
        let identity = try MDMDeviceIdentity.issue(from: ca)
        let controller = MDMServerController(
            ca: ca, config: .generate(port: 0), deviceIdentity: identity, queue: MDMCommandQueue())

        let fda = Data("<plist>FDA</plist>".utf8)
        await controller.queueInstallProfiles([fda])

        let server = controller.makeServer(log: { NSLog("[mdm] %@", $0) })
        let port = try await server.start(host: "127.0.0.1")
        defer { Task { await server.stop() } }

        let client = MDMTestClient(ca: ca, host: "127.0.0.1", port: Int(port),
                                    dnsName: controller.config.serverDNSName)

        // 1. TokenUpdate check-in → empty 200.
        let checkIn = try await client.put(
            path: "/checkin",
            plist: ["MessageType": "TokenUpdate", "UDID": "TEST-UDID",
                    "PushMagic": "M", "Token": Data([0x01, 0x02])])
        XCTAssertEqual(checkIn.status, 200)
        XCTAssertTrue(checkIn.body.isEmpty)

        // 2. Idle poll → the queued InstallProfile command.
        let poll1 = try await client.put(
            path: "/server", plist: ["Status": "Idle", "UDID": "TEST-UDID"])
        XCTAssertEqual(poll1.status, 200)
        let cmd = try MDMPlist.parse(poll1.body)
        let command = try XCTUnwrap(cmd["Command"] as? [String: Any])
        XCTAssertEqual(command["RequestType"] as? String, "InstallProfile")
        XCTAssertEqual(command["Payload"] as? Data, fda)
        let commandUUID = try XCTUnwrap(cmd["CommandUUID"] as? String)

        // 3. Acknowledge → empty 200 (queue drained).
        let poll2 = try await client.put(
            path: "/server",
            plist: ["Status": "Acknowledged", "UDID": "TEST-UDID", "CommandUUID": commandUUID])
        XCTAssertEqual(poll2.status, 200)
        XCTAssertTrue(poll2.body.isEmpty)

        let drained = await controller.queue.isDrained
        XCTAssertTrue(drained)
    }

    func testUnknownPathIs404() async throws {
        let ca = try MDMCertificateAuthority.generate()
        let identity = try MDMDeviceIdentity.issue(from: ca)
        let controller = MDMServerController(
            ca: ca, config: .generate(port: 0), deviceIdentity: identity)
        let server = controller.makeServer()
        let port = try await server.start(host: "127.0.0.1")
        defer { Task { await server.stop() } }
        let client = MDMTestClient(
            ca: ca, host: "127.0.0.1", port: Int(port), dnsName: controller.config.serverDNSName)
        let resp = try await client.put(path: "/nope", plist: ["x": "y"])
        XCTAssertEqual(resp.status, 404)
    }

    // MARK: - Failure-body preview (docs/MACOS_VM_PROVISIONING_REPAIR_PLAN.md, Workstream 0)
    //
    // A 400 on the check-in channel is fatal to enrollment, so the body that caused it has to reach
    // the transcript: an unsupported MessageType and a malformed plist need opposite fixes, and until
    // now both surfaced as a bare status code.

    func testBodyPreviewFlattensXMLPlistOntoOneLine() {
        let body = Data("""
            <?xml version="1.0"?>
            <plist version="1.0"><dict>
              <key>MessageType</key><string>SetBootstrapToken</string>
            </dict></plist>
            """.utf8)
        let preview = NucleicMDMServer.bodyPreview(body)
        XCTAssertTrue(preview.contains("SetBootstrapToken"), preview)
        XCTAssertFalse(preview.contains("\n"), "the preview must stay on one transcript line")
    }

    func testBodyPreviewIsBoundedAndReportsTheFullSize() {
        let body = Data(String(repeating: "a", count: 5_000).utf8)
        let preview = NucleicMDMServer.bodyPreview(body, limit: 100)
        XCTAssertLessThan(preview.count, 200)
        XCTAssertTrue(preview.contains("(5000 bytes)"), preview)
    }

    func testBodyPreviewFallsBackToHexForNonTextBodies() {
        let preview = NucleicMDMServer.bodyPreview(Data([0xFF, 0xFE, 0x00, 0x01]))
        XCTAssertEqual(preview, "fffe0001")
    }

    func testBodyPreviewNamesAnEmptyBody() {
        XCTAssertEqual(NucleicMDMServer.bodyPreview(Data()), "<empty>")
    }
}

/// A `URLSession` client that trusts only the given local CA and rewrites the Host header to the
/// pinned DNS name (so the server cert's SAN matches while we connect to 127.0.0.1).
private final class MDMTestClient: NSObject, URLSessionDelegate, @unchecked Sendable {
    let ca: MDMCertificateAuthority
    let host: String
    let port: Int
    let dnsName: String
    private lazy var session = URLSession(
        configuration: .ephemeral, delegate: self, delegateQueue: nil)

    init(ca: MDMCertificateAuthority, host: String, port: Int, dnsName: String) {
        self.ca = ca
        self.host = host
        self.port = port
        self.dnsName = dnsName
    }

    struct Response { let status: Int; let body: Data }

    func put(path: String, plist: [String: Any]) async throws -> Response {
        // Connect to the pinned DNS name via a per-request resolver override is complex; instead we
        // connect to the IP and accept the cert by chaining to our CA (SAN hostname check is bypassed
        // by our trust evaluation, which pins the anchor and validates the chain, not the hostname).
        var req = URLRequest(url: URL(string: "https://\(host):\(port)\(path)")!)
        req.httpMethod = "PUT"
        req.httpBody = try MDMPlist.serialize(plist)
        req.setValue("application/x-apple-aspen-mdm", forHTTPHeaderField: "Content-Type")
        let (data, response) = try await session.data(for: req)
        let http = response as! HTTPURLResponse
        return Response(status: http.statusCode, body: data)
    }

    func urlSession(
        _ session: URLSession, didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
    ) {
        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
              let trust = challenge.protectionSpace.serverTrust
        else {
            completionHandler(.performDefaultHandling, nil)
            return
        }
        // Anchor evaluation to our CA only; the leaf's SAN is mdm.nucleic.internal, so also relax the
        // hostname policy by evaluating with an SSL policy for that name (we connected by IP).
        let caCertDER = (try? ca.rootCertificateDER()).flatMap { Data($0) }
        if let caCertDER, let caCert = SecCertificateCreateWithData(nil, caCertDER as CFData) {
            SecTrustSetAnchorCertificates(trust, [caCert] as CFArray)
            SecTrustSetAnchorCertificatesOnly(trust, true)
            SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, dnsName as CFString))
            var error: CFError?
            if SecTrustEvaluateWithError(trust, &error) {
                completionHandler(.useCredential, URLCredential(trust: trust))
                return
            }
            NSLog("[mdm-test] TLS trust eval failed: %@", String(describing: error))
        }
        completionHandler(.cancelAuthenticationChallenge, nil)
    }
}
#endif
