import AppKit
import CodexBarCore
import SwiftUI
import Testing
@testable import CodexBar

@MainActor
private final class RecordingMenuHighlightView: NSView, MenuCardHighlighting {
    private(set) var isHighlighted = false

    func setHighlighted(_ highlighted: Bool) {
        self.isHighlighted = highlighted
    }
}

extension StatusMenuTests {
    private func makeRecyclingController(settings: SettingsStore) -> StatusItemController {
        let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
        return StatusItemController(
            store: store,
            settings: settings,
            account: UsageFetcher().loadAccountInfo(),
            updater: DisabledUpdaterController(),
            preferencesSelection: PreferencesSelection(),
            statusBar: self.makeStatusBarForTesting())
    }

    private func cardViewIdentities(in menu: NSMenu) -> [String: ObjectIdentifier] {
        var identities: [String: ObjectIdentifier] = [:]
        for item in menu.items {
            guard let id = item.representedObject as? String else { continue }
            guard let view = item.view, view is any MenuCardMeasuring else { continue }
            identities[id] = ObjectIdentifier(view)
        }
        return identities
    }

    @Test
    func `menu card enabled state follows interaction affordances`() {
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        for renderingEnabled in [false, true] {
            StatusItemController.menuCardRenderingEnabled = renderingEnabled
            let settings = self.makeSettings()
            settings.statusChecksEnabled = false
            let controller = self.makeRecyclingController(settings: settings)
            defer { controller.releaseStatusItemsForTesting() }

            let informational = controller.makeMenuCardItem(Text("Info"), id: "info", width: 300)
            let embedded = controller.makeMenuCardItem(
                Text("Embedded"),
                id: "embedded",
                width: 300,
                containsInteractiveControls: true)
            let clickable = controller.makeMenuCardItem(Text("Click"), id: "click", width: 300, onClick: {})
            let submenu = controller.makeMenuCardItem(
                Text("Submenu"),
                id: "submenu",
                width: 300,
                submenu: NSMenu())

            #expect(!informational.isEnabled)
            #expect(embedded.isEnabled == renderingEnabled)
            #expect(clickable.isEnabled)
            #expect(submenu.isEnabled)
        }
    }

    @Test
    func `embedded controls stay enabled without highlighting the card`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let item = controller.makeMenuCardItem(
            Text("Embedded"),
            id: "embedded",
            width: 300,
            containsInteractiveControls: true)
        menu.addItem(item)

        controller.menu(menu, willHighlight: item)

        #expect(item.isEnabled)
        #expect(controller.highlightedMenuItems[ObjectIdentifier(menu)] == nil)
        guard let hosting = item.view as? ErasedMenuCardHostingView
        else {
            Issue.record("expected a card hosting view")
            return
        }
        #expect(!hosting.allowsMenuHighlight)
        #expect(!hosting.highlightState.isHighlighted)
    }

    @Test
    func `merged menu width uses widest provider action set`() {
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let narrow = [
            MenuDescriptor.Section(entries: [
                .action("Usage Dashboard", .dashboard),
            ]),
        ]
        let wide = [
            MenuDescriptor.Section(entries: [
                .action(String(repeating: "W", count: 60), .dashboard),
            ]),
        ]

        let narrowWidth = controller.measuredMenuCardWidth(for: [narrow])
        let stableWidth = controller.measuredMenuCardWidth(for: [narrow, wide])

        #expect(narrowWidth == StatusItemController.menuCardBaseWidth)
        #expect(stableWidth > narrowWidth)
        #expect(controller.measuredMenuCardWidth(for: [wide, narrow]) == stableWidth)
    }

    @Test
    func `menu width normalization includes usage history submenu row`() {
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let usageHistoryItem = controller.makeMenuCardItem(
            Text("Plan Usage"),
            id: "usageHistorySubmenu",
            width: StatusItemController.menuCardBaseWidth)
        menu.addItem(usageHistoryItem)
        menu.addItem(NSMenuItem(
            title: String(repeating: "W", count: 60),
            action: nil,
            keyEquivalent: ""))

        let expectedWidth = controller.renderedMenuWidth(for: menu)
        #expect(expectedWidth > StatusItemController.menuCardBaseWidth)

        controller.refreshMenuCardHeights(in: menu)

        #expect(abs((usageHistoryItem.view?.frame.width ?? 0) - expectedWidth) <= 0.5)
    }

    @Test
    func `rendered menu width keeps tracked window width after AppKit shrink`() {
        let width = StatusItemController.resolvedRenderedMenuWidth(
            menuWidth: 310,
            trackedWindowWidth: 356)

        #expect(width == 356)
        #expect(StatusItemController.resolvedRenderedMenuWidth(
            menuWidth: 310,
            trackedWindowWidth: nil) == 310)
    }

    @Test
    func `data only repopulate reuses menu card hosting views`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = false
        let registry = ProviderRegistry.shared
        for provider in UsageProvider.allCases {
            if let metadata = registry.metadata[provider] {
                settings.setProviderEnabled(provider: provider, metadata: metadata, enabled: provider == .codex)
            }
        }

        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = controller.makeMenu()
        controller.populateMenu(menu, provider: .codex)
        let firstPass = self.cardViewIdentities(in: menu)
        #expect(!firstPass.isEmpty)

        controller.invalidateMenus(allowStaleContentDuringDataRefresh: true)
        controller.populateMenu(menu, provider: .codex)
        let secondPass = self.cardViewIdentities(in: menu)

        #expect(secondPass.keys.sorted() == firstPass.keys.sorted())
        for (id, identity) in firstPass {
            #expect(secondPass[id] == identity, "card \(id) should reuse its hosting view")
        }
        #expect(controller.menuCardViewRecyclePool.isEmpty)
    }

    @Test
    func `merged data tick keeps row count and card views stable`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        settings.mergeIcons = true
        settings.mergedMenuLastSelectedWasOverview = false
        let registry = ProviderRegistry.shared
        let enabled: Set<UsageProvider> = [.codex, .claude]
        for provider in UsageProvider.allCases {
            if let metadata = registry.metadata[provider] {
                settings.setProviderEnabled(
                    provider: provider,
                    metadata: metadata,
                    enabled: enabled.contains(provider))
            }
        }
        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.selectedMenuProvider = .codex
        let menu = controller.makeMenu()
        controller.populateMenu(menu, provider: .codex)
        let itemCountBefore = menu.items.count
        let cardViewsBefore = self.cardViewIdentities(in: menu)
        #expect(!cardViewsBefore.isEmpty)

        controller.invalidateMenus(allowStaleContentDuringDataRefresh: true)
        controller.populateMenu(menu, provider: .codex)

        #expect(menu.items.count == itemCountBefore, "data-only repopulate should keep row count stable")
        let cardViewsAfter = self.cardViewIdentities(in: menu)
        for (id, identity) in cardViewsBefore {
            #expect(cardViewsAfter[id] == identity, "card \(id) should reuse its hosting view")
        }
    }

    @Test
    func `reconcile keeps matching edge rows when the middle differs`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        func plainItem(_ title: String) -> NSMenuItem {
            NSMenuItem(title: title, action: nil, keyEquivalent: "")
        }

        let menu = NSMenu()
        menu.addItem(controller.makeMenuCardItem(Text("card"), id: "menuCard", width: 300))
        menu.addItem(.separator())
        menu.addItem(plainItem("Old Provider Action"))
        menu.addItem(plainItem("Old Provider Detail"))
        menu.addItem(.separator())
        menu.addItem(plainItem("Settings"))
        let cardItem = menu.items[0]
        let cardView = cardItem.view
        let settingsItem = menu.items[5]

        let shapes = controller.menuContentShapes(in: menu, fromIndex: 0)
        controller.harvestRecyclableMenuCardViews(in: menu, fromIndex: 0, displacedSelection: nil)
        defer { controller.clearMenuCardViewRecyclePool() }

        let scratch = NSMenu()
        scratch.addItem(controller.makeMenuCardItem(Text("other provider card"), id: "menuCard", width: 300))
        scratch.addItem(.separator())
        scratch.addItem(plainItem("New Provider Action"))
        scratch.addItem(.separator())
        scratch.addItem(plainItem("Settings"))

        controller.reconcileMenuContent(menu, fromIndex: 0, shapes: shapes, with: scratch)

        #expect(menu.items.count == 5)
        #expect(menu.items[0] === cardItem, "card row should be updated in place")
        #expect(menu.items[0].view === cardView, "card hosting view should be recycled in place")
        #expect(menu.items[4] === settingsItem, "shared trailing row should be updated in place")
        #expect(menu.items[2].title == "New Provider Action")
    }

    @Test
    func `cached provider content replaces native image rows and preserves switch back items`() {
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let outgoing = NSMenuItem(title: "Status Page", action: nil, keyEquivalent: "")
        outgoing.image = NSImage(size: NSSize(width: 16, height: 16))
        let incoming = NSMenuItem(title: "Dashboard", action: nil, keyEquivalent: "")
        incoming.image = NSImage(size: NSSize(width: 16, height: 16))
        let menu = NSMenu()
        menu.addItem(outgoing)

        let displacedOutgoing = controller.replaceMenuContentKeepingRowsVisible(
            menu,
            fromIndex: 0,
            with: [incoming])

        #expect(menu.items.first === incoming)
        #expect(displacedOutgoing.first === outgoing)

        let displacedIncoming = controller.replaceMenuContentKeepingRowsVisible(
            menu,
            fromIndex: 0,
            with: displacedOutgoing)

        #expect(menu.items.first === outgoing)
        #expect(displacedIncoming.first === incoming)
    }

    @Test
    func `cached provider content swap preserves both item sets for switch back`() {
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let switcher = NSMenuItem(title: "Switcher", action: nil, keyEquivalent: "")
        let outgoing = [
            NSMenuItem(title: "Overview Card", action: nil, keyEquivalent: ""),
            NSMenuItem.separator(),
            NSMenuItem(title: "Overview Action", action: nil, keyEquivalent: ""),
        ]
        let incoming = [
            NSMenuItem(title: "Codex Card", action: nil, keyEquivalent: ""),
            NSMenuItem.separator(),
            NSMenuItem(title: "Codex Usage", action: nil, keyEquivalent: ""),
            NSMenuItem(title: "Codex Settings", action: nil, keyEquivalent: ""),
        ]
        let menu = NSMenu()
        menu.addItem(switcher)
        outgoing.forEach(menu.addItem)

        let displacedOutgoing = controller.replaceMenuContentKeepingRowsVisible(
            menu,
            fromIndex: 1,
            with: incoming)

        #expect(menu.items.first === switcher)
        #expect(menu.items.dropFirst().map(\.title) == ["Codex Card", "", "Codex Usage", "Codex Settings"])
        #expect(Array(menu.items[1...3]).map(ObjectIdentifier.init) == outgoing.map(ObjectIdentifier.init))
        #expect(displacedOutgoing.map(ObjectIdentifier.init) == incoming.prefix(3).map(ObjectIdentifier.init))
        #expect(displacedOutgoing.map(\.title) == ["Overview Card", "", "Overview Action"])

        let displacedIncoming = controller.replaceMenuContentKeepingRowsVisible(
            menu,
            fromIndex: 1,
            with: displacedOutgoing)

        #expect(Array(menu.items.dropFirst()).map(ObjectIdentifier.init) == outgoing.map(ObjectIdentifier.init))
        #expect(displacedIncoming.map(ObjectIdentifier.init) == incoming.map(ObjectIdentifier.init))
        #expect(displacedIncoming.allSatisfy { $0.menu == nil })
        #expect(displacedIncoming.map(\.title) == ["Codex Card", "", "Codex Usage", "Codex Settings"])
    }

    @Test
    func `reconcile preserves highlight on a retained custom action row`() {
        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let liveItem = NSMenuItem()
        liveItem.isEnabled = true
        liveItem.representedObject = "action"
        liveItem.view = RecordingMenuHighlightView()
        menu.addItem(liveItem)
        controller.menu(menu, willHighlight: liveItem)

        let replacementView = RecordingMenuHighlightView()
        let replacementItem = NSMenuItem()
        replacementItem.isEnabled = true
        replacementItem.representedObject = "action"
        replacementItem.view = replacementView
        let scratch = NSMenu()
        scratch.addItem(replacementItem)

        let shapes = controller.menuContentShapes(in: menu, fromIndex: 0)
        controller.reconcileMenuContent(menu, fromIndex: 0, shapes: shapes, with: scratch)

        #expect(menu.items[0] === liveItem)
        #expect(liveItem.view === replacementView)
        #expect(replacementView.isHighlighted)
        #expect(controller.highlightedMenuItems[ObjectIdentifier(menu)] === liveItem)
    }

    @Test
    func `reconcile restores highlight on a retained recycled card`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let liveItem = controller.makeMenuCardItem(Text("before"), id: "menuCard", width: 300, onClick: {})
        menu.addItem(liveItem)
        controller.menu(menu, willHighlight: liveItem)
        guard let hosting = liveItem.view as? ErasedMenuCardHostingView
        else {
            Issue.record("expected a card hosting view")
            return
        }

        let shapes = controller.menuContentShapes(in: menu, fromIndex: 0)
        controller.harvestRecyclableMenuCardViews(
            in: menu,
            fromIndex: 0,
            displacedSelection: nil,
            preserveHighlightedItem: true)
        defer { controller.clearMenuCardViewRecyclePool() }
        #expect(!hosting.highlightState.isHighlighted)
        #expect(controller.highlightedMenuItems[ObjectIdentifier(menu)] === liveItem)

        let scratch = NSMenu()
        scratch.addItem(controller.makeMenuCardItem(Text("after"), id: "menuCard", width: 300, onClick: {}))
        controller.reconcileMenuContent(menu, fromIndex: 0, shapes: shapes, with: scratch)

        #expect(menu.items[0] === liveItem)
        #expect(liveItem.view === hosting)
        #expect(hosting.highlightState.isHighlighted)
        #expect(controller.highlightedMenuItems[ObjectIdentifier(menu)] === liveItem)
    }

    @Test
    func `reconcile clears highlight when a retained card becomes disabled`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let liveItem = controller.makeMenuCardItem(Text("before"), id: "menuCard", width: 300, onClick: {})
        menu.addItem(liveItem)
        controller.menu(menu, willHighlight: liveItem)
        guard let liveView = liveItem.view as? ErasedMenuCardHostingView
        else {
            Issue.record("expected a card hosting view")
            return
        }
        #expect(liveView.highlightState.isHighlighted)
        #expect(controller.highlightedMenuItems[ObjectIdentifier(menu)] === liveItem)

        let shapes = controller.menuContentShapes(in: menu, fromIndex: 0)
        let scratch = NSMenu()
        scratch.addItem(controller.makeMenuCardItem(Text("after"), id: "menuCard", width: 300))
        controller.reconcileMenuContent(menu, fromIndex: 0, shapes: shapes, with: scratch)

        #expect(menu.items[0] === liveItem)
        #expect(!liveItem.isEnabled)
        #expect(controller.highlightedMenuItems[ObjectIdentifier(menu)] == nil)
        guard let rebuiltView = liveItem.view as? ErasedMenuCardHostingView
        else {
            Issue.record("expected the rebuilt card hosting view")
            return
        }
        #expect(!rebuiltView.highlightState.isHighlighted)
    }

    @Test
    func `harvesting consumes only the displaced selection cache entry`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let item = controller.makeMenuCardItem(Text("card"), id: "menuCard", width: 300, onClick: {})
        menu.addItem(item)

        let entry = CachedMergedSwitcherMenuContent(
            requiredMenuContentVersion: 0,
            menuWidth: 300,
            codexAccountDisplay: nil,
            tokenAccountDisplay: nil,
            localizationSignature: "",
            items: [])
        controller.mergedSwitcherContentCaches[ObjectIdentifier(menu)] = [
            .overview: entry,
            .provider(.codex): entry,
        ]
        controller.harvestRecyclableMenuCardViews(
            in: menu,
            fromIndex: 0,
            displacedSelection: .provider(.codex))
        defer { controller.clearMenuCardViewRecyclePool() }

        #expect(controller.menuCardViewRecyclePool.count == 1)
        #expect(item.view == nil)
        let remaining = controller.mergedSwitcherContentCaches[ObjectIdentifier(menu)]
        #expect(remaining?[.provider(.codex)] == nil)
        #expect(remaining?[.overview] != nil)
    }

    @Test
    func `harvesting consumes displaced cache when card rendering is disabled`() {
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = false
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let entry = CachedMergedSwitcherMenuContent(
            requiredMenuContentVersion: 0,
            menuWidth: 300,
            codexAccountDisplay: nil,
            tokenAccountDisplay: nil,
            localizationSignature: "",
            items: [])
        controller.mergedSwitcherContentCaches[ObjectIdentifier(menu)] = [
            .overview: entry,
            .provider(.codex): entry,
        ]

        controller.harvestRecyclableMenuCardViews(
            in: menu,
            fromIndex: 0,
            displacedSelection: .provider(.codex))

        let remaining = controller.mergedSwitcherContentCaches[ObjectIdentifier(menu)]
        #expect(remaining?[.provider(.codex)] == nil)
        #expect(remaining?[.overview] != nil)
    }

    @Test
    func `type compatible leftover is adopted across card identifiers`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let original = controller.makeMenuCardItem(Text("codex usage"), id: "menuCard-0", width: 300)
        menu.addItem(original)
        let originalView = original.view

        controller.harvestRecyclableMenuCardViews(in: menu, fromIndex: 0, displacedSelection: nil)
        defer { controller.clearMenuCardViewRecyclePool() }
        let switched = controller.makeMenuCardItem(Text("claude usage"), id: "menuCard", width: 300)

        #expect(switched.view === originalView)
        #expect(controller.menuCardViewRecyclePool.isEmpty)
    }

    @Test
    func `recycled card keeps its hosting view and highlight state`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let original = controller.makeMenuCardItem(Text("before"), id: "menuCard", width: 300)
        menu.addItem(original)
        guard let originalView = original.view as? ErasedMenuCardHostingView
        else {
            Issue.record("expected a card hosting view")
            return
        }

        controller.harvestRecyclableMenuCardViews(in: menu, fromIndex: 0, displacedSelection: nil)
        defer { controller.clearMenuCardViewRecyclePool() }
        let rebuilt = controller.makeMenuCardItem(Text("after"), id: "menuCard", width: 300)

        #expect(rebuilt.view === originalView)
        guard let rebuiltView = rebuilt.view as? ErasedMenuCardHostingView
        else {
            Issue.record("expected the recycled hosting view")
            return
        }
        #expect(rebuiltView.highlightState === originalView.highlightState)
        rebuiltView.setHighlighted(true)
        #expect(rebuiltView.highlightState.isHighlighted)
        rebuiltView.setHighlighted(false)
    }

    @Test
    func `recycled card clears button role when click action is removed`() {
        let clickable = MenuCardRowPayload(
            content: AnyView(Text("clickable")),
            showsSubmenuIndicator: false,
            submenuIndicatorAlignment: .trailing,
            submenuIndicatorTopPadding: 0,
            allowsMenuHighlight: true,
            containsInteractiveControls: false,
            usesGPUSelection: false,
            onClick: {})
        let hosting = MenuRowContainerView(payload: clickable, refreshMonitor: nil)

        #expect(hosting.accessibilityRole() == .button)

        hosting.replant(
            MenuCardRowPayload(
                content: AnyView(Text("informational")),
                showsSubmenuIndicator: false,
                submenuIndicatorAlignment: .trailing,
                submenuIndicatorTopPadding: 0,
                allowsMenuHighlight: false,
                containsInteractiveControls: false,
                usesGPUSelection: false,
                onClick: nil),
            refreshMonitor: nil)

        #expect(hosting.accessibilityRole() != .button)
    }

    @Test
    func `harvesting a highlighted card clears its highlight and tracking entry`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let item = controller.makeMenuCardItem(Text("card"), id: "menuCard", width: 300, onClick: {})
        menu.addItem(item)
        controller.menu(menu, willHighlight: item)
        guard let hosting = item.view as? ErasedMenuCardHostingView
        else {
            Issue.record("expected a card hosting view")
            return
        }
        #expect(hosting.highlightState.isHighlighted)
        #expect(controller.highlightedMenuItems[ObjectIdentifier(menu)] === item)

        controller.harvestRecyclableMenuCardViews(in: menu, fromIndex: 0, displacedSelection: nil)
        defer { controller.clearMenuCardViewRecyclePool() }

        #expect(!hosting.highlightState.isHighlighted)
        #expect(controller.highlightedMenuItems[ObjectIdentifier(menu)] == nil)

        let rebuilt = controller.makeMenuCardItem(Text("rebuilt"), id: "menuCard", width: 300, onClick: {})
        #expect(rebuilt.view === hosting)
        #expect(!hosting.highlightState.isHighlighted)
    }

    @Test
    func `same id with different content type reuses the erased hosting view`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        settings.refreshFrequency = .manual
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let original = controller.makeMenuCardItem(Text("text card"), id: "menuCard", width: 300)
        menu.addItem(original)
        let originalView = original.view

        controller.harvestRecyclableMenuCardViews(in: menu, fromIndex: 0, displacedSelection: nil)
        defer { controller.clearMenuCardViewRecyclePool() }
        let rebuilt = controller.makeMenuCardItem(Image(systemName: "clock"), id: "menuCard", width: 300)

        // Row content is erased to AnyView, so hosting views recycle across
        // content types: the SwiftUI payload is replanted in place instead of
        // detaching `item.view` (the tab-switch placeholder-flash mechanism).
        #expect(rebuilt.view != nil)
        #expect(rebuilt.view === originalView)
        #expect(controller.menuCardViewRecyclePool.isEmpty)
    }

    @Test
    func `gpu and standard rows share the recycling pool`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let overview = controller.makeMenuCardItem(
            Text("Overview"),
            id: "overview",
            width: 300,
            submenu: NSMenu(),
            usesGPUSelection: true,
            onClick: {})
        menu.addItem(overview)
        guard let overviewContainer = overview.view as? MenuRowContainerView else {
            Issue.record("expected a shared menu row container")
            return
        }

        controller.harvestRecyclableMenuCardViews(in: menu, fromIndex: 0, displacedSelection: nil)
        defer { controller.clearMenuCardViewRecyclePool() }
        let provider = controller.makeMenuCardItem(Text("Provider"), id: "provider", width: 300)

        #expect(provider.view === overviewContainer)
        #expect(!overviewContainer.usesGPUSelectionForTesting)
        #expect(!overviewContainer.hasGPUSelectionLayerForTesting)
        #expect(controller.menuCardViewRecyclePool.isEmpty)
    }

    @Test
    func `gpu selection highlight bypasses swiftui highlight state`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let menu = NSMenu()
        let item = controller.makeMenuCardItem(
            Text("Overview row"),
            id: "overview-gpu",
            width: 300,
            submenu: NSMenu(),
            usesGPUSelection: true,
            onClick: {})
        menu.addItem(item)

        guard let gpuView = item.view as? MenuRowContainerView
        else {
            Issue.record("expected a shared menu row container")
            return
        }

        // The menu highlights the AppKit row, but the hosted SwiftUI highlight state must stay false
        // so selection never re-invalidates the SwiftUI graph.
        controller.menu(menu, willHighlight: item)
        #expect(gpuView.isHighlightedForTesting)
        #expect(!gpuView.swiftUIHighlightStateIsHighlightedForTesting)

        controller.menu(menu, willHighlight: nil)
        #expect(!gpuView.isHighlightedForTesting)
        #expect(!gpuView.swiftUIHighlightStateIsHighlightedForTesting)
    }

    @Test
    func `overview and provider rows swap payloads without detaching their containers`() {
        StatusItemController.setMenuRefreshEnabledForTesting(false)
        let previousRendering = StatusItemController.menuCardRenderingEnabled
        StatusItemController.menuCardRenderingEnabled = true
        defer { StatusItemController.menuCardRenderingEnabled = previousRendering }

        let settings = self.makeSettings()
        settings.statusChecksEnabled = false
        let controller = self.makeRecyclingController(settings: settings)
        defer { controller.releaseStatusItemsForTesting() }

        let overviewItem = controller.makeMenuCardItem(
            Text("Overview"),
            id: "overview",
            width: 300,
            submenu: NSMenu(),
            usesGPUSelection: true,
            onClick: {})
        let providerItem = controller.makeMenuCardItem(
            Text("Provider"),
            id: "provider",
            width: 300,
            onClick: {})
        let menu = NSMenu()
        menu.addItem(overviewItem)
        guard let attachedContainer = overviewItem.view as? MenuRowContainerView,
              let cachedContainer = providerItem.view as? MenuRowContainerView
        else {
            Issue.record("expected shared menu row containers")
            return
        }

        let displaced = controller.replaceMenuContentKeepingRowsVisible(
            menu,
            fromIndex: 0,
            with: [providerItem])

        #expect(menu.items[0] === overviewItem)
        #expect(menu.items[0].view === attachedContainer)
        #expect(!attachedContainer.usesGPUSelectionForTesting)
        #expect(!attachedContainer.hasGPUSelectionLayerForTesting)
        #expect(displaced.count == 1)
        #expect(displaced[0] === providerItem)
        #expect(displaced[0].view === cachedContainer)
        #expect(cachedContainer.usesGPUSelectionForTesting)
        #expect(cachedContainer.hasGPUSelectionLayerForTesting)
    }
}
