import CodexBarCore
import Foundation
import Testing
@testable import CodexBar

@MainActor
struct SettingsStoreCoverageTests {
    @Test
    func `provider ordering and caching`() throws {
        let suite = "SettingsStoreCoverageTests-ordering"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)
        let config = CodexBarConfig(providers: [
            ProviderConfig(id: .zai),
            ProviderConfig(id: .codex),
            ProviderConfig(id: .claude),
        ])
        try configStore.save(config)
        let settings = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        let ordered = settings.orderedProviders()
        let cached = settings.orderedProviders()

        #expect(ordered == cached)
        #expect(ordered.first == .zai)
        #expect(ordered.contains(.minimax))

        let configRevisionBeforeOrder = settings.configRevision
        let backgroundRevisionBeforeOrder = settings.backgroundWorkSettingsRevision
        settings.moveProvider(fromOffsets: IndexSet(integer: 0), toOffset: 2)
        #expect(settings.orderedProviders() != ordered)
        #expect(settings.configRevision == configRevisionBeforeOrder + 1)
        #expect(settings.backgroundWorkSettingsRevision == backgroundRevisionBeforeOrder)

        let metadata = ProviderRegistry.shared.metadata
        let backgroundRevisionBeforeEnablement = settings.backgroundWorkSettingsRevision
        try settings.setProviderEnabled(provider: .codex, metadata: #require(metadata[.codex]), enabled: true)
        #expect(settings.backgroundWorkSettingsRevision == backgroundRevisionBeforeEnablement + 1)
        try settings.setProviderEnabled(provider: .claude, metadata: #require(metadata[.claude]), enabled: false)
        let enabled = settings.enabledProvidersOrdered(metadataByProvider: metadata)
        #expect(enabled.contains(.codex))
    }

    @Test
    func `disabling selected provider clears menu selection`() throws {
        let settings = Self.makeSettingsStore()
        let metadata = ProviderRegistry.shared.metadata

        try settings.setProviderEnabled(provider: .codex, metadata: #require(metadata[.codex]), enabled: true)
        try settings.setProviderEnabled(provider: .claude, metadata: #require(metadata[.claude]), enabled: true)
        settings.selectedMenuProvider = .claude

        try settings.setProviderEnabled(provider: .claude, metadata: #require(metadata[.claude]), enabled: false)

        #expect(settings.selectedMenuProvider == nil)
        #expect(settings.enabledProvidersOrdered(metadataByProvider: metadata) == [.codex])
    }

    @Test
    func `menu bar metric preferences and display modes`() {
        let settings = Self.makeSettingsStore()

        settings.setMenuBarMetricPreference(.average, for: .codex)
        #expect(settings.menuBarMetricPreference(for: .codex) == .automatic)

        settings.setMenuBarMetricPreference(.average, for: .gemini)
        #expect(settings.menuBarMetricPreference(for: .gemini) == .average)
        #expect(settings.menuBarMetricSupportsAverage(for: .gemini))

        settings.setMenuBarMetricPreference(.secondary, for: .zai)
        #expect(settings.menuBarMetricPreference(for: .zai) == .secondary)

        settings.menuBarDisplayMode = .pace
        #expect(settings.menuBarDisplayMode == .pace)
        #expect(settings.historicalTrackingEnabled == false)
        settings.historicalTrackingEnabled = true
        #expect(settings.historicalTrackingEnabled == true)

        settings.resetTimesShowAbsolute = true
        #expect(settings.resetTimeDisplayStyle == .absolute)
    }

    @Test
    func `minimax settings snapshot uses selected token account as manual cookie`() {
        let settings = Self.makeSettingsStore(suiteName: "SettingsStoreCoverageTests-minimax-token-account")
        settings.minimaxCookieSource = .auto
        settings.minimaxCookieHeader = "HERTZ-SESSION=global"
        settings.addTokenAccount(provider: .minimax, label: "account", token: "HERTZ-SESSION=selected")

        let snapshot = settings.minimaxSettingsSnapshot(tokenOverride: nil)

        #expect(snapshot.cookieSource == .manual)
        #expect(snapshot.manualCookieHeader == "HERTZ-SESSION=selected")
    }

    @Test
    func `minimax settings snapshot falls back to global cookie without token accounts`() {
        let settings = Self.makeSettingsStore(suiteName: "SettingsStoreCoverageTests-minimax-global-cookie")
        settings.minimaxCookieSource = .auto
        settings.minimaxCookieHeader = "HERTZ-SESSION=global"

        let snapshot = settings.minimaxSettingsSnapshot(tokenOverride: nil)

        #expect(snapshot.cookieSource == .auto)
        #expect(snapshot.manualCookieHeader == "HERTZ-SESSION=global")
    }

    @Test
    func `copilot budget extras default off and persist in provider snapshot`() throws {
        let suite = "SettingsStoreCoverageTests-copilot-budget-extras"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)

        let initial = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(initial.copilotBudgetExtrasEnabled == false)
        #expect(initial.copilotSettingsSnapshot(tokenOverride: nil).budgetExtrasEnabled == false)

        initial.copilotBudgetExtrasEnabled = true

        let reloaded = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded.copilotBudgetExtrasEnabled)
        #expect(reloaded.copilotSettingsSnapshot(tokenOverride: nil).budgetExtrasEnabled)
    }

    @Test
    func `agent sessions default off and persist explicit opt in`() throws {
        let suite = "SettingsStoreCoverageTests-agent-sessions"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)

        let initial = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(initial.agentSessionsEnabled == false)
        #expect(initial.agentSessionLabelStyle == .project)
        #expect(defaults.object(forKey: "agentSessionsEnabled") == nil)
        #expect(defaults.object(forKey: "agentSessionLabelStyle") == nil)

        initial.agentSessionsEnabled = true
        initial.agentSessionLabelStyle = .descriptiveAndProject
        #expect(defaults.object(forKey: "agentSessionsEnabled") as? Bool == true)
        #expect(defaults.string(forKey: "agentSessionLabelStyle") == "descriptiveAndProject")

        let reloaded = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded.agentSessionsEnabled)
        #expect(reloaded.agentSessionLabelStyle == .descriptiveAndProject)
    }

    @Test
    func `background low power mode defaults off persists and drives effective web saver`() throws {
        let suite = "SettingsStoreCoverageTests-background-low-power"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)

        let initial = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(initial.backgroundWorkLowPowerModeEnabled == false)
        #expect(defaults.object(forKey: "backgroundWorkLowPowerModeEnabled") == nil)
        #expect(initial.effectiveOpenAIWebBatterySaverEnabled == false)

        let revision = initial.backgroundWorkSettingsRevision
        initial.backgroundWorkLowPowerModeEnabled = true

        #expect(initial.backgroundWorkSettingsRevision == revision + 1)
        #expect(initial.effectiveOpenAIWebBatterySaverEnabled)

        let reloaded = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded.backgroundWorkLowPowerModeEnabled)
        #expect(reloaded.effectiveOpenAIWebBatterySaverEnabled)

        reloaded.backgroundWorkLowPowerModeEnabled = false
        reloaded.openAIWebBatterySaverEnabled = true
        #expect(reloaded.effectiveOpenAIWebBatterySaverEnabled)
    }

    @Test
    func `multi account menu layout persists and bridges legacy show all token accounts`() throws {
        let suite = "SettingsStoreCoverageTests-multi-account-layout"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)

        let initial = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(initial.multiAccountMenuLayout == .segmented)

        initial.multiAccountMenuLayout = .stacked
        #expect(defaults.string(forKey: "multiAccountMenuLayout") == MultiAccountMenuLayout.stacked.rawValue)
        #expect(initial.showAllTokenAccountsInMenu)

        let reloaded = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded.multiAccountMenuLayout == .stacked)
        reloaded.showAllTokenAccountsInMenu = false
        #expect(reloaded.multiAccountMenuLayout == .segmented)
    }

    @Test
    func `legacy show all token accounts migrates to stacked layout`() throws {
        let suite = "SettingsStoreCoverageTests-legacy-token-account-layout"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        defaults.set(true, forKey: "showAllTokenAccountsInMenu")
        let configStore = testConfigStore(suiteName: suite)

        let settings = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)

        #expect(settings.multiAccountMenuLayout == .stacked)
    }

    @Test
    func `token account mutations apply side effects`() {
        let settings = Self.makeSettingsStore()

        settings.addTokenAccount(provider: .claude, label: "Primary", token: "token")
        #expect(settings.tokenAccounts(for: .claude).count == 1)
        #expect(settings.claudeCookieSource == .manual)

        let account = settings.selectedTokenAccount(for: .claude)
        #expect(account != nil)

        settings.setActiveTokenAccountIndex(10, for: .claude)
        #expect(settings.selectedTokenAccount(for: .claude)?.id == account?.id)

        if let id = account?.id {
            settings.removeTokenAccount(provider: .claude, accountID: id)
        }
        #expect(settings.tokenAccounts(for: .claude).isEmpty)

        settings.reloadTokenAccounts()
    }

    @Test
    func `token account update preserves identity and selection`() throws {
        let settings = Self.makeSettingsStore()

        settings.addTokenAccount(provider: .copilot, label: "Primary", token: "token-1")
        settings.addTokenAccount(provider: .copilot, label: "Secondary", token: "token-2")
        settings.setActiveTokenAccountIndex(0, for: .copilot)

        let original = try #require(settings.selectedTokenAccount(for: .copilot))
        settings.updateTokenAccount(
            provider: .copilot,
            accountID: original.id,
            label: "Primary (Pro)",
            token: "token-1b")

        let updated = try #require(settings.selectedTokenAccount(for: .copilot))
        #expect(updated.id == original.id)
        #expect(updated.label == "Primary (Pro)")
        #expect(updated.token == "token-1b")
        #expect(settings.tokenAccounts(for: .copilot).count == 2)
    }

    @Test
    func `zai token account update preserves team metadata`() throws {
        let settings = Self.makeSettingsStore()

        settings.addTokenAccount(
            provider: .zai,
            label: "Team",
            token: "token-1",
            usageScope: "team",
            organizationID: "org-team",
            workspaceID: "proj-team")

        let original = try #require(settings.selectedTokenAccount(for: .zai))
        settings.updateTokenAccount(
            provider: .zai,
            accountID: original.id,
            label: "Team Updated",
            token: "token-2")

        let updated = try #require(settings.selectedTokenAccount(for: .zai))
        #expect(updated.usageScope == "team")
        #expect(updated.organizationID == "org-team")
        #expect(updated.workspaceID == "proj-team")
    }

    @Test
    func `copilot token accounts clear legacy api key fallback`() throws {
        let settings = Self.makeSettingsStore()
        settings.copilotAPIToken = "legacy-token"

        settings.addTokenAccount(provider: .copilot, label: "Primary", token: "token-1")

        #expect(settings.copilotAPIToken.isEmpty)
        #expect(settings.copilotSettingsSnapshot(tokenOverride: nil).apiToken == "token-1")

        settings.copilotAPIToken = "legacy-token"
        let account = try #require(settings.selectedTokenAccount(for: .copilot))
        settings.removeTokenAccount(provider: .copilot, accountID: account.id)

        #expect(settings.tokenAccounts(for: .copilot).isEmpty)
        #expect(settings.copilotAPIToken.isEmpty)
        #expect(settings.copilotSettingsSnapshot(tokenOverride: nil).apiToken == nil)
    }

    @Test
    func `copilot settings snapshot carries selected account identifier`() {
        let settings = Self.makeSettingsStore()
        settings.addTokenAccount(
            provider: .copilot,
            label: "octocat (Pro)",
            token: "token-1",
            externalIdentifier: "github:user:123")

        let snapshot = settings.copilotSettingsSnapshot(tokenOverride: nil)

        #expect(snapshot.apiToken == "token-1")
        #expect(snapshot.selectedAccountExternalIdentifier == "github:user:123")
    }

    @Test
    func `copilot enterprise host persists in provider config`() throws {
        let suite = "SettingsStoreCoverageTests-copilot-enterprise-host"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)
        let first = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)

        first.copilotEnterpriseHost = "https://octocorp.ghe.com/login"
        #expect(first.copilotEnterpriseHost == "https://octocorp.ghe.com/login")
        #expect(first.copilotSettingsSnapshot(tokenOverride: nil).enterpriseHost == "octocorp.ghe.com")

        let second = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(second.copilotEnterpriseHost == "https://octocorp.ghe.com/login")

        second.copilotEnterpriseHost = "github.com"
        #expect(second.copilotEnterpriseHost == "github.com")
        #expect(second.copilotSettingsSnapshot(tokenOverride: nil).enterpriseHost == nil)
    }

    @Test
    func `removing another token account preserves active selection`() throws {
        let settings = Self.makeSettingsStore()

        settings.addTokenAccount(provider: .copilot, label: "A", token: "token-a")
        settings.addTokenAccount(provider: .copilot, label: "B", token: "token-b")
        settings.addTokenAccount(provider: .copilot, label: "C", token: "token-c")
        settings.setActiveTokenAccountIndex(1, for: .copilot)

        let activeBefore = try #require(settings.selectedTokenAccount(for: .copilot))
        let accountToRemove = try #require(settings.tokenAccounts(for: .copilot).first)
        settings.removeTokenAccount(provider: .copilot, accountID: accountToRemove.id)

        let activeAfter = try #require(settings.selectedTokenAccount(for: .copilot))
        #expect(activeAfter.id == activeBefore.id)
        #expect(activeAfter.label == "B")
        #expect(settings.tokenAccounts(for: .copilot).map(\.label) == ["B", "C"])
    }

    @Test
    func `claude snapshot uses OAuth routing for OAuth token accounts`() {
        let settings = Self.makeSettingsStore()
        settings.addTokenAccount(provider: .claude, label: "OAuth", token: "Bearer sk-ant-oat-account-token")

        let snapshot = settings.claudeSettingsSnapshot(tokenOverride: nil)

        #expect(snapshot.usageDataSource == .oauth)
        #expect(snapshot.cookieSource == .off)
        #expect(snapshot.manualCookieHeader?.isEmpty == true)
    }

    @Test
    func `claude snapshot uses manual cookie routing for session key accounts`() {
        let settings = Self.makeSettingsStore()
        settings.addTokenAccount(provider: .claude, label: "Cookie", token: "sk-ant-session-token")

        let snapshot = settings.claudeSettingsSnapshot(tokenOverride: nil)

        #expect(snapshot.usageDataSource == .web)
        #expect(snapshot.cookieSource == .manual)
        #expect(snapshot.manualCookieHeader == "sessionKey=sk-ant-session-token")
    }

    @Test
    func `claude snapshot normalizes config manual cookie input through shared route`() {
        let settings = Self.makeSettingsStore()
        settings.claudeCookieSource = .manual
        settings.claudeCookieHeader = "Cookie: sessionKey=sk-ant-session-token; foo=bar"

        let snapshot = settings.claudeSettingsSnapshot(tokenOverride: nil)

        #expect(snapshot.usageDataSource == .auto)
        #expect(snapshot.cookieSource == .manual)
        #expect(snapshot.manualCookieHeader == "sessionKey=sk-ant-session-token; foo=bar")
    }

    @Test
    func `claude snapshot does not fall back to config cookie for malformed selected token account`() {
        let settings = Self.makeSettingsStore()
        settings.claudeCookieSource = .manual
        settings.claudeCookieHeader = "Cookie: sessionKey=sk-ant-config-cookie"
        settings.addTokenAccount(provider: .claude, label: "Malformed", token: "Cookie:")

        let snapshot = settings.claudeSettingsSnapshot(tokenOverride: nil)

        #expect(snapshot.cookieSource == .manual)
        #expect(snapshot.manualCookieHeader?.isEmpty == true)
    }

    @Test
    func `opencode go token accounts force manual cookie routing`() {
        let settings = Self.makeSettingsStore()
        settings.addTokenAccount(provider: .opencodego, label: "Go", token: "auth=go-cookie")

        let snapshot = settings.opencodegoSettingsSnapshot(tokenOverride: nil)

        #expect(settings.opencodegoCookieSource == .manual)
        #expect(snapshot.cookieSource == .manual)
        #expect(snapshot.manualCookieHeader == "auth=go-cookie")
    }

    @Test
    func `opencode go snapshot preserves nil workspace id when settings are unset`() {
        let settings = Self.makeSettingsStore()

        let snapshot = settings.opencodegoSettingsSnapshot(tokenOverride: nil)

        #expect(settings.opencodegoWorkspaceID.isEmpty)
        #expect(snapshot.workspaceID == nil)
    }

    @Test
    func `token cost usage source detection`() throws {
        let fileManager = FileManager.default
        let root = fileManager.temporaryDirectory.appendingPathComponent(
            "token-cost-\(UUID().uuidString)",
            isDirectory: true)
        let codexRoot = root.appendingPathComponent("sessions", isDirectory: true)
        try fileManager.createDirectory(at: codexRoot, withIntermediateDirectories: true)
        let codexFile = codexRoot.appendingPathComponent("usage.jsonl")
        fileManager.createFile(atPath: codexFile.path, contents: Data("{}".utf8))

        #expect(SettingsStore.hasAnyTokenCostUsageSources(
            env: ["CODEX_HOME": root.path],
            fileManager: fileManager))

        let claudeRoot = fileManager.temporaryDirectory.appendingPathComponent(
            "claude-\(UUID().uuidString)",
            isDirectory: true)
        let claudeProjects = claudeRoot.appendingPathComponent("projects", isDirectory: true)
        try fileManager.createDirectory(at: claudeProjects, withIntermediateDirectories: true)
        let claudeFile = claudeProjects.appendingPathComponent("usage.jsonl")
        fileManager.createFile(atPath: claudeFile.path, contents: Data("{}".utf8))

        #expect(SettingsStore.hasAnyTokenCostUsageSources(
            env: ["CLAUDE_CONFIG_DIR": claudeRoot.path],
            fileManager: fileManager))

        let metadataOnlyHome = fileManager.temporaryDirectory.appendingPathComponent(
            "claude-desktop-metadata-\(UUID().uuidString)",
            isDirectory: true)
        let metadataFile = metadataOnlyHome
            .appendingPathComponent("Library/Application Support/Claude/claude-code-sessions", isDirectory: true)
            .appendingPathComponent("account-id/org-id/local_session.json", isDirectory: false)
        try fileManager.createDirectory(at: metadataFile.deletingLastPathComponent(), withIntermediateDirectories: true)
        try Data(#"{"cliSessionId":"desktop-cli-session"}"#.utf8).write(to: metadataFile)

        #expect(!SettingsStore.hasAnyTokenCostUsageSources(
            env: [:],
            fileManager: fileManager,
            homeDirectory: metadataOnlyHome))

        let desktopHome = fileManager.temporaryDirectory.appendingPathComponent(
            "claude-desktop-\(UUID().uuidString)",
            isDirectory: true)
        let desktopProjects = desktopHome
            .appendingPathComponent("Library", isDirectory: true)
            .appendingPathComponent("Application Support", isDirectory: true)
            .appendingPathComponent("Claude", isDirectory: true)
            .appendingPathComponent("local-agent-mode-sessions", isDirectory: true)
            .appendingPathComponent("workspace-id", isDirectory: true)
            .appendingPathComponent("session-id", isDirectory: true)
            .appendingPathComponent("local_agent", isDirectory: true)
            .appendingPathComponent(".claude", isDirectory: true)
            .appendingPathComponent("projects", isDirectory: true)
        try fileManager.createDirectory(at: desktopProjects, withIntermediateDirectories: true)
        let desktopFile = desktopProjects
            .appendingPathComponent("project-a", isDirectory: true)
            .appendingPathComponent("session-a.jsonl", isDirectory: false)
        try fileManager.createDirectory(at: desktopFile.deletingLastPathComponent(), withIntermediateDirectories: true)
        fileManager.createFile(atPath: desktopFile.path, contents: Data("{}".utf8))

        #expect(SettingsStore.hasAnyTokenCostUsageSources(
            env: [:],
            fileManager: fileManager,
            homeDirectory: desktopHome))

        let desktopCodeHome = fileManager.temporaryDirectory.appendingPathComponent(
            "claude-desktop-code-\(UUID().uuidString)",
            isDirectory: true)
        let desktopCodeFile = desktopCodeHome
            .appendingPathComponent("Library/Application Support/Claude/claude-code-sessions", isDirectory: true)
            .appendingPathComponent("account-id/org-id/.claude/projects/project-a", isDirectory: true)
            .appendingPathComponent("session-a.jsonl", isDirectory: false)
        try fileManager.createDirectory(
            at: desktopCodeFile.deletingLastPathComponent(),
            withIntermediateDirectories: true)
        fileManager.createFile(atPath: desktopCodeFile.path, contents: Data("{}".utf8))

        #expect(SettingsStore.hasAnyTokenCostUsageSources(
            env: [:],
            fileManager: fileManager,
            homeDirectory: desktopCodeHome))
    }

    @Test
    func `ensure token loaders execute`() {
        let settings = Self.makeSettingsStore()

        settings.ensureZaiAPITokenLoaded()
        settings.ensureSyntheticAPITokenLoaded()
        settings.ensureCodexCookieLoaded()
        settings.ensureClaudeCookieLoaded()
        settings.ensureCursorCookieLoaded()
        settings.ensureOpenCodeCookieLoaded()
        settings.ensureFactoryCookieLoaded()
        settings.ensureMiniMaxCookieLoaded()
        settings.ensureMiniMaxAPITokenLoaded()
        settings.ensureKimiAuthTokenLoaded()
        settings.ensureAugmentCookieLoaded()
        settings.ensureAmpCookieLoaded()
        settings.ensureOllamaCookieLoaded()
        settings.ensureCopilotAPITokenLoaded()
        settings.ensureTokenAccountsLoaded()

        #expect(settings.zaiAPIToken.isEmpty)
        #expect(settings[providerConfig: .synthetic, field: .apiKey].isEmpty)
    }

    @Test
    func `keychain disable forces manual cookie sources`() throws {
        let suite = "SettingsStoreCoverageTests-keychain"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)
        let settings = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)

        settings.codexCookieSource = .auto
        settings.claudeCookieSource = .auto
        settings.kimiCookieSource = .off
        settings.debugDisableKeychainAccess = true

        #expect(settings.codexCookieSource == .manual)
        #expect(settings.claudeCookieSource == .manual)
        #expect(settings.kimiCookieSource == .off)
    }

    @Test
    func `claude keychain prompt mode defaults to only on user action`() {
        let settings = Self.makeSettingsStore()
        #expect(settings.claudeOAuthKeychainPromptMode == .onlyOnUserAction)
    }

    @Test
    func `claude keychain prompt mode persists across store reload`() throws {
        let suite = "SettingsStoreCoverageTests-claude-keychain-prompt-mode"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)

        let first = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        first.claudeOAuthKeychainPromptMode = .never
        #expect(
            defaults.string(forKey: "claudeOAuthKeychainPromptMode")
                == ClaudeOAuthKeychainPromptMode.never.rawValue)

        let second = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(second.claudeOAuthKeychainPromptMode == .never)
    }

    @Test
    func `claude keychain prompt mode invalid raw falls back to only on user action`() throws {
        let suite = "SettingsStoreCoverageTests-claude-keychain-prompt-mode-invalid"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        defaults.set("invalid-mode", forKey: "claudeOAuthKeychainPromptMode")
        let configStore = testConfigStore(suiteName: suite)

        let settings = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(settings.claudeOAuthKeychainPromptMode == .onlyOnUserAction)
    }

    @Test
    func `claude keychain read strategy defaults to security framework`() {
        let settings = Self.makeSettingsStore()
        #expect(settings.claudeOAuthKeychainReadStrategy == .securityFramework)
    }

    @Test
    func `claude keychain read strategy persists across store reload`() throws {
        let suite = "SettingsStoreCoverageTests-claude-keychain-read-strategy"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)

        let first = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        first.claudeOAuthKeychainReadStrategy = .securityFramework
        #expect(
            defaults.string(forKey: "claudeOAuthKeychainReadStrategy")
                == ClaudeOAuthKeychainReadStrategy.securityFramework.rawValue)

        let second = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(second.claudeOAuthKeychainReadStrategy == .securityFramework)
    }

    @Test
    func `claude legacy security CLI read strategy preserves no prompt intent`() throws {
        let suite = "SettingsStoreCoverageTests-claude-keychain-read-strategy-migration"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        defaults.set(
            ClaudeOAuthKeychainReadStrategy.securityCLIExperimental.rawValue,
            forKey: "claudeOAuthKeychainReadStrategy")
        let configStore = testConfigStore(suiteName: suite)

        let settings = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)

        #expect(settings.claudeOAuthKeychainReadStrategy == .securityFramework)
        #expect(settings.claudeOAuthKeychainPromptMode == .never)
        #expect(settings.claudeOAuthPromptFreeCredentialsEnabled)
        #expect(
            defaults.string(forKey: "claudeOAuthKeychainReadStrategy")
                == ClaudeOAuthKeychainReadStrategy.securityFramework.rawValue)
        #expect(
            defaults.string(forKey: "claudeOAuthKeychainPromptMode")
                == ClaudeOAuthKeychainPromptMode.never.rawValue)
    }

    @Test
    func `claude legacy security CLI migration preserves explicit prompt policy`() throws {
        let suite = "SettingsStoreCoverageTests-claude-keychain-explicit-prompt-migration"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        defaults.set(
            ClaudeOAuthKeychainReadStrategy.securityCLIExperimental.rawValue,
            forKey: "claudeOAuthKeychainReadStrategy")
        defaults.set(
            ClaudeOAuthKeychainPromptMode.always.rawValue,
            forKey: "claudeOAuthKeychainPromptMode")
        let configStore = testConfigStore(suiteName: suite)

        let settings = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)

        #expect(settings.claudeOAuthKeychainReadStrategy == .securityFramework)
        #expect(settings.claudeOAuthKeychainPromptMode == .always)
        #expect(!settings.claudeOAuthPromptFreeCredentialsEnabled)
    }

    @Test
    func `claude keychain read strategy invalid raw falls back to security framework`() throws {
        let suite = "SettingsStoreCoverageTests-claude-keychain-read-strategy-invalid"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        defaults.set("invalid-strategy", forKey: "claudeOAuthKeychainReadStrategy")
        let configStore = testConfigStore(suiteName: suite)

        let settings = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(settings.claudeOAuthKeychainReadStrategy == .securityFramework)
    }

    @Test
    func `claude prompt free credentials toggle maps to never prompt policy`() {
        let settings = Self.makeSettingsStore()
        #expect(settings.claudeOAuthPromptFreeCredentialsEnabled == false)

        settings.claudeOAuthPromptFreeCredentialsEnabled = true
        #expect(settings.claudeOAuthKeychainReadStrategy == .securityFramework)
        #expect(settings.claudeOAuthKeychainPromptMode == .never)

        settings.claudeOAuthPromptFreeCredentialsEnabled = false
        #expect(settings.claudeOAuthKeychainReadStrategy == .securityFramework)
        #expect(settings.claudeOAuthKeychainPromptMode == .onlyOnUserAction)
    }

    @Test
    func `upsert antigravity oauth account adds and updates active token account`() throws {
        let settings = Self.makeSettingsStore()
        let first = AntigravityOAuthCredentials(
            accessToken: "first-access",
            refreshToken: "first-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_000),
            email: "user@example.com")
        let updated = AntigravityOAuthCredentials(
            accessToken: "updated-access",
            refreshToken: "first-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_100),
            email: "user@example.com")

        settings.upsertAntigravityOAuthAccount(first)
        settings.upsertAntigravityOAuthAccount(updated)

        let accounts = settings.tokenAccounts(for: .antigravity)
        #expect(accounts.count == 1)
        let account = try #require(accounts.first)
        #expect(account.label == "user@example.com")
        #expect(account.externalIdentifier == "user@example.com")
        #expect(settings.selectedTokenAccount(for: .antigravity)?.id == account.id)

        let decoded = try #require(AntigravityOAuthCredentialsStore.credentials(fromTokenAccountValue: account.token))
        #expect(decoded.accessToken == "updated-access")
    }

    @Test
    func `upsert antigravity oauth account does not merge missing email accounts by fallback label`() {
        let settings = Self.makeSettingsStore()
        let first = AntigravityOAuthCredentials(
            accessToken: "first-access",
            refreshToken: "first-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_000),
            email: nil)
        let second = AntigravityOAuthCredentials(
            accessToken: "second-access",
            refreshToken: "second-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_100),
            email: nil)

        settings.upsertAntigravityOAuthAccount(first)
        settings.upsertAntigravityOAuthAccount(second)

        let accounts = settings.tokenAccounts(for: .antigravity)
        #expect(accounts.count == 2)
        #expect(accounts.map(\.label) == ["Google Account 1", "Google Account 2"])
        #expect(settings.selectedTokenAccount(for: .antigravity)?.id == accounts.last?.id)
    }

    @Test
    func `removing last antigravity oauth account clears matching shared credentials`() throws {
        let root = FileManager.default.temporaryDirectory
            .appendingPathComponent("antigravity-shared-removal-\(UUID().uuidString)", isDirectory: true)
        try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let sharedStore = AntigravityOAuthCredentialsStore(
            fileURL: root.appendingPathComponent("oauth_creds.json"))
        let credentials = AntigravityOAuthCredentials(
            accessToken: "shared-access",
            refreshToken: "shared-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_000),
            email: "user@example.com")
        try sharedStore.save(credentials)
        let settings = Self.makeSettingsStore(
            suiteName: "SettingsStoreCoverageTests-antigravity-remove-shared",
            antigravityOAuthCredentialsStore: sharedStore)

        settings.upsertAntigravityOAuthAccount(credentials)
        let account = try #require(settings.selectedTokenAccount(for: .antigravity))
        settings.removeTokenAccount(provider: .antigravity, accountID: account.id)

        #expect(settings.tokenAccounts(for: .antigravity).isEmpty)
        #expect(try sharedStore.load() == nil)
    }

    @Test
    func `removing antigravity oauth account preserves freshly reauthenticated credentials`() throws {
        let root = FileManager.default.temporaryDirectory
            .appendingPathComponent("antigravity-shared-reauth-\(UUID().uuidString)", isDirectory: true)
        try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let sharedStore = AntigravityOAuthCredentialsStore(
            fileURL: root.appendingPathComponent("oauth_creds.json"))
        let removed = AntigravityOAuthCredentials(
            accessToken: "removed-access",
            refreshToken: "removed-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_000),
            email: "user@example.com")
        let refreshed = AntigravityOAuthCredentials(
            accessToken: "fresh-access",
            refreshToken: "fresh-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_100),
            email: "user@example.com")
        try sharedStore.save(refreshed)
        let settings = Self.makeSettingsStore(
            suiteName: "SettingsStoreCoverageTests-antigravity-preserve-reauth",
            antigravityOAuthCredentialsStore: sharedStore)

        settings.upsertAntigravityOAuthAccount(removed)
        let account = try #require(settings.selectedTokenAccount(for: .antigravity))
        settings.removeTokenAccount(provider: .antigravity, accountID: account.id)

        #expect(try sharedStore.load() == refreshed)
    }

    @Test
    func `removing antigravity oauth account preserves different shared credentials`() async throws {
        let root = FileManager.default.temporaryDirectory
            .appendingPathComponent("antigravity-shared-preserve-\(UUID().uuidString)", isDirectory: true)
        try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let sharedStore = AntigravityOAuthCredentialsStore(
            fileURL: root.appendingPathComponent("oauth_creds.json"))
        let removed = AntigravityOAuthCredentials(
            accessToken: "removed-access",
            refreshToken: "removed-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_000),
            email: "removed@example.com")
        let shared = AntigravityOAuthCredentials(
            accessToken: "shared-access",
            refreshToken: "shared-refresh",
            expiryDate: Date(timeIntervalSince1970: 1_700_000_000),
            email: "shared@example.com")
        try sharedStore.save(shared)
        let settings = Self.makeSettingsStore(
            suiteName: "SettingsStoreCoverageTests-antigravity-preserve-shared",
            antigravityOAuthCredentialsStore: sharedStore)

        settings.upsertAntigravityOAuthAccount(removed)
        let account = try #require(settings.selectedTokenAccount(for: .antigravity))
        settings.removeTokenAccount(provider: .antigravity, accountID: account.id)

        #expect(settings.tokenAccounts(for: .antigravity).isEmpty)
        try await Task.sleep(nanoseconds: 50_000_000)
        #expect(try sharedStore.load() == shared)
    }

    @Test
    func `weekly progress work days defaults to nil and persists across store reload`() throws {
        let suite = "SettingsStoreCoverageTests-weekly-progress-work-days"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)

        let fresh = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(fresh.weeklyProgressWorkDays == nil)

        fresh.weeklyProgressWorkDays = 5
        #expect(defaults.object(forKey: "weeklyProgressWorkDays") as? Int == 5)

        let reloaded = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded.weeklyProgressWorkDays == 5)

        fresh.weeklyProgressWorkDays = 4
        #expect(reloaded.weeklyProgressWorkDays == 5)

        let reloaded2 = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded2.weeklyProgressWorkDays == 4)

        reloaded2.weeklyProgressWorkDays = 7
        let reloaded3 = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded3.weeklyProgressWorkDays == 7)

        reloaded3.weeklyProgressWorkDays = nil
        #expect(defaults.object(forKey: "weeklyProgressWorkDays") == nil)
        let reloaded4 = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded4.weeklyProgressWorkDays == nil)
    }

    @Test
    func `preferred currency defaults to USD and persists an explicit selection`() throws {
        let suite = "SettingsStoreCoverageTests-preferred-currency"
        let defaults = try #require(UserDefaults(suiteName: suite))
        defaults.removePersistentDomain(forName: suite)
        let configStore = testConfigStore(suiteName: suite)

        let fresh = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(fresh.preferredCurrencyCode == "USD")

        fresh.preferredCurrencyCode = "GBP"
        let reloaded = Self.makeSettingsStore(userDefaults: defaults, configStore: configStore)
        #expect(reloaded.preferredCurrencyCode == "GBP")
    }

    private static func makeSettingsStore(
        suiteName: String = "SettingsStoreCoverageTests",
        antigravityOAuthCredentialsStore: AntigravityOAuthCredentialsStore = AntigravityOAuthCredentialsStore())
        -> SettingsStore
    {
        let defaults = UserDefaults(suiteName: suiteName)!
        defaults.removePersistentDomain(forName: suiteName)
        defaults.set(false, forKey: "debugDisableKeychainAccess")
        let configStore = testConfigStore(suiteName: suiteName)
        return Self.makeSettingsStore(
            userDefaults: defaults,
            configStore: configStore,
            antigravityOAuthCredentialsStore: antigravityOAuthCredentialsStore)
    }

    private static func makeSettingsStore(
        userDefaults: UserDefaults,
        configStore: CodexBarConfigStore,
        antigravityOAuthCredentialsStore: AntigravityOAuthCredentialsStore = AntigravityOAuthCredentialsStore())
        -> SettingsStore
    {
        SettingsStore(
            userDefaults: userDefaults,
            configStore: configStore,
            zaiTokenStore: NoopZaiTokenStore(),
            syntheticTokenStore: NoopSyntheticTokenStore(),
            codexCookieStore: InMemoryCookieHeaderStore(),
            claudeCookieStore: InMemoryCookieHeaderStore(),
            cursorCookieStore: InMemoryCookieHeaderStore(),
            opencodeCookieStore: InMemoryCookieHeaderStore(),
            factoryCookieStore: InMemoryCookieHeaderStore(),
            minimaxCookieStore: InMemoryMiniMaxCookieStore(),
            minimaxAPITokenStore: InMemoryMiniMaxAPITokenStore(),
            kimiTokenStore: InMemoryKimiTokenStore(),
            augmentCookieStore: InMemoryCookieHeaderStore(),
            ampCookieStore: InMemoryCookieHeaderStore(),
            copilotTokenStore: InMemoryCopilotTokenStore(),
            tokenAccountStore: InMemoryTokenAccountStore(),
            antigravityOAuthCredentialsStore: antigravityOAuthCredentialsStore)
    }

    private static func waitForSharedAntigravityCredentials(
        in store: AntigravityOAuthCredentialsStore,
        matches predicate: (AntigravityOAuthCredentials?) -> Bool) async throws
        -> AntigravityOAuthCredentials?
    {
        for _ in 0..<100 {
            let credentials = try store.load()
            if predicate(credentials) {
                return credentials
            }
            try await Task.sleep(nanoseconds: 10_000_000)
        }
        return try store.load()
    }
}
