import Foundation
import Testing
@testable import CodexBarCore

@Suite(.serialized)
struct ClaudeOAuthCredentialsStorePromptPolicyTests {
    @Test
    func `keychain prompt notify preserves its void function signature`() {
        let notify: (KeychainPromptContext) -> Void = KeychainPromptHandler.notify
        _ = notify
    }

    @Test
    func `safety does not inherit the application prompt preference`() throws {
        guard ProcessInfo.processInfo.environment[KeychainTestSafety.allowAccessEnvironmentKey] != "1" else {
            return
        }

        #expect(ClaudeOAuthKeychainPromptPreference.currentTaskOverrideForTesting == nil)

        let domain = "ClaudeOAuthPromptPolicyIsolationTests.\(UUID().uuidString)"
        let key = "claudeOAuthKeychainPromptMode"
        let defaults = try #require(UserDefaults(suiteName: domain))
        defer {
            defaults.removePersistentDomain(forName: domain)
            defaults.synchronize()
        }
        defaults.set(ClaudeOAuthKeychainPromptMode.never.rawValue, forKey: key)
        defaults.synchronize()

        ClaudeOAuthKeychainPromptPreference.withImplicitApplicationUserDefaultsOverrideForTesting(defaults) {
            // Isolation must ignore a conflicting value in the implicit application defaults domain.
            #expect(ClaudeOAuthKeychainPromptPreference.storedMode() == .onlyOnUserAction)
            #expect(ClaudeOAuthKeychainPromptPreference.storedMode(userDefaults: defaults) == .never)

            let explicit = ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.always) {
                ClaudeOAuthKeychainPromptPreference.storedMode()
            }
            #expect(explicit == .always)
        }
    }

    private func makeCredentialsData(accessToken: String, expiresAt: Date, refreshToken: String? = nil) -> Data {
        let millis = Int(expiresAt.timeIntervalSince1970 * 1000)
        let refreshField: String = {
            guard let refreshToken else { return "" }
            return ",\n            \"refreshToken\": \"\(refreshToken)\""
        }()
        let json = """
        {
          "claudeAiOauth": {
            "accessToken": "\(accessToken)",
            "expiresAt": \(millis),
            "scopes": ["user:profile"]\(refreshField)
          }
        }
        """
        return Data(json.utf8)
    }

    @Test
    func `does not read claude keychain in background when prompt mode only on user action`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                ClaudeOAuthCredentialsStore.invalidateCache()
                ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                defer {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    ClaudeOAuthCredentialsStore._resetClaudeKeychainChangeTrackingForTesting()
                }

                let tempDir = FileManager.default.temporaryDirectory
                    .appendingPathComponent(UUID().uuidString, isDirectory: true)
                try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                let fileURL = tempDir.appendingPathComponent("credentials.json")

                try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                    ClaudeOAuthCredentialsStore._resetClaudeKeychainChangeTrackingForTesting()

                    let fingerprint = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprint(
                        modifiedAt: 1,
                        createdAt: 1,
                        persistentRefHash: "ref1")
                    let keychainData = self.makeCredentialsData(
                        accessToken: "keychain-token",
                        expiresAt: Date(timeIntervalSinceNow: 3600))

                    do {
                        _ = try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.onlyOnUserAction) {
                            try ProviderInteractionContext.$current.withValue(.background) {
                                try ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                    data: keychainData,
                                    fingerprint: fingerprint)
                                {
                                    try ClaudeOAuthCredentialsStore.load(environment: [:], allowKeychainPrompt: false)
                                }
                            }
                        }
                        Issue.record("Expected ClaudeOAuthCredentialsError.notFound")
                    } catch let error as ClaudeOAuthCredentialsError {
                        guard case .notFound = error else {
                            Issue.record("Expected .notFound, got \(error)")
                            return
                        }
                    }
                }
            }
        }
    }

    @Test
    func `can read claude keychain on user action when prompt mode only on user action`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                ClaudeOAuthCredentialsStore.invalidateCache()
                ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                defer {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    ClaudeOAuthCredentialsStore._resetClaudeKeychainChangeTrackingForTesting()
                }

                let tempDir = FileManager.default.temporaryDirectory
                    .appendingPathComponent(UUID().uuidString, isDirectory: true)
                try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                let fileURL = tempDir.appendingPathComponent("credentials.json")

                try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                    ClaudeOAuthCredentialsStore._resetClaudeKeychainChangeTrackingForTesting()

                    let fingerprint = ClaudeOAuthCredentialsStore.ClaudeKeychainFingerprint(
                        modifiedAt: 1,
                        createdAt: 1,
                        persistentRefHash: "ref1")
                    let keychainData = self.makeCredentialsData(
                        accessToken: "keychain-token",
                        expiresAt: Date(timeIntervalSinceNow: 3600))

                    let creds = try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.onlyOnUserAction) {
                        try ProviderInteractionContext.$current.withValue(.userInitiated) {
                            try ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                data: keychainData,
                                fingerprint: fingerprint)
                            {
                                try ClaudeOAuthCredentialsStore.load(environment: [:], allowKeychainPrompt: false)
                            }
                        }
                    }

                    #expect(creds.accessToken == "keychain-token")
                }
            }
        }
    }

    @Test
    func `user initiated claude keychain reads respect pre alert acknowledgement cooldown`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let keychainData = self.makeCredentialsData(
                            accessToken: "keychain-token",
                            expiresAt: Date(timeIntervalSinceNow: 3600))

                        var preAlertHits = 0
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .allowed
                        }
                        let promptHandler: (KeychainPromptContext) -> Void = { _ in
                            preAlertHits += 1
                        }
                        let credentials = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                            preflightOverride,
                            operation: {
                                try KeychainPromptHandler.withHandlerForTesting(promptHandler, operation: {
                                    try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
                                        .onlyOnUserAction)
                                    {
                                        try ProviderInteractionContext.$current.withValue(.userInitiated) {
                                            try ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                                data: keychainData,
                                                fingerprint: nil)
                                            {
                                                let first = try ClaudeOAuthCredentialsStore.load(
                                                    environment: [:],
                                                    allowKeychainPrompt: true)
                                                ClaudeOAuthCredentialsStore.invalidateCache()
                                                let second = try ClaudeOAuthCredentialsStore.load(
                                                    environment: [:],
                                                    allowKeychainPrompt: true)
                                                return (first, second)
                                            }
                                        }
                                    }
                                })
                            })

                        #expect(credentials.0.accessToken == "keychain-token")
                        #expect(credentials.1.accessToken == "keychain-token")
                        #expect(preAlertHits == 1)
                    }
                }
            }
        }
    }

    @Test
    func `shows pre alert when claude keychain likely requires interaction`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let keychainData = self.makeCredentialsData(
                            accessToken: "keychain-token",
                            expiresAt: Date(timeIntervalSinceNow: 3600))

                        var preAlertHits = 0
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .interactionRequired
                        }
                        let promptHandler: (KeychainPromptContext) -> Void = { _ in
                            preAlertHits += 1
                        }
                        let creds = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                            preflightOverride,
                            operation: {
                                try KeychainPromptHandler.withHandlerForTesting(promptHandler, operation: {
                                    try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
                                        .onlyOnUserAction)
                                    {
                                        try ProviderInteractionContext.$current.withValue(.userInitiated) {
                                            try ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                                data: keychainData,
                                                fingerprint: nil)
                                            {
                                                try ClaudeOAuthCredentialsStore.load(
                                                    environment: [:],
                                                    allowKeychainPrompt: true)
                                            }
                                        }
                                    }
                                })
                            })

                        #expect(creds.accessToken == "keychain-token")
                        #expect(preAlertHits == 1)
                    }
                }
            }
        }
    }

    @Test
    func `shows pre alert when claude keychain preflight fails`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let keychainData = self.makeCredentialsData(
                            accessToken: "keychain-token",
                            expiresAt: Date(timeIntervalSinceNow: 3600))

                        var preAlertHits = 0
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .failure(-1)
                        }
                        let promptHandler: (KeychainPromptContext) -> Void = { _ in
                            preAlertHits += 1
                        }
                        let creds = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                            preflightOverride,
                            operation: {
                                try KeychainPromptHandler.withHandlerForTesting(promptHandler, operation: {
                                    try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
                                        .onlyOnUserAction)
                                    {
                                        try ProviderInteractionContext.$current.withValue(.userInitiated) {
                                            try ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                                data: keychainData,
                                                fingerprint: nil)
                                            {
                                                try ClaudeOAuthCredentialsStore.load(
                                                    environment: [:],
                                                    allowKeychainPrompt: true)
                                            }
                                        }
                                    }
                                })
                            })

                        #expect(creds.accessToken == "keychain-token")
                        #expect(preAlertHits == 1)
                    }
                }
            }
        }
    }

    @Test
    func `experimental reader skips pre alert when security CLI read succeeds`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let securityData = self.makeCredentialsData(
                            accessToken: "security-token",
                            expiresAt: Date(timeIntervalSinceNow: 3600))

                        var preAlertHits = 0
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .interactionRequired
                        }
                        let promptHandler: (KeychainPromptContext) -> Void = { _ in
                            preAlertHits += 1
                        }
                        let creds = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                            preflightOverride,
                            operation: {
                                try KeychainPromptHandler.withHandlerForTesting(promptHandler, operation: {
                                    try ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
                                        .securityCLIExperimental)
                                    {
                                        try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.always) {
                                            try ProviderInteractionContext.$current.withValue(.userInitiated) {
                                                try ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(
                                                    .data(securityData))
                                                {
                                                    try ClaudeOAuthCredentialsStore.load(
                                                        environment: [:],
                                                        allowKeychainPrompt: true)
                                                }
                                            }
                                        }
                                    }
                                })
                            })

                        #expect(creds.accessToken == "security-token")
                        #expect(preAlertHits == 0)
                    }
                }
            }
        }
    }

    @Test
    func `experimental reader shows pre alert when security CLI fails and fallback needs interaction`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let fallbackData = self.makeCredentialsData(
                            accessToken: "fallback-token",
                            expiresAt: Date(timeIntervalSinceNow: 3600))

                        var preAlertHits = 0
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .interactionRequired
                        }
                        let promptHandler: (KeychainPromptContext) -> Void = { _ in
                            preAlertHits += 1
                        }
                        let creds = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                            preflightOverride,
                            operation: {
                                try KeychainPromptHandler.withHandlerForTesting(promptHandler, operation: {
                                    try ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
                                        .securityCLIExperimental)
                                    {
                                        try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.always) {
                                            try ProviderInteractionContext.$current.withValue(.userInitiated) {
                                                try ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                                    data: fallbackData,
                                                    fingerprint: nil)
                                                {
                                                    try ClaudeOAuthCredentialsStore
                                                        .withSecurityCLIReadOverrideForTesting(.timedOut) {
                                                            try ClaudeOAuthCredentialsStore.load(
                                                                environment: [:],
                                                                allowKeychainPrompt: true)
                                                        }
                                                }
                                            }
                                        }
                                    }
                                })
                            })

                        #expect(creds.accessToken == "fallback-token")
                        #expect(preAlertHits == 1)
                    }
                }
            }
        }
    }

    @Test
    func `experimental reader does not fallback in background when stored mode only on user action`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let fallbackData = self.makeCredentialsData(
                            accessToken: "fallback-token",
                            expiresAt: Date(timeIntervalSinceNow: 3600))

                        var preAlertHits = 0
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .interactionRequired
                        }
                        let promptHandler: (KeychainPromptContext) -> Void = { _ in
                            preAlertHits += 1
                        }

                        do {
                            _ = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                                preflightOverride,
                                operation: {
                                    try KeychainPromptHandler.withHandlerForTesting(promptHandler, operation: {
                                        try ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
                                            .securityCLIExperimental)
                                        {
                                            try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
                                                .onlyOnUserAction)
                                            {
                                                try ProviderInteractionContext.$current.withValue(.background) {
                                                    try ClaudeOAuthCredentialsStore
                                                        .withClaudeKeychainOverridesForTesting(
                                                            data: fallbackData,
                                                            fingerprint: nil)
                                                        {
                                                            try ClaudeOAuthCredentialsStore
                                                                .withSecurityCLIReadOverrideForTesting(.timedOut) {
                                                                    try ClaudeOAuthCredentialsStore.load(
                                                                        environment: [:],
                                                                        allowKeychainPrompt: true,
                                                                        respectKeychainPromptCooldown: true)
                                                                }
                                                        }
                                                }
                                            }
                                        }
                                    })
                                })
                            Issue.record("Expected ClaudeOAuthCredentialsError.notFound")
                        } catch let error as ClaudeOAuthCredentialsError {
                            guard case .notFound = error else {
                                Issue.record("Expected .notFound, got \(error)")
                                return
                            }
                        }

                        #expect(preAlertHits == 0)
                    }
                }
            }
        }
    }

    @Test
    func `experimental reader does not fallback when stored mode never`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let fallbackData = self.makeCredentialsData(
                            accessToken: "fallback-token",
                            expiresAt: Date(timeIntervalSinceNow: 3600))

                        var preAlertHits = 0
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .interactionRequired
                        }
                        let promptHandler: (KeychainPromptContext) -> Void = { _ in
                            preAlertHits += 1
                        }

                        do {
                            _ = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                                preflightOverride,
                                operation: {
                                    try KeychainPromptHandler.withHandlerForTesting(promptHandler, operation: {
                                        try ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
                                            .securityCLIExperimental)
                                        {
                                            try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.never) {
                                                try ProviderInteractionContext.$current.withValue(.userInitiated) {
                                                    try ClaudeOAuthCredentialsStore
                                                        .withClaudeKeychainOverridesForTesting(
                                                            data: fallbackData,
                                                            fingerprint: nil)
                                                        {
                                                            try ClaudeOAuthCredentialsStore
                                                                .withSecurityCLIReadOverrideForTesting(.timedOut) {
                                                                    try ClaudeOAuthCredentialsStore.load(
                                                                        environment: [:],
                                                                        allowKeychainPrompt: true)
                                                                }
                                                        }
                                                }
                                            }
                                        }
                                    })
                                })
                            Issue.record("Expected ClaudeOAuthCredentialsError.notFound")
                        } catch let error as ClaudeOAuthCredentialsError {
                            guard case .notFound = error else {
                                Issue.record("Expected .notFound, got \(error)")
                                return
                            }
                        }

                        #expect(preAlertHits == 0)
                    }
                }
            }
        }
    }

    @Test
    func `experimental reader non interactive fallback blocked in background when stored mode only on user action`()
        throws
    {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let fallbackData = self.makeCredentialsData(
                            accessToken: "fallback-token-only-on-user-action",
                            expiresAt: Date(timeIntervalSinceNow: 3600))
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .allowed
                        }

                        do {
                            _ = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                                preflightOverride,
                                operation: {
                                    try ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
                                        .securityCLIExperimental)
                                    {
                                        try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
                                            .onlyOnUserAction)
                                        {
                                            try ProviderInteractionContext.$current.withValue(.background) {
                                                try ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                                    data: fallbackData,
                                                    fingerprint: nil)
                                                {
                                                    try ClaudeOAuthCredentialsStore
                                                        .withSecurityCLIReadOverrideForTesting(.timedOut) {
                                                            try ClaudeOAuthCredentialsStore.load(
                                                                environment: [:],
                                                                allowKeychainPrompt: false,
                                                                respectKeychainPromptCooldown: true)
                                                        }
                                                }
                                            }
                                        }
                                    }
                                })
                            Issue.record("Expected ClaudeOAuthCredentialsError.notFound")
                        } catch let error as ClaudeOAuthCredentialsError {
                            guard case .notFound = error else {
                                Issue.record("Expected .notFound, got \(error)")
                                return
                            }
                        }
                    }
                }
            }
        }
    }

    @Test
    func `experimental reader allows fallback in background when stored mode always`() throws {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        try KeychainCacheStore.withServiceOverrideForTesting(service) {
            try KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

                try ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
                    ClaudeOAuthCredentialsStore.invalidateCache()
                    ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    defer {
                        ClaudeOAuthCredentialsStore.invalidateCache()
                        ClaudeOAuthCredentialsStore._resetCredentialsFileTrackingForTesting()
                    }

                    let tempDir = FileManager.default.temporaryDirectory
                        .appendingPathComponent(UUID().uuidString, isDirectory: true)
                    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
                    let fileURL = tempDir.appendingPathComponent("credentials.json")
                    try ClaudeOAuthCredentialsStore.withCredentialsURLOverrideForTesting(fileURL) {
                        let fallbackData = self.makeCredentialsData(
                            accessToken: "fallback-token",
                            expiresAt: Date(timeIntervalSinceNow: 3600))

                        var preAlertHits = 0
                        let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                            .interactionRequired
                        }
                        let promptHandler: (KeychainPromptContext) -> Void = { _ in
                            preAlertHits += 1
                        }

                        let creds = try KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                            preflightOverride,
                            operation: {
                                try KeychainPromptHandler.withHandlerForTesting(promptHandler, operation: {
                                    try ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
                                        .securityCLIExperimental)
                                    {
                                        try ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(.always) {
                                            try ProviderInteractionContext.$current.withValue(.background) {
                                                try ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                                    data: fallbackData,
                                                    fingerprint: nil)
                                                {
                                                    try ClaudeOAuthCredentialsStore
                                                        .withSecurityCLIReadOverrideForTesting(.timedOut) {
                                                            try ClaudeOAuthCredentialsStore.load(
                                                                environment: [:],
                                                                allowKeychainPrompt: true,
                                                                respectKeychainPromptCooldown: false)
                                                        }
                                                }
                                            }
                                        }
                                    }
                                })
                            })

                        #expect(creds.accessToken == "fallback-token")
                        #expect(preAlertHits == 1)
                    }
                }
            }
        }
    }
}
