import AppKit
import QuartzCore
import AlignerCore

@MainActor
final class QuickSwitchSessionController {
    static let overlayTitle = "Aligner Quick Switch"
    private static let closeRequestStillPresentMessage = "已发送关闭请求，但窗口仍在列表中。"
    private static let closeTargetNotFoundMessage = "没有找到这个窗口的关闭控件，已刷新列表。"
    private static let closeUnsupportedMessage = "这个窗口没有可用的关闭按钮。"
    private static let closeFailedMessage = "关闭请求没有被系统接受。"
    private static let closeVerificationRetryDelays: [TimeInterval] = [0.35, 0.70, 1.20]

    private let view = QuickSwitchRootView()
    private let snapshotLoader: any QuickSwitchSnapshotLoading
    private let screenshotProvider: any ScreenshotProviderProtocol
    private let windowActivationService: any WindowActivationServiceProtocol
    private let spaceActivationService: any SpaceActivationServiceProtocol
    private let windowCloseService: any WindowCloseServiceProtocol
    private let systemCriticalWindowDetector: any SystemCriticalWindowDetecting
    private let disableScreenshotRefresh: Bool
    private var waterfallViewMode: QuickSwitchWaterfallViewMode
    private var theme: ThemePreference
    private var closeConfirmationRequired: Bool
    private let onCloseConfirmationDisabled: (() -> Void)?
    private let debugOverlayWidth: CGFloat?
    private let debugKeySequence: [String]
    private let debugMouseSequence: [String]
    private let debugSystemCriticalAfter: TimeInterval?
    private lazy var coordinator = WindowCoordinator(
        contentView: view,
        title: Self.overlayTitle,
        debugOverlayWidth: debugOverlayWidth
    )
    private var currentSnapshot: QuickSwitchSnapshot?
    private var sourceViewModel: QuickSwitchViewModel?
    private var currentViewModel: QuickSwitchViewModel?
    private var lockedSpaceFilterID: UInt64?
    private var selectionBeforeSpaceFilter: QuickSwitchSelection?
    private var lastSpaceFilterAction: String?
    private var pendingLockedSpaceUnlockSpaceID: UInt64?
    private var pendingLockedSpaceUnlockTask: Task<Void, Never>?
    private var lastSnapshotError: String?
    private var snapshotTask: Task<Void, Never>?
    private var screenshotTask: Task<Void, Never>?
    private var systemCriticalMonitorTimer: Timer?
    private var debugSystemCriticalTimer: Timer?
    private var screenshotSession = ScreenshotCaptureSession()
    private var sessionGeneration = 0
    private var showStartTime: CFTimeInterval?
    private var overlayOpenElapsedMilliseconds: Double?
    private var snapshotStartElapsedMilliseconds: Double?
    private var snapshotDurationMilliseconds: Double?
    private var snapshotRanOnMainThread: Bool?
    private var lastCommittedSelection: QuickSwitchSelection?
    private var lastCommitSource: QuickSwitchCommitSource?
    private var lastActivationWindowID: UInt32?
    private var lastActivationResult: WindowActivationResult?
    private var lastActivationError: String?
    private var lastActivationDurationMilliseconds: Double?
    private var lastSpaceActivationSpaceID: UInt64?
    private var lastSpaceActivationDidRequestFocus: Bool?
    private var lastSpaceActivationDisplayIdentifier: String?
    private var lastSpaceActivationPreviousCurrentSpaceID: UInt64?
    private var lastSpaceActivationError: String?
    private var lastCloseTargetKind: String?
    private var lastCloseAppGroupIndex: Int?
    private var lastCloseAppName: String?
    private var lastCloseWindowID: UInt32?
    private var lastCloseResult: WindowCloseResult?
    private var lastCloseError: String?
    private var pendingCloseVerification: PendingCloseVerification?
    private var suppressedCloseTargets: [SuppressedCloseTarget] = []
    private var suppressedActivationWindowIDs = Set<UInt32>()
    private var lastDismissReason: DismissReason?
    private var lastSystemCriticalAction: String?
    private var lastSystemCriticalMonitorSource: String?
    private var lastSystemCriticalWindowCount = 0
    private var lastSystemCriticalWindowTitleHashes: [String] = []
    private var lastSystemCriticalWindowTitleLengths: [Int] = []
    private var lastSystemCriticalWindowTitleIsEmpty: [Bool] = []
    private var lastSystemCriticalWindowOwners: [String] = []
    private var lastSystemCriticalOverlayLevel: CGWindowLevel?
    private var systemCriticalDetectionCount = 0
    private var lifecycleTargetCycles = 0
    private var lifecycleCompletedCycles = 0
    private var lifecycleMaximumOverlayWindows = 0
    private var lifecycleMaximumVisibleOverlayWindows = 0
    private var lifecycleResidualOverlayWindows = 0
    private var lifecycleResidualVisibleOverlayWindows = 0
    var onSnapshotUpdated: (() -> Void)?
    var onToggleWaterfallViewMode: (() -> Void)?
    var onToggleTheme: (() -> Void)?

    private struct PendingCloseVerification {
        let request: QuickSwitchCloseRequest
        let generation: Int
        let attempt: Int
    }

    private enum SuppressedCloseTarget: Equatable {
        case app(AlignerApp)
        case window(UInt32)

        init(_ request: QuickSwitchCloseRequest) {
            switch request {
            case .app:
                self = .app(request.app)
            case .window:
                self = .window(request.windowID ?? 0)
            }
        }
    }

    init(
        snapshotLoader: any QuickSwitchSnapshotLoading = LiveQuickSwitchSnapshotLoader(),
        screenshotProvider: any ScreenshotProviderProtocol = ScreenCaptureKitScreenshotProvider(
            debugLogger: StderrScreenshotDebugLogger()
        ),
        windowActivationService: any WindowActivationServiceProtocol = CGWindowAXWindowService(
            spaceIDsByWindowIDProvider: { _ in [:] }
        ),
        spaceActivationService: any SpaceActivationServiceProtocol = PrivateSpaceActivationService(),
        windowCloseService: any WindowCloseServiceProtocol = CGWindowAXWindowService(
            spaceIDsByWindowIDProvider: { _ in [:] }
        ),
        systemCriticalWindowDetector: any SystemCriticalWindowDetecting = CGWindowSystemCriticalWindowDetector(),
        disableScreenshotRefresh: Bool = false,
        waterfallViewMode: QuickSwitchWaterfallViewMode = .verticalColumns,
        theme: ThemePreference = .light,
        closeConfirmationRequired: Bool = true,
        onCloseConfirmationDisabled: (() -> Void)? = nil,
        debugHoveredAppGroupIndex: Int? = nil,
        debugOverlayWidth: CGFloat? = nil,
        debugKeySequence: [String] = [],
        debugMouseSequence: [String] = [],
        debugSystemCriticalAfter: TimeInterval? = nil
    ) {
        self.snapshotLoader = snapshotLoader
        self.screenshotProvider = screenshotProvider
        self.windowActivationService = windowActivationService
        self.spaceActivationService = spaceActivationService
        self.windowCloseService = windowCloseService
        self.systemCriticalWindowDetector = systemCriticalWindowDetector
        self.disableScreenshotRefresh = disableScreenshotRefresh
        self.waterfallViewMode = waterfallViewMode
        self.theme = theme
        self.closeConfirmationRequired = closeConfirmationRequired
        self.onCloseConfirmationDisabled = onCloseConfirmationDisabled
        self.debugOverlayWidth = debugOverlayWidth
        self.debugKeySequence = debugKeySequence
        self.debugMouseSequence = debugMouseSequence
        self.debugSystemCriticalAfter = debugSystemCriticalAfter
        view.setDebugHoveredAppGroupIndex(debugHoveredAppGroupIndex)
    }

    var isVisible: Bool {
        coordinator.isQuickSwitchVisible
    }

    func show() {
        sessionGeneration += 1
        let generation = sessionGeneration
        DevelopmentDiagnostics.log("quickSwitch.session.show.start", [
            "generation": generation,
            "wasVisible": isVisible
        ])
        showStartTime = CACurrentMediaTime()
        overlayOpenElapsedMilliseconds = nil
        snapshotStartElapsedMilliseconds = nil
        snapshotDurationMilliseconds = nil
        snapshotRanOnMainThread = nil
        lastCommittedSelection = nil
        lastCommitSource = nil
        lastActivationWindowID = nil
        lastActivationResult = nil
        lastActivationError = nil
        lastActivationDurationMilliseconds = nil
        lastSpaceActivationSpaceID = nil
        lastSpaceActivationDidRequestFocus = nil
        lastSpaceActivationDisplayIdentifier = nil
        lastSpaceActivationPreviousCurrentSpaceID = nil
        lastSpaceActivationError = nil
        lastCloseTargetKind = nil
        lastCloseAppGroupIndex = nil
        lastCloseAppName = nil
        lastCloseWindowID = nil
        lastCloseResult = nil
        lastCloseError = nil
        pendingCloseVerification = nil
        suppressedCloseTargets = []
        suppressedActivationWindowIDs = []
        cancelPendingLockedSpaceUnlock()
        lockedSpaceFilterID = nil
        selectionBeforeSpaceFilter = nil
        lastSpaceFilterAction = nil
        lastDismissReason = nil
        lastSystemCriticalAction = nil
        lastSystemCriticalMonitorSource = nil
        lastSystemCriticalWindowCount = 0
        lastSystemCriticalWindowTitleHashes = []
        lastSystemCriticalWindowTitleLengths = []
        lastSystemCriticalWindowTitleIsEmpty = []
        lastSystemCriticalWindowOwners = []
        lastSystemCriticalOverlayLevel = nil
        systemCriticalDetectionCount = 0
        invalidateSystemCriticalMonitoring()
        screenshotTask?.cancel()
        screenshotTask = nil
        screenshotSession = ScreenshotCaptureSession()
        clearSnapshotState()
        view.onEscape = { [weak self] in
            self?.hide(reason: .escape)
        }
        view.onCommitSelection = { [weak self] selection, source in
            self?.commitSelection(selection, source: source)
        }
        view.onSpaceLaneClick = { [weak self] spaceID, clickCount in
            self?.handleSpaceLaneClick(spaceID, clickCount: clickCount)
        }
        view.onBackgroundClick = { [weak self] in
            self?.handleBackgroundClick() ?? false
        }
        view.onToggleWaterfallViewMode = { [weak self] in
            self?.onToggleWaterfallViewMode?()
        }
        view.onToggleTheme = { [weak self] in
            self?.onToggleTheme?()
        }
        view.setSuppressEventInput(!debugMouseSequence.isEmpty || !debugKeySequence.isEmpty)
        view.onRequestClose = { [weak self] request in
            self?.requestClose(request)
        }
        view.onCloseConfirmationDisabled = { [weak self] in
            guard let self else { return }
            self.closeConfirmationRequired = false
            self.onCloseConfirmationDisabled?()
        }
        view.setCloseConfirmationRequired(closeConfirmationRequired)
        view.setWaterfallViewMode(waterfallViewMode)
        view.setTheme(theme)
        view.apply(viewModel: nil)
        view.beginPerformanceFirstFrameMeasurement()
        coordinator.openQuickSwitch()
        coordinator.promoteForTextInput()
        view.window?.makeFirstResponder(view)
        overlayOpenElapsedMilliseconds = elapsedSinceShowStart()
        updateLifecycleWindowHighWaterMark()
        startSystemCriticalMonitoring()
        scheduleSnapshotRefresh(for: generation)
        DevelopmentDiagnostics.log("quickSwitch.session.show.end", [
            "generation": generation,
            "overlayOpenElapsedMilliseconds": overlayOpenElapsedMilliseconds,
            "overlayLevel": coordinator.currentLevel,
            "windowVisible": view.window?.isVisible ?? false
        ])
    }

    func hide(reason: DismissReason = .userClosed) {
        DevelopmentDiagnostics.log("quickSwitch.session.hide.start", [
            "reason": Self.dismissReasonString(reason),
            "wasVisible": isVisible,
            "generation": sessionGeneration
        ])
        sessionGeneration += 1
        lastDismissReason = reason
        snapshotTask?.cancel()
        snapshotTask = nil
        screenshotTask?.cancel()
        screenshotTask = nil
        cancelPendingLockedSpaceUnlock()
        pendingCloseVerification = nil
        view.clearCloseFeedback()
        invalidateSystemCriticalMonitoring()
        coordinator.closeQuickSwitch(reason: reason)
        lifecycleResidualOverlayWindows = overlayWindowCount()
        lifecycleResidualVisibleOverlayWindows = visibleOverlayWindowCount()
        updateLifecycleWindowHighWaterMark()
        DevelopmentDiagnostics.log("quickSwitch.session.hide.end", [
            "reason": Self.dismissReasonString(reason),
            "generation": sessionGeneration,
            "residualOverlayWindows": lifecycleResidualOverlayWindows,
            "residualVisibleOverlayWindows": lifecycleResidualVisibleOverlayWindows
        ])
    }

    func refocus() {
        DevelopmentDiagnostics.log("quickSwitch.session.refocus")
        coordinator.promoteForTextInput()
        view.window?.makeFirstResponder(view)
    }

    func retriggerFromShortcut() {
        DevelopmentDiagnostics.log("quickSwitch.session.retriggerFromShortcut")
        guard isVisible else {
            show()
            return
        }

        guard currentViewModel != nil else {
            DevelopmentDiagnostics.log("quickSwitch.session.retriggerFromShortcut.noSnapshot", [
                "action": "fallbackShow"
            ])
            show()
            return
        }

        let didCycle = view.hoverNextAppGroupIndex()
        DevelopmentDiagnostics.log("quickSwitch.session.retriggerFromShortcut.hoverApp", [
            "didCycle": didCycle
        ])

        if !didCycle {
            show()
        }
    }

    func setWaterfallViewMode(_ mode: QuickSwitchWaterfallViewMode) {
        guard waterfallViewMode != mode else { return }

        waterfallViewMode = mode
        DevelopmentDiagnostics.log("quickSwitch.session.waterfallViewModeChanged", [
            "mode": mode.rawValue,
            "visible": isVisible,
            "hasViewModel": currentViewModel != nil
        ])

        view.setWaterfallViewMode(mode)
        if isVisible, let currentViewModel {
            view.applyProjected(viewModel: currentViewModel)
        }
        onSnapshotUpdated?()
    }

    func setTheme(_ theme: ThemePreference) {
        guard self.theme != theme else { return }

        self.theme = theme
        DevelopmentDiagnostics.log("quickSwitch.session.themeChanged", [
            "theme": theme.rawValue,
            "visible": isVisible,
            "hasViewModel": currentViewModel != nil
        ])

        view.setTheme(theme)
        onSnapshotUpdated?()
    }

    func reportDictionary() -> [String: Any] {
        let spaceCount = currentViewModel?.spaceCount ?? 0
        let appCount = currentViewModel?.appShelf.count ?? 0
        let columnCount = currentViewModel?.waterfallColumns.count ?? 0
        let windowCount = currentViewModel?.windowCount ?? 0

        return [
            "snapshotLoaded": currentSnapshot != nil,
            "snapshotError": lastSnapshotError ?? NSNull(),
            "quickSwitchVisible": isVisible,
            "diagnosticLogLocationKind": DevelopmentDiagnostics.pathSummary(DevelopmentDiagnostics.logPath)["locationKind"] ?? "unknown",
            "diagnosticLogBasename": DevelopmentDiagnostics.pathSummary(DevelopmentDiagnostics.logPath)["basename"] ?? "aligner-dev.log",
            "displayCount": currentViewModel?.displays.count ?? 0,
            "spaceCount": spaceCount,
            "appCount": appCount,
            "columnCount": columnCount,
            "windowCount": windowCount,
            "initialSelectionWindowID": currentViewModel?.initialSelection?.windowID ?? NSNull(),
            "appShelfNames": currentViewModel?.appShelf.map(\.app.name) ?? [],
            "spaceLabels": currentViewModel?.displays.flatMap { $0.spaces.map(\.label) } ?? [],
            "spaceFilterLockedSpaceID": lockedSpaceFilterID ?? NSNull(),
            "spaceFilterActive": lockedSpaceFilterID != nil,
            "lastSpaceFilterAction": lastSpaceFilterAction ?? NSNull(),
            "overlayOpenElapsedMilliseconds": overlayOpenElapsedMilliseconds ?? NSNull(),
            "snapshotStartElapsedMilliseconds": snapshotStartElapsedMilliseconds ?? NSNull(),
            "snapshotDurationMilliseconds": snapshotDurationMilliseconds ?? NSNull(),
            "snapshotRanOnMainThread": snapshotRanOnMainThread ?? NSNull(),
            "lastCommittedAppGroupIndex": lastCommittedSelection?.appGroupIndex ?? NSNull(),
            "lastCommittedWindowIndex": lastCommittedSelection?.windowIndex ?? NSNull(),
            "lastCommittedWindowID": lastCommittedSelection?.windowID ?? NSNull(),
            "lastCommitSource": lastCommitSource?.rawValue ?? NSNull(),
            "lastActivationWindowID": lastActivationWindowID ?? NSNull(),
            "lastActivationResult": lastActivationResult.map(Self.activationResultString) ?? NSNull(),
            "lastActivationError": lastActivationError ?? NSNull(),
            "lastActivationDurationMilliseconds": lastActivationDurationMilliseconds ?? NSNull(),
            "lastSpaceActivationSpaceID": lastSpaceActivationSpaceID ?? NSNull(),
            "lastSpaceActivationDidRequestFocus": lastSpaceActivationDidRequestFocus ?? NSNull(),
            "lastSpaceActivationDisplayIdentifier": lastSpaceActivationDisplayIdentifier ?? NSNull(),
            "lastSpaceActivationPreviousCurrentSpaceID": lastSpaceActivationPreviousCurrentSpaceID ?? NSNull(),
            "lastSpaceActivationError": lastSpaceActivationError ?? NSNull(),
            "lastCloseTargetKind": lastCloseTargetKind ?? NSNull(),
            "lastCloseAppGroupIndex": lastCloseAppGroupIndex ?? NSNull(),
            "lastCloseAppName": lastCloseAppName ?? NSNull(),
            "lastCloseWindowID": lastCloseWindowID ?? NSNull(),
            "lastCloseResult": lastCloseResult.map(Self.closeResultString) ?? NSNull(),
            "lastCloseError": lastCloseError ?? NSNull(),
            "suppressedCloseTargetCount": suppressedCloseTargets.count,
            "suppressedActivationWindowCount": suppressedActivationWindowIDs.count,
            "suppressedActivationWindowIDs": Array(suppressedActivationWindowIDs).sorted(),
            "lastDismissReason": lastDismissReason.map(Self.dismissReasonString) ?? NSNull(),
            "overlayLevel": Int(coordinator.currentLevel),
            "overlayMainMenuLevel": Int(CGWindowLevelForKey(.mainMenuWindow)),
            "overlayBelowRescueSystemWindows": coordinator.currentLevel < CGWindowLevelForKey(.mainMenuWindow),
            "lastSystemCriticalAction": lastSystemCriticalAction ?? NSNull(),
            "lastSystemCriticalMonitorSource": lastSystemCriticalMonitorSource ?? NSNull(),
            "lastSystemCriticalWindowCount": lastSystemCriticalWindowCount,
            "lastSystemCriticalWindowTitleHashes": lastSystemCriticalWindowTitleHashes,
            "lastSystemCriticalWindowTitleLengths": lastSystemCriticalWindowTitleLengths,
            "lastSystemCriticalWindowTitleIsEmpty": lastSystemCriticalWindowTitleIsEmpty,
            "lastSystemCriticalWindowOwners": lastSystemCriticalWindowOwners,
            "lastSystemCriticalOverlayLevel": lastSystemCriticalOverlayLevel.map { Int($0) } ?? NSNull(),
            "systemCriticalDetectionCount": systemCriticalDetectionCount,
            "lifecycleTargetCycles": lifecycleTargetCycles,
            "lifecycleCompletedCycles": lifecycleCompletedCycles,
            "lifecycleMaximumOverlayWindows": lifecycleMaximumOverlayWindows,
            "lifecycleMaximumVisibleOverlayWindows": lifecycleMaximumVisibleOverlayWindows,
            "lifecycleResidualOverlayWindows": lifecycleResidualOverlayWindows,
            "lifecycleResidualVisibleOverlayWindows": lifecycleResidualVisibleOverlayWindows,
            "lifecycleGatePassed": lifecycleTargetCycles == 0
                || (
                    lifecycleCompletedCycles == lifecycleTargetCycles
                        && lifecycleMaximumOverlayWindows <= 1
                        && lifecycleMaximumVisibleOverlayWindows <= 1
                        && lifecycleResidualOverlayWindows <= 1
                        && lifecycleResidualVisibleOverlayWindows == 0
                ),
            "rootView": view.reportDictionary()
        ]
    }

    func prepareLifecycleRun(targetCycles: Int) {
        lifecycleTargetCycles = max(0, targetCycles)
        lifecycleCompletedCycles = 0
        lifecycleMaximumOverlayWindows = 0
        lifecycleMaximumVisibleOverlayWindows = 0
        lifecycleResidualOverlayWindows = 0
        lifecycleResidualVisibleOverlayWindows = 0
        view.resetPerformanceMeasurements()
    }

    func markLifecycleCycleCompleted() {
        lifecycleCompletedCycles += 1
        updateLifecycleWindowHighWaterMark()
    }

    func finalizeLifecycleRun() {
        lifecycleResidualOverlayWindows = overlayWindowCount()
        lifecycleResidualVisibleOverlayWindows = visibleOverlayWindowCount()
        updateLifecycleWindowHighWaterMark()
    }

    private func scheduleSnapshotRefresh(
        for generation: Int,
        replayDebugCommands: Bool = true,
        delay: TimeInterval = 0
    ) {
        DevelopmentDiagnostics.log("quickSwitch.snapshot.schedule", [
            "generation": generation,
            "visible": isVisible,
            "replayDebugCommands": replayDebugCommands,
            "delayMilliseconds": Int(delay * 1_000)
        ])
        snapshotTask?.cancel()
        let loader = snapshotLoader
        let showStartTime = showStartTime
        snapshotTask = Task { [weak self] in
            guard let self, !Task.isCancelled else { return }
            if delay > 0 {
                try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
                guard !Task.isCancelled else { return }
            }

            let loadTask = Task.detached(priority: .userInitiated) {
                SnapshotLoadResult.load(using: loader, showStartTime: showStartTime)
            }
            let result = await withTaskCancellationHandler {
                await loadTask.value
            } onCancel: {
                loadTask.cancel()
            }

            guard !Task.isCancelled else { return }
            self.applySnapshotResult(
                result,
                generation: generation,
                replayDebugCommands: replayDebugCommands
            )
        }
    }

    private func clearSnapshotState() {
        currentSnapshot = nil
        sourceViewModel = nil
        currentViewModel = nil
        lastSnapshotError = nil
    }

    private func startSystemCriticalMonitoring() {
        invalidateSystemCriticalMonitoring()
        checkForSystemCriticalWindows(source: "initial")
        guard isVisible else { return }

        let monitorTimer = Timer(timeInterval: 0.20, repeats: true) { [weak self] _ in
            Task { @MainActor [weak self] in
                self?.checkForSystemCriticalWindows(source: "poll")
            }
        }
        RunLoop.main.add(monitorTimer, forMode: .common)
        systemCriticalMonitorTimer = monitorTimer

        if let debugSystemCriticalAfter {
            let debugTimer = Timer(timeInterval: debugSystemCriticalAfter, repeats: false) { [weak self] _ in
                Task { @MainActor [weak self] in
                    self?.handleSystemCriticalWindows(
                        [
                            SystemCriticalWindowRecord(
                                ownerName: "SecurityAgent",
                                title: "Debug Security Confirmation",
                                processIdentifier: 0,
                                layer: Int(CGWindowLevelForKey(.mainMenuWindow)),
                                bounds: .zero
                            )
                        ],
                        source: "debug"
                    )
                }
            }
            RunLoop.main.add(debugTimer, forMode: .common)
            debugSystemCriticalTimer = debugTimer
        }
    }

    private func invalidateSystemCriticalMonitoring() {
        systemCriticalMonitorTimer?.invalidate()
        systemCriticalMonitorTimer = nil
        debugSystemCriticalTimer?.invalidate()
        debugSystemCriticalTimer = nil
    }

    private func checkForSystemCriticalWindows(source: String) {
        guard isVisible else { return }

        let criticalWindows = systemCriticalWindowDetector.visibleSystemCriticalWindows()
        guard !criticalWindows.isEmpty else { return }

        handleSystemCriticalWindows(criticalWindows, source: source)
    }

    private func handleSystemCriticalWindows(_ windows: [SystemCriticalWindowRecord], source: String) {
        guard isVisible, !windows.isEmpty else { return }

        systemCriticalDetectionCount += 1
        lastSystemCriticalAction = "closeOverlay"
        lastSystemCriticalMonitorSource = source
        lastSystemCriticalWindowCount = windows.count
        lastSystemCriticalWindowTitleHashes = windows.map { DevelopmentDiagnostics.stableFingerprint($0.title) }
        lastSystemCriticalWindowTitleLengths = windows.map { $0.title.count }
        lastSystemCriticalWindowTitleIsEmpty = windows.map { $0.title.isEmpty }
        lastSystemCriticalWindowOwners = windows.map(\.ownerName)
        lastSystemCriticalOverlayLevel = coordinator.currentLevel
        hide(reason: .systemCriticalWindow)
        onSnapshotUpdated?()
    }

    private func commitSelection(
        _ selection: QuickSwitchSelection,
        source: QuickSwitchCommitSource,
        in viewModel: QuickSwitchViewModel? = nil
    ) {
        DevelopmentDiagnostics.log("quickSwitch.activation.commit.start", [
            "source": source.rawValue,
            "appGroupIndex": selection.appGroupIndex,
            "windowIndex": selection.windowIndex,
            "windowID": selection.windowID
        ])
        lastCommittedSelection = selection
        lastCommitSource = source
        lastActivationWindowID = selection.windowID
        lastActivationError = nil
        lastActivationDurationMilliseconds = nil

        guard let window = window(for: selection, in: viewModel ?? currentViewModel) else {
            lastActivationResult = .windowNotFound
            suppressActivationWindowID(selection.windowID, reason: "selectionMissing")
            DevelopmentDiagnostics.log("quickSwitch.activation.commit.windowNotFound", [
                "source": source.rawValue,
                "appGroupIndex": selection.appGroupIndex,
                "windowIndex": selection.windowIndex,
                "windowID": selection.windowID
            ])
            onSnapshotUpdated?()
            return
        }

        DevelopmentDiagnostics.log("quickSwitch.activation.commit.hideBeforeActivation", [
            "source": source.rawValue,
            "windowID": window.id
        ])
        hide(reason: .userClosed)

        let activationStartedAt = CACurrentMediaTime()
        do {
            lastActivationResult = try windowActivationService.activate(window: window)
            lastActivationDurationMilliseconds = (CACurrentMediaTime() - activationStartedAt) * 1_000
            DevelopmentDiagnostics.log("quickSwitch.activation.commit.result", [
                "source": source.rawValue,
                "windowID": window.id,
                "appName": window.app.name,
                "appPID": window.app.processIdentifier,
                "titleHash": DevelopmentDiagnostics.stableFingerprint(window.title),
                "titleLength": window.title.count,
                "titleIsEmpty": window.title.isEmpty,
                "result": lastActivationResult.map(Self.activationResultString),
                "durationMilliseconds": lastActivationDurationMilliseconds
            ])
        } catch {
            lastActivationDurationMilliseconds = (CACurrentMediaTime() - activationStartedAt) * 1_000
            let errorSummary = DevelopmentDiagnostics.errorSummaryString(error)
            lastActivationResult = .failed(errorSummary)
            lastActivationError = errorSummary
            var fields: [String: CustomStringConvertible?] = [
                "source": source.rawValue,
                "windowID": window.id,
                "appName": window.app.name,
                "titleHash": DevelopmentDiagnostics.stableFingerprint(window.title),
                "titleLength": window.title.count,
                "titleIsEmpty": window.title.isEmpty,
                "durationMilliseconds": lastActivationDurationMilliseconds
            ]
            DevelopmentDiagnostics.errorSummaryFields(error).forEach { fields[$0.key] = $0.value }
            DevelopmentDiagnostics.log("quickSwitch.activation.commit.error", fields)
        }

        if shouldSuppressAfterActivationResult(lastActivationResult) {
            suppressActivationWindowID(window.id, reason: lastActivationResult.map(Self.activationResultString) ?? "unknown")
        }

        if shouldCloseAfterActivation(lastActivationResult) {
            DevelopmentDiagnostics.log("quickSwitch.activation.commit.closeAfterActivation", [
                "result": lastActivationResult.map(Self.activationResultString)
            ])
        } else {
            DevelopmentDiagnostics.log("quickSwitch.activation.commit.refreshAfterActivation", [
                "result": lastActivationResult.map(Self.activationResultString)
            ])
        }
        onSnapshotUpdated?()
    }

    private func handleSpaceLaneClick(_ spaceID: UInt64, clickCount: Int) {
        if clickCount >= 2 {
            if pendingLockedSpaceUnlockSpaceID == spaceID || lockedSpaceFilterID == spaceID {
                cancelPendingLockedSpaceUnlock()
                activateLockedSpace(spaceID)
            } else {
                DevelopmentDiagnostics.log("quickSwitch.spaceFilter.doubleClick.ignored", [
                    "spaceID": spaceID,
                    "lockedSpaceID": lockedSpaceFilterID ?? NSNull(),
                    "pendingUnlockSpaceID": pendingLockedSpaceUnlockSpaceID ?? NSNull()
                ])
            }
            return
        }

        cancelPendingLockedSpaceUnlock()
        if lockedSpaceFilterID == spaceID {
            scheduleLockedSpaceUnlock(spaceID)
            return
        }

        applySpaceLaneSingleClick(spaceID)
    }

    private func applySpaceLaneSingleClick(_ spaceID: UInt64) {
        guard let sourceViewModel else {
            lastSpaceFilterAction = "blockedNoSourceViewModel"
            DevelopmentDiagnostics.log("quickSwitch.spaceFilter.click.blocked", [
                "spaceID": spaceID,
                "reason": "noSourceViewModel"
            ])
            onSnapshotUpdated?()
            return
        }

        if let fullscreenSelection = QuickSwitchSpaceFilterPolicy.singleFullscreenSelection(
            inSpaceID: spaceID,
            viewModel: sourceViewModel
        ) {
            lockedSpaceFilterID = nil
            selectionBeforeSpaceFilter = nil
            lastSpaceFilterAction = "activateSingleFullscreen"
            let visibleViewModel = projectedViewModel(from: sourceViewModel)
            currentViewModel = visibleViewModel
            view.applyProjected(viewModel: visibleViewModel)
            DevelopmentDiagnostics.log("quickSwitch.spaceFilter.singleFullscreenActivate", [
                "spaceID": spaceID,
                "windowID": fullscreenSelection.windowID
            ])
            commitSelection(fullscreenSelection, source: .mouse, in: sourceViewModel)
            return
        }

        let preferredSelection: QuickSwitchSelection?
        if lockedSpaceFilterID == spaceID {
            lockedSpaceFilterID = nil
            preferredSelection = selectionBeforeSpaceFilter
            selectionBeforeSpaceFilter = nil
            lastSpaceFilterAction = "unlock"
        } else {
            if lockedSpaceFilterID == nil {
                selectionBeforeSpaceFilter = view.effectiveSelectionForProjection()
                lastSpaceFilterAction = "lock"
            } else {
                lastSpaceFilterAction = "switch"
            }
            lockedSpaceFilterID = spaceID
            preferredSelection = nil
        }

        let visibleViewModel = projectedViewModel(
            from: sourceViewModel,
            preferredSelection: preferredSelection
        )
        currentViewModel = visibleViewModel
        view.applyProjected(viewModel: visibleViewModel)
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.apply", [
            "spaceID": spaceID,
            "lockedSpaceID": lockedSpaceFilterID,
            "action": lastSpaceFilterAction,
            "visibleAppCount": visibleViewModel.appShelf.count,
            "visibleWindowCount": visibleViewModel.windowCount
        ])

        if disableScreenshotRefresh {
            view.markScreenshotsNotRequested(for: visibleViewModel, reason: "disabledByLaunchOption")
            screenshotTask?.cancel()
            screenshotTask = nil
        } else {
            scheduleScreenshotRefresh(for: visibleViewModel, generation: sessionGeneration)
        }

        onSnapshotUpdated?()
    }

    @discardableResult
    private func handleBackgroundClick() -> Bool {
        guard lockedSpaceFilterID != nil else { return false }

        cancelPendingLockedSpaceUnlock()
        let unlocked = unlockSpaceFilter(action: "unlockBackground")
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.backgroundUnlock", [
            "unlocked": unlocked
        ])
        return unlocked
    }

    private func scheduleLockedSpaceUnlock(_ spaceID: UInt64) {
        pendingLockedSpaceUnlockSpaceID = spaceID
        let delay = max(0.12, NSEvent.doubleClickInterval + 0.03)
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.unlock.pending", [
            "spaceID": spaceID,
            "delay": delay
        ])

        pendingLockedSpaceUnlockTask = Task { @MainActor [weak self] in
            try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
            guard !Task.isCancelled,
                  let self,
                  self.pendingLockedSpaceUnlockSpaceID == spaceID
            else {
                return
            }

            self.pendingLockedSpaceUnlockSpaceID = nil
            self.pendingLockedSpaceUnlockTask = nil
            self.applySpaceLaneSingleClick(spaceID)
        }
    }

    private func cancelPendingLockedSpaceUnlock() {
        pendingLockedSpaceUnlockTask?.cancel()
        pendingLockedSpaceUnlockTask = nil
        pendingLockedSpaceUnlockSpaceID = nil
    }

    @discardableResult
    private func unlockSpaceFilter(action: String) -> Bool {
        guard let sourceViewModel, lockedSpaceFilterID != nil else {
            return false
        }

        lockedSpaceFilterID = nil
        let preferredSelection = selectionBeforeSpaceFilter
        selectionBeforeSpaceFilter = nil
        lastSpaceFilterAction = action

        let visibleViewModel = projectedViewModel(
            from: sourceViewModel,
            preferredSelection: preferredSelection
        )
        currentViewModel = visibleViewModel
        view.applyProjected(viewModel: visibleViewModel)

        if disableScreenshotRefresh {
            view.markScreenshotsNotRequested(for: visibleViewModel, reason: "disabledByLaunchOption")
            screenshotTask?.cancel()
            screenshotTask = nil
        } else {
            scheduleScreenshotRefresh(for: visibleViewModel, generation: sessionGeneration)
        }

        onSnapshotUpdated?()
        return true
    }

    private func activateLockedSpace(_ spaceID: UInt64) {
        guard lockedSpaceFilterID == spaceID else {
            DevelopmentDiagnostics.log("quickSwitch.spaceFilter.activateSpace.ignored", [
                "spaceID": spaceID,
                "lockedSpaceID": lockedSpaceFilterID ?? NSNull()
            ])
            return
        }

        guard let sourceViewModel else {
            lastSpaceFilterAction = "blockedNoSourceViewModel"
            DevelopmentDiagnostics.log("quickSwitch.spaceFilter.activateSpace.blocked", [
                "spaceID": spaceID,
                "reason": "noSourceViewModel"
            ])
            onSnapshotUpdated?()
            return
        }

        lockedSpaceFilterID = nil
        selectionBeforeSpaceFilter = nil
        lastSpaceFilterAction = "activateSpace"
        lastSpaceActivationSpaceID = spaceID
        lastSpaceActivationDidRequestFocus = nil
        lastSpaceActivationDisplayIdentifier = nil
        lastSpaceActivationPreviousCurrentSpaceID = nil
        lastSpaceActivationError = nil

        let visibleViewModel = projectedViewModel(from: sourceViewModel)
        currentViewModel = visibleViewModel
        view.applyProjected(viewModel: visibleViewModel)

        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.activateSpace.start", [
            "spaceID": spaceID
        ])
        hide(reason: .userClosed)
        let outcome = spaceActivationService.activate(spaceID: spaceID)
        lastSpaceActivationSpaceID = outcome.targetSpaceID
        lastSpaceActivationDidRequestFocus = outcome.didRequestFocus
        lastSpaceActivationDisplayIdentifier = outcome.displayIdentifier
        lastSpaceActivationPreviousCurrentSpaceID = outcome.previousCurrentSpaceID
        lastSpaceActivationError = outcome.error
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.activateSpace.result", [
            "spaceID": outcome.targetSpaceID,
            "displayIdentifier": outcome.displayIdentifier ?? NSNull(),
            "previousCurrentSpaceID": outcome.previousCurrentSpaceID ?? NSNull(),
            "didRequestFocus": outcome.didRequestFocus,
            "error": outcome.error ?? NSNull()
        ])
        onSnapshotUpdated?()
    }

    private func requestClose(_ request: QuickSwitchCloseRequest) {
        DevelopmentDiagnostics.log("quickSwitch.close.request.start", [
            "targetKind": request.kindDescription,
            "appGroupIndex": request.appGroupIndex,
            "appName": request.appName,
            "windowID": request.windowID,
            "titleHash": DevelopmentDiagnostics.stableFingerprint(request.title),
            "titleLength": request.title.count
        ])
        lastCloseTargetKind = request.kindDescription
        lastCloseAppGroupIndex = request.appGroupIndex
        lastCloseAppName = request.appName
        lastCloseWindowID = request.windowID
        lastCloseError = nil
        pendingCloseVerification = nil
        view.showCloseProgress(for: request)

        do {
            let result: WindowCloseResult
            switch request {
            case .app:
                result = try windowCloseService.close(app: request.app)
            case .window:
                guard let window = request.window else {
                    lastCloseResult = .windowNotFound
                    suppressCloseTarget(request)
                    view.clearCloseFeedback()
                    onSnapshotUpdated?()
                    return
                }
                result = try windowCloseService.close(window: window)
            }
            lastCloseResult = result
            DevelopmentDiagnostics.log("quickSwitch.close.request.result", [
                "targetKind": request.kindDescription,
                "appGroupIndex": request.appGroupIndex,
                "appName": request.appName,
                "windowID": request.windowID,
                "result": Self.closeResultString(result)
            ])
            if result == .requested {
                DevelopmentDiagnostics.log("quickSwitch.close.request.refreshAfterClose", [
                    "targetKind": request.kindDescription,
                    "appGroupIndex": request.appGroupIndex,
                    "appName": request.appName,
                    "windowID": request.windowID,
                    "optimisticRemove": false
                ])
                pendingCloseVerification = PendingCloseVerification(
                    request: request,
                    generation: sessionGeneration,
                    attempt: 0
                )
                scheduleSnapshotRefresh(
                    for: sessionGeneration,
                    replayDebugCommands: false,
                    delay: Self.closeVerificationRetryDelays[0]
                )
            } else if result == .windowNotFound {
                suppressCloseTarget(request)
                view.clearCloseFeedback()
                DevelopmentDiagnostics.log("quickSwitch.close.request.staleTargetRemoved", [
                    "targetKind": request.kindDescription,
                    "appGroupIndex": request.appGroupIndex,
                    "appName": request.appName,
                    "windowID": request.windowID
                ])
                scheduleSnapshotRefresh(
                    for: sessionGeneration,
                    replayDebugCommands: false,
                    delay: 0.10
                )
            } else {
                view.showCloseFailure(message: closeFailureMessage(for: result), for: request)
            }
        } catch {
            let errorSummary = DevelopmentDiagnostics.errorSummaryString(error)
            lastCloseResult = .failed(errorSummary)
            lastCloseError = errorSummary
            pendingCloseVerification = nil
            view.showCloseFailure(message: Self.closeFailedMessage, for: request)
            var fields: [String: CustomStringConvertible?] = [
                "targetKind": request.kindDescription,
                "appGroupIndex": request.appGroupIndex,
                "appName": request.appName,
                "windowID": request.windowID,
                "titleHash": DevelopmentDiagnostics.stableFingerprint(request.title),
                "titleLength": request.title.count
            ]
            DevelopmentDiagnostics.errorSummaryFields(error).forEach { fields[$0.key] = $0.value }
            DevelopmentDiagnostics.log("quickSwitch.close.request.error", fields)
        }

        onSnapshotUpdated?()
    }

    private func suppressCloseTarget(_ request: QuickSwitchCloseRequest) {
        let target = SuppressedCloseTarget(request)
        if !suppressedCloseTargets.contains(target) {
            suppressedCloseTargets.append(target)
        }

        guard let sourceViewModel else { return }
        let visibleViewModel = projectedViewModel(from: sourceViewModel, preferredSelection: view.effectiveSelectionForProjection())
        self.currentViewModel = visibleViewModel
        view.applyProjected(viewModel: visibleViewModel)
        DevelopmentDiagnostics.log("quickSwitch.close.optimisticRemove", [
            "targetKind": request.kindDescription,
            "appGroupIndex": request.appGroupIndex,
            "appName": request.appName,
            "windowID": request.windowID,
            "remainingWindowCount": visibleViewModel.windowCount,
            "suppressedCloseTargetCount": suppressedCloseTargets.count
        ])
    }

    private func suppressActivationWindowID(_ windowID: UInt32, reason: String) {
        guard windowID != 0 else { return }
        suppressedActivationWindowIDs.insert(windowID)

        guard let sourceViewModel else { return }
        let visibleViewModel = projectedViewModel(from: sourceViewModel, preferredSelection: view.effectiveSelectionForProjection())
        self.currentViewModel = visibleViewModel
        view.applyProjected(viewModel: visibleViewModel)
        DevelopmentDiagnostics.log("quickSwitch.activation.suppressWindow", [
            "windowID": windowID,
            "reason": reason,
            "remainingWindowCount": visibleViewModel.windowCount,
            "suppressedActivationWindowCount": suppressedActivationWindowIDs.count
        ])
    }

    private func viewModel(
        _ viewModel: QuickSwitchViewModel,
        excluding suppressedTargets: [SuppressedCloseTarget],
        suppressedActivationWindowIDs: Set<UInt32> = []
    ) -> QuickSwitchViewModel {
        guard !suppressedTargets.isEmpty || !suppressedActivationWindowIDs.isEmpty else { return viewModel }

        var nextGlobalIndex = 0
        let columns = viewModel.waterfallColumns.compactMap { column -> QuickSwitchWaterfallColumnViewModel? in
            let windows = column.windows.filter { card in
                let isActivationSuppressed = suppressedActivationWindowIDs.contains(card.window.id)
                let isCloseSuppressed = suppressedTargets.contains { target in
                    suppressedTarget(target, matches: card)
                }
                return !isActivationSuppressed && !isCloseSuppressed
            }
            guard !windows.isEmpty else { return nil }

            let reindexedWindows = windows.enumerated().map { windowIndex, card in
                defer { nextGlobalIndex += 1 }
                return QuickSwitchWindowCardViewModel(
                    appGroupIndex: column.appGroupIndex,
                    windowIndex: windowIndex,
                    globalIndex: nextGlobalIndex,
                    window: card.window,
                    primarySpaceID: card.primarySpaceID,
                    primarySpaceLabel: card.primarySpaceLabel
                )
            }

            return QuickSwitchWaterfallColumnViewModel(
                appGroupIndex: column.appGroupIndex,
                app: column.app,
                windows: reindexedWindows
            )
        }

        let columnsByAppGroupIndex = Dictionary(uniqueKeysWithValues: columns.map { ($0.appGroupIndex, $0) })
        let appShelf = viewModel.appShelf.compactMap { item -> QuickSwitchAppShelfItemViewModel? in
            guard let column = columnsByAppGroupIndex[item.appGroupIndex] else { return nil }
            return QuickSwitchAppShelfItemViewModel(
                appGroupIndex: item.appGroupIndex,
                app: item.app,
                windowCount: column.windows.count,
                primarySpaceIDs: orderedPrimarySpaceIDs(in: column.windows),
                hasMinimizedWindows: column.windows.contains { $0.window.isMinimized },
                hasFullscreenWindows: column.windows.contains { $0.window.isFullscreen }
            )
        }

        return QuickSwitchViewModel(
            displays: viewModel.displays,
            appShelf: appShelf,
            waterfallColumns: columns,
            initialSelection: initialSelection(in: columns, fallback: viewModel.initialSelection),
            lockedSpaceID: viewModel.lockedSpaceID
        )
    }

    private func projectedViewModel(
        from sourceViewModel: QuickSwitchViewModel,
        preferredSelection: QuickSwitchSelection? = nil
    ) -> QuickSwitchViewModel {
        let closeProjectedViewModel = viewModel(
            sourceViewModel,
            excluding: suppressedCloseTargets,
            suppressedActivationWindowIDs: suppressedActivationWindowIDs
        )
        return QuickSwitchSpaceFilterPolicy.projectedViewModel(
            from: closeProjectedViewModel,
            lockedSpaceID: lockedSpaceFilterID,
            preferredSelection: preferredSelection
        )
    }

    private func orderedPrimarySpaceIDs(in cards: [QuickSwitchWindowCardViewModel]) -> [UInt64] {
        var seen = Set<UInt64>()
        var result: [UInt64] = []
        for card in cards {
            guard let spaceID = card.primarySpaceID,
                  !seen.contains(spaceID)
            else {
                continue
            }
            seen.insert(spaceID)
            result.append(spaceID)
        }
        return result
    }

    private func suppressedTarget(
        _ target: SuppressedCloseTarget,
        matches card: QuickSwitchWindowCardViewModel
    ) -> Bool {
        switch target {
        case .window(let windowID):
            return card.window.id == windowID
        case .app(let app):
            return appMatches(card.window.app, app)
        }
    }

    private func initialSelection(
        in columns: [QuickSwitchWaterfallColumnViewModel],
        fallback: QuickSwitchSelection?
    ) -> QuickSwitchSelection? {
        if let fallback,
           columns.contains(where: { column in
               column.appGroupIndex == fallback.appGroupIndex
                   && column.windows.contains { $0.window.id == fallback.windowID }
           }) {
            return fallback
        }

        guard let firstCard = columns.first?.windows.first else { return nil }
        return QuickSwitchSelection(
            appGroupIndex: firstCard.appGroupIndex,
            windowIndex: firstCard.windowIndex,
            windowID: firstCard.window.id
        )
    }

    private func shouldCloseAfterActivation(_ result: WindowActivationResult?) -> Bool {
        switch result {
        case .activated, .restored:
            return true
        case .appActivatedOnly, .windowNotFound, .unsupported, .activationFailed, .failed, nil:
            return false
        }
    }

    private func shouldSuppressAfterActivationResult(_ result: WindowActivationResult?) -> Bool {
        switch result {
        case .windowNotFound, .unsupported, .activationFailed, .failed:
            return true
        case .activated, .restored, .appActivatedOnly, nil:
            return false
        }
    }

    private func window(
        for selection: QuickSwitchSelection,
        in viewModel: QuickSwitchViewModel?
    ) -> AlignerWindow? {
        viewModel?
            .waterfallColumns
            .flatMap(\.windows)
            .first { card in
                card.appGroupIndex == selection.appGroupIndex
                    && card.windowIndex == selection.windowIndex
                    && card.window.id == selection.windowID
            }?
            .window
    }

    private func applySnapshotResult(
        _ result: SnapshotLoadResult,
        generation: Int,
        replayDebugCommands: Bool
    ) {
        guard generation == sessionGeneration, isVisible else {
            DevelopmentDiagnostics.log("quickSwitch.snapshot.drop", [
                "resultGeneration": generation,
                "currentGeneration": sessionGeneration,
                "visible": isVisible
            ])
            return
        }

        switch result {
        case .success(let success):
            currentSnapshot = success.snapshot
            sourceViewModel = success.viewModel
            let visibleViewModel = projectedViewModel(
                from: success.viewModel,
                preferredSelection: view.effectiveSelectionForProjection()
            )
            currentViewModel = visibleViewModel
            lastSnapshotError = nil
            snapshotStartElapsedMilliseconds = success.timing.startElapsedMilliseconds
            snapshotDurationMilliseconds = success.timing.durationMilliseconds
            snapshotRanOnMainThread = success.timing.ranOnMainThread
            view.apply(viewModel: visibleViewModel)
            view.recordPerformanceFirstFrameIfNeeded()
            if replayDebugCommands {
                view.performDebugKeyboardCommands(debugKeySequence)
                view.performDebugMouseCommands(debugMouseSequence)
            }
            verifyPendingCloseIfNeeded(in: success.viewModel)
            DevelopmentDiagnostics.log("quickSwitch.snapshot.success", [
                "generation": generation,
                "displayCount": success.viewModel.displays.count,
                "spaceCount": success.viewModel.spaceCount,
                "appCount": success.viewModel.appShelf.count,
                "windowCount": success.viewModel.windowCount,
                "durationMilliseconds": success.timing.durationMilliseconds,
                "ranOnMainThread": success.timing.ranOnMainThread,
                "initialSelectionWindowID": success.viewModel.initialSelection?.windowID
            ])
            guard isVisible else { return }
            if disableScreenshotRefresh {
                view.markScreenshotsNotRequested(for: visibleViewModel, reason: "disabledByLaunchOption")
                screenshotTask?.cancel()
                screenshotTask = nil
                DevelopmentDiagnostics.log("quickSwitch.screenshot.disabledByLaunchOption", [
                    "generation": generation
                ])
            } else {
                scheduleScreenshotRefresh(for: visibleViewModel, generation: generation)
            }
        case .failure(let failure):
            currentSnapshot = nil
            sourceViewModel = nil
            currentViewModel = nil
            lastSnapshotError = failure.message
            snapshotStartElapsedMilliseconds = failure.timing.startElapsedMilliseconds
            snapshotDurationMilliseconds = failure.timing.durationMilliseconds
            snapshotRanOnMainThread = failure.timing.ranOnMainThread
            view.apply(viewModel: nil)
            screenshotTask?.cancel()
            screenshotTask = nil
            failPendingCloseVerificationIfNeeded(reason: "snapshotFailure")
            DevelopmentDiagnostics.log("quickSwitch.snapshot.failure", [
                "generation": generation,
                "message": failure.message,
                "durationMilliseconds": failure.timing.durationMilliseconds,
                "ranOnMainThread": failure.timing.ranOnMainThread
            ])
        }

        onSnapshotUpdated?()
    }

    private func verifyPendingCloseIfNeeded(in viewModel: QuickSwitchViewModel) {
        guard let pendingCloseVerification else { return }

        self.pendingCloseVerification = nil
        let request = pendingCloseVerification.request
        let stillPresent = closeTargetStillPresent(request, in: viewModel)
        DevelopmentDiagnostics.log("quickSwitch.close.verify", [
            "targetKind": request.kindDescription,
            "appGroupIndex": request.appGroupIndex,
            "appName": request.appName,
            "windowID": request.windowID,
            "requestedGeneration": pendingCloseVerification.generation,
            "currentGeneration": sessionGeneration,
            "attempt": pendingCloseVerification.attempt,
            "stillPresent": stillPresent
        ])

        if stillPresent {
            let nextAttempt = pendingCloseVerification.attempt + 1
            if nextAttempt < Self.closeVerificationRetryDelays.count {
                self.pendingCloseVerification = PendingCloseVerification(
                    request: request,
                    generation: sessionGeneration,
                    attempt: nextAttempt
                )
                scheduleSnapshotRefresh(
                    for: sessionGeneration,
                    replayDebugCommands: false,
                    delay: Self.closeVerificationRetryDelays[nextAttempt]
                )
            } else {
                view.showCloseFailure(message: Self.closeRequestStillPresentMessage, for: request)
            }
        } else {
            view.clearCloseFeedback()
        }
    }

    private func failPendingCloseVerificationIfNeeded(reason: String) {
        guard let pendingCloseVerification else { return }

        self.pendingCloseVerification = nil
        let request = pendingCloseVerification.request
        DevelopmentDiagnostics.log("quickSwitch.close.verify.failed", [
            "targetKind": request.kindDescription,
            "appGroupIndex": request.appGroupIndex,
            "appName": request.appName,
            "windowID": request.windowID,
            "requestedGeneration": pendingCloseVerification.generation,
            "currentGeneration": sessionGeneration,
            "reason": reason
        ])
        view.showCloseFailure(message: Self.closeFailedMessage, for: request)
    }

    private func closeFailureMessage(for result: WindowCloseResult) -> String {
        switch result {
        case .requested:
            return Self.closeRequestStillPresentMessage
        case .windowNotFound:
            return Self.closeTargetNotFoundMessage
        case .unsupported:
            return Self.closeUnsupportedMessage
        case .appNotFound, .failed:
            return Self.closeFailedMessage
        }
    }

    private func closeTargetStillPresent(
        _ request: QuickSwitchCloseRequest,
        in viewModel: QuickSwitchViewModel
    ) -> Bool {
        switch request {
        case .window:
            guard let windowID = request.windowID else { return false }
            return viewModel.waterfallColumns
                .flatMap(\.windows)
                .contains { $0.window.id == windowID }
        case .app:
            return viewModel.appShelf.contains { appMatches($0.app, request.app) }
                || viewModel.waterfallColumns.contains { appMatches($0.app, request.app) }
        }
    }

    private func appMatches(_ candidate: AlignerApp, _ target: AlignerApp) -> Bool {
        if let candidatePID = candidate.processIdentifier,
           let targetPID = target.processIdentifier {
            return candidatePID == targetPID
        }

        if !candidate.bundleIdentifier.isEmpty,
           candidate.bundleIdentifier == target.bundleIdentifier {
            return true
        }

        return candidate.name == target.name
    }

    private func scheduleScreenshotRefresh(
        for viewModel: QuickSwitchViewModel,
        generation: Int
    ) {
        screenshotTask?.cancel()
        let provider = screenshotProvider
        let session = screenshotSession
        let allWindows = viewModel.waterfallColumns.flatMap { column in
            column.windows.map(\.window)
        }
        let windows = allWindows.filter(Self.shouldRequestScreenshot)
        let skippedWindows = allWindows.filter { !Self.shouldRequestScreenshot(for: $0) }
        DevelopmentDiagnostics.log("quickSwitch.screenshot.schedule", [
            "generation": generation,
            "eligibleCount": windows.count,
            "skippedCount": skippedWindows.count
        ])

        for window in skippedWindows {
            view.markScreenshotNotRequested(for: window.id, reason: "skeletonPreferred")
        }
        if !skippedWindows.isEmpty {
            onSnapshotUpdated?()
        }
        guard !windows.isEmpty else {
            screenshotTask = nil
            return
        }

        screenshotTask = Task { [weak self] in
            guard let self else { return }

            var retryWindows: [AlignerWindow] = []
            for window in windows {
                guard !Task.isCancelled else { return }
                let resolution = await provider.resolvedScreenshot(for: window, in: session)
                guard !Task.isCancelled, generation == self.sessionGeneration, self.isVisible else { return }
                self.view.applyScreenshot(resolution, for: window.id)
                DevelopmentDiagnostics.log("quickSwitch.screenshot.resolved", [
                    "generation": generation,
                    "windowID": window.id,
                    "appName": window.app.name,
                    "source": Self.screenshotSourceString(resolution.source)
                ])
                self.onSnapshotUpdated?()

                if case .skeletonFallback(let reason) = resolution.source,
                   Self.shouldRetryScreenshot(after: reason) {
                    retryWindows.append(window)
                }
            }

            for window in retryWindows {
                guard !Task.isCancelled else { return }
                let retryResolution = await provider.resolvedScreenshot(for: window, in: session)
                guard !Task.isCancelled, generation == self.sessionGeneration, self.isVisible else { return }
                self.view.applyScreenshot(retryResolution, for: window.id)
                DevelopmentDiagnostics.log("quickSwitch.screenshot.retryResolved", [
                    "generation": generation,
                    "windowID": window.id,
                    "appName": window.app.name,
                    "source": Self.screenshotSourceString(retryResolution.source)
                ])
                self.onSnapshotUpdated?()
            }
        }
    }

    private static func shouldRequestScreenshot(for window: AlignerWindow) -> Bool {
        switch ThumbnailPolicy.preferredStrategy(for: window.app.category) {
        case .screenshotPreferred:
            return true
        case .skeletonPreferred, .fallbackSkeleton:
            return false
        }
    }

    private static func shouldRetryScreenshot(after reason: ScreenshotFallbackReason) -> Bool {
        switch reason {
        case .screenRecordingDenied, .syntheticWindowID, .retryLimitReached:
            return false
        case .captureFailed, .timedOut, .invalidCapture:
            return true
        }
    }

    private func elapsedSinceShowStart() -> Double? {
        guard let showStartTime else { return nil }
        return (CACurrentMediaTime() - showStartTime) * 1000
    }

    private func updateLifecycleWindowHighWaterMark() {
        lifecycleMaximumOverlayWindows = max(
            lifecycleMaximumOverlayWindows,
            overlayWindowCount()
        )
        lifecycleMaximumVisibleOverlayWindows = max(
            lifecycleMaximumVisibleOverlayWindows,
            visibleOverlayWindowCount()
        )
    }

    private func overlayWindowCount() -> Int {
        NSApp.windows.filter { window in
            window.title == Self.overlayTitle
        }.count
    }

    private func visibleOverlayWindowCount() -> Int {
        NSApp.windows.filter { window in
            window.title == Self.overlayTitle && window.isVisible
        }.count
    }

    private static func activationResultString(_ result: WindowActivationResult) -> String {
        switch result {
        case .activated:
            return "activated"
        case .restored:
            return "restored"
        case .appActivatedOnly:
            return "appActivatedOnly"
        case .windowNotFound:
            return "windowNotFound"
        case .unsupported:
            return "unsupported"
        case .activationFailed:
            return "activationFailed"
        case .failed(let message):
            return "failed(\(message))"
        }
    }

    private static func closeResultString(_ result: WindowCloseResult) -> String {
        switch result {
        case .requested:
            return "requested"
        case .windowNotFound:
            return "windowNotFound"
        case .appNotFound:
            return "appNotFound"
        case .unsupported:
            return "unsupported"
        case .failed(let message):
            return "failed(\(message))"
        }
    }

    private static func screenshotSourceString(_ source: ScreenshotResolutionSource) -> String {
        switch source {
        case .realScreenshot:
            return "realScreenshot"
        case .skeletonFallback(let reason):
            return "skeletonFallback(\(reason))"
        }
    }

    private static func dismissReasonString(_ reason: DismissReason) -> String {
        switch reason {
        case .escape:
            return "escape"
        case .focusLost:
            return "focusLost"
        case .systemCriticalWindow:
            return "systemCriticalWindow"
        case .timeout:
            return "timeout"
        case .userClosed:
            return "userClosed"
        }
    }

}

private struct SnapshotLoadTiming: Sendable {
    let startElapsedMilliseconds: Double?
    let durationMilliseconds: Double
    let ranOnMainThread: Bool
}

private struct SnapshotLoadSuccess: Sendable {
    let snapshot: QuickSwitchSnapshot
    let viewModel: QuickSwitchViewModel
    let timing: SnapshotLoadTiming
}

private struct SnapshotLoadFailure: Sendable {
    let message: String
    let timing: SnapshotLoadTiming
}

private enum SnapshotLoadResult: Sendable {
    case success(SnapshotLoadSuccess)
    case failure(SnapshotLoadFailure)

    static func load(
        using loader: any QuickSwitchSnapshotLoading,
        showStartTime: CFTimeInterval?
    ) -> SnapshotLoadResult {
        let startedAt = CACurrentMediaTime()
        let startedOnMainThread = Thread.isMainThread

        do {
            let load = try loader.snapshot()
            let viewModel = QuickSwitchViewModelBuilder.viewModel(
                from: load.snapshot,
                currentSpaceIDs: load.currentSpaceIDs
            )
            return .success(SnapshotLoadSuccess(
                snapshot: load.snapshot,
                viewModel: viewModel,
                timing: SnapshotLoadTiming(
                    startElapsedMilliseconds: showStartTime.map { (startedAt - $0) * 1000 },
                    durationMilliseconds: (CACurrentMediaTime() - startedAt) * 1000,
                    ranOnMainThread: startedOnMainThread
                )
            ))
        } catch {
            return .failure(SnapshotLoadFailure(
                message: DevelopmentDiagnostics.errorSummaryString(error),
                timing: SnapshotLoadTiming(
                    startElapsedMilliseconds: showStartTime.map { (startedAt - $0) * 1000 },
                    durationMilliseconds: (CACurrentMediaTime() - startedAt) * 1000,
                    ranOnMainThread: startedOnMainThread
                )
            ))
        }
    }
}

struct Round1QuickSwitchLaunchOptions {
    let openQuickSwitch: Bool
    let simulateAccessibilityDenied: Bool
    let simulateScreenRecordingDenied: Bool
    let autoHideAfter: TimeInterval?
    let quitAfter: TimeInterval?
    let reportPath: String?
    let snapshotLoadDelay: TimeInterval?
    let fixtureAppCount: Int?
    let fixtureWindowsPerApp: Int?
    let fixtureMultiDisplay: Bool
    let fixtureCandidateFiltering: Bool
    let fixtureActivation: Bool
    let fixtureSpaceFilter: Bool
    let fixtureSplitView: Bool
    let fixtureMultiPageIdentity: Bool
    let debugHoveredAppGroupIndex: Int?
    let debugOverlayWidth: CGFloat?
    let debugKeySequence: [String]
    let debugMouseSequence: [String]
    let debugSystemCriticalAfter: TimeInterval?
    let debugWindowActivation: Bool
    let debugWindowClose: Bool
    let disableScreenshotRefresh: Bool
    let waterfallViewMode: QuickSwitchWaterfallViewMode?
    let lifecycleCycles: Int?
    let lifecycleInterval: TimeInterval
    let lifecycleVisibleDuration: TimeInterval
    let simulateDockReopenAfter: TimeInterval?

    static func parse(arguments: [String]) -> Round1QuickSwitchLaunchOptions {
        Round1QuickSwitchLaunchOptions(
            openQuickSwitch: arguments.contains("--round01-open-quick-switch"),
            simulateAccessibilityDenied: arguments.contains("--round01-simulate-accessibility-denied"),
            simulateScreenRecordingDenied: arguments.contains("--round01-simulate-screen-recording-denied"),
            autoHideAfter: timeInterval(for: "--round01-quick-switch-auto-hide-after", in: arguments),
            quitAfter: timeInterval(for: "--round01-quick-switch-quit-after", in: arguments),
            reportPath: stringValue(for: "--round01-quick-switch-report", in: arguments),
            snapshotLoadDelay: timeInterval(for: "--round01-snapshot-load-delay", in: arguments),
            fixtureAppCount: intValue(for: "--round01-fixture-app-count", in: arguments),
            fixtureWindowsPerApp: intValue(for: "--round01-fixture-windows-per-app", in: arguments),
            fixtureMultiDisplay: arguments.contains("--round01-fixture-multi-display"),
            fixtureCandidateFiltering: arguments.contains("--round01-fixture-candidate-filtering"),
            fixtureActivation: arguments.contains("--round01-fixture-window-activation"),
            fixtureSpaceFilter: arguments.contains("--round01-fixture-space-filter"),
            fixtureSplitView: arguments.contains("--round01-fixture-split-view"),
            fixtureMultiPageIdentity: arguments.contains("--round01-fixture-multi-page-identity"),
            debugHoveredAppGroupIndex: intValue(for: "--round01-debug-hover-app-index", in: arguments),
            debugOverlayWidth: cgFloatValue(for: "--round01-debug-overlay-width", in: arguments),
            debugKeySequence: stringListValue(for: "--round01-debug-key-sequence", in: arguments),
            debugMouseSequence: stringListValue(for: "--round01-debug-mouse-sequence", in: arguments),
            debugSystemCriticalAfter: timeInterval(for: "--round01-debug-system-critical-after", in: arguments),
            debugWindowActivation: arguments.contains("--round01-debug-window-activation"),
            debugWindowClose: arguments.contains("--round01-debug-window-close"),
            disableScreenshotRefresh: arguments.contains("--round01-disable-screenshot-refresh"),
            waterfallViewMode: waterfallViewModeValue(for: "--round01-waterfall-view-mode", in: arguments),
            lifecycleCycles: intValue(for: "--round01-quick-switch-lifecycle-cycles", in: arguments),
            lifecycleInterval: timeInterval(for: "--round01-quick-switch-lifecycle-interval", in: arguments) ?? 0.02,
            lifecycleVisibleDuration: timeInterval(for: "--round01-quick-switch-lifecycle-visible-duration", in: arguments) ?? 0.16,
            simulateDockReopenAfter: timeInterval(for: "--round01-simulate-dock-reopen-after", in: arguments)
        )
    }

    private static func timeInterval(for key: String, in arguments: [String]) -> TimeInterval? {
        let prefix = "\(key)="
        guard let argument = arguments.first(where: { $0.hasPrefix(prefix) }) else {
            return nil
        }

        return TimeInterval(argument.dropFirst(prefix.count))
    }

    private static func stringValue(for key: String, in arguments: [String]) -> String? {
        let prefix = "\(key)="
        guard let argument = arguments.first(where: { $0.hasPrefix(prefix) }) else {
            return nil
        }

        return String(argument.dropFirst(prefix.count))
    }

    private static func waterfallViewModeValue(
        for key: String,
        in arguments: [String]
    ) -> QuickSwitchWaterfallViewMode? {
        guard let value = stringValue(for: key, in: arguments) else { return nil }

        switch value {
        case "vertical", "vertical-columns", "verticalColumns":
            return .verticalColumns
        case "horizontal", "horizontal-masonry", "horizontalMasonry":
            return .horizontalMasonry
        default:
            return nil
        }
    }

    private static func intValue(for key: String, in arguments: [String]) -> Int? {
        stringValue(for: key, in: arguments).flatMap(Int.init)
    }

    private static func cgFloatValue(for key: String, in arguments: [String]) -> CGFloat? {
        stringValue(for: key, in: arguments)
            .flatMap(Double.init)
            .map { CGFloat($0) }
    }

    private static func stringListValue(for key: String, in arguments: [String]) -> [String] {
        guard let value = stringValue(for: key, in: arguments) else { return [] }

        return value
            .split(separator: ",")
            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
            .filter { !$0.isEmpty }
    }
}

@MainActor
private final class StderrScreenshotDebugLogger: ScreenshotDebugLogging {
    func record(_ event: ScreenshotDebugEvent) {
        DevelopmentDiagnostics.log("quickSwitch.screenshot.debug", [
            "event": event.diagnosticDescription
        ])
        fputs("Round01 screenshot debug: \(event.diagnosticDescription)\n", stderr)
    }
}
