import Foundation

/// C-iOS-1 · The pure, synchronous decision at the heart of mutual TLS.
///
/// It maps a URLSession auth challenge (+ the optionally-installed device
/// identity) to a disposition and credential. Deliberately free of any
/// URLSession / socket state so the full truth table is unit-testable without a
/// live connection — the transports (WS task delegate, HTTP session delegate)
/// are thin adapters that call `resolve` and forward its result to the
/// completion handler.
///
/// Truth table (plan §C-iOS-1):
///  a. `ClientCertificate` + identity present → `.useCredential` with the
///     identity's `URLCredential`.
///  b. `ClientCertificate` + no identity      → `.cancelAuthenticationChallenge`
///     (a clean, classifiable failure — never a silent no-cert continue).
///  c. `ServerTrust`                          → `.performDefaultHandling`
///     (the LE wildcard is validated by the system trust store).
///  d. anything else                          → `.performDefaultHandling`.
public struct MutualTLSChallengeResponder: Sendable {
    /// The responder's decision. Not `Sendable` (it may carry a `URLCredential`,
    /// which isn't) — it is produced and consumed synchronously on the delegate
    /// queue, never sent across isolation domains.
    public struct Resolution {
        public let disposition: URLSession.AuthChallengeDisposition
        public let credential: URLCredential?

        public init(
            disposition: URLSession.AuthChallengeDisposition,
            credential: URLCredential? = nil
        ) {
            self.disposition = disposition
            self.credential = credential
        }
    }

    public init() {}

    /// Resolve a full `URLAuthenticationChallenge` by dispatching on its
    /// authentication method.
    public func resolve(
        _ challenge: URLAuthenticationChallenge, identity: ClientIdentity?
    ) -> Resolution {
        resolve(
            authenticationMethod: challenge.protectionSpace.authenticationMethod,
            identity: identity
        )
    }

    /// Method-keyed core (challenge-free) — the directly-tested pure function.
    public func resolve(
        authenticationMethod: String, identity: ClientIdentity?
    ) -> Resolution {
        switch authenticationMethod {
        case NSURLAuthenticationMethodClientCertificate:
            guard let identity else {
                // (b) No installed identity: cancel so the failure surfaces as a
                // classifiable client-cert error rather than a bare TLS reset.
                return Resolution(disposition: .cancelAuthenticationChallenge)
            }
            // (a) Present the device certificate for this session.
            return Resolution(
                disposition: .useCredential, credential: identity.urlCredential()
            )
        case NSURLAuthenticationMethodServerTrust:
            // (c) System-trusted LE wildcard — default evaluation.
            return Resolution(disposition: .performDefaultHandling)
        default:
            // (d) Basic/Digest/NTLM/etc. — not used by this app; default.
            return Resolution(disposition: .performDefaultHandling)
        }
    }
}
