import AppKit
import CodexBarCore
import Observation
import ServiceManagement

enum RefreshFrequency: String, CaseIterable, Identifiable {
    case manual
    case oneMinute
    case twoMinutes
    case fiveMinutes
    case fifteenMinutes
    case thirtyMinutes
    case adaptive
    /// Adaptive plus consent-gated local agent activity. Kept after plain Adaptive so the
    /// privacy-preserving mode remains the first adaptive choice.
    case adaptiveAgentAware

    var id: String {
        self.rawValue
    }

    /// nil for `.manual` (no timer) and adaptive modes (delay is computed per tick by
    /// `AdaptiveRefreshPolicy`, not a fixed interval).
    var seconds: TimeInterval? {
        switch self {
        case .manual: nil
        case .oneMinute: 60
        case .twoMinutes: 120
        case .fiveMinutes: 300
        case .fifteenMinutes: 900
        case .thirtyMinutes: 1800
        case .adaptive, .adaptiveAgentAware: nil
        }
    }

    var label: String {
        switch self {
        case .manual: L("refresh_manual")
        case .oneMinute: L("refresh_1min")
        case .twoMinutes: L("refresh_2min")
        case .fiveMinutes: L("refresh_5min")
        case .fifteenMinutes: L("refresh_15min")
        case .thirtyMinutes: L("refresh_30min")
        case .adaptive: L("refresh_adaptive")
        case .adaptiveAgentAware: L("refresh_adaptive_agent_aware")
        }
    }

    var usesAdaptivePolicy: Bool {
        self == .adaptive || self == .adaptiveAgentAware
    }
}

enum AdaptiveActivityScanConsent: String, Sendable {
    case undecided
    case allowed
    case declined
}

enum MenuBarMetricPreference: String, CaseIterable, Identifiable {
    case automatic
    case primary
    case secondary
    case primaryAndSecondary
    case tertiary
    case extraUsage
    case average
    case monthlyPlan

    var id: String {
        self.rawValue
    }

    var label: String {
        switch self {
        case .automatic: L("metric_pref_automatic")
        case .primary: L("metric_pref_primary")
        case .secondary: L("metric_pref_secondary")
        case .primaryAndSecondary: "\(L("metric_pref_primary")) + \(L("metric_pref_secondary"))"
        case .tertiary: L("metric_pref_tertiary")
        case .extraUsage: L("metric_pref_extra_usage")
        case .average: L("metric_pref_average")
        case .monthlyPlan: L("metric_mistral_monthly_plan")
        }
    }
}

enum KiroMenuBarDisplayMode: String, CaseIterable, Identifiable {
    case automatic
    case hidden
    case creditsLeft
    case percentLeft
    case creditsAndPercent
    case usedAndTotal
    case overageCreditsWhenExhausted
    case overageCostWhenExhausted
    case overageCreditsAndCostWhenExhausted

    var id: String {
        self.rawValue
    }

    var label: String {
        switch self {
        case .automatic: L("Automatic")
        case .hidden: L("Hidden")
        case .creditsLeft: L("Credits left")
        case .percentLeft: L("Percent left")
        case .creditsAndPercent: L("Credits + percent")
        case .usedAndTotal: L("Used / total")
        case .overageCreditsWhenExhausted: L("Overage credits at zero")
        case .overageCostWhenExhausted: L("Overage cost at zero")
        case .overageCreditsAndCostWhenExhausted: L("Overage credits + cost at zero")
        }
    }
}

enum MultiAccountMenuLayout: String, CaseIterable, Identifiable {
    case segmented
    case stacked

    var id: String {
        self.rawValue
    }

    var label: String {
        switch self {
        case .segmented: L("multi_account_layout_segmented")
        case .stacked: L("multi_account_layout_stacked")
        }
    }
}

enum CostSummaryDisplayStyle: String, CaseIterable, Identifiable {
    case inlineSummary
    case costSubmenu
    case both

    var id: String {
        self.rawValue
    }

    var label: String {
        switch self {
        case .inlineSummary: L("cost_summary_style_inline")
        case .costSubmenu: L("cost_summary_style_submenu")
        case .both: L("cost_summary_style_both")
        }
    }

    var helpText: String {
        switch self {
        case .inlineSummary: L("cost_summary_style_inline_help")
        case .costSubmenu: L("cost_summary_style_submenu_help")
        case .both: L("cost_summary_style_both_help")
        }
    }

    var showsInlineSummary: Bool {
        self != .costSubmenu
    }

    var showsCostSubmenu: Bool {
        self != .inlineSummary
    }
}

struct CachedCodexAccountReconciliationSnapshot {
    let activeSource: CodexActiveSource
    let loadedAt: Date
    let snapshot: CodexAccountReconciliationSnapshot
}

struct CachedCodexAccountMenuProjection: Equatable {
    let activeSource: CodexActiveSource
    let loadedAt: Date
    let projection: CodexVisibleAccountProjection
}

enum CodexAccountMenuProjectionRevalidationResult: Equatable {
    case skipped
    case discarded
    case unchanged
    case updated
}

@MainActor
@Observable
final class SettingsStore {
    static let sharedDefaults = AppGroupSupport.sharedDefaults()
    static let mergedOverviewProviderLimit = 6
    static let productionCodexAccountReconciliationSnapshotCacheInterval: TimeInterval = 2
    static let isRunningTests: Bool = {
        let env = ProcessInfo.processInfo.environment
        if env["XCTestConfigurationFilePath"] != nil {
            return true
        }
        if env["TESTING_LIBRARY_VERSION"] != nil {
            return true
        }
        if env["SWIFT_TESTING"] != nil {
            return true
        }
        return NSClassFromString("XCTestCase") != nil
    }()

    #if DEBUG
    static var codexAccountReconciliationSnapshotCacheIntervalOverrideForTesting: TimeInterval?
    #endif

    @ObservationIgnored let userDefaults: UserDefaults
    @ObservationIgnored let configStore: CodexBarConfigStore
    @ObservationIgnored let antigravityOAuthCredentialsStore: AntigravityOAuthCredentialsStore
    @ObservationIgnored var config: CodexBarConfig
    @ObservationIgnored var configPersistTask: Task<Void, Never>?
    @ObservationIgnored var configFileWatcher: ConfigFileWatcher?
    @ObservationIgnored var configLoading = false
    @ObservationIgnored var tokenAccountsLoaded = false
    @ObservationIgnored var cachedCodexAccountReconciliationSnapshot:
        CachedCodexAccountReconciliationSnapshot?
    @ObservationIgnored var cachedCodexAccountMenuProjection: CachedCodexAccountMenuProjection?
    @ObservationIgnored var codexAccountReconciliationGeneration: UInt = 0
    #if DEBUG
    @ObservationIgnored var _test_codexAccountSnapshotLoader:
        (@Sendable (CodexActiveSource) -> CodexAccountReconciliationSnapshot)?
    #endif
    @ObservationIgnored var mergedMenuLastSelectedWasOverviewStorage = false
    @ObservationIgnored var selectedMenuProviderRawStorage: String?
    var defaultsState: SettingsDefaultsState
    var configRevision: Int = 0
    var providerDetailSettingsRevision: Int = 0
    var backgroundWorkSettingsRevision: Int = 0
    var costUsageSettingsRevision: UInt64 = 0
    var providerOrder: [ProviderInstanceID] = []
    var providerEnablement: [ProviderInstanceID: Bool] = [:]
    @ObservationIgnored var providerEnablementRevisions: [ProviderInstanceID: UInt64] = [:]
    @ObservationIgnored var providerConfigRevisions: [ProviderInstanceID: UInt64] = [:]
    @ObservationIgnored var providerConfigFingerprints: [ProviderInstanceID: Data] = [:]

    static func shouldBridgeSharedDefaults(for userDefaults: UserDefaults) -> Bool {
        if !self.isRunningTests {
            return true
        }
        if userDefaults === UserDefaults.standard {
            return true
        }
        if let shared = sharedDefaults, userDefaults === shared {
            return true
        }
        return false
    }

    init(
        userDefaults: UserDefaults = .standard,
        configStore: CodexBarConfigStore = CodexBarConfigStore(),
        zaiTokenStore: any ZaiTokenStoring = KeychainZaiTokenStore(),
        syntheticTokenStore: any SyntheticTokenStoring = KeychainSyntheticTokenStore(),
        codexCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(
            account: "codex-cookie",
            promptKind: .codexCookie),
        claudeCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(
            account: "claude-cookie",
            promptKind: .claudeCookie),
        cursorCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(
            account: "cursor-cookie",
            promptKind: .cursorCookie),
        opencodeCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(
            account: "opencode-cookie",
            promptKind: .opencodeCookie),
        factoryCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(
            account: "factory-cookie",
            promptKind: .factoryCookie),
        minimaxCookieStore: any MiniMaxCookieStoring = KeychainMiniMaxCookieStore(),
        minimaxAPITokenStore: any MiniMaxAPITokenStoring = KeychainMiniMaxAPITokenStore(),
        kimiTokenStore: any KimiTokenStoring = KeychainKimiTokenStore(),
        augmentCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(
            account: "augment-cookie",
            promptKind: .augmentCookie),
        ampCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(
            account: "amp-cookie",
            promptKind: .ampCookie),
        copilotTokenStore: any CopilotTokenStoring = KeychainCopilotTokenStore(),
        tokenAccountStore: any ProviderTokenAccountStoring = FileTokenAccountStore(),
        antigravityOAuthCredentialsStore: AntigravityOAuthCredentialsStore = AntigravityOAuthCredentialsStore(),
        performInitialProviderDetection: Bool = !SettingsStore.isRunningTests)
    {
        if !Self.isRunningTests {
            _ = UserProviderPluginRegistry.refresh()
        }
        // Capture this before app-group/config migrations can create prior-installation state.
        let hadExistingConfig = (try? configStore.load()) != nil
        let hadPreviousInstallationState = hadExistingConfig || Self.hadPreviousAppLaunch(userDefaults: userDefaults)
        let appGroupID = AppGroupSupport.currentGroupID()
        let appGroupMigration: AppGroupSupport.MigrationResult
        if Self.isRunningTests {
            appGroupMigration = AppGroupSupport.migrateLegacyDataIfNeeded(standardDefaults: userDefaults)
        } else {
            Self.scheduleAppGroupMigration()
            appGroupMigration = AppGroupSupport.MigrationResult(status: .targetUnavailable)
        }
        let sharedDefaultsAvailable = Self.sharedDefaults != nil
        if !Self.isRunningTests {
            CodexBarLog.logger(LogCategories.settings).info(
                "App group resolved",
                metadata: [
                    "groupID": appGroupID,
                    "sharedDefaultsAvailable": sharedDefaultsAvailable ? "1" : "0",
                    "migrationStatus": appGroupMigration.status.rawValue,
                    "migratedSnapshot": appGroupMigration.copiedSnapshot ? "1" : "0",
                    "migratedDefaults": "\(appGroupMigration.copiedDefaults)",
                ])
        }

        if userDefaults.object(forKey: "openAIWebAccessEnabled") == nil,
           let legacyOpenAIWebAccess = userDefaults.object(forKey: "openAIWebAccess") as? Bool
        {
            userDefaults.set(legacyOpenAIWebAccess, forKey: "openAIWebAccessEnabled")
        }
        let hasStoredOpenAIWebAccessPreference = userDefaults.object(forKey: "openAIWebAccessEnabled") != nil
        let legacyStores = CodexBarConfigMigrator.LegacyStores(
            zaiTokenStore: zaiTokenStore,
            syntheticTokenStore: syntheticTokenStore,
            codexCookieStore: codexCookieStore,
            claudeCookieStore: claudeCookieStore,
            cursorCookieStore: cursorCookieStore,
            opencodeCookieStore: opencodeCookieStore,
            factoryCookieStore: factoryCookieStore,
            minimaxCookieStore: minimaxCookieStore,
            minimaxAPITokenStore: minimaxAPITokenStore,
            kimiTokenStore: kimiTokenStore,
            augmentCookieStore: augmentCookieStore,
            ampCookieStore: ampCookieStore,
            copilotTokenStore: copilotTokenStore,
            tokenAccountStore: tokenAccountStore)
        let config = CodexBarConfigMigrator.loadOrMigrate(
            configStore: configStore,
            userDefaults: userDefaults,
            stores: legacyStores)
        self.userDefaults = userDefaults
        self.configStore = configStore
        self.antigravityOAuthCredentialsStore = antigravityOAuthCredentialsStore
        self.config = config
        self.configLoading = true
        let defaultsState = Self.loadDefaultsState(
            userDefaults: userDefaults,
            hadPreviousInstallationState: hadPreviousInstallationState)
        self.defaultsState = defaultsState
        self.mergedMenuLastSelectedWasOverviewStorage = defaultsState.mergedMenuLastSelectedWasOverview
        self.selectedMenuProviderRawStorage = defaultsState.selectedMenuProviderRaw
        self.updateProviderState(config: config)
        self.configLoading = false
        CodexBarLog.setFileLoggingEnabled(self.debugFileLoggingEnabled)
        userDefaults.removeObject(forKey: "showCodexUsage")
        userDefaults.removeObject(forKey: "showClaudeUsage")
        LaunchAtLoginManager.setEnabled(self.launchAtLogin)
        if performInitialProviderDetection {
            self.runInitialProviderDetectionIfNeeded()
        }
        self.ensureAlibabaProviderAutoEnabledIfNeeded()
        self.applyTokenCostDefaultIfNeeded()
        if self.claudeUsageDataSource != .cli {
            if Self.isRunningTests {
                self.claudeWebExtrasEnabled = false
            } else {
                self.defaultsState.claudeWebExtrasEnabledRaw = false
            }
        }
        let resolvedOpenAIWebAccessEnabled = if hasStoredOpenAIWebAccessPreference {
            self.defaultsState.openAIWebAccessEnabled
        } else {
            Self.inferredInitialOpenAIWebAccessEnabled(
                config: config,
                hadExistingConfig: hadExistingConfig)
        }
        if Self.isRunningTests {
            self.openAIWebAccessEnabled = resolvedOpenAIWebAccessEnabled
        } else {
            self.defaultsState.openAIWebAccessEnabled = resolvedOpenAIWebAccessEnabled
        }
        KeychainAccessGate.isDisabled = self.debugDisableKeychainAccess
        self.startConfigFileWatcher()
    }

    deinit {
        self.configFileWatcher?.stop()
    }
}

extension SettingsStore {
    private struct NotificationDefaults {
        let statusChecksEnabled: Bool
        let sessionQuotaNotificationsEnabled: Bool
        let predictivePaceWarningNotificationsEnabled: Bool
    }

    private static func scheduleAppGroupMigration() {
        Task.detached(priority: .utility) {
            let result = AppGroupSupport.migrateLegacyDataIfNeeded()
            CodexBarLog.logger(LogCategories.settings).info(
                "App group migration completed",
                metadata: [
                    "migrationStatus": result.status.rawValue,
                    "migratedSnapshot": result.copiedSnapshot ? "1" : "0",
                    "migratedDefaults": "\(result.copiedDefaults)",
                ])
        }
    }

    private static func inferredInitialOpenAIWebAccessEnabled(
        config: CodexBarConfig,
        hadExistingConfig: Bool) -> Bool
    {
        // Provider-specific by design: the legacy OpenAI web-access flag was inferred from Codex's cookie config.
        guard let codex = config.providerConfig(for: .codex) else { return false }
        if let cookieSource = codex.cookieSource {
            return cookieSource.isEnabled
        }
        if codex.sanitizedCookieHeader != nil {
            return true
        }
        return hadExistingConfig
    }

    // swiftlint:disable:next function_body_length
    private static func loadDefaultsState(
        userDefaults: UserDefaults,
        hadPreviousInstallationState: Bool) -> SettingsDefaultsState
    {
        let refreshFrequency = Self.loadRefreshFrequency(
            userDefaults: userDefaults,
            hadPreviousInstallationState: hadPreviousInstallationState)
        let adaptiveActivityScanConsent = Self.loadAdaptiveActivityScanConsent(userDefaults: userDefaults)
        let refreshAllProvidersOnMenuOpen = userDefaults.object(
            forKey: "refreshAllProvidersOnMenuOpen") as? Bool ?? false
        let launchAtLogin = userDefaults.object(forKey: "launchAtLogin") as? Bool ?? false
        let debugMenuEnabled = userDefaults.object(forKey: "debugMenuEnabled") as? Bool ?? false
        let debugDisableKeychainAccess = Self.loadDebugDisableKeychainAccess(userDefaults: userDefaults)
        let debugFileLoggingEnabled = userDefaults.object(forKey: "debugFileLoggingEnabled") as? Bool ?? false
        let debugLogLevelRaw = userDefaults.string(forKey: "debugLogLevel") ?? CodexBarLog.Level.verbose.rawValue
        if Self.isRunningTests, userDefaults.string(forKey: "debugLogLevel") == nil {
            userDefaults.set(debugLogLevelRaw, forKey: "debugLogLevel")
        }
        let debugLoadingPatternRaw = userDefaults.string(forKey: "debugLoadingPattern")
        let debugKeepCLISessionsAlive = userDefaults.object(forKey: "debugKeepCLISessionsAlive") as? Bool ?? false
        let notificationDefaults = Self.loadNotificationDefaults(userDefaults: userDefaults)
        let quotaWarnings = Self.loadQuotaWarningDefaults(userDefaults: userDefaults)
        let quotaWarningMarkersVisibleDefault = userDefaults.object(forKey: "quotaWarningMarkersVisible") as? Bool
        let quotaWarningMarkersVisible = quotaWarningMarkersVisibleDefault ?? true
        if Self.isRunningTests, quotaWarningMarkersVisibleDefault == nil {
            userDefaults.set(true, forKey: "quotaWarningMarkersVisible")
        }
        let weeklyProgressWorkDays = userDefaults.object(forKey: "weeklyProgressWorkDays") as? Int
        let usageBarsShowUsed = userDefaults.object(forKey: "usageBarsShowUsed") as? Bool ?? false
        let resetTimesShowAbsolute = userDefaults.object(forKey: "resetTimesShowAbsolute") as? Bool ?? false
        let providerChangelogLinksEnabled = userDefaults.object(
            forKey: "providerChangelogLinksEnabled") as? Bool ?? false
        let menuBarShowsBrandIconWithPercent = userDefaults.object(
            forKey: "menuBarShowsBrandIconWithPercent") as? Bool ?? false
        let menuBarHidesCritters = userDefaults.object(forKey: "menuBarHidesCritters") as? Bool ?? false
        let menuBarHighContrastOnInactiveDisplays = userDefaults.object(
            forKey: "menuBarHighContrastOnInactiveDisplays") as? Bool ?? false
        let menuBarDisplayModeRaw = userDefaults.string(forKey: "menuBarDisplayMode")
            ?? MenuBarDisplayMode.percent.rawValue
        let menuBarShowsResetTimeWhenExhausted = userDefaults.object(
            forKey: "menuBarShowsResetTimeWhenExhausted") as? Bool ?? false
        let kiroMenuBarDisplayModeRaw = userDefaults.string(forKey: "kiroMenuBarDisplayMode")
            ?? KiroMenuBarDisplayMode.automatic.rawValue
        let historicalTrackingEnabled = userDefaults.object(forKey: "historicalTrackingEnabled") as? Bool ?? false
        let multiAccountMenuLayoutRaw = Self.loadMultiAccountMenuLayoutRaw(userDefaults: userDefaults)
        let resolvedPreferences = Self.loadMenuBarMetricPreferences(userDefaults: userDefaults)
        let storedMenuBarLayout = Self.loadMenuBarLayout(userDefaults: userDefaults, key: "menuBarLayout")
        let menuBarLayoutOverridesRaw = Self.loadMenuBarLayoutOverrides(userDefaults: userDefaults)
        let menuBarLayoutSizeRaw = userDefaults.string(forKey: "menuBarLayoutSize")
            ?? MenuBarLayoutSize.regular.rawValue
        let menuBarLayoutGapRaw = userDefaults.string(forKey: "menuBarLayoutGap")
            ?? MenuBarLayoutGap.regular.rawValue
        let copilotBudgetExtrasEnabled = userDefaults.object(forKey: "copilotBudgetExtrasEnabled") as? Bool ?? false
        let copilotIconSecondaryWindowIDRaw = Self.loadCopilotIconSecondaryWindowIDRaw(userDefaults: userDefaults)
        let costUsageEnabled = userDefaults.object(forKey: "tokenCostUsageEnabled") as? Bool ?? false
        let codexLocalSessionCostLedgerEnabled = userDefaults.object(
            forKey: "codexLocalSessionCostLedgerEnabled") as? Bool ?? false
        let rawCostUsageHistoryDays = userDefaults.object(forKey: "tokenCostUsageHistoryDays") as? Int ?? 30
        let costUsageHistoryDays = max(1, min(365, rawCostUsageHistoryDays))
        let costComparisonPeriodsEnabled = userDefaults.object(
            forKey: "costComparisonPeriodsEnabled") as? Bool ?? false
        let costSummaryDisplayStyleRaw = Self.loadCostSummaryDisplayStyleRaw(
            userDefaults: userDefaults,
            costUsageEnabled: costUsageEnabled)
        let hidePersonalInfo = userDefaults.object(forKey: "hidePersonalInfo") as? Bool ?? false
        let randomBlinkEnabled = userDefaults.object(forKey: "randomBlinkEnabled") as? Bool ?? false
        let confettiOnReset = Self.loadConfettiOnResetDefaults(userDefaults: userDefaults)
        let menuBarShowsHighestUsage = userDefaults.object(forKey: "menuBarShowsHighestUsage") as? Bool ?? false
        let claudeOAuthKeychainReadStrategyRaw = Self.loadClaudeOAuthKeychainReadStrategyRaw(userDefaults: userDefaults)
        let claudeOAuthKeychainPromptModeRaw = userDefaults.string(forKey: "claudeOAuthKeychainPromptMode")
        let claudeWebExtrasEnabledRaw = userDefaults.object(forKey: "claudeWebExtrasEnabled") as? Bool ?? false
        let creditsExtrasDefault = userDefaults.object(forKey: "showOptionalCreditsAndExtraUsage") as? Bool
        let showOptionalCreditsAndExtraUsage = creditsExtrasDefault ?? true
        if Self.isRunningTests, creditsExtrasDefault == nil {
            userDefaults.set(true, forKey: "showOptionalCreditsAndExtraUsage")
        }
        let claudeDailyRoutinesUsageVisibleDefault = userDefaults.object(
            forKey: "claudeDailyRoutinesUsageVisible") as? Bool
        let claudeDailyRoutinesUsageVisible = claudeDailyRoutinesUsageVisibleDefault ?? true
        if Self.isRunningTests, claudeDailyRoutinesUsageVisibleDefault == nil {
            userDefaults.set(true, forKey: "claudeDailyRoutinesUsageVisible")
        }
        let codexSparkUsageVisibleDefault = userDefaults.object(forKey: "codexSparkUsageVisible") as? Bool
        let codexSparkUsageVisible = codexSparkUsageVisibleDefault ?? true
        if Self.isRunningTests, codexSparkUsageVisibleDefault == nil {
            userDefaults.set(true, forKey: "codexSparkUsageVisible")
        }
        let openAIWebAccessDefault = userDefaults.object(forKey: "openAIWebAccessEnabled") as? Bool
        let openAIWebAccessEnabled = openAIWebAccessDefault ?? false
        if Self.isRunningTests, openAIWebAccessDefault == nil {
            userDefaults.set(false, forKey: "openAIWebAccessEnabled")
        }
        let openAIWebBatterySaverDefault = userDefaults.object(forKey: "openAIWebBatterySaverEnabled") as? Bool
        let openAIWebBatterySaverEnabled = openAIWebBatterySaverDefault ?? false
        if Self.isRunningTests, openAIWebBatterySaverDefault == nil {
            userDefaults.set(false, forKey: "openAIWebBatterySaverEnabled")
        }
        let backgroundWorkLowPowerModeEnabled =
            userDefaults.object(forKey: "backgroundWorkLowPowerModeEnabled") as? Bool ?? false
        let providerStorageFootprintsDefault = userDefaults.object(forKey: "providerStorageFootprintsEnabled") as? Bool
        let providerStorageFootprintsEnabled = providerStorageFootprintsDefault ?? false
        if Self.isRunningTests, providerStorageFootprintsDefault == nil {
            userDefaults.set(false, forKey: "providerStorageFootprintsEnabled")
        }
        let jetbrainsIDEBasePath = userDefaults.string(forKey: "jetbrainsIDEBasePath") ?? ""
        let mergeIcons = userDefaults.object(forKey: "mergeIcons") as? Bool ?? true
        let switcherShowsIcons = userDefaults.object(forKey: "switcherShowsIcons") as? Bool ?? true
        let mergedMenuLastSelectedWasOverview = userDefaults.object(
            forKey: "mergedMenuLastSelectedWasOverview") as? Bool ?? false
        let mergedOverviewSelectedProvidersRaw = userDefaults.array(
            forKey: "mergedOverviewSelectedProviders") as? [String] ?? []
        let selectedMenuProviderRaw = userDefaults.string(forKey: "selectedMenuProvider")
        let providerDetectionCompleted = userDefaults.object(forKey: "providerDetectionCompleted") as? Bool ?? false
        let providersSortedAlphabetically = userDefaults.object(
            forKey: "providersSortedAlphabetically") as? Bool ?? false
        let appLanguageRaw = userDefaults.string(forKey: "appLanguage")
        let agentSessionsEnabled = userDefaults.object(forKey: "agentSessionsEnabled") as? Bool ?? false
        let agentSessionLabelStyleRaw = userDefaults.string(forKey: "agentSessionLabelStyle")
            ?? AgentSessionLabelStyle.project.rawValue
        let agentSessionsManualHosts = userDefaults.string(forKey: "agentSessionsManualHosts") ?? ""
        let preferredCurrencyCode = userDefaults.string(forKey: "preferredCurrencyCode") ?? "USD"
        let iCloudSyncEnabled = userDefaults.object(forKey: "iCloudSyncEnabled") as? Bool ?? false
        let iCloudSyncIncludeSecrets = userDefaults.object(forKey: "iCloudSyncIncludeSecrets") as? Bool ?? true
        let iCloudSyncSnapshotsEnabled = userDefaults.object(forKey: "iCloudSyncSnapshotsEnabled") as? Bool ?? true
        let iCloudSyncShowFleetAccounts = userDefaults.object(forKey: "iCloudSyncShowFleetAccounts") as? Bool ?? true
        let iCloudSyncDeviceID = userDefaults.string(forKey: "iCloudSyncDeviceID") ?? UUID().uuidString.lowercased()
        if userDefaults.string(forKey: "iCloudSyncDeviceID") == nil {
            userDefaults.set(iCloudSyncDeviceID, forKey: "iCloudSyncDeviceID")
        }
        return SettingsDefaultsState(
            refreshFrequency: refreshFrequency,
            adaptiveActivityScanConsent: adaptiveActivityScanConsent,
            refreshAllProvidersOnMenuOpen: refreshAllProvidersOnMenuOpen,
            launchAtLogin: launchAtLogin,
            debugMenuEnabled: debugMenuEnabled,
            debugDisableKeychainAccess: debugDisableKeychainAccess,
            debugFileLoggingEnabled: debugFileLoggingEnabled,
            debugLogLevelRaw: debugLogLevelRaw,
            debugLoadingPatternRaw: debugLoadingPatternRaw,
            debugKeepCLISessionsAlive: debugKeepCLISessionsAlive,
            statusChecksEnabled: notificationDefaults.statusChecksEnabled,
            sessionQuotaNotificationsEnabled: notificationDefaults.sessionQuotaNotificationsEnabled,
            quotaWarningNotificationsEnabled: quotaWarnings.notificationsEnabled,
            predictivePaceWarningNotificationsEnabled: notificationDefaults.predictivePaceWarningNotificationsEnabled,
            quotaWarningThresholdsRaw: quotaWarnings.thresholdsRaw,
            quotaWarningSessionThresholdsRaw: quotaWarnings.sessionThresholdsRaw,
            quotaWarningWeeklyThresholdsRaw: quotaWarnings.weeklyThresholdsRaw,
            quotaWarningSessionEnabled: quotaWarnings.sessionEnabled,
            quotaWarningWeeklyEnabled: quotaWarnings.weeklyEnabled,
            quotaWarningSoundEnabled: quotaWarnings.soundEnabled,
            quotaWarningOnScreenAlertEnabled: quotaWarnings.onScreenAlertEnabled,
            quotaWarningMarkersVisible: quotaWarningMarkersVisible,
            weeklyProgressWorkDays: weeklyProgressWorkDays,
            usageBarsShowUsed: usageBarsShowUsed,
            resetTimesShowAbsolute: resetTimesShowAbsolute,
            providerChangelogLinksEnabled: providerChangelogLinksEnabled,
            menuBarShowsBrandIconWithPercent: menuBarShowsBrandIconWithPercent,
            menuBarHidesCritters: menuBarHidesCritters,
            menuBarHighContrastOnInactiveDisplays: menuBarHighContrastOnInactiveDisplays,
            menuBarDisplayModeRaw: menuBarDisplayModeRaw,
            menuBarShowsResetTimeWhenExhausted: menuBarShowsResetTimeWhenExhausted,
            kiroMenuBarDisplayModeRaw: kiroMenuBarDisplayModeRaw,
            historicalTrackingEnabled: historicalTrackingEnabled,
            multiAccountMenuLayoutRaw: multiAccountMenuLayoutRaw,
            menuBarMetricPreferencesRaw: resolvedPreferences,
            storedMenuBarLayout: storedMenuBarLayout,
            menuBarLayoutOverridesRaw: menuBarLayoutOverridesRaw,
            menuBarLayoutSizeRaw: menuBarLayoutSizeRaw,
            menuBarLayoutGapRaw: menuBarLayoutGapRaw,
            copilotBudgetExtrasEnabled: copilotBudgetExtrasEnabled,
            copilotIconSecondaryWindowIDRaw: copilotIconSecondaryWindowIDRaw,
            costUsageEnabled: costUsageEnabled,
            codexLocalSessionCostLedgerEnabled: codexLocalSessionCostLedgerEnabled,
            costUsageHistoryDays: costUsageHistoryDays,
            costComparisonPeriodsEnabled: costComparisonPeriodsEnabled,
            costSummaryDisplayStyleRaw: costSummaryDisplayStyleRaw,
            hidePersonalInfo: hidePersonalInfo,
            randomBlinkEnabled: randomBlinkEnabled,
            confettiOnSessionLimitResetsEnabled: confettiOnReset.session,
            confettiOnWeeklyLimitResetsEnabled: confettiOnReset.weekly,
            menuBarShowsHighestUsage: menuBarShowsHighestUsage,
            claudeOAuthKeychainPromptModeRaw: claudeOAuthKeychainPromptModeRaw,
            claudeOAuthKeychainReadStrategyRaw: claudeOAuthKeychainReadStrategyRaw,
            claudeWebExtrasEnabledRaw: claudeWebExtrasEnabledRaw,
            showOptionalCreditsAndExtraUsage: showOptionalCreditsAndExtraUsage,
            claudeDailyRoutinesUsageVisible: claudeDailyRoutinesUsageVisible,
            codexSparkUsageVisible: codexSparkUsageVisible,
            openAIWebAccessEnabled: openAIWebAccessEnabled,
            openAIWebBatterySaverEnabled: openAIWebBatterySaverEnabled,
            backgroundWorkLowPowerModeEnabled: backgroundWorkLowPowerModeEnabled,
            providerStorageFootprintsEnabled: providerStorageFootprintsEnabled,
            jetbrainsIDEBasePath: jetbrainsIDEBasePath,
            mergeIcons: mergeIcons,
            switcherShowsIcons: switcherShowsIcons,
            mergedMenuLastSelectedWasOverview: mergedMenuLastSelectedWasOverview,
            mergedOverviewSelectedProvidersRaw: mergedOverviewSelectedProvidersRaw,
            selectedMenuProviderRaw: selectedMenuProviderRaw,
            providerDetectionCompleted: providerDetectionCompleted,
            providersSortedAlphabetically: providersSortedAlphabetically,
            appLanguageRaw: appLanguageRaw,
            terminalAppRaw: userDefaults.string(forKey: "terminalApp"),
            agentSessionsEnabled: agentSessionsEnabled,
            agentSessionLabelStyleRaw: agentSessionLabelStyleRaw,
            agentSessionsManualHosts: agentSessionsManualHosts,
            preferredCurrencyCode: preferredCurrencyCode,
            iCloudSyncEnabled: iCloudSyncEnabled,
            iCloudSyncIncludeSecrets: iCloudSyncIncludeSecrets,
            iCloudSyncSnapshotsEnabled: iCloudSyncSnapshotsEnabled,
            iCloudSyncShowFleetAccounts: iCloudSyncShowFleetAccounts,
            iCloudSyncDeviceID: iCloudSyncDeviceID)
    }

    private static func hadPreviousAppLaunch(userDefaults: UserDefaults) -> Bool {
        userDefaults.object(forKey: "providerDetectionCompleted") != nil ||
            userDefaults.object(forKey: AppGroupSupport.migrationVersionKey) != nil
    }

    private static func loadRefreshFrequency(
        userDefaults: UserDefaults,
        hadPreviousInstallationState: Bool) -> RefreshFrequency
    {
        let rawValue = userDefaults.object(forKey: "refreshFrequency")
        if let stored = rawValue as? String,
           let frequency = RefreshFrequency(rawValue: stored)
        {
            return frequency
        }

        // An invalid value is existing state. Missing state is Adaptive only when no prior-installation
        // state existed before migrations began; legacy unset users keep the old five-minute fallback.
        let frequency: RefreshFrequency = rawValue == nil && !hadPreviousInstallationState ? .adaptive : .fiveMinutes
        userDefaults.set(frequency.rawValue, forKey: "refreshFrequency")
        return frequency
    }

    private static func loadAdaptiveActivityScanConsent(
        userDefaults: UserDefaults) -> AdaptiveActivityScanConsent
    {
        if let rawValue = userDefaults.string(forKey: "adaptiveActivityScanConsent"),
           let consent = AdaptiveActivityScanConsent(rawValue: rawValue)
        {
            return consent
        }

        userDefaults.set(AdaptiveActivityScanConsent.undecided.rawValue, forKey: "adaptiveActivityScanConsent")
        return .undecided
    }

    private static func loadNotificationDefaults(userDefaults: UserDefaults) -> NotificationDefaults {
        NotificationDefaults(
            statusChecksEnabled: userDefaults.object(forKey: "statusChecksEnabled") as? Bool ?? true,
            sessionQuotaNotificationsEnabled: self.loadSessionQuotaNotificationsDefault(userDefaults: userDefaults),
            predictivePaceWarningNotificationsEnabled: userDefaults.object(
                forKey: "predictivePaceWarningNotificationsEnabled") as? Bool ?? false)
    }

    private static func loadCostSummaryDisplayStyleRaw(
        userDefaults: UserDefaults,
        costUsageEnabled: Bool) -> String
    {
        if let storedCostSummaryDisplayStyle = userDefaults.string(forKey: "costSummaryDisplayStyle"),
           CostSummaryDisplayStyle(rawValue: storedCostSummaryDisplayStyle) != nil
        {
            return storedCostSummaryDisplayStyle
        }
        let migratedStyle = CostSummaryDisplayStyle.both.rawValue
        if costUsageEnabled || userDefaults.object(forKey: "costSummaryDisplayStyle") != nil {
            userDefaults.set(migratedStyle, forKey: "costSummaryDisplayStyle")
        }
        return migratedStyle
    }

    private static func loadClaudeOAuthKeychainReadStrategyRaw(userDefaults: UserDefaults) -> String? {
        let key = "claudeOAuthKeychainReadStrategy"
        guard let raw = userDefaults.string(forKey: key) else { return nil }
        guard let strategy = ClaudeOAuthKeychainReadStrategy(rawValue: raw) else { return raw }
        guard strategy == .securityCLIExperimental else { return raw }

        let migrated = ClaudeOAuthKeychainReadStrategy.securityFramework.rawValue
        userDefaults.set(migrated, forKey: key)
        let promptModeKey = "claudeOAuthKeychainPromptMode"
        if userDefaults.string(forKey: promptModeKey) == nil {
            userDefaults.set(ClaudeOAuthKeychainPromptMode.never.rawValue, forKey: promptModeKey)
        }
        return migrated
    }

    private static func loadConfettiOnResetDefaults(userDefaults: UserDefaults) -> (session: Bool, weekly: Bool) {
        (
            session: userDefaults.object(forKey: "confettiOnSessionLimitResetsEnabled") as? Bool ?? false,
            weekly: userDefaults.object(forKey: "confettiOnWeeklyLimitResetsEnabled") as? Bool ?? false)
    }

    private static func loadMenuBarMetricPreferences(userDefaults: UserDefaults) -> [String: String] {
        let storedPreferences = userDefaults.dictionary(forKey: "menuBarMetricPreferences") as? [String: String] ?? [:]
        let preferences: [String: String] = if !storedPreferences.isEmpty {
            storedPreferences
        } else if let menuBarMetricRaw = userDefaults.string(forKey: "menuBarMetricPreference"),
                  let legacyPreference = MenuBarMetricPreference(rawValue: menuBarMetricRaw)
        {
            Dictionary(
                uniqueKeysWithValues: UsageProvider.allCases.map { ($0.rawValue, legacyPreference.rawValue) })
        } else {
            [:]
        }

        let migrationKey = "antigravityTwoPoolMetricPreferenceMigrated"
        guard !userDefaults.bool(forKey: migrationKey) else { return preferences }

        // Tagged builds through v0.35 used primary=Claude, secondary=Gemini Pro,
        // and tertiary=Gemini Flash. Remap those meanings once to the two-pool schema.
        // Provider-specific by design: this one-time migration rewrites Antigravity's historical persisted lanes.
        var migrated = preferences
        switch MenuBarMetricPreference(rawValue: migrated[UsageProvider.antigravity.rawValue] ?? "") {
        case .primary:
            migrated[UsageProvider.antigravity.rawValue] = MenuBarMetricPreference.secondary.rawValue
        case .secondary:
            migrated[UsageProvider.antigravity.rawValue] = MenuBarMetricPreference.primary.rawValue
        case .tertiary:
            migrated[UsageProvider.antigravity.rawValue] = MenuBarMetricPreference.primary.rawValue
        case .automatic, .primaryAndSecondary, .extraUsage, .average, .monthlyPlan, .none:
            break
        }
        userDefaults.set(migrated, forKey: "menuBarMetricPreferences")
        userDefaults.set(true, forKey: migrationKey)
        return migrated
    }

    private static func loadMenuBarLayout(userDefaults: UserDefaults, key: String) -> MenuBarLayout? {
        guard let data = userDefaults.data(forKey: key) else { return nil }
        return try? JSONDecoder().decode(MenuBarLayout.self, from: data)
    }

    private static func loadMenuBarLayoutOverrides(userDefaults: UserDefaults) -> [String: MenuBarLayout] {
        guard let data = userDefaults.data(forKey: "menuBarLayoutOverrides") else { return [:] }
        return (try? JSONDecoder().decode([String: MenuBarLayout].self, from: data)) ?? [:]
    }

    private static func loadMultiAccountMenuLayoutRaw(userDefaults: UserDefaults) -> String {
        if let layout = userDefaults.string(forKey: "multiAccountMenuLayout") {
            return layout
        }
        let legacyShowAll = userDefaults.object(forKey: "showAllTokenAccountsInMenu") as? Bool ?? false
        return legacyShowAll ? MultiAccountMenuLayout.stacked.rawValue : MultiAccountMenuLayout.segmented.rawValue
    }

    private static func loadCopilotIconSecondaryWindowIDRaw(userDefaults: UserDefaults) -> String {
        userDefaults.string(forKey: "copilotIconSecondaryWindowID") ?? CopilotIconSecondaryWindowSelection.chat
    }

    private static func loadDebugDisableKeychainAccess(userDefaults: UserDefaults) -> Bool {
        if let stored = userDefaults.object(forKey: "debugDisableKeychainAccess") as? Bool {
            return stored
        }
        if Self.shouldBridgeSharedDefaults(for: userDefaults),
           let shared = Self.sharedDefaults?.object(forKey: "debugDisableKeychainAccess") as? Bool
        {
            if Self.isRunningTests {
                userDefaults.set(shared, forKey: "debugDisableKeychainAccess")
            }
            return shared
        }
        return false
    }

    private struct LoadedQuotaWarningDefaults {
        var notificationsEnabled: Bool
        var thresholdsRaw: [Int]
        var sessionThresholdsRaw: [Int]
        var weeklyThresholdsRaw: [Int]
        var sessionEnabled: Bool
        var weeklyEnabled: Bool
        var soundEnabled: Bool
        var onScreenAlertEnabled: Bool
    }

    private static func loadSessionQuotaNotificationsDefault(userDefaults: UserDefaults) -> Bool {
        let stored = userDefaults.object(forKey: "sessionQuotaNotificationsEnabled") as? Bool
        if Self.isRunningTests, stored == nil {
            userDefaults.set(true, forKey: "sessionQuotaNotificationsEnabled")
        }
        return stored ?? true
    }

    private static func loadQuotaWarningDefaults(userDefaults: UserDefaults) -> LoadedQuotaWarningDefaults {
        let notificationsEnabled = userDefaults.object(forKey: "quotaWarningNotificationsEnabled") as? Bool ?? false
        let rawThresholds = userDefaults.array(forKey: "quotaWarningThresholds") as? [Int]
        let thresholdsRaw = QuotaWarningThresholds.sanitized(rawThresholds ?? QuotaWarningThresholds.defaults)
        if Self.isRunningTests, rawThresholds != thresholdsRaw {
            userDefaults.set(thresholdsRaw, forKey: "quotaWarningThresholds")
        }
        let rawSessionThresholds = userDefaults.array(forKey: "quotaWarningSessionThresholds") as? [Int]
        let sessionThresholdsRaw = QuotaWarningThresholds.sanitized(rawSessionThresholds ?? thresholdsRaw)
        if Self.isRunningTests, rawSessionThresholds != sessionThresholdsRaw {
            userDefaults.set(sessionThresholdsRaw, forKey: "quotaWarningSessionThresholds")
        }
        let rawWeeklyThresholds = userDefaults.array(forKey: "quotaWarningWeeklyThresholds") as? [Int]
        let weeklyThresholdsRaw = QuotaWarningThresholds.sanitized(rawWeeklyThresholds ?? thresholdsRaw)
        if Self.isRunningTests, rawWeeklyThresholds != weeklyThresholdsRaw {
            userDefaults.set(weeklyThresholdsRaw, forKey: "quotaWarningWeeklyThresholds")
        }

        let sessionDefault = userDefaults.object(forKey: "quotaWarningSessionEnabled") as? Bool
        let sessionEnabled = sessionDefault ?? true
        if Self.isRunningTests, sessionDefault == nil {
            userDefaults.set(true, forKey: "quotaWarningSessionEnabled")
        }

        let weeklyDefault = userDefaults.object(forKey: "quotaWarningWeeklyEnabled") as? Bool
        let weeklyEnabled = weeklyDefault ?? true
        if Self.isRunningTests, weeklyDefault == nil {
            userDefaults.set(true, forKey: "quotaWarningWeeklyEnabled")
        }

        let soundDefault = userDefaults.object(forKey: "quotaWarningSoundEnabled") as? Bool
        let soundEnabled = soundDefault ?? true
        if Self.isRunningTests, soundDefault == nil {
            userDefaults.set(true, forKey: "quotaWarningSoundEnabled")
        }

        let onScreenAlertDefault = userDefaults.object(forKey: "quotaWarningOnScreenAlertEnabled") as? Bool
        let onScreenAlertEnabled = onScreenAlertDefault ?? false
        if Self.isRunningTests, onScreenAlertDefault == nil {
            userDefaults.set(false, forKey: "quotaWarningOnScreenAlertEnabled")
        }

        return LoadedQuotaWarningDefaults(
            notificationsEnabled: notificationsEnabled,
            thresholdsRaw: thresholdsRaw,
            sessionThresholdsRaw: sessionThresholdsRaw,
            weeklyThresholdsRaw: weeklyThresholdsRaw,
            sessionEnabled: sessionEnabled,
            weeklyEnabled: weeklyEnabled,
            soundEnabled: soundEnabled,
            onScreenAlertEnabled: onScreenAlertEnabled)
    }
}

extension SettingsStore {
    var configSnapshot: CodexBarConfig {
        _ = self.configRevision
        return self.config
    }

    func updateProviderState(config: CodexBarConfig) {
        let rawOrder = config.providers.map(\.id.rawValue)
        self.providerOrder = Self.effectiveProviderOrder(raw: rawOrder)
        let metadata = ProviderDescriptorRegistry.metadata
        var enablement: [ProviderInstanceID: Bool] = [:]
        enablement.reserveCapacity(metadata.count)
        for provider in UsageProvider.allCases {
            let instanceID = provider.instanceID
            let defaultEnabled = metadata[provider]?.defaultEnabled ?? false
            let providerConfig = config.providerConfig(for: instanceID) ?? ProviderConfig(id: instanceID)
            let isEnabled = providerConfig.enabled ?? defaultEnabled
            if let previous = self.providerEnablement[instanceID], previous != isEnabled {
                self.providerEnablementRevisions[instanceID, default: 0] &+= 1
            }
            let fingerprint = Self.providerConfigFingerprint(providerConfig)
            if let previous = self.providerConfigFingerprints[instanceID], previous != fingerprint {
                self.providerConfigRevisions[instanceID, default: 0] &+= 1
            }
            self.providerConfigFingerprints[instanceID] = fingerprint
            enablement[instanceID] = isEnabled
        }
        for plugin in UserProviderPluginRegistry.all {
            let instanceID = plugin.manifest.id
            let providerConfig = config.providerConfig(for: instanceID) ?? ProviderConfig(id: instanceID)
            let isEnabled = providerConfig.enabled ?? true
            if let previous = self.providerEnablement[instanceID], previous != isEnabled {
                self.providerEnablementRevisions[instanceID, default: 0] &+= 1
            }
            let fingerprint = Self.providerConfigFingerprint(providerConfig)
            if let previous = self.providerConfigFingerprints[instanceID], previous != fingerprint {
                self.providerConfigRevisions[instanceID, default: 0] &+= 1
            }
            self.providerConfigFingerprints[instanceID] = fingerprint
            enablement[instanceID] = isEnabled
        }
        self.providerEnablement = enablement
    }

    private static func providerConfigFingerprint(_ config: ProviderConfig) -> Data {
        let encoder = JSONEncoder()
        encoder.outputFormatting = [.sortedKeys]
        return (try? encoder.encode(config)) ?? Data()
    }

    func providerEnablementRevision(for provider: UsageProvider) -> UInt64 {
        self.providerEnablementRevisions[provider.instanceID, default: 0]
    }

    func providerConfigRevision(for provider: UsageProvider) -> UInt64 {
        self.providerConfigRevisions[provider.instanceID, default: 0]
    }

    func orderedProviders() -> [ProviderInstanceID] {
        if self.providerOrder.isEmpty {
            self.updateProviderState(config: self.configSnapshot)
        }
        return self.providerOrder
    }

    func orderedFirstPartyProviders() -> [UsageProvider] {
        self.orderedProviders().compactMap(\.firstPartyProvider)
    }

    func moveProvider(fromOffsets: IndexSet, toOffset: Int) {
        var order = self.orderedProviders()
        order.move(fromOffsets: fromOffsets, toOffset: toOffset)
        self.setProviderOrder(order)
    }

    func isProviderEnabled(provider: UsageProvider, metadata: ProviderMetadata) -> Bool {
        self.providerEnablement[provider.instanceID] ?? metadata.defaultEnabled
    }

    func isProviderEnabledCached(
        provider: UsageProvider,
        metadataByProvider: [UsageProvider: ProviderMetadata]) -> Bool
    {
        let defaultEnabled = metadataByProvider[provider]?.defaultEnabled ?? false
        return self.providerEnablement[provider.instanceID] ?? defaultEnabled
    }

    func enabledProvidersOrdered(metadataByProvider: [UsageProvider: ProviderMetadata]) -> [ProviderInstanceID] {
        _ = metadataByProvider
        return self.orderedProviders().filter { self.providerEnablement[$0] ?? false }
    }

    func setProviderEnabled(provider: UsageProvider, metadata _: ProviderMetadata, enabled: Bool) {
        CodexBarLog.logger(LogCategories.settings).debug(
            "Provider toggle updated",
            metadata: ["provider": provider.rawValue, "enabled": "\(enabled)"])
        self.updateProviderConfig(provider: provider) { entry in
            entry.enabled = enabled
        }
        if !enabled, self.selectedMenuProvider == provider.instanceID {
            self.selectedMenuProvider = nil
        }
    }

    func isPluginEnabled(_ instanceID: ProviderInstanceID) -> Bool {
        self.providerEnablement[instanceID] ?? false
    }

    func setPluginEnabled(_ instanceID: ProviderInstanceID, enabled: Bool) {
        self.updatePluginConfig(instanceID: instanceID) { $0.enabled = enabled }
        if !enabled, self.selectedMenuProvider == instanceID {
            self.selectedMenuProvider = nil
        }
    }

    func pluginConfig(_ instanceID: ProviderInstanceID) -> ProviderConfig? {
        self.configSnapshot.providerConfig(for: instanceID)
    }

    func updatePluginConfig(instanceID: ProviderInstanceID, mutate: (inout ProviderConfig) -> Void) {
        self.updateConfig(reason: "plugin-\(instanceID.rawValue)", affectsBackgroundWork: true) { config in
            if let index = config.providers.firstIndex(where: { $0.id == instanceID }) {
                mutate(&config.providers[index])
            } else {
                var entry = ProviderConfig(id: instanceID, enabled: true)
                mutate(&entry)
                config.providers.append(entry)
            }
        }
    }

    func rerunProviderDetection() {
        self.runInitialProviderDetectionIfNeeded(force: true)
    }
}

extension SettingsStore {
    private static func effectiveProviderOrder(raw: [String]) -> [ProviderInstanceID] {
        var seen: Set<ProviderInstanceID> = []
        var ordered: [ProviderInstanceID] = []

        for rawValue in raw {
            guard let instanceID = ProviderInstanceID(rawValue: rawValue),
                  instanceID.firstPartyProvider != nil || UserProviderPluginRegistry.plugin(for: instanceID) != nil
            else {
                continue
            }
            guard !seen.contains(instanceID) else { continue }
            seen.insert(instanceID)
            ordered.append(instanceID)
        }

        if ordered.isEmpty {
            ordered = UsageProvider.allCases.map(\.instanceID)
            seen = Set(ordered)
        }

        if !seen.contains(.factory), let zaiIndex = ordered.firstIndex(of: .zai) {
            ordered.insert(.factory, at: zaiIndex)
            seen.insert(.factory)
        }

        if !seen.contains(.minimax), let zaiIndex = ordered.firstIndex(of: .zai) {
            let insertIndex = ordered.index(after: zaiIndex)
            ordered.insert(.minimax, at: insertIndex)
            seen.insert(.minimax)
        }

        for provider in UsageProvider.allCases where !seen.contains(provider.instanceID) {
            ordered.append(provider.instanceID)
        }

        for plugin in UserProviderPluginRegistry.all where !seen.contains(plugin.manifest.id) {
            ordered.append(plugin.manifest.id)
            seen.insert(plugin.manifest.id)
        }

        return ordered
    }
}
