import AppKit
import CodexBarCore
import Foundation
import Testing
@testable import CodexBar

extension StatusMenuTests {
    @Test
    func `store observation marks open menu stale without rebuilding during tracking`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = controller.menuVersions[key]
        var rebuildCount = 0
        controller._test_openMenuRebuildObserver = { _ in
            rebuildCount += 1
        }

        let now = Date()
        store._setSnapshotForTesting(
            UsageSnapshot(
                primary: RateWindow(
                    usedPercent: 33,
                    windowMinutes: 300,
                    resetsAt: now.addingTimeInterval(1800),
                    resetDescription: nil),
                secondary: nil,
                tertiary: nil,
                updatedAt: now,
                identity: ProviderIdentitySnapshot(
                    providerID: .codex,
                    accountEmail: "codex@example.com",
                    accountOrganization: nil,
                    loginMethod: "Plus Plan")),
            provider: .codex)

        for _ in 0..<20 where controller.menuContentVersion == openedVersion {
            await Task.yield()
        }

        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)
        #expect(rebuildCount == 0)
    }

    @Test
    func `closed merged menu defers rebuild until next open instead of pre-warming`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }
        StatusItemController.setClosedMenuPreparationDelayForTesting(.zero)
        defer { StatusItemController.resetClosedMenuPreparationDelayForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        controller.mergedMenu = menu
        controller.statusItem.menu = menu
        for _ in 0..<20 {
            await Task.yield()
        }

        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        let key = ObjectIdentifier(menu)
        for _ in 0..<40 {
            await Task.yield()
        }
        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        controller.cancelAllClosedMenuRebuilds()
        controller.closedMenusDeferredUntilNextOpen.removeAll(keepingCapacity: false)
        let openedVersion = controller.menuVersions[key]

        // Background data-refresh tick (stale allowed): closed prep is skipped entirely, so
        // the closed merged menu must not be pre-warmed or marked deferred.
        controller.invalidateMenus(allowStaleContentDuringDataRefresh: true)
        for _ in 0..<40 {
            await Task.yield()
        }
        #expect(controller.openMenus.isEmpty)
        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)
        #expect(!controller.closedMenusDeferredUntilNextOpen.contains(key))

        // A required (non-stale) invalidation must also leave the closed merged menu deferred.
        controller.invalidateMenus()
        for _ in 0..<40 {
            await Task.yield()
        }
        #expect(controller.menuVersions[key] == openedVersion)
        #expect(controller.closedMenusDeferredUntilNextOpen.contains(key))

        // The deferred merged menu is repopulated synchronously on the next open.
        controller.menuWillOpen(menu)
        defer { controller.menuDidClose(menu) }
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
        #expect(!controller.closedMenusDeferredUntilNextOpen.contains(key))
    }

    @Test
    func `data refresh invalidation does not rebuild closed non merged attached menu`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }
        StatusItemController.setClosedMenuPreparationDelayForTesting(.zero)
        defer { StatusItemController.resetClosedMenuPreparationDelayForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        // Use a non-merged attached menu: stale data-refresh invalidations should not pre-warm any
        // closed attached menu, while required invalidations still may prepare non-merged menus.
        controller.fallbackMenu = menu
        controller.statusItem.menu = menu

        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        let key = ObjectIdentifier(menu)
        for _ in 0..<40 {
            await Task.yield()
        }
        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        controller.cancelAllClosedMenuRebuilds()
        let openedVersion = controller.menuVersions[key]

        controller.invalidateMenus(allowStaleContentDuringDataRefresh: true)
        for _ in 0..<40 {
            await Task.yield()
        }

        #expect(controller.openMenus.isEmpty)
        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)

        controller.menuWillOpen(menu)
        defer { controller.menuDidClose(menu) }

        for _ in 0..<40 where controller.menuVersions[key] != controller.menuContentVersion {
            await Task.yield()
        }
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
    }

    @Test
    func `required non merged closed menu preparation survives later data refresh invalidation`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }
        StatusItemController.setClosedMenuPreparationDelayForTesting(.zero)
        defer { StatusItemController.resetClosedMenuPreparationDelayForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        // Use a non-merged attached menu so this covers the delayed closed-menu rebuild path. Merged
        // menus are intentionally deferred until next open on current main (#1274).
        controller.fallbackMenu = menu
        controller.statusItem.menu = menu

        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        let key = ObjectIdentifier(menu)
        let openedVersion = controller.menuVersions[key]

        controller.invalidateMenus()
        let requiredVersion = controller.latestRequiredMenuRebuildVersion
        controller.invalidateMenus(allowStaleContentDuringDataRefresh: true)
        for _ in 0..<40 where controller.menuVersions[key] == openedVersion {
            await Task.yield()
        }

        #expect(controller.openMenus.isEmpty)
        #expect(requiredVersion > (openedVersion ?? -1))
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
    }

    @Test
    func `closed attached menu preparation waits for store refresh to finish`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }
        StatusItemController.setClosedMenuPreparationDelayForTesting(.zero)
        defer { StatusItemController.resetClosedMenuPreparationDelayForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        // Use a non-merged attached menu: the merged menu is intentionally never pre-warmed while
        // closed (#1274), so the in-flight-refresh prep machinery is exercised via the fallback menu.
        controller.fallbackMenu = menu
        controller.statusItem.menu = menu

        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        let key = ObjectIdentifier(menu)
        let openedVersion = controller.menuVersions[key]

        store.isRefreshing = true
        controller.invalidateMenus()
        for _ in 0..<40 {
            await Task.yield()
        }

        #expect(controller.menuVersions[key] == openedVersion)

        store.isRefreshing = false
        controller.invalidateMenus()
        for _ in 0..<40 where controller.menuVersions[key] == openedVersion {
            await Task.yield()
        }

        #expect(controller.openMenus.isEmpty)
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
    }

    @Test
    func `closed attached menu preparation waits for token refresh to finish`() async {
        StatusItemController.setClosedMenuPreparationDelayForTesting(.zero)
        defer { StatusItemController.resetClosedMenuPreparationDelayForTesting() }

        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        // Use a non-merged attached menu: the merged menu is intentionally never pre-warmed while
        // closed (#1274), so the in-flight-refresh prep machinery is exercised via the fallback menu.
        controller.fallbackMenu = menu
        controller.statusItem.menu = menu

        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        let key = ObjectIdentifier(menu)
        let openedVersion = controller.menuVersions[key]

        store.tokenRefreshInFlight.insert(.codex)
        controller.invalidateMenus()
        for _ in 0..<40 {
            await Task.yield()
        }

        #expect(controller.menuVersions[key] == openedVersion)

        store.tokenRefreshInFlight.remove(.codex)
        controller.invalidateMenus()
        for _ in 0..<40 where controller.menuVersions[key] == openedVersion {
            await Task.yield()
        }

        #expect(controller.openMenus.isEmpty)
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
    }

    @Test
    func `closed menu rebuild cleanup runs when weak menu disappears`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }
        StatusItemController.setClosedMenuPreparationDelayForTesting(.zero)
        defer { StatusItemController.resetClosedMenuPreparationDelayForTesting() }

        let key: ObjectIdentifier
        do {
            let menu = NSMenu()
            key = ObjectIdentifier(menu)
            controller.rebuildClosedMenuIfNeeded(menu)
            #expect(controller.closedMenuRebuildTasks[key] != nil)
            #expect(controller.closedMenuRebuildTokens[key] != nil)
        }

        for _ in 0..<40 where controller.closedMenuRebuildTasks[key] != nil {
            await Task.yield()
        }

        #expect(controller.closedMenuRebuildTasks[key] == nil)
        #expect(controller.closedMenuRebuildTokens[key] == nil)
    }

    @Test
    func `merged menu close defers stale rebuild until next open`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }
        StatusItemController.setClosedMenuPreparationDelayForTesting(.zero)
        defer { StatusItemController.resetClosedMenuPreparationDelayForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        controller.mergedMenu = menu
        controller.statusItem.menu = menu
        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        controller.menuWillOpen(menu)

        let key = ObjectIdentifier(menu)
        let openedVersion = controller.menuVersions[key]
        controller.invalidateMenus(refreshOpenMenus: false)
        #expect(controller.menuNeedsRefresh(menu))

        controller.menuDidClose(menu)
        await self.waitUntilClosedMenuRebuildRemainsDeferred(controller, key: key, openedVersion: openedVersion)

        #expect(controller.closedMenuRebuildTasks[key] == nil)
        #expect(controller.menuVersions[key] == openedVersion)

        controller.menuWillOpen(menu)
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
    }

    @Test
    func `menu open keeps stale nonempty content while store refresh is active`() {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        controller.mergedMenu = menu
        controller.statusItem.menu = menu

        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        let key = ObjectIdentifier(menu)
        let openedVersion = controller.menuVersions[key]
        let openedItemCount = menu.items.count

        store.isRefreshing = true
        defer { store.isRefreshing = false }
        controller.invalidateMenus(allowStaleContentDuringDataRefresh: true)
        controller.menuWillOpen(menu)
        defer { controller.menuDidClose(menu) }

        #expect(controller.menuVersions[key] == openedVersion)
        #expect(controller.menuContentVersion != openedVersion)
        #expect(menu.items.count == openedItemCount)
        #expect(controller.openMenus[key] === menu)
    }

    @Test
    func `menu open rebuilds stale content after privacy setting changes during refresh`() {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        controller.mergedMenu = menu
        controller.statusItem.menu = menu

        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        let key = ObjectIdentifier(menu)
        let openedVersion = controller.menuVersions[key]

        store.isRefreshing = true
        defer { store.isRefreshing = false }
        controller.invalidateMenus(allowStaleContentDuringDataRefresh: true)
        settings.hidePersonalInfo = true
        controller.invalidateMenus()
        controller.menuWillOpen(menu)
        defer { controller.menuDidClose(menu) }

        #expect(controller.menuVersions[key] == controller.menuContentVersion)
        #expect(controller.menuVersions[key] != openedVersion)
    }

    @Test
    func `menu open keeps stale nonempty content while token refresh is active`() {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        let menu = controller.makeMenu()
        controller.mergedMenu = menu
        controller.statusItem.menu = menu

        controller.populateMenu(menu, provider: nil)
        controller.markMenuFresh(menu)
        let key = ObjectIdentifier(menu)
        let openedVersion = controller.menuVersions[key]
        let openedItemCount = menu.items.count

        store.tokenRefreshInFlight.insert(.codex)
        defer { store.tokenRefreshInFlight.remove(.codex) }
        controller.invalidateMenus(allowStaleContentDuringDataRefresh: true)
        controller.menuWillOpen(menu)
        defer { controller.menuDidClose(menu) }

        #expect(controller.menuVersions[key] == openedVersion)
        #expect(controller.menuContentVersion != openedVersion)
        #expect(menu.items.count == openedItemCount)
        #expect(controller.openMenus[key] === menu)
    }

    @Test
    func `explicit store actions defer visible parent menu rebuild`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = controller.menuVersions[key]
        var rebuildCount = 0
        controller._test_openMenuRebuildObserver = { _ in
            rebuildCount += 1
        }
        defer { controller._test_openMenuRebuildObserver = nil }

        controller.refreshOpenMenusAfterExplicitStoreAction()
        for _ in 0..<20 {
            await Task.yield()
        }

        #expect(controller.menuContentVersion != openedVersion)
        #expect(rebuildCount == 0)
        #expect(controller.menuVersions[key] == openedVersion)
        #expect(controller.parentMenuRebuildsDeferredDuringTracking.contains(key))
    }

    @Test
    func `repeated explicit store actions keep parent rebuild deferred`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        var rebuildCount = 0
        controller._test_openMenuRebuildObserver = { _ in
            rebuildCount += 1
        }
        defer { controller._test_openMenuRebuildObserver = nil }

        controller.refreshOpenMenusAfterExplicitStoreAction()
        controller.refreshOpenMenusAfterExplicitStoreAction()
        controller.refreshOpenMenusAfterExplicitStoreAction()

        for _ in 0..<20 {
            await Task.yield()
        }

        #expect(rebuildCount == 0)
        #expect(controller.menuVersions[key] != controller.menuContentVersion)
        #expect(controller.parentMenuRebuildsDeferredDuringTracking.contains(key))
    }

    @Test
    func `explicit refresh rebuilds stale parent after hosted submenu closes`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let menuKey = ObjectIdentifier(menu)
        controller.openMenus[menuKey] = menu

        let submenu = controller.makeHostedSubviewPlaceholderMenu(
            chartID: StatusItemController.usageBreakdownChartID,
            provider: .codex)
        let submenuKey = ObjectIdentifier(submenu)
        controller.openMenus[submenuKey] = submenu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = controller.menuVersions[menuKey]
        var rebuildCount = 0
        controller._test_openMenuRebuildObserver = { _ in
            rebuildCount += 1
        }
        defer { controller._test_openMenuRebuildObserver = nil }

        controller.refreshOpenMenusAfterExplicitStoreAction()
        for _ in 0..<20 where controller.menuContentVersion == openedVersion {
            await Task.yield()
        }
        #expect(controller.menuVersions[menuKey] == openedVersion)

        controller.menuDidClose(submenu)
        for _ in 0..<20 where rebuildCount == 0 {
            await Task.yield()
        }

        #expect(controller.openMenus[submenuKey] == nil)
        #expect(rebuildCount == 1)
        #expect(controller.menuVersions[menuKey] == controller.menuContentVersion)
        #expect(!controller.parentMenuRebuildsDeferredDuringTracking.contains(menuKey))
    }

    @Test
    func `hosted submenu close waits for active refresh before rebuilding parent`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let menuKey = ObjectIdentifier(menu)
        controller.openMenus[menuKey] = menu

        let submenu = controller.makeHostedSubviewPlaceholderMenu(
            chartID: StatusItemController.usageBreakdownChartID,
            provider: .codex)
        let submenuKey = ObjectIdentifier(submenu)
        controller.openMenus[submenuKey] = submenu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = controller.menuVersions[menuKey]
        var rebuildCount = 0
        controller._test_openMenuRebuildObserver = { _ in
            rebuildCount += 1
        }
        defer { controller._test_openMenuRebuildObserver = nil }

        store.isRefreshing = true
        controller.refreshOpenMenusAfterExplicitStoreAction()
        controller.menuDidClose(submenu)
        for _ in 0..<20 {
            await Task.yield()
        }

        #expect(controller.openMenus[submenuKey] == nil)
        #expect(rebuildCount == 0)
        #expect(controller.menuVersions[menuKey] == openedVersion)
        #expect(controller.parentMenuRebuildPendingAfterHostedSubviewClose)

        store.isRefreshing = false
        controller.handleObservedStoreMenuChange()
        for _ in 0..<20 where rebuildCount == 0 {
            await Task.yield()
        }

        #expect(rebuildCount == 1)
        #expect(controller.menuVersions[menuKey] == controller.menuContentVersion)
        #expect(!controller.parentMenuRebuildPendingAfterHostedSubviewClose)
        #expect(!controller.parentMenuRebuildsDeferredDuringTracking.contains(menuKey))
    }

    @Test
    func `plain open menu refresh preserves pending switcher hosted submenu cleanup`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let menuKey = ObjectIdentifier(menu)
        controller.openMenus[menuKey] = menu

        let submenu = controller.makeHostedSubviewPlaceholderMenu(
            chartID: StatusItemController.usageBreakdownChartID,
            provider: .codex)
        let submenuKey = ObjectIdentifier(submenu)
        controller.openMenus[submenuKey] = submenu
        controller.menuRefreshEnabledOverrideForTesting = true

        var rootRebuildCount = 0
        controller._test_openMenuRebuildObserver = { rebuiltMenu in
            guard rebuiltMenu === menu else { return }
            rootRebuildCount += 1
        }
        defer { controller._test_openMenuRebuildObserver = nil }

        controller.deferSwitcherMenuRebuildIfStillVisible(menu, provider: .codex)
        controller.refreshOpenMenuIfStillVisible(menu, provider: .codex)

        for _ in 0..<20 where rootRebuildCount == 0 {
            await Task.yield()
        }

        #expect(controller.openMenus[submenuKey] == nil)
        #expect(rootRebuildCount == 1)
        #expect(controller.menuVersions[menuKey] == controller.menuContentVersion)
    }

    @Test
    func `rapid switcher rebuild requests coalesce before populating open menu`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let menuKey = ObjectIdentifier(menu)
        controller.openMenus[menuKey] = menu
        controller.menuRefreshEnabledOverrideForTesting = true
        controller._test_providerSwitcherMenuRebuildDebounceNanoseconds = 0
        defer { controller._test_providerSwitcherMenuRebuildDebounceNanoseconds = nil }

        var rebuildCount = 0
        controller._test_openMenuRebuildObserver = { _ in
            rebuildCount += 1
        }
        defer { controller._test_openMenuRebuildObserver = nil }
        var refreshGateEntries = 0
        var pendingRefreshGates: [CheckedContinuation<Void, Never>] = []
        func resumePendingRefreshGates() {
            let gates = pendingRefreshGates
            pendingRefreshGates.removeAll(keepingCapacity: true)
            for gate in gates {
                gate.resume()
            }
        }
        controller._test_openMenuRefreshYieldOverride = {
            refreshGateEntries += 1
            await withCheckedContinuation { continuation in
                pendingRefreshGates.append(continuation)
            }
        }
        defer {
            resumePendingRefreshGates()
            controller._test_openMenuRefreshYieldOverride = nil
        }

        controller.deferSwitcherMenuRebuildIfStillVisible(menu, provider: .codex)
        for _ in 0..<20 where refreshGateEntries == 0 {
            await Task.yield()
        }
        #expect(refreshGateEntries == 1)
        #expect(rebuildCount == 0)

        controller.deferSwitcherMenuRebuildIfStillVisible(menu, provider: .codex)
        resumePendingRefreshGates()
        for _ in 0..<20 where refreshGateEntries < 2 {
            await Task.yield()
        }
        #expect(refreshGateEntries == 2)
        #expect(rebuildCount == 0)
        resumePendingRefreshGates()

        for _ in 0..<20 where rebuildCount == 0 {
            await Task.yield()
        }

        #expect(rebuildCount == 1)
        for _ in 0..<20 {
            await Task.yield()
        }
        #expect(rebuildCount == 1)
    }

    @Test
    func `codex parent menu open defers stale OpenAI web refresh until tracking ends`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.openAIWebAccessEnabled = true
        settings.openAIWebBatterySaverEnabled = true
        settings.codexCookieSource = .auto
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store.openAIDashboard = nil
        store.lastOpenAIDashboardSnapshot = nil
        store._test_providerRefreshOverride = { _ in }
        defer { store._test_providerRefreshOverride = nil }
        store._test_codexCreditsLoaderOverride = {
            CreditsSnapshot(remaining: 25, events: [], updatedAt: Date())
        }
        defer { store._test_codexCreditsLoaderOverride = nil }
        let blocker = BlockingManagedOpenAIDashboardLoader()
        var refreshInteractions: [ProviderInteraction] = []
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            refreshInteractions.append(ProviderInteractionContext.current)
            return try await blocker.awaitResult()
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        StatusItemController.setDeferredMenuInteractionRefreshDelayForTesting(.zero)
        defer { StatusItemController.resetDeferredMenuInteractionRefreshDelayForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)

        for _ in 0..<20 {
            await Task.yield()
        }
        #expect(await blocker.startedCount() == 0)
        #expect(controller.deferredOpenAIDashboardRefreshReason != nil)

        controller.menuDidClose(menu)
        await blocker.waitUntilStarted(count: 1)
        #expect(await blocker.startedCount() == 1)
        #expect(refreshInteractions == [.background])

        await blocker.resumeNext(with: .success(self.makeOpenAIDashboard(
            dailyBreakdown: [],
            updatedAt: Date())))
    }

    @Test
    func `programmatic parent menu close schedules deferred OpenAI web refresh`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.openAIWebAccessEnabled = true
        settings.openAIWebBatterySaverEnabled = true
        settings.codexCookieSource = .auto
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store.openAIDashboard = nil
        store.lastOpenAIDashboardSnapshot = nil
        store._test_codexCreditsLoaderOverride = {
            CreditsSnapshot(remaining: 0, events: [], updatedAt: Date())
        }
        defer { store._test_codexCreditsLoaderOverride = nil }
        let blocker = BlockingManagedOpenAIDashboardLoader()
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            try await blocker.awaitResult()
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        StatusItemController.setDeferredMenuInteractionRefreshDelayForTesting(.zero)
        defer { StatusItemController.resetDeferredMenuInteractionRefreshDelayForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        #expect(controller.deferredOpenAIDashboardRefreshReason != nil)

        controller.forgetClosedMenu(menu)
        await blocker.waitUntilStarted(count: 1)
        #expect(await blocker.startedCount() == 1)

        await blocker.resumeNext(with: .success(self.makeOpenAIDashboard(
            dailyBreakdown: [],
            updatedAt: Date())))
    }

    @Test
    func `deferred OpenAI web refresh retries after active store refresh completes`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.openAIWebAccessEnabled = true
        settings.openAIWebBatterySaverEnabled = true
        settings.codexCookieSource = .auto
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store.openAIDashboard = nil
        store.lastOpenAIDashboardSnapshot = nil
        store.isRefreshing = true
        let blocker = BlockingManagedOpenAIDashboardLoader()
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            try await blocker.awaitResult()
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        StatusItemController.setDeferredMenuInteractionRefreshDelayForTesting(.zero)
        defer { StatusItemController.resetDeferredMenuInteractionRefreshDelayForTesting() }

        controller.deferOpenAIDashboardRefreshUntilMenuCloses(reason: "parent menu open")
        controller.scheduleDeferredMenuInteractionRefreshIfNeeded()

        try? await Task.sleep(for: .milliseconds(50))
        #expect(await blocker.startedCount() == 0)
        #expect(controller.deferredOpenAIDashboardRefreshReason != nil)

        store.isRefreshing = false
        await blocker.waitUntilStarted(count: 1)
        #expect(await blocker.startedCount() == 1)

        await blocker.resumeNext(with: .success(self.makeOpenAIDashboard(
            dailyBreakdown: [],
            updatedAt: Date())))
    }

    @Test
    func `deferred OpenAI web refresh waits for deferred store refresh`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.openAIWebAccessEnabled = true
        settings.openAIWebBatterySaverEnabled = true
        settings.codexCookieSource = .auto
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store._setSnapshotForTesting(nil, provider: .codex)
        store.openAIDashboard = nil
        store.lastOpenAIDashboardSnapshot = nil
        let providerBlocker = BlockingStatusMenuProviderRefresh()
        store._test_providerRefreshOverride = { provider in
            guard provider == .codex else { return }
            await providerBlocker.awaitRelease()
        }
        defer { store._test_providerRefreshOverride = nil }
        let dashboardBlocker = BlockingManagedOpenAIDashboardLoader()
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            try await dashboardBlocker.awaitResult()
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        StatusItemController.setDeferredMenuInteractionRefreshDelayForTesting(.zero)
        defer { StatusItemController.resetDeferredMenuInteractionRefreshDelayForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        controller.menuDidClose(menu)

        await providerBlocker.waitUntilStarted()
        #expect(await dashboardBlocker.startedCount() == 0)

        await providerBlocker.resumeNext()
        await dashboardBlocker.waitUntilStarted(count: 1)
        #expect(await dashboardBlocker.startedCount() == 1)

        await dashboardBlocker.resumeNext(with: .success(self.makeOpenAIDashboard(
            dailyBreakdown: [],
            updatedAt: Date())))
    }

    @Test
    func `reopened menu keeps dashboard refresh deferred after store refresh`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.openAIWebAccessEnabled = true
        settings.openAIWebBatterySaverEnabled = true
        settings.codexCookieSource = .auto
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store._setSnapshotForTesting(nil, provider: .codex)
        store.openAIDashboard = nil
        store.lastOpenAIDashboardSnapshot = nil
        let providerBlocker = BlockingStatusMenuProviderRefresh()
        store._test_providerRefreshOverride = { provider in
            guard provider == .codex else { return }
            await providerBlocker.awaitRelease()
        }
        defer { store._test_providerRefreshOverride = nil }
        let dashboardBlocker = BlockingManagedOpenAIDashboardLoader()
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            try await dashboardBlocker.awaitResult()
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        StatusItemController.setDeferredMenuInteractionRefreshDelayForTesting(.zero)
        defer { StatusItemController.resetDeferredMenuInteractionRefreshDelayForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        controller.menuDidClose(menu)
        await providerBlocker.waitUntilStarted()

        let reopenedMenu = controller.makeMenu()
        controller.menuWillOpen(reopenedMenu)
        await providerBlocker.resumeNext()
        try? await Task.sleep(for: .milliseconds(50))
        #expect(await dashboardBlocker.startedCount() == 0)
        #expect(controller.deferredOpenAIDashboardRefreshReason != nil)

        controller.menuDidClose(reopenedMenu)
        await dashboardBlocker.waitUntilStarted(count: 1)
        #expect(await dashboardBlocker.startedCount() == 1)

        await dashboardBlocker.resumeNext(with: .success(self.makeOpenAIDashboard(
            dailyBreakdown: [],
            updatedAt: Date())))
    }

    @Test
    func `codex parent menu close refreshes recent dashboard cache with no chart history`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.openAIWebAccessEnabled = true
        settings.openAIWebBatterySaverEnabled = true
        settings.codexCookieSource = .auto
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store.openAIDashboard = self.makeOpenAIDashboard(dailyBreakdown: [], updatedAt: Date())
        store.lastOpenAIDashboardSnapshot = store.openAIDashboard
        store._test_providerRefreshOverride = { _ in }
        defer { store._test_providerRefreshOverride = nil }
        let blocker = BlockingManagedOpenAIDashboardLoader()
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            try await blocker.awaitResult()
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true
        StatusItemController.setDeferredMenuInteractionRefreshDelayForTesting(.zero)
        defer { StatusItemController.resetDeferredMenuInteractionRefreshDelayForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)

        for _ in 0..<20 {
            await Task.yield()
        }
        #expect(await blocker.startedCount() == 0)

        controller.menuDidClose(menu)
        await blocker.waitUntilStarted(count: 1)
        #expect(await blocker.startedCount() == 1)

        await blocker.resumeNext(with: .success(self.makeOpenAIDashboard(
            dailyBreakdown: [
                OpenAIDashboardDailyBreakdown(day: "2026-05-24", services: [], totalCreditsUsed: 12),
            ],
            updatedAt: Date())))
    }

    @Test
    func `codex parent menu open throttles recent empty dashboard retry`() async {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.openAIWebAccessEnabled = true
        settings.openAIWebBatterySaverEnabled = true
        settings.codexCookieSource = .auto
        self.enableOnlyCodex(settings)

        let now = Date()
        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store.openAIDashboard = self.makeOpenAIDashboard(dailyBreakdown: [], updatedAt: now.addingTimeInterval(-120))
        store.lastOpenAIDashboardSnapshot = store.openAIDashboard
        store.lastOpenAIDashboardAttemptAt = now.addingTimeInterval(-60)
        store._test_providerRefreshOverride = { _ in }
        defer { store._test_providerRefreshOverride = nil }
        let blocker = BlockingManagedOpenAIDashboardLoader()
        store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
            try await blocker.awaitResult()
        }
        defer { store._test_openAIDashboardLoaderOverride = nil }

        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        controller.menuRefreshEnabledOverrideForTesting = true

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        controller.menuDidClose(menu)

        try? await Task.sleep(for: .milliseconds(150))
        #expect(await blocker.startedCount() == 0)
    }

    @Test
    func `credits history arriving after open rebuilds parent menu after tracking ends`() async throws {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.showOptionalCreditsAndExtraUsage = true
        self.enableOnlyCodex(settings)

        let now = Date()
        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: true)
        store.credits = CreditsSnapshot(remaining: 100, events: [], updatedAt: now)
        store.openAIDashboard = self.makeOpenAIDashboard(dailyBreakdown: [], updatedAt: now)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = try #require(controller.menuVersions[key])
        #expect(self.menuItem(in: menu, id: "menuCardCredits") == nil)

        store.openAIDashboard = self.makeOpenAIDashboard(
            dailyBreakdown: [
                OpenAIDashboardDailyBreakdown(day: "2026-05-24", services: [], totalCreditsUsed: 12),
            ],
            updatedAt: now.addingTimeInterval(10))

        await self.waitUntilOpenMenuStaysStale(controller, key: key, after: openedVersion)

        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)

        await self.closeMenuAndWaitUntilFresh(controller, menu: menu, key: key)

        let creditsItem = try #require(self.menuItem(in: menu, id: "menuCardCredits"))
        #expect(
            creditsItem.submenu?.items.first?.representedObject as? String ==
                StatusItemController.creditsHistoryChartID)
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
    }

    @Test
    func `fresh dashboard history with same day count rebuilds parent menu after tracking ends`() async throws {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.showOptionalCreditsAndExtraUsage = true
        self.enableOnlyCodex(settings)

        let now = Date(timeIntervalSince1970: 100)
        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: true)
        store.credits = CreditsSnapshot(remaining: 100, events: [], updatedAt: now)
        store.openAIDashboard = self.makeOpenAIDashboard(
            dailyBreakdown: [
                OpenAIDashboardDailyBreakdown(day: "2026-05-24", services: [], totalCreditsUsed: 12),
            ],
            updatedAt: now)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = try #require(controller.menuVersions[key])
        _ = try #require(self.menuItem(in: menu, id: "menuCardCredits"))

        store.openAIDashboard = self.makeOpenAIDashboard(
            dailyBreakdown: [
                OpenAIDashboardDailyBreakdown(day: "2026-05-24", services: [], totalCreditsUsed: 99),
            ],
            updatedAt: now.addingTimeInterval(10))

        await self.waitUntilOpenMenuStaysStale(controller, key: key, after: openedVersion)

        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)

        await self.closeMenuAndWaitUntilFresh(controller, menu: menu, key: key)

        let creditsItem = try #require(self.menuItem(in: menu, id: "menuCardCredits"))
        #expect(creditsItem.submenu?.items.first?.representedObject as? String == StatusItemController
            .creditsHistoryChartID)
    }

    @Test
    func `token cost history arriving after open rebuilds parent menu after tracking ends`() async throws {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.costUsageEnabled = true
        settings.costSummaryDisplayStyle = .both
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = try #require(controller.menuVersions[key])
        #expect(self.menuItem(in: menu, id: "menuCardCost") == nil)

        store._setTokenSnapshotForTesting(self.makeCodexTokenCostSnapshot(), provider: .codex)

        await self.waitUntilOpenMenuStaysStale(controller, key: key, after: openedVersion)

        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)

        await self.closeMenuAndWaitUntilFresh(controller, menu: menu, key: key)

        let costItem = try #require(self.menuItem(in: menu, id: "menuCardCost"))
        #expect(costItem.submenu?.items.first?.representedObject as? String == StatusItemController.costHistoryChartID)
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
    }

    @Test
    func `fresh token cost history with same day count rebuilds parent menu after tracking ends`() async throws {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.costUsageEnabled = true
        settings.costSummaryDisplayStyle = .both
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store._setTokenSnapshotForTesting(
            self.makeCodexTokenCostSnapshot(
                sessionTokens: 123,
                sessionCostUSD: 0.12,
                last30DaysTokens: 456,
                last30DaysCostUSD: 1.23,
                updatedAt: Date(timeIntervalSince1970: 100)),
            provider: .codex)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = try #require(controller.menuVersions[key])
        _ = try #require(self.menuItem(in: menu, id: "menuCardCost"))

        store._setTokenSnapshotForTesting(
            self.makeCodexTokenCostSnapshot(
                sessionTokens: 999,
                sessionCostUSD: 0.99,
                last30DaysTokens: 888,
                last30DaysCostUSD: 8.88,
                updatedAt: Date(timeIntervalSince1970: 200)),
            provider: .codex)

        await self.waitUntilOpenMenuStaysStale(controller, key: key, after: openedVersion)

        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)

        await self.closeMenuAndWaitUntilFresh(controller, menu: menu, key: key)

        let costItem = try #require(self.menuItem(in: menu, id: "menuCardCost"))
        #expect(costItem.submenu?.items.first?.representedObject as? String == StatusItemController.costHistoryChartID)
    }

    @Test
    func `plan utilization history arriving after open rebuilds parent menu after tracking ends`() async throws {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        self.enableOnlyCodex(settings)

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = try #require(controller.menuVersions[key])
        let usageHistoryItem = try #require(self.menuItem(in: menu, id: "usageHistorySubmenu"))
        #expect(usageHistoryItem.submenu?.items.first?.representedObject as? String == StatusItemController
            .usageHistoryChartID)
        let openedRevision = store.planUtilizationHistoryRevision

        await store.recordPlanUtilizationHistorySample(
            provider: .codex,
            snapshot: self.makeCodexPlanUtilizationSnapshot(),
            now: Date())

        await self.waitUntilOpenMenuStaysStale(controller, key: key, after: openedVersion)

        #expect(store.planUtilizationHistoryRevision > openedRevision)
        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)

        await self.closeMenuAndWaitUntilFresh(controller, menu: menu, key: key)
    }

    @Test
    func `dashboard attachment authorization arriving after open rebuilds parent menu after close`() async throws {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        settings.openAIWebAccessEnabled = true
        settings.codexCookieSource = .auto
        self.enableOnlyCodex(settings)

        let now = Date()
        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        store.openAIDashboard = self.makeOpenAIDashboard(
            dailyBreakdown: [
                OpenAIDashboardDailyBreakdown(day: "2026-05-24", services: [], totalCreditsUsed: 12),
            ],
            updatedAt: now)
        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)
        let key = ObjectIdentifier(menu)
        controller.openMenus[key] = menu
        controller.menuRefreshEnabledOverrideForTesting = true

        let openedVersion = try #require(controller.menuVersions[key])
        #expect(store.openAIDashboardAttachmentRevision == 0)

        store.openAIDashboardAttachmentAuthorized = true

        await self.waitUntilOpenMenuStaysStale(controller, key: key, after: openedVersion)

        #expect(store.openAIDashboardAttachmentRevision == 1)
        #expect(controller.menuContentVersion != openedVersion)
        #expect(controller.menuVersions[key] == openedVersion)

        await self.closeMenuAndWaitUntilFresh(controller, menu: menu, key: key)
    }

    private func enableOnlyCodex(_ settings: SettingsStore) {
        let registry = ProviderRegistry.shared
        for provider in UsageProvider.allCases {
            guard let metadata = registry.metadata[provider] else { continue }
            settings.setProviderEnabled(provider: provider, metadata: metadata, enabled: provider == .codex)
        }
    }

    private func menuItem(in menu: NSMenu, id: String) -> NSMenuItem? {
        menu.items.first { ($0.representedObject as? String) == id }
    }

    private func waitUntilMenuVersionChanges(
        _ controller: StatusItemController,
        from version: Int?) async
    {
        for _ in 0..<20 where controller.menuContentVersion == version {
            await Task.yield()
        }
    }

    private func waitUntilOpenMenuStaysStale(
        _ controller: StatusItemController,
        key: ObjectIdentifier,
        after version: Int?) async
    {
        for _ in 0..<40 {
            guard controller.menuContentVersion != version else {
                await Task.yield()
                continue
            }
            guard controller.menuVersions[key] == version else {
                await Task.yield()
                continue
            }
            return
        }
    }

    private func closeMenuAndWaitUntilFresh(
        _ controller: StatusItemController,
        menu: NSMenu,
        key: ObjectIdentifier) async
    {
        controller.menuDidClose(menu)
        for _ in 0..<40 where controller.menuVersions[key] != controller.menuContentVersion {
            await Task.yield()
        }
        if controller.menuVersions[key] != controller.menuContentVersion {
            controller.menuWillOpen(menu)
        }
        for _ in 0..<40 where controller.menuVersions[key] != controller.menuContentVersion {
            await Task.yield()
        }
        #expect(controller.menuVersions[key] == controller.menuContentVersion)
    }

    private func waitUntilClosedMenuRebuildRemainsDeferred(
        _ controller: StatusItemController,
        key: ObjectIdentifier,
        openedVersion: Int?) async
    {
        for _ in 0..<40
            where controller.closedMenuRebuildTasks[key] != nil ||
            controller.menuVersions[key] != openedVersion
        {
            await Task.yield()
        }
    }

    private func makeOpenAIDashboard(
        dailyBreakdown: [OpenAIDashboardDailyBreakdown],
        updatedAt: Date) -> OpenAIDashboardSnapshot
    {
        OpenAIDashboardSnapshot(
            signedInEmail: "codex@example.com",
            codeReviewRemainingPercent: nil,
            creditEvents: [],
            dailyBreakdown: dailyBreakdown,
            usageBreakdown: [],
            creditsPurchaseURL: nil,
            updatedAt: updatedAt)
    }

    private func makeCodexTokenCostSnapshot(
        sessionTokens: Int = 123,
        sessionCostUSD: Double = 0.12,
        last30DaysTokens: Int = 456,
        last30DaysCostUSD: Double = 1.23,
        updatedAt: Date = Date()) -> CostUsageTokenSnapshot
    {
        CostUsageTokenSnapshot(
            sessionTokens: sessionTokens,
            sessionCostUSD: sessionCostUSD,
            last30DaysTokens: last30DaysTokens,
            last30DaysCostUSD: last30DaysCostUSD,
            daily: [
                CostUsageDailyReport.Entry(
                    date: "2026-05-24",
                    inputTokens: nil,
                    outputTokens: nil,
                    totalTokens: sessionTokens,
                    costUSD: last30DaysCostUSD,
                    modelsUsed: nil,
                    modelBreakdowns: nil),
            ],
            updatedAt: updatedAt)
    }

    private func makeCodexPlanUtilizationSnapshot() -> UsageSnapshot {
        UsageSnapshot(
            primary: RateWindow(
                usedPercent: 35,
                windowMinutes: 300,
                resetsAt: Date().addingTimeInterval(1800),
                resetDescription: nil),
            secondary: RateWindow(
                usedPercent: 42,
                windowMinutes: 10080,
                resetsAt: Date().addingTimeInterval(86400),
                resetDescription: nil),
            updatedAt: Date(),
            identity: ProviderIdentitySnapshot(
                providerID: .codex,
                accountEmail: "codex@example.com",
                accountOrganization: nil,
                loginMethod: "Plus Plan"))
    }

    /// The recent-interaction signal that `AdaptiveRefreshPolicy` reads has exactly one production
    /// entry point: `StatusItemController.menuWillOpen(_:)` calling `store.noteMenuOpened()`. Every
    /// other adaptive-refresh test drives `UsageStore` directly, so none of them would catch that
    /// wiring line being deleted — this test drives the real menu-open path instead.
    @Test
    func `menuWillOpen records the menu-open signal on the store`() {
        self.disableMenuCardsForTesting()
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false

        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        #expect(store.lastMenuOpenAt == nil)

        let controller = StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.menuWillOpen(menu)

        #expect(store.lastMenuOpenAt != nil)
    }
}

private actor BlockingStatusMenuProviderRefresh {
    private var continuations: [CheckedContinuation<Void, Never>] = []
    private var startWaiters: [CheckedContinuation<Void, Never>] = []
    private var started = 0

    func awaitRelease() async {
        self.started += 1
        self.resumeStartWaiters()
        await withCheckedContinuation { continuation in
            self.continuations.append(continuation)
        }
    }

    func waitUntilStarted() async {
        if self.started > 0 { return }
        await withCheckedContinuation { continuation in
            self.startWaiters.append(continuation)
        }
    }

    func resumeNext() {
        guard !self.continuations.isEmpty else { return }
        self.continuations.removeFirst().resume()
    }

    private func resumeStartWaiters() {
        let waiters = self.startWaiters
        self.startWaiters = []
        for waiter in waiters {
            waiter.resume()
        }
    }
}
