import Foundation

/// Replace a keychain-backed item so a crash/failure mid-write can NEVER leave
/// zero installed copies — which, for the device identity, would lock the device
/// out of its own mTLS cert until a manual re-enroll. This is the inverse of a
/// delete-then-add sequence, whose window between the two calls has zero items:
///
///   1. `addNew` FIRST. Once it succeeds the replacement is installed, so at
///      least one valid item exists from here on. If `addNew` throws, the prior
///      item is left untouched (the device keeps a working identity) and the
///      error propagates.
///   2. `deleteOld` SECOND, removing the prior copy. A failure here is NON-fatal:
///      the new item is already installed, so the write is a success. The stale
///      leftover is reported via `reportStaleDeleteFailure` (never a lockout) and
///      swept on the next write — throwing here would strand a working new cert.
///
/// Kept pure (closures, no `Security` types) so the ordering invariant is unit-
/// testable with fakes; the keychain plumbing lives at the call sites.
func replaceKeychainItemAtomically(
    addNew: () throws -> Void,
    deleteOld: () throws -> Void,
    reportStaleDeleteFailure: (Error) -> Void = { _ in }
) throws {
    try addNew()
    do {
        try deleteOld()
    } catch {
        reportStaleDeleteFailure(error)
    }
}
