import CodexBarCore
import Foundation
import Testing
@testable import CodexBar

@Suite(.serialized)
@MainActor
struct CodexAccountScopedRefreshTests {
    @Test
    func `account transition invalidates codex scoped state and preserves token usage`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-invalidate")
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .auto
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        let staleSnapshot = self.codexSnapshot(email: "alpha@example.com", usedPercent: 10)
        let staleCredits = self.credits(remaining: 42)
        let staleDashboard = self.dashboard(email: "alpha@example.com", creditsRemaining: 42, usedPercent: 20)
        let tokenSnapshot = CostUsageTokenSnapshot(
            sessionTokens: 120,
            sessionCostUSD: 1.2,
            last30DaysTokens: 900,
            last30DaysCostUSD: 9.0,
            daily: [],
            updatedAt: Date())
        var widgetSnapshots: [WidgetSnapshot] = []

        store._setSnapshotForTesting(staleSnapshot, provider: .codex)
        store.credits = staleCredits
        store.lastCreditsSnapshot = staleCredits
        store.lastCreditsSnapshotAccountKey = "alpha@example.com"
        store.lastCreditsSource = .api
        store.openAIDashboard = staleDashboard
        store.lastOpenAIDashboardSnapshot = staleDashboard
        store.lastOpenAIDashboardTargetEmail = "alpha@example.com"
        store._setTokenSnapshotForTesting(tokenSnapshot, provider: .codex)
        store.lastCodexAccountScopedRefreshGuard = store
            .currentCodexAccountScopedRefreshGuard(preferCurrentSnapshot: false)
        store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
        defer { store._test_widgetSnapshotSaveOverride = nil }

        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")

        let didInvalidate = store.prepareCodexAccountScopedRefreshIfNeeded()
        await store.widgetSnapshotPersistTask?.value

        #expect(didInvalidate)
        #expect(store.snapshots[.codex] == nil)
        #expect(store.credits == nil)
        #expect(store.lastCreditsSnapshot == nil)
        #expect(store.lastCreditsSnapshotAccountKey == nil)
        #expect(store.lastCreditsSource == .none)
        #expect(store.openAIDashboard == nil)
        #expect(store.lastOpenAIDashboardSnapshot == nil)
        #expect(store.tokenSnapshots[.codex] == tokenSnapshot)
        #expect(widgetSnapshots.count == 1)
        #expect(widgetSnapshots[0].entries.contains(where: { $0.provider == .codex }) == false)
    }

    @Test
    func `first switch invalidates after codex refresh seeds the previous account guard`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-first-switch")
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .off
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        self.installImmediateCodexProvider(
            on: store,
            snapshot: self.codexSnapshot(email: "alpha@example.com", usedPercent: 10))

        await store.refreshProvider(.codex, allowDisabled: true)

        #expect(store.lastCodexAccountScopedRefreshGuard?.accountKey == "alpha@example.com")

        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")

        let didInvalidate = store.prepareCodexAccountScopedRefreshIfNeeded()

        #expect(didInvalidate)
        #expect(store.snapshots[.codex] == nil)
    }

    @Test
    func `stale codex usage success is discarded after account switch`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-stale-success")
        settings.refreshFrequency = .manual
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        let blocker = BlockingCodexFetchStrategy()
        self.installBlockingCodexProvider(on: store, blocker: blocker)

        let refreshTask = Task { await store.refreshProvider(.codex, allowDisabled: true) }
        await blocker.waitUntilStarted()
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")
        await blocker.resume(with: .success(self.codexSnapshot(email: "alpha@example.com", usedPercent: 25)))
        await refreshTask.value

        #expect(store.snapshots[.codex] == nil)
        #expect(store.errors[.codex] == nil)
    }

    @Test
    func `same email provider account switch discards stale codex usage success`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-stale-same-email-provider-account")
        settings.refreshFrequency = .manual
        settings._test_liveSystemCodexAccount = self.liveAccount(
            email: "alpha@example.com",
            identity: .providerAccount(id: "acct-alpha"))

        let store = self.makeUsageStore(settings: settings)
        let blocker = BlockingCodexFetchStrategy()
        self.installBlockingCodexProvider(on: store, blocker: blocker)

        let refreshTask = Task { await store.refreshProvider(.codex, allowDisabled: true) }
        await blocker.waitUntilStarted()
        settings._test_liveSystemCodexAccount = self.liveAccount(
            email: "alpha@example.com",
            identity: .providerAccount(id: "acct-beta"))
        await blocker.resume(with: .success(self.codexSnapshot(email: "alpha@example.com", usedPercent: 25)))
        await refreshTask.value

        #expect(store.snapshots[.codex] == nil)
        #expect(store.errors[.codex] == nil)
    }

    @Test
    func `stale codex usage failure does not clear newer account snapshot`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-stale-failure")
        settings.refreshFrequency = .manual
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        let blocker = BlockingCodexFetchStrategy()
        self.installBlockingCodexProvider(on: store, blocker: blocker)

        let refreshTask = Task { await store.refreshProvider(.codex, allowDisabled: true) }
        await blocker.waitUntilStarted()
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")
        let freshSnapshot = self.codexSnapshot(email: "beta@example.com", usedPercent: 5)
        store._setSnapshotForTesting(freshSnapshot, provider: .codex)
        let betaGuard = store.freshCodexAccountScopedRefreshGuard()
        store.lastCodexUsagePublicationGuard = betaGuard
        store.lastCodexAccountScopedRefreshGuard = betaGuard
        await blocker.resume(with: .failure(TestRefreshError(message: "stale failure")))
        await refreshTask.value

        #expect(store.snapshots[.codex]?.accountEmail(for: .codex) == "beta@example.com")
        #expect(store.errors[.codex] == nil)
    }

    @Test
    func `codex visible account refresh preserves prior snapshots when network fails`() async throws {
        try await self.withCodexVisibleAccountFailureStore(
            suite: "CodexAccountScopedRefreshTests-preserve-codex-snapshots",
            errorMessage: "Network error: offline")
        { store, snapshotStore, priorSnapshots in
            await store.refreshCodexVisibleAccountsForMenu()

            #expect(store.codexAccountSnapshots.count == priorSnapshots.count)
            #expect(store.codexAccountSnapshots.allSatisfy { $0.snapshot?.primary?.usedPercent == 17 })
            #expect(store.codexAccountSnapshots.allSatisfy { $0.error == "Network error: offline" })
            #expect(store.codexAccountSnapshots.allSatisfy { $0.sourceLabel == "cached" })

            let persisted = snapshotStore.storedSnapshots
            #expect(persisted.count == priorSnapshots.count)
            #expect(persisted.allSatisfy { $0.snapshot?.primary?.usedPercent == 17 })
            #expect(persisted.allSatisfy { $0.error == "Network error: offline" })
        }
    }

    @Test
    func `codex visible account refresh drops prior snapshots when auth fails`() async throws {
        try await self.withCodexVisibleAccountFailureStore(
            suite: "CodexAccountScopedRefreshTests-drop-auth-failed-snapshots",
            errorMessage: "401 Unauthorized")
        { store, snapshotStore, priorSnapshots in
            await store.refreshCodexVisibleAccountsForMenu()

            #expect(store.codexAccountSnapshots.count == priorSnapshots.count)
            #expect(store.codexAccountSnapshots.allSatisfy { $0.snapshot == nil })
            #expect(store.codexAccountSnapshots.allSatisfy { $0.error == "401 Unauthorized" })

            let persisted = snapshotStore.storedSnapshots
            #expect(persisted.count == priorSnapshots.count)
            #expect(persisted.allSatisfy { $0.snapshot == nil })
            #expect(persisted.allSatisfy { $0.error == "401 Unauthorized" })
        }
    }

    @Test
    func `credits fallback only reuses cache for the same codex account`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-credits")
        settings.refreshFrequency = .manual
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        let cachedCredits = self.credits(remaining: 12)
        store._setSnapshotForTesting(self.codexSnapshot(email: "alpha@example.com", usedPercent: 10), provider: .codex)
        store.lastCreditsSnapshot = cachedCredits
        store.lastCreditsSnapshotAccountKey = "alpha@example.com"
        store._test_codexCreditsLoaderOverride = {
            throw TestRefreshError(message: "Codex credits data not available yet")
        }
        defer { store._test_codexCreditsLoaderOverride = nil }

        await store.refreshCreditsIfNeeded()
        #expect(store.credits == cachedCredits)
        #expect(store.lastCreditsError == nil)

        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")
        store._setSnapshotForTesting(self.codexSnapshot(email: "beta@example.com", usedPercent: 10), provider: .codex)

        await store.refreshCreditsIfNeeded()
        #expect(store.credits == nil)
        #expect(store.lastCreditsSource == .none)
        #expect(store.lastCreditsError == "Codex credits are still loading; will retry shortly.")
    }

    @Test
    func `managed refresh invalidation keeps state when provider account is unchanged`() throws {
        let settings = self.makeSettingsStore(
            suite: "CodexAccountScopedRefreshTests-managed-renamed-email")
        let managedHome = FileManager.default.temporaryDirectory
            .appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: managedHome) }
        try Self.writeCodexAuthFile(
            homeURL: managedHome,
            email: "renamed@example.com",
            plan: "pro",
            accountId: "acct-managed")

        let managedAccountID = UUID()
        let managedAccount = ManagedCodexAccount(
            id: managedAccountID,
            email: "legacy@example.com",
            managedHomePath: managedHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        settings._test_activeManagedCodexAccount = managedAccount
        settings.codexActiveSource = .managedAccount(id: managedAccountID)
        defer { settings._test_activeManagedCodexAccount = nil }

        let store = self.makeUsageStore(settings: settings)
        let currentSnapshot = self.codexSnapshot(email: "renamed@example.com", usedPercent: 10)
        let currentCredits = self.credits(remaining: 42)
        let currentDashboard = self.dashboard(email: "renamed@example.com", creditsRemaining: 42, usedPercent: 20)

        store._setSnapshotForTesting(currentSnapshot, provider: .codex)
        store.credits = currentCredits
        store.lastCreditsSnapshot = currentCredits
        store.lastCreditsSnapshotAccountKey = "renamed@example.com"
        store.openAIDashboard = currentDashboard
        store.lastOpenAIDashboardSnapshot = currentDashboard
        store.lastOpenAIDashboardTargetEmail = "renamed@example.com"
        store.seedCodexAccountScopedRefreshGuard(
            source: .managedAccount(id: managedAccountID),
            accountEmail: "renamed@example.com")

        let currentGuard = store.currentCodexAccountScopedRefreshGuard(
            preferCurrentSnapshot: false,
            allowLastKnownLiveFallback: false)
        let didInvalidate = store.prepareCodexAccountScopedRefreshIfNeeded()

        #expect(currentGuard.identity == .providerAccount(id: "acct-managed"))
        #expect(currentGuard.accountKey == "renamed@example.com")
        #expect(didInvalidate == false)
        #expect(store.snapshots[.codex]?.accountEmail(for: .codex) == "renamed@example.com")
        #expect(store.credits?.remaining == 42)
        #expect(store.openAIDashboard?.signedInEmail == "renamed@example.com")
    }

    @Test
    func `credits refresh returns quickly when no live codex account is available`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-credits-no-live-account")
        let isolatedHome = FileManager.default.temporaryDirectory
            .appendingPathComponent("codex-credits-no-live-\(UUID().uuidString)", isDirectory: true)
        try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
        settings.refreshFrequency = .manual
        settings._test_liveSystemCodexAccount = nil
        settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
        defer {
            settings._test_codexReconciliationEnvironment = nil
            try? FileManager.default.removeItem(at: isolatedHome)
        }

        let store = self.makeUsageStore(settings: settings)
        var loaderCalled = false
        store._test_codexCreditsLoaderOverride = {
            loaderCalled = true
            return self.credits(remaining: 1)
        }
        defer { store._test_codexCreditsLoaderOverride = nil }

        let startedAt = ContinuousClock.now
        await store.refreshCreditsIfNeeded(minimumSnapshotUpdatedAt: Date())
        let elapsed = startedAt.duration(to: .now)

        #expect(loaderCalled == false)
        #expect(elapsed < .seconds(3))
    }

    @Test
    func `stale dashboard apply is discarded after account switch`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-dashboard")
        settings.refreshFrequency = .manual
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        let expectedGuard = store.currentCodexAccountScopedRefreshGuard()
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")

        await store.applyOpenAIDashboard(
            self.dashboard(email: "alpha@example.com", creditsRemaining: 11, usedPercent: 35),
            targetEmail: "alpha@example.com",
            expectedGuard: expectedGuard,
            allowCodexUsageBackfill: true)

        #expect(store.openAIDashboard == nil)
        #expect(store.snapshots[.codex] == nil)
        #expect(store.credits == nil)
    }

    @Test
    func `dashboard refresh fail closes when live identity is unresolved without trusted continuity`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-dashboard-unresolved-fail-closed")
        let isolatedHome = FileManager.default.temporaryDirectory
            .appendingPathComponent("codex-openai-web-unresolved-fail-closed-\(UUID().uuidString)", isDirectory: true)
        try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .auto
        settings._test_liveSystemCodexAccount = nil
        settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
        defer {
            settings._test_codexReconciliationEnvironment = nil
            try? FileManager.default.removeItem(at: isolatedHome)
        }

        let store = self.makeUsageStore(settings: settings)
        store.lastKnownLiveSystemCodexEmail = nil
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            self.dashboard(email: "seeded@example.com", creditsRemaining: 33, usedPercent: 12)
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let expectedGuard = store.currentCodexAccountScopedRefreshGuard()
        #expect(expectedGuard.source == .liveSystem)
        #expect(expectedGuard.identity == .unresolved)
        #expect(expectedGuard.accountKey == nil)

        await store.refreshOpenAIDashboardIfNeeded(force: true, expectedGuard: expectedGuard)

        #expect(store.openAIDashboard == nil)
        #expect(store.lastOpenAIDashboardSnapshot == nil)
        #expect(store.snapshots[.codex] == nil)
        #expect(store.credits == nil)
        #expect(store.openAIDashboardRequiresLogin == true)
        #expect(store.lastOpenAIDashboardError?.contains("could not be verified") == true)
    }

    @Test
    func `dashboard refresh attaches for unresolved live identity with trusted non dashboard continuity`() async {
        let settings = self.makeSettingsStore(
            suite: "CodexAccountScopedRefreshTests-dashboard-unresolved-trusted-continuity")
        let isolatedHome = FileManager.default.temporaryDirectory
            .appendingPathComponent("codex-openai-web-unresolved-trusted-\(UUID().uuidString)", isDirectory: true)
        try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .auto
        settings._test_liveSystemCodexAccount = nil
        settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
        defer {
            settings._test_codexReconciliationEnvironment = nil
            try? FileManager.default.removeItem(at: isolatedHome)
        }

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(
            self.codexSnapshot(email: "trusted@example.com", usedPercent: 12),
            provider: .codex)
        store.lastSourceLabels[.codex] = "codex-cli"
        let trustedGuard = CodexAccountScopedRefreshGuard(
            source: .liveSystem,
            identity: .emailOnly(normalizedEmail: "trusted@example.com"),
            accountKey: "trusted@example.com")
        store.lastCodexUsagePublicationGuard = trustedGuard
        store.lastCodexAccountScopedRefreshGuard = trustedGuard
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            self.dashboard(email: "trusted@example.com", creditsRemaining: 33, usedPercent: 12)
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let expectedGuard = store.currentCodexOpenAIWebRefreshGuard()
        #expect(expectedGuard.identity == .unresolved)

        await store.refreshOpenAIDashboardIfNeeded(force: true, expectedGuard: expectedGuard)

        #expect(store.openAIDashboard?.signedInEmail == "trusted@example.com")
        #expect(store.snapshots[.codex]?.accountEmail(for: .codex) == "trusted@example.com")
        #expect(store.lastSourceLabels[.codex] == "codex-cli")
        #expect(store.credits?.remaining == 33)
        #expect(store.lastCreditsSource == .dashboardWeb)
        #expect(store.lastCreditsSnapshotAccountKey == "trusted@example.com")
        #expect(
            store.lastCodexAccountScopedRefreshGuard?.identity ==
                .emailOnly(normalizedEmail: "trusted@example.com"))
        #expect(store.lastCodexAccountScopedRefreshGuard?.accountKey == "trusted@example.com")
    }

    @Test
    func `no usable codex usage does not block weekly only dashboard backfill`() async {
        let settings = self.makeSettingsStore(
            suite: "CodexAccountScopedRefreshTests-no-usable-usage-weekly-dashboard-backfill")
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .auto
        settings._test_liveSystemCodexAccount = self.liveAccount(
            email: "weekly@example.com",
            identity: .providerAccount(id: "acct-weekly"))

        let store = self.makeUsageStore(settings: settings)
        self.installFailingCodexProvider(on: store, error: UsageError.noRateLimitsFound)

        await store.refreshProvider(.codex, allowDisabled: true)

        #expect(store.snapshots[.codex] == nil)

        await store.applyOpenAIDashboard(
            OpenAIDashboardSnapshot(
                signedInEmail: "weekly@example.com",
                codeReviewRemainingPercent: 88,
                creditEvents: [],
                dailyBreakdown: [],
                usageBreakdown: [],
                creditsPurchaseURL: nil,
                primaryLimit: nil,
                secondaryLimit: RateWindow(
                    usedPercent: 27,
                    windowMinutes: 10080,
                    resetsAt: Date(timeIntervalSince1970: 1_775_000_000),
                    resetDescription: "next week"),
                creditsRemaining: 14,
                accountPlan: "Pro",
                updatedAt: Date(timeIntervalSince1970: 1_774_900_000)),
            targetEmail: "weekly@example.com",
            allowCodexUsageBackfill: true)

        #expect(store.openAIDashboard?.signedInEmail == "weekly@example.com")
        #expect(store.snapshots[.codex]?.primary == nil)
        #expect(store.snapshots[.codex]?.secondary?.usedPercent == 27)
        #expect(store.snapshots[.codex]?.secondary?.windowMinutes == 10080)
        #expect(store.lastSourceLabels[.codex] == "openai-web")
    }

    @Test
    func `dashboard display only keeps dashboard visible and clears dashboard derived data`() async throws {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-dashboard-display-only-cleanup")
        let managedHome = FileManager.default.temporaryDirectory
            .appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: managedHome) }
        try Self.writeCodexAuthFile(
            homeURL: managedHome,
            email: "shared@example.com",
            plan: "pro",
            accountId: "acct-managed")

        let managedAccount = ManagedCodexAccount(
            id: UUID(),
            email: "shared@example.com",
            managedHomePath: managedHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let managedStoreURL = try self.makeManagedAccountStoreURL(accounts: [managedAccount])
        defer {
            settings._test_managedCodexAccountStoreURL = nil
            try? FileManager.default.removeItem(at: managedStoreURL)
            OpenAIDashboardCacheStore.clear()
        }

        settings.refreshFrequency = .manual
        settings.codexCookieSource = .auto
        settings._test_managedCodexAccountStoreURL = managedStoreURL
        settings._test_liveSystemCodexAccount = self.liveAccount(
            email: "shared@example.com",
            identity: .emailOnly(normalizedEmail: "shared@example.com"))
        settings.codexActiveSource = .liveSystem

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(self.codexSnapshot(email: "shared@example.com", usedPercent: 20), provider: .codex)
        store.lastSourceLabels[.codex] = "openai-web"
        let staleCredits = self.credits(remaining: 20)
        store.credits = staleCredits
        store.lastCreditsSnapshot = staleCredits
        store.lastCreditsSnapshotAccountKey = "shared@example.com"
        store.lastCreditsSource = .dashboardWeb
        OpenAIDashboardCacheStore.save(OpenAIDashboardCache(
            accountEmail: "shared@example.com",
            snapshot: self.dashboard(email: "shared@example.com", creditsRemaining: 20, usedPercent: 20)))

        await store.applyOpenAIDashboard(
            self.dashboard(email: "shared@example.com", creditsRemaining: 9, usedPercent: 35),
            targetEmail: "shared@example.com")

        #expect(store.openAIDashboard?.signedInEmail == "shared@example.com")
        #expect(store.lastOpenAIDashboardSnapshot?.signedInEmail == "shared@example.com")
        #expect(store.snapshots[.codex] == nil)
        #expect(store.lastSourceLabels[.codex] == nil)
        #expect(store.credits == nil)
        #expect(store.lastCreditsSource == .none)
        #expect(OpenAIDashboardCacheStore.load() == nil)
    }

    @Test
    func `dashboard downgrade from real attach to display only retires owned state immediately`() async throws {
        OpenAIDashboardCacheStore.clear()
        defer { OpenAIDashboardCacheStore.clear() }

        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-dashboard-downgrade")
        let managedHome = FileManager.default.temporaryDirectory
            .appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: managedHome) }
        try Self.writeCodexAuthFile(
            homeURL: managedHome,
            email: "shared@example.com",
            plan: "pro",
            accountId: "acct-managed")

        let managedAccount = ManagedCodexAccount(
            id: UUID(),
            email: "shared@example.com",
            managedHomePath: managedHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let managedStoreURL = try self.makeManagedAccountStoreURL(accounts: [managedAccount])
        defer {
            settings._test_managedCodexAccountStoreURL = nil
            try? FileManager.default.removeItem(at: managedStoreURL)
        }

        settings.refreshFrequency = .manual
        settings.codexCookieSource = .auto
        settings._test_liveSystemCodexAccount = self.liveAccount(
            email: "shared@example.com",
            identity: .emailOnly(normalizedEmail: "shared@example.com"))
        settings.codexActiveSource = .liveSystem

        let store = self.makeUsageStore(settings: settings)
        await store.applyOpenAIDashboard(
            self.dashboard(email: "shared@example.com", creditsRemaining: 20, usedPercent: 20),
            targetEmail: "shared@example.com")

        #expect(store.openAIDashboard?.signedInEmail == "shared@example.com")
        #expect(store.snapshots[.codex]?.accountEmail(for: .codex) == "shared@example.com")
        #expect(store.lastSourceLabels[.codex] == "openai-web")
        #expect(store.credits?.remaining == 20)
        #expect(store.lastCreditsSource == .dashboardWeb)
        #expect(OpenAIDashboardCacheStore.load()?.accountEmail == "shared@example.com")

        settings._test_managedCodexAccountStoreURL = managedStoreURL

        await store.applyOpenAIDashboard(
            self.dashboard(email: "shared@example.com", creditsRemaining: 9, usedPercent: 35),
            targetEmail: "shared@example.com")

        #expect(store.openAIDashboard?.signedInEmail == "shared@example.com")
        #expect(store.lastOpenAIDashboardSnapshot?.signedInEmail == "shared@example.com")
        #expect(store.snapshots[.codex] == nil)
        #expect(store.lastSourceLabels[.codex] == nil)
        #expect(store.credits == nil)
        #expect(store.lastCreditsSource == .none)
        #expect(OpenAIDashboardCacheStore.load() == nil)
    }

    @Test
    func `dashboard refresh rejects stale completion during live account reconciliation lag`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-dashboard-reject-stale-live-lag")
        let isolatedHome = FileManager.default.temporaryDirectory
            .appendingPathComponent("codex-openai-web-stale-live-lag-\(UUID().uuidString)", isDirectory: true)
        try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .auto
        settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
        settings.codexActiveSource = .liveSystem
        defer {
            settings._test_codexReconciliationEnvironment = nil
            try? FileManager.default.removeItem(at: isolatedHome)
        }

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(self.codexSnapshot(email: "alpha@example.com", usedPercent: 12), provider: .codex)

        let expectedGuard = store.currentCodexOpenAIWebRefreshGuard()
        #expect(expectedGuard.accountKey == nil)

        store._setSnapshotForTesting(self.codexSnapshot(email: "beta@example.com", usedPercent: 18), provider: .codex)

        await store.applyOpenAIDashboard(
            self.dashboard(email: "alpha@example.com", creditsRemaining: 40, usedPercent: 20),
            targetEmail: nil,
            expectedGuard: expectedGuard,
            allowCodexUsageBackfill: true)

        #expect(store.openAIDashboard == nil)
        #expect(store.credits == nil)
        #expect(store.snapshots[.codex]?.accountEmail(for: .codex) == "beta@example.com")
    }

    @Test
    func `default dashboard refresh path discards stale completion after account switch`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-dashboard-guard")
        settings.refreshFrequency = .manual
        settings.openAIWebAccessEnabled = true
        settings.codexCookieSource = .auto
        settings.statusChecksEnabled = false
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        self.installImmediateCodexProvider(
            on: store,
            snapshot: self.codexSnapshot(email: "alpha@example.com", usedPercent: 18))
        await store.refresh()

        let dashboardBlocker = BlockingOpenAIDashboardLoader()
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            try await dashboardBlocker.awaitResult()
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let refreshTask = Task { await store.refresh() }
        await dashboardBlocker.waitUntilStarted()

        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")
        store._setSnapshotForTesting(self.codexSnapshot(email: "beta@example.com", usedPercent: 7), provider: .codex)
        store.openAIDashboard = nil
        store.credits = nil

        await dashboardBlocker.resume(with: .success(
            self.dashboard(email: "alpha@example.com", creditsRemaining: 44, usedPercent: 21)))
        await refreshTask.value

        #expect(store.openAIDashboard == nil)
        #expect(store.credits == nil)
        #expect(store.snapshots[.codex]?.accountEmail(for: .codex) == "beta@example.com")
    }

    @Test
    func `same email provider account switch discards stale dashboard completion`() async {
        let settings = self
            .makeSettingsStore(suite: "CodexAccountScopedRefreshTests-dashboard-same-email-provider-account")
        settings.refreshFrequency = .manual
        settings._test_liveSystemCodexAccount = self.liveAccount(
            email: "alpha@example.com",
            identity: .providerAccount(id: "acct-alpha"))

        let store = self.makeUsageStore(settings: settings)
        let expectedGuard = store.currentCodexOpenAIWebRefreshGuard()
        #expect(expectedGuard.identity == .providerAccount(id: "acct-alpha"))

        settings._test_liveSystemCodexAccount = self.liveAccount(
            email: "alpha@example.com",
            identity: .providerAccount(id: "acct-beta"))

        await store.applyOpenAIDashboard(
            self.dashboard(email: "alpha@example.com", creditsRemaining: 11, usedPercent: 35),
            targetEmail: "alpha@example.com",
            expectedGuard: expectedGuard,
            allowCodexUsageBackfill: true)

        #expect(store.openAIDashboard == nil)
        #expect(store.snapshots[.codex] == nil)
        #expect(store.credits == nil)
    }

    @Test
    func `live switch invalidates stale codex state even when only last known live email remains`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-invalidate-with-stale-last-known")
        let isolatedHome = FileManager.default.temporaryDirectory
            .appendingPathComponent("codex-invalidate-stale-last-known-\(UUID().uuidString)", isDirectory: true)
        try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .auto
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")
        settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
        defer {
            settings._test_codexReconciliationEnvironment = nil
            try? FileManager.default.removeItem(at: isolatedHome)
        }

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(self.codexSnapshot(email: "alpha@example.com", usedPercent: 10), provider: .codex)
        store.credits = self.credits(remaining: 12)
        store.lastCreditsSnapshot = self.credits(remaining: 12)
        store.lastCreditsSnapshotAccountKey = "alpha@example.com"
        store.openAIDashboard = self.dashboard(email: "alpha@example.com", creditsRemaining: 12, usedPercent: 20)
        store.lastOpenAIDashboardSnapshot = store.openAIDashboard
        store.lastKnownLiveSystemCodexEmail = "alpha@example.com"
        store.lastCodexAccountScopedRefreshGuard = store.currentCodexAccountScopedRefreshGuard(
            preferCurrentSnapshot: false,
            allowLastKnownLiveFallback: true)

        settings._test_liveSystemCodexAccount = nil
        store.snapshots.removeValue(forKey: .codex)

        let didInvalidate = store.prepareCodexAccountScopedRefreshIfNeeded()
        await store.widgetSnapshotPersistTask?.value

        #expect(didInvalidate)
        #expect(store.snapshots[.codex] == nil)
        #expect(store.credits == nil)
        #expect(store.openAIDashboard == nil)
        #expect(store.lastCodexAccountScopedRefreshGuard?.accountKey == nil)
    }

    @Test
    func `codex account refresh persists widget snapshots on invalidation and completion`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-widgets")
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .off
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(self.codexSnapshot(email: "alpha@example.com", usedPercent: 18), provider: .codex)
        store.lastCodexAccountScopedRefreshGuard = store
            .currentCodexAccountScopedRefreshGuard(preferCurrentSnapshot: false)

        let blocker = BlockingCodexFetchStrategy()
        self.installBlockingCodexProvider(on: store, blocker: blocker)
        store._test_codexCreditsLoaderOverride = { self.credits(remaining: 77) }
        defer { store._test_codexCreditsLoaderOverride = nil }

        var widgetSnapshots: [WidgetSnapshot] = []
        store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
        defer { store._test_widgetSnapshotSaveOverride = nil }

        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")
        let refreshTask = Task { await store.refreshCodexAccountScopedState(allowDisabled: true) }
        await blocker.waitUntilStarted()
        await blocker.resume(with: .success(self.codexSnapshot(email: "beta@example.com", usedPercent: 8)))
        await refreshTask.value
        await store.widgetSnapshotPersistTask?.value

        #expect(widgetSnapshots.count == 2)
        #expect(widgetSnapshots[0].entries.contains(where: { $0.provider == .codex }) == false)
        #expect(widgetSnapshots[1].entries.first { $0.provider == .codex }?.creditsRemaining == 77)
    }

    @Test
    func `widget snapshot saves stay ordered across codex account invalidation and completion`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-widget-order")
        settings.refreshFrequency = .manual

        let store = self.makeUsageStore(settings: settings)
        let saver = BlockingWidgetSnapshotSaver()
        store._test_widgetSnapshotSaveOverride = { snapshot in
            await saver.save(snapshot)
        }
        defer { store._test_widgetSnapshotSaveOverride = nil }

        store.persistWidgetSnapshot(reason: "codex-account-invalidate")
        await saver.waitUntilStarted(count: 1)
        #expect(await saver.startedCount() == 1)

        store._setSnapshotForTesting(self.codexSnapshot(email: "beta@example.com", usedPercent: 8), provider: .codex)
        store.credits = self.credits(remaining: 77)
        store.persistWidgetSnapshot(reason: "codex-account-refresh")

        try? await Task.sleep(nanoseconds: 50_000_000)
        #expect(await saver.startedCount() == 1)

        await saver.resumeNext()
        await saver.waitUntilStarted(count: 2)
        await saver.resumeNext()
        await store.widgetSnapshotPersistTask?.value

        let snapshots = await saver.savedSnapshots()
        #expect(snapshots.count == 2)
        #expect(snapshots[0].entries.contains(where: { $0.provider == .codex }) == false)
        #expect(snapshots[1].entries.first { $0.provider == .codex }?.creditsRemaining == 77)
    }

    @Test
    func `widget snapshot excludes display only dashboard code review`() async throws {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-widget-display-only-dashboard")
        settings.refreshFrequency = .manual

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(self.codexSnapshot(email: "alpha@example.com", usedPercent: 18), provider: .codex)
        store.credits = CreditsSnapshot(remaining: 12, events: [], updatedAt: Date())
        store.openAIDashboard = self.dashboard(
            email: "alpha@example.com",
            creditsRemaining: 12,
            usedPercent: 20)
        store.openAIDashboardAttachmentAuthorized = false

        var widgetSnapshots: [WidgetSnapshot] = []
        store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
        defer { store._test_widgetSnapshotSaveOverride = nil }

        store.persistWidgetSnapshot(reason: "display-only-dashboard")
        await store.widgetSnapshotPersistTask?.value

        let codexEntry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .codex })
        #expect(codexEntry.creditsRemaining == nil)
        #expect(codexEntry.codeReviewRemainingPercent == nil)
    }

    @Test
    func `widget snapshot includes attached dashboard code review`() async throws {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-widget-attached-dashboard")
        settings.refreshFrequency = .manual

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(self.codexSnapshot(email: "alpha@example.com", usedPercent: 18), provider: .codex)
        store.openAIDashboard = self.dashboard(
            email: "alpha@example.com",
            creditsRemaining: 12,
            usedPercent: 20)
        store.openAIDashboardAttachmentAuthorized = true

        var widgetSnapshots: [WidgetSnapshot] = []
        store._test_widgetSnapshotSaveOverride = { widgetSnapshots.append($0) }
        defer { store._test_widgetSnapshotSaveOverride = nil }

        store.persistWidgetSnapshot(reason: "attached-dashboard")
        await store.widgetSnapshotPersistTask?.value

        let codexEntry = try #require(widgetSnapshots.last?.entries.first { $0.provider == .codex })
        #expect(codexEntry.codeReviewRemainingPercent == 88)
    }

    @Test
    func `codex account refresh reports usage and credits phases before completion`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-phases")
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .off
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "alpha@example.com")

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(self.codexSnapshot(email: "alpha@example.com", usedPercent: 18), provider: .codex)
        store.lastCodexAccountScopedRefreshGuard = store
            .currentCodexAccountScopedRefreshGuard(preferCurrentSnapshot: false)

        let blocker = BlockingCodexFetchStrategy()
        self.installBlockingCodexProvider(on: store, blocker: blocker)
        store._test_codexCreditsLoaderOverride = { self.credits(remaining: 77) }
        defer { store._test_codexCreditsLoaderOverride = nil }

        var phases: [CodexAccountScopedRefreshPhase] = []
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "beta@example.com")

        let refreshTask = Task {
            await store.refreshCodexAccountScopedState(
                allowDisabled: true,
                phaseDidChange: { phases.append($0) })
        }

        await blocker.waitUntilStarted()
        #expect(phases == [.invalidated])

        await blocker.resume(with: .success(self.codexSnapshot(email: "beta@example.com", usedPercent: 8)))
        await refreshTask.value

        #expect(phases == [.invalidated, .usage, .credits, .completed])
    }

    @Test
    func `refresh loads credits when codex email is discovered by usage in the same cycle`() async {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-refresh-credits")
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .off

        let store = self.makeUsageStore(settings: settings)
        let blocker = BlockingCodexFetchStrategy()
        self.installBlockingCodexProvider(on: store, blocker: blocker)
        store._test_codexCreditsLoaderOverride = { self.credits(remaining: 55) }
        defer { store._test_codexCreditsLoaderOverride = nil }

        let refreshTask = Task { await store.refresh() }
        await blocker.waitUntilStarted()
        await blocker.resume(with: .success(self.codexSnapshot(email: "alpha@example.com", usedPercent: 12)))
        await refreshTask.value
        #expect(store.lastCodexAccountScopedRefreshGuard?.accountKey == "alpha@example.com")

        await store.creditsRefreshTask?.value

        #expect(store.credits?.remaining == 55)
        #expect(store.lastCreditsSource == .api)
    }

    @Test
    func `settings codex account selection refreshes credits on the first switch`() async throws {
        let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-settings-selection")
        settings.refreshFrequency = .manual
        settings.codexCookieSource = .off
        let managedAccountID = try #require(UUID(uuidString: "AAAAAAAA-BBBB-CCCC-DDDD-111111111111"))
        let managedAccount = ManagedCodexAccount(
            id: managedAccountID,
            email: "managed@example.com",
            managedHomePath: "/tmp/managed-home",
            createdAt: 1,
            updatedAt: 2,
            lastAuthenticatedAt: 2)
        let storeURL = try self.makeManagedAccountStoreURL(accounts: [managedAccount])
        defer {
            settings._test_managedCodexAccountStoreURL = nil
            settings._test_liveSystemCodexAccount = nil
            try? FileManager.default.removeItem(at: storeURL)
        }

        settings._test_managedCodexAccountStoreURL = storeURL
        settings._test_liveSystemCodexAccount = self.liveAccount(email: "live@example.com")

        let store = self.makeUsageStore(settings: settings)
        store._setSnapshotForTesting(self.codexSnapshot(email: "live@example.com", usedPercent: 30), provider: .codex)
        store.lastCodexAccountScopedRefreshGuard = store
            .currentCodexAccountScopedRefreshGuard(preferCurrentSnapshot: false)
        self.installImmediateCodexProvider(
            on: store,
            snapshot: self.codexSnapshot(email: "managed@example.com", usedPercent: 9))
        store._test_codexCreditsLoaderOverride = { self.credits(remaining: 55) }
        defer { store._test_codexCreditsLoaderOverride = nil }

        let pane = ProvidersPane(settings: settings, store: store)
        await pane._test_selectCodexVisibleAccount(id: "managed@example.com")

        #expect(settings.codexActiveSource == .managedAccount(id: managedAccountID))
        #expect(store.snapshots[.codex]?.accountEmail(for: .codex) == "managed@example.com")
        #expect(store.credits?.remaining == 55)
    }
}
