import Foundation
import Testing
@testable import CodexBarCore

@Suite(.serialized)
struct ClaudeOAuthCredentialsStoreSecurityCLIFallbackPolicyTests {
    private func makeCredentialsData(accessToken: String, expiresAt: Date) -> Data {
        let millis = Int(expiresAt.timeIntervalSince1970 * 1000)
        let json = """
        {
          "claudeAiOauth": {
            "accessToken": "\(accessToken)",
            "expiresAt": \(millis),
            "scopes": ["user:profile"]
          }
        }
        """
        return Data(json.utf8)
    }

    @Test
    func `experimental reader blocks background fallback per stored policy`() {
        let fallbackData = self.makeCredentialsData(
            accessToken: "fallback-should-be-blocked",
            expiresAt: Date(timeIntervalSinceNow: 3600))

        let hasCredentials = KeychainAccessGate.withTaskOverrideForTesting(false) {
            ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
                .securityCLIExperimental,
                operation: {
                    ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
                        .onlyOnUserAction,
                        operation: {
                            ProviderInteractionContext.$current.withValue(.background) {
                                ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                    data: fallbackData,
                                    fingerprint: nil)
                                {
                                    ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(
                                        .nonZeroExit)
                                    {
                                        ClaudeOAuthCredentialsStore.hasClaudeKeychainCredentialsWithoutPrompt()
                                    }
                                }
                            }
                        })
                })
        }

        #expect(hasCredentials == false)
    }

    @Test
    func `experimental reader sync from claude keychain without prompt background fallback blocked by stored policy`() {
        let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
        KeychainCacheStore.withServiceOverrideForTesting(service) {
            KeychainAccessGate.withTaskOverrideForTesting(false) {
                KeychainCacheStore.setTestStoreForTesting(true)
                defer { KeychainCacheStore.setTestStoreForTesting(false) }

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

                    let fallbackData = self.makeCredentialsData(
                        accessToken: "sync-fallback-should-be-blocked",
                        expiresAt: Date(timeIntervalSinceNow: 3600))
                    final class Counter: @unchecked Sendable {
                        var value = 0
                    }
                    let preflightCalls = Counter()
                    let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
                        preflightCalls.value += 1
                        return .allowed
                    }

                    let synced = KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
                        preflightOverride,
                        operation: {
                            ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
                                .securityCLIExperimental,
                                operation: {
                                    ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
                                        .onlyOnUserAction,
                                        operation: {
                                            ProviderInteractionContext.$current.withValue(.background) {
                                                ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
                                                    data: fallbackData,
                                                    fingerprint: nil)
                                                {
                                                    ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(
                                                        .timedOut)
                                                    {
                                                        ClaudeOAuthCredentialsStore
                                                            .syncFromClaudeKeychainWithoutPrompt(now: Date())
                                                    }
                                                }
                                            }
                                        })
                                })
                        })

                    #expect(synced == false)
                    #expect(preflightCalls.value == 0)
                }
            }
        }
    }
}
