import AppKit
import Darwin
import AlignerCore

@MainActor
final class AlignerApplicationDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
    private static let quickSwitchConfirmBeforeCloseDefaultsKey = "QuickSwitchConfirmBeforeClose"

    private let permissionChecker = SystemPermissionChecker()
    private let preferenceStore = UserDefaultsPreferenceStore()
    private let launchOptions = Round0DebugLaunchOptions.parse(arguments: ProcessInfo.processInfo.arguments)
    private let round1PoCOptions = Round1PerformancePoCOptions.parse(arguments: ProcessInfo.processInfo.arguments)
    private let round1TriggerProbeOptions = Round1TriggerProbeOptions.parse(arguments: ProcessInfo.processInfo.arguments)
    private let round1SnapshotDumpOptions = Round1SnapshotDumpOptions.parse(arguments: ProcessInfo.processInfo.arguments)
    private let round1ScreenshotProbeOptions = Round1ScreenshotProbeOptions.parse(arguments: ProcessInfo.processInfo.arguments)
    private let round1QuickSwitchOptions = Round1QuickSwitchLaunchOptions.parse(arguments: ProcessInfo.processInfo.arguments)
    private let round015EntitlementOptions = Round015EntitlementLaunchOptions.parse(arguments: ProcessInfo.processInfo.arguments)
    private var permissionWindowController: PermissionsWindowController?
    private var debugOverlayController: Round0DebugOverlayController?
    private var round1PerformancePoCController: Round1PerformancePoCController?
    private var round1TriggerProbe: CGEventTapTriggerProbe?
    private var quickSwitchSessionController: QuickSwitchSessionController?
    private var quickSwitchTriggerService: CGEventTapTriggerService?
    private var settingsWindowController: SettingsWindowController?
    private var isRunningQuickSwitchLifecycle = false
    private var lastQuickSwitchOpenRequestSource: String?
    private var statusItem: NSStatusItem?
    private var verticalWaterfallViewModeMenuItems: [NSMenuItem] = []
    private var horizontalWaterfallViewModeMenuItems: [NSMenuItem] = []
    private let performanceDiagnosticsExporter = PerformanceDiagnosticsExporter()

    func applicationDidFinishLaunching(_ notification: Notification) {
        let bundlePathSummary = DevelopmentDiagnostics.pathSummary(Bundle.main.bundlePath)
        DevelopmentDiagnostics.log("app.lifecycle.didFinishLaunching.start", [
            "bundlePathKind": bundlePathSummary["locationKind"],
            "bundlePathBasename": bundlePathSummary["basename"],
            "bundlePathHash": bundlePathSummary["fingerprint"],
            "accessibility": permissionStatusString(accessibilityStatus()),
            "screenRecording": permissionStatusString(screenRecordingStatus()),
            "skipPermissions": launchOptions.skipPermissions,
            "openQuickSwitch": round1QuickSwitchOptions.openQuickSwitch,
            "quickSwitchLifecycle": round1QuickSwitchOptions.lifecycleCycles
        ])

        if runRound1SnapshotDumpIfRequested() {
            DevelopmentDiagnostics.log("app.lifecycle.didFinishLaunching.exitForSnapshotDump")
            return
        }
        if runRound1ScreenshotProbeIfRequested() {
            DevelopmentDiagnostics.log("app.lifecycle.didFinishLaunching.exitForScreenshotProbe")
            return
        }

        configureMainMenu()
        configureStatusItem()
        if !launchOptions.skipPermissions {
            showPermissionsIfNeeded()
        }
        startQuickSwitchTriggerServiceIfPossible()
        showDebugOverlayIfRequested()
        showRound1PerformancePoCIfRequested()
        showQuickSwitchIfRequested()
        startRound1TriggerProbeIfRequested()

        DevelopmentDiagnostics.log("app.lifecycle.didFinishLaunching.end", [
            "activationPolicy": NSApp.activationPolicy().rawValue,
            "quickSwitchTriggerRunning": quickSwitchTriggerService?.isRunning ?? false,
            "statusItemVisible": statusItem != nil
        ])
    }

    func applicationWillTerminate(_ notification: Notification) {
        DevelopmentDiagnostics.logSync("app.lifecycle.willTerminate", [
            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false,
            "hasPermissionWindow": permissionWindowController?.window != nil,
            "activationPolicy": NSApp.activationPolicy().rawValue
        ])
    }

    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        DevelopmentDiagnostics.log("app.lifecycle.shouldTerminateAfterLastWindowClosed", [
            "decision": false,
            "visibleWindowCount": sender.windows.filter(\.isVisible).count
        ])
        return false
    }

    func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
        DevelopmentDiagnostics.log("app.lifecycle.shouldHandleReopen", [
            "hasVisibleWindows": flag,
            "activationPolicy": sender.activationPolicy().rawValue
        ])
        openQuickSwitchFromDockReopen(hasVisibleWindows: flag)
        return false
    }

    private func configureStatusItem() {
        DevelopmentDiagnostics.log("app.statusItem.configure.start")
        let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
        item.button?.title = "A"

        let menu = NSMenu()
        menu.delegate = self
        menu.addItem(menuItem(title: "About Aligner...", action: #selector(showAbout), keyEquivalent: ""))
        menu.addItem(menuItem(title: "Settings...", action: #selector(showSettingsFromMenu), keyEquivalent: ""))
        menu.addItem(.separator())
        menu.addItem(menuItem(title: "Check Permissions...", action: #selector(showPermissions), keyEquivalent: ""))
        menu.addItem(menuItem(title: "Export Performance Diagnostics...", action: #selector(exportPerformanceDiagnostics), keyEquivalent: ""))
        menu.addItem(.separator())
        menu.addItem(menuItem(title: "Show Quick Switch", action: #selector(showQuickSwitchFromMenu), keyEquivalent: ""))
        menu.addItem(menuItem(title: "Hide Quick Switch", action: #selector(hideQuickSwitchFromMenu), keyEquivalent: ""))
        menu.addItem(quickSwitchViewModeMenuItem())
        menu.addItem(.separator())
        menu.addItem(menuItem(title: "Show Round0 Debug Overlay", action: #selector(showRound0DebugOverlay), keyEquivalent: "d"))
        menu.addItem(menuItem(title: "Hide Round0 Debug Overlay", action: #selector(hideRound0DebugOverlay), keyEquivalent: ""))
        menu.addItem(.separator())
        menu.addItem(menuItem(title: "Quit Aligner", action: #selector(quit), keyEquivalent: "q"))
        item.menu = menu

        statusItem = item
        updateWaterfallViewModeMenuState()
        DevelopmentDiagnostics.log("app.statusItem.configure.end", [
            "hasButton": item.button != nil,
            "hasAboutItem": true,
            "hasSettingsItem": true,
            "hasWaterfallViewModeItems": !verticalWaterfallViewModeMenuItems.isEmpty
                && !horizontalWaterfallViewModeMenuItems.isEmpty
        ])
    }

    private func configureMainMenu() {
        DevelopmentDiagnostics.log("app.mainMenu.configure.start")

        let mainMenu = NSMenu(title: "Main Menu")
        let appMenuItem = NSMenuItem(title: "Aligner", action: nil, keyEquivalent: "")
        let appMenu = NSMenu(title: "Aligner")
        appMenu.delegate = self
        appMenuItem.submenu = appMenu
        appMenu.addItem(menuItem(title: "About Aligner...", action: #selector(showAbout), keyEquivalent: ""))
        appMenu.addItem(.separator())
        appMenu.addItem(menuItem(title: "Settings...", action: #selector(showSettingsFromMenu), keyEquivalent: ","))
        appMenu.addItem(.separator())
        appMenu.addItem(menuItem(title: "Check Permissions...", action: #selector(showPermissions), keyEquivalent: ""))
        appMenu.addItem(menuItem(title: "Export Performance Diagnostics...", action: #selector(exportPerformanceDiagnostics), keyEquivalent: ""))
        appMenu.addItem(menuItem(title: "Show Quick Switch", action: #selector(showQuickSwitchFromMenu), keyEquivalent: ""))
        appMenu.addItem(menuItem(title: "Hide Quick Switch", action: #selector(hideQuickSwitchFromMenu), keyEquivalent: ""))
        appMenu.addItem(quickSwitchViewModeMenuItem())
        appMenu.addItem(.separator())
        appMenu.addItem(menuItem(title: "Quit Aligner", action: #selector(quit), keyEquivalent: "q"))
        mainMenu.addItem(appMenuItem)

        let editMenuItem = NSMenuItem(title: "Edit", action: nil, keyEquivalent: "")
        let editMenu = NSMenu(title: "Edit")
        editMenuItem.submenu = editMenu
        editMenu.addItem(responderMenuItem(title: "Undo", action: Selector(("undo:")), keyEquivalent: "z"))
        editMenu.addItem(responderMenuItem(title: "Redo", action: Selector(("redo:")), keyEquivalent: "Z"))
        editMenu.addItem(.separator())
        editMenu.addItem(responderMenuItem(title: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x"))
        editMenu.addItem(responderMenuItem(title: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c"))
        editMenu.addItem(responderMenuItem(title: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v"))
        editMenu.addItem(responderMenuItem(title: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a"))
        mainMenu.addItem(editMenuItem)

        let viewMenuItem = NSMenuItem(title: "View", action: nil, keyEquivalent: "")
        let viewMenu = NSMenu(title: "View")
        viewMenu.delegate = self
        viewMenuItem.submenu = viewMenu
        viewMenu.addItem(quickSwitchViewModeMenuItem())
        mainMenu.addItem(viewMenuItem)

        let windowMenuItem = NSMenuItem(title: "Window", action: nil, keyEquivalent: "")
        let windowMenu = NSMenu(title: "Window")
        windowMenuItem.submenu = windowMenu
        windowMenu.addItem(responderMenuItem(title: "Minimize", action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent: "m"))
        windowMenu.addItem(responderMenuItem(title: "Zoom", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: ""))
        windowMenu.addItem(.separator())
        windowMenu.addItem(responderMenuItem(title: "Bring All to Front", action: #selector(NSApplication.arrangeInFront(_:)), keyEquivalent: ""))
        mainMenu.addItem(windowMenuItem)
        NSApp.windowsMenu = windowMenu

        let helpMenuItem = NSMenuItem(title: "Help", action: nil, keyEquivalent: "")
        helpMenuItem.submenu = NSMenu(title: "Help")
        mainMenu.addItem(helpMenuItem)

        NSApp.mainMenu = mainMenu
        updateWaterfallViewModeMenuState()
        DevelopmentDiagnostics.log("app.mainMenu.configure.end", [
            "hasSettingsItem": appMenu.item(withTitle: "Settings...") != nil,
            "hasQuickSwitchViewSubmenu": appMenu.item(withTitle: "Quick Switch View")?.submenu != nil
        ])
    }

    func menuWillOpen(_ menu: NSMenu) {
        updateWaterfallViewModeMenuState()
    }

    private func menuItem(title: String, action: Selector?, keyEquivalent: String) -> NSMenuItem {
        let item = NSMenuItem(title: title, action: action, keyEquivalent: keyEquivalent)
        item.target = self
        return item
    }

    private func responderMenuItem(title: String, action: Selector?, keyEquivalent: String) -> NSMenuItem {
        NSMenuItem(title: title, action: action, keyEquivalent: keyEquivalent)
    }

    private func quickSwitchViewModeMenuItem() -> NSMenuItem {
        let submenu = NSMenu(title: "Quick Switch View")
        let verticalItem = menuItem(
            title: "Vertical Columns",
            action: #selector(useVerticalWaterfallViewMode),
            keyEquivalent: ""
        )
        let horizontalItem = menuItem(
            title: "Horizontal Waterfall",
            action: #selector(useHorizontalWaterfallViewMode),
            keyEquivalent: ""
        )
        submenu.addItem(verticalItem)
        submenu.addItem(horizontalItem)

        verticalWaterfallViewModeMenuItems.append(verticalItem)
        horizontalWaterfallViewModeMenuItems.append(horizontalItem)

        let rootItem = NSMenuItem(title: "Quick Switch View", action: nil, keyEquivalent: "")
        rootItem.submenu = submenu
        return rootItem
    }

    private func updateWaterfallViewModeMenuState() {
        let mode = quickSwitchWaterfallViewMode()
        for item in verticalWaterfallViewModeMenuItems {
            item.state = mode == .verticalColumns ? .on : .off
        }
        for item in horizontalWaterfallViewModeMenuItems {
            item.state = mode == .horizontalMasonry ? .on : .off
        }
    }

    private func showPermissionsIfNeeded() {
        let accessibility = accessibilityStatus()
        let screenRecording = screenRecordingStatus()
        DevelopmentDiagnostics.log("app.permissions.checkOnLaunch", [
            "accessibility": permissionStatusString(accessibility),
            "screenRecording": permissionStatusString(screenRecording)
        ])
        if accessibility != .granted || screenRecording != .granted {
            showPermissions()
        }
    }

    private func runRound1SnapshotDumpIfRequested() -> Bool {
        guard round1SnapshotDumpOptions.enabled else { return false }
        DevelopmentDiagnostics.log("app.diagnostic.snapshotDump.start")

        do {
            try Round1SnapshotDump.run(prettyPrinted: round1SnapshotDumpOptions.prettyPrinted)
            DevelopmentDiagnostics.logSync("app.diagnostic.snapshotDump.success")
            NSApp.terminate(nil)
        } catch {
            DevelopmentDiagnostics.logSync("app.diagnostic.snapshotDump.failed", DevelopmentDiagnostics.errorSummaryFields(error))
            fputs("Round01 snapshot dump failed: \(DevelopmentDiagnostics.errorSummaryString(error))\n", stderr)
            exit(2)
        }

        return true
    }

    private func runRound1ScreenshotProbeIfRequested() -> Bool {
        guard round1ScreenshotProbeOptions.enabled else { return false }
        DevelopmentDiagnostics.log("app.diagnostic.screenshotProbe.start")

        Task { @MainActor in
            do {
                try await Round1ScreenshotProbe.run(prettyPrinted: round1ScreenshotProbeOptions.prettyPrinted)
                DevelopmentDiagnostics.logSync("app.diagnostic.screenshotProbe.success")
                NSApp.terminate(nil)
            } catch {
                DevelopmentDiagnostics.logSync("app.diagnostic.screenshotProbe.failed", DevelopmentDiagnostics.errorSummaryFields(error))
                fputs("Round01 screenshot probe failed: \(DevelopmentDiagnostics.errorSummaryString(error))\n", stderr)
                exit(2)
            }
        }

        return true
    }

    private func showDebugOverlayIfRequested() {
        guard launchOptions.showOverlay else { return }

        showDebugOverlay(includeSettingsChild: launchOptions.showSettingsChild)

        if let autoHideAfter = launchOptions.autoHideAfter {
            DispatchQueue.main.asyncAfter(deadline: .now() + autoHideAfter) { [weak self] in
                self?.hideDebugOverlay()
            }
        }

        if let quitAfter = launchOptions.quitAfter {
            DispatchQueue.main.asyncAfter(deadline: .now() + quitAfter) {
                NSApp.terminate(nil)
            }
        }
    }

    private func showRound1PerformancePoCIfRequested() {
        guard round1PoCOptions.showPerformancePoC else { return }

        if let lifecycleCycles = round1PoCOptions.lifecycleCycles {
            runRound1PerformancePoCLifecycle(cycles: lifecycleCycles)
            return
        }

        openRound1PerformancePoC(autoCycle: round1PoCOptions.autoCycle)

        if let autoHideAfter = round1PoCOptions.autoHideAfter {
            DispatchQueue.main.asyncAfter(deadline: .now() + autoHideAfter) { [weak self] in
                self?.writeRound1PerformancePoCReportIfNeeded()
                self?.hideRound1PerformancePoC()
            }
        }

        if let quitAfter = round1PoCOptions.quitAfter {
            DispatchQueue.main.asyncAfter(deadline: .now() + quitAfter) { [weak self] in
                self?.writeRound1PerformancePoCReportIfNeeded()
                NSApp.terminate(nil)
            }
        }
    }

    private func runRound1PerformancePoCLifecycle(cycles: Int) {
        if round1PerformancePoCController == nil {
            round1PerformancePoCController = Round1PerformancePoCController()
        }

        round1PerformancePoCController?.prepareLifecycleRun(targetCycles: cycles)
        runRound1PerformancePoCLifecycleCycle(currentCycle: 0, totalCycles: max(0, cycles))
    }

    private func runRound1PerformancePoCLifecycleCycle(currentCycle: Int, totalCycles: Int) {
        guard currentCycle < totalCycles else {
            round1PerformancePoCController?.finalizeLifecycleRun()
            writeRound1PerformancePoCReportIfNeeded()
            NSApp.terminate(nil)
            return
        }

        openRound1PerformancePoC(autoCycle: false)

        DispatchQueue.main.asyncAfter(deadline: .now() + round1PoCOptions.lifecycleVisibleDuration) { [weak self] in
            guard let self else { return }
            self.hideRound1PerformancePoC()
            self.round1PerformancePoCController?.markLifecycleCycleCompleted()

            DispatchQueue.main.asyncAfter(deadline: .now() + self.round1PoCOptions.lifecycleInterval) { [weak self] in
                self?.runRound1PerformancePoCLifecycleCycle(currentCycle: currentCycle + 1, totalCycles: totalCycles)
            }
        }
    }

    private func startRound1TriggerProbeIfRequested() {
        guard round1TriggerProbeOptions.enabled else { return }
        DevelopmentDiagnostics.log("app.triggerProbe.startRequested")

        let probe = CGEventTapTriggerProbe()

        do {
            try probe.start()
            round1TriggerProbe = probe
            DevelopmentDiagnostics.log("app.triggerProbe.started")
            print("Round01 trigger probe started")
            fflush(stdout)
        } catch {
            DevelopmentDiagnostics.logSync("app.triggerProbe.failed", DevelopmentDiagnostics.errorSummaryFields(error))
            fputs("Round01 trigger probe failed: \(DevelopmentDiagnostics.errorSummaryString(error))\n", stderr)
            exit(2)
        }

        if let quitAfter = round1TriggerProbeOptions.quitAfter {
            DispatchQueue.main.asyncAfter(deadline: .now() + quitAfter) {
                NSApp.terminate(nil)
            }
        }
    }

    private func startQuickSwitchTriggerServiceIfPossible() {
        guard !round1TriggerProbeOptions.enabled else {
            DevelopmentDiagnostics.log("app.quickSwitchTrigger.skip", [
                "reason": "trigger-probe-enabled"
            ])
            return
        }
        let accessibility = accessibilityStatus()
        DevelopmentDiagnostics.log("app.quickSwitchTrigger.startCheck", [
            "accessibility": permissionStatusString(accessibility),
            "alreadyRunning": quickSwitchTriggerService?.isRunning ?? false
        ])
        guard accessibility == .granted else {
            stopQuickSwitchTriggerService(reason: "accessibility-not-granted")
            return
        }
        guard quickSwitchTriggerService == nil else {
            DevelopmentDiagnostics.log("app.quickSwitchTrigger.alreadyRunning")
            return
        }

        let service = CGEventTapTriggerService(
            overlayVisibleProvider: { [weak self] in
                self?.quickSwitchSessionController?.isVisible ?? false
            },
            accessibilityTrustedProvider: { [weak self] in
                self?.accessibilityStatus() == .granted
            }
        )
        service.onQuickSwitch = { [weak self] in
            self?.openQuickSwitchFromTrigger()
        }
        service.onStartFailure = { error in
            DevelopmentDiagnostics.log("app.quickSwitchTrigger.startFailure", DevelopmentDiagnostics.errorSummaryFields(error))
            fputs("Round01 Quick Switch trigger service failed: \(DevelopmentDiagnostics.errorSummaryString(error))\n", stderr)
        }
        service.start()

        if service.isRunning {
            quickSwitchTriggerService = service
        }
        DevelopmentDiagnostics.log("app.quickSwitchTrigger.startResult", [
            "isRunning": service.isRunning
        ])
    }

    private func stopQuickSwitchTriggerService(reason: String) {
        guard let quickSwitchTriggerService else {
            DevelopmentDiagnostics.log("app.quickSwitchTrigger.stopSkipped", [
                "reason": reason,
                "state": "not-running"
            ])
            return
        }

        quickSwitchTriggerService.stop()
        self.quickSwitchTriggerService = nil
        DevelopmentDiagnostics.log("app.quickSwitchTrigger.stopped", [
            "reason": reason
        ])
        fputs("Round01 Quick Switch trigger service stopped: \(reason)\n", stderr)
    }

    private func showQuickSwitchIfRequested() {
        guard round1QuickSwitchOptions.openQuickSwitch
                || round1QuickSwitchOptions.lifecycleCycles != nil
                || round1QuickSwitchOptions.simulateDockReopenAfter != nil else { return }
        DevelopmentDiagnostics.log("app.quickSwitch.launchOptionRequested", [
            "openQuickSwitch": round1QuickSwitchOptions.openQuickSwitch,
            "lifecycleCycles": round1QuickSwitchOptions.lifecycleCycles,
            "simulateDockReopenAfter": round1QuickSwitchOptions.simulateDockReopenAfter
        ])

        if let lifecycleCycles = round1QuickSwitchOptions.lifecycleCycles {
            runRound1QuickSwitchLifecycle(cycles: lifecycleCycles)
            return
        }

        if let simulateDockReopenAfter = round1QuickSwitchOptions.simulateDockReopenAfter {
            DispatchQueue.main.asyncAfter(deadline: .now() + simulateDockReopenAfter) { [weak self] in
                guard let self else { return }
                let hasVisibleWindows = NSApp.windows.contains { $0.isVisible }
                self.openQuickSwitchFromDockReopen(hasVisibleWindows: hasVisibleWindows)
                self.writeQuickSwitchReportIfNeeded()
                self.scheduleQuickSwitchAutoHideAndQuitIfNeeded()
            }
            return
        }

        openQuickSwitchFromTrigger(source: "launchOption")
        writeQuickSwitchReportIfNeeded()
        scheduleQuickSwitchAutoHideAndQuitIfNeeded()
    }

    private func scheduleQuickSwitchAutoHideAndQuitIfNeeded() {
        if let autoHideAfter = round1QuickSwitchOptions.autoHideAfter {
            DispatchQueue.main.asyncAfter(deadline: .now() + autoHideAfter) { [weak self] in
                self?.hideQuickSwitchSession(reason: .timeout)
            }
        }

        if let quitAfter = round1QuickSwitchOptions.quitAfter {
            DispatchQueue.main.asyncAfter(deadline: .now() + quitAfter) {
                NSApp.terminate(nil)
            }
        }
    }

    private func runRound1QuickSwitchLifecycle(cycles: Int) {
        DevelopmentDiagnostics.log("app.quickSwitch.lifecycle.start", [
            "cycles": cycles
        ])
        isRunningQuickSwitchLifecycle = true
        if quickSwitchSessionController == nil {
            let controller = QuickSwitchSessionController(
                snapshotLoader: quickSwitchSnapshotLoader(),
                windowActivationService: quickSwitchWindowActivationService(),
                spaceActivationService: quickSwitchSpaceActivationService(),
                windowCloseService: quickSwitchWindowCloseService(),
                disableScreenshotRefresh: round1QuickSwitchOptions.disableScreenshotRefresh,
                waterfallViewMode: quickSwitchWaterfallViewMode(),
                theme: quickSwitchThemePreference(),
                closeConfirmationRequired: quickSwitchConfirmBeforeClose,
                onCloseConfirmationDisabled: { [weak self] in
                    self?.setQuickSwitchConfirmBeforeClose(false)
                },
                debugHoveredAppGroupIndex: round1QuickSwitchOptions.debugHoveredAppGroupIndex,
                debugOverlayWidth: round1QuickSwitchOptions.debugOverlayWidth,
                debugKeySequence: round1QuickSwitchOptions.debugKeySequence,
                debugMouseSequence: round1QuickSwitchOptions.debugMouseSequence,
                debugSystemCriticalAfter: round1QuickSwitchOptions.debugSystemCriticalAfter
            )
            controller.onSnapshotUpdated = { [weak self] in
                self?.writeQuickSwitchReportIfNeeded()
            }
            controller.onToggleWaterfallViewMode = { [weak self] in
                self?.toggleQuickSwitchWaterfallViewModeFromQuickSwitch()
            }
            controller.onToggleTheme = { [weak self] in
                self?.toggleQuickSwitchThemeFromQuickSwitch()
            }
            quickSwitchSessionController = controller
        }

        quickSwitchSessionController?.prepareLifecycleRun(targetCycles: cycles)
        runRound1QuickSwitchLifecycleCycle(currentCycle: 0, totalCycles: max(0, cycles))
    }

    private func runRound1QuickSwitchLifecycleCycle(currentCycle: Int, totalCycles: Int) {
        guard currentCycle < totalCycles else {
            DevelopmentDiagnostics.log("app.quickSwitch.lifecycle.complete", [
                "totalCycles": totalCycles
            ])
            quickSwitchSessionController?.finalizeLifecycleRun()
            isRunningQuickSwitchLifecycle = false
            writeQuickSwitchReportIfNeeded()
            NSApp.terminate(nil)
            return
        }

        openQuickSwitchSession()

        DispatchQueue.main.asyncAfter(deadline: .now() + round1QuickSwitchOptions.lifecycleVisibleDuration) { [weak self] in
            guard let self else { return }
            self.hideQuickSwitchSession(reason: .timeout)
            self.quickSwitchSessionController?.markLifecycleCycleCompleted()

            DispatchQueue.main.asyncAfter(deadline: .now() + self.round1QuickSwitchOptions.lifecycleInterval) { [weak self] in
                self?.runRound1QuickSwitchLifecycleCycle(currentCycle: currentCycle + 1, totalCycles: totalCycles)
            }
        }
    }

    private func openQuickSwitchFromDockReopen(hasVisibleWindows: Bool) {
        DevelopmentDiagnostics.log("app.quickSwitch.openFromDockReopen", [
            "hasVisibleWindows": hasVisibleWindows,
            "alreadyVisible": quickSwitchSessionController?.isVisible ?? false
        ])
        openQuickSwitchFromTrigger(source: "dockReopen")
    }

    private func openQuickSwitchFromTrigger(source: String = "trigger") {
        lastQuickSwitchOpenRequestSource = source
        let accessibility = accessibilityStatus()
        DevelopmentDiagnostics.log("app.quickSwitch.openFromTrigger", [
            "source": source,
            "accessibility": permissionStatusString(accessibility),
            "screenRecording": permissionStatusString(screenRecordingStatus()),
            "alreadyVisible": quickSwitchSessionController?.isVisible ?? false
        ])
        guard accessibility == .granted else {
            stopQuickSwitchTriggerService(reason: "quick-switch-trigger-without-accessibility")
            hideQuickSwitchSession(reason: .userClosed)
            showPermissions()
            return
        }

        if quickSwitchSessionController?.isVisible == true {
            quickSwitchSessionController?.retriggerFromShortcut()
        } else {
            openQuickSwitchSession()
        }
    }

    private func openQuickSwitchSession() {
        if quickSwitchSessionController == nil {
            DevelopmentDiagnostics.log("app.quickSwitch.createSessionController")
            let controller = QuickSwitchSessionController(
                snapshotLoader: quickSwitchSnapshotLoader(),
                windowActivationService: quickSwitchWindowActivationService(),
                spaceActivationService: quickSwitchSpaceActivationService(),
                windowCloseService: quickSwitchWindowCloseService(),
                disableScreenshotRefresh: round1QuickSwitchOptions.disableScreenshotRefresh,
                waterfallViewMode: quickSwitchWaterfallViewMode(),
                theme: quickSwitchThemePreference(),
                closeConfirmationRequired: quickSwitchConfirmBeforeClose,
                onCloseConfirmationDisabled: { [weak self] in
                    self?.setQuickSwitchConfirmBeforeClose(false)
                },
                debugHoveredAppGroupIndex: round1QuickSwitchOptions.debugHoveredAppGroupIndex,
                debugOverlayWidth: round1QuickSwitchOptions.debugOverlayWidth,
                debugKeySequence: round1QuickSwitchOptions.debugKeySequence,
                debugMouseSequence: round1QuickSwitchOptions.debugMouseSequence,
                debugSystemCriticalAfter: round1QuickSwitchOptions.debugSystemCriticalAfter
            )
            controller.onSnapshotUpdated = { [weak self] in
                self?.writeQuickSwitchReportIfNeeded()
            }
            controller.onToggleWaterfallViewMode = { [weak self] in
                self?.toggleQuickSwitchWaterfallViewModeFromQuickSwitch()
            }
            controller.onToggleTheme = { [weak self] in
                self?.toggleQuickSwitchThemeFromQuickSwitch()
            }
            quickSwitchSessionController = controller
        }

        DevelopmentDiagnostics.log("app.quickSwitch.session.showRequested")
        quickSwitchSessionController?.show()
        writeQuickSwitchReportIfNeeded()
    }

    private func hideQuickSwitchSession(reason: DismissReason = .userClosed) {
        DevelopmentDiagnostics.log("app.quickSwitch.session.hideRequested", [
            "reason": Self.dismissReasonString(reason),
            "wasVisible": quickSwitchSessionController?.isVisible ?? false
        ])
        quickSwitchSessionController?.hide(reason: reason)
    }

    private func writeQuickSwitchReportIfNeeded() {
        guard let reportPath = round1QuickSwitchOptions.reportPath else { return }
        guard !isRunningQuickSwitchLifecycle else { return }
        let reportPathSummary = DevelopmentDiagnostics.pathSummary(reportPath)

        var report: [String: Any] = quickSwitchSessionController?.reportDictionary() ?? [
            "snapshotLoaded": false,
            "quickSwitchVisible": false,
            "permissionStatus": "\(accessibilityStatus())",
            "diagnosticLogLocationKind": DevelopmentDiagnostics.pathSummary(DevelopmentDiagnostics.logPath)["locationKind"] ?? "unknown",
            "diagnosticLogBasename": DevelopmentDiagnostics.pathSummary(DevelopmentDiagnostics.logPath)["basename"] ?? "aligner-dev.log"
        ]
        let versionBuild = appVersionBuild()
        report["preferenceWaterfallViewMode"] = preferenceStore.read().quickSwitchWaterfallViewMode.rawValue
        report["resolvedWaterfallViewMode"] = quickSwitchWaterfallViewMode().rawValue
        report["preferenceTheme"] = preferenceStore.read().theme.rawValue
        report["resolvedQuickSwitchTheme"] = quickSwitchThemePreference().rawValue
        report["lastQuickSwitchOpenRequestSource"] = lastQuickSwitchOpenRequestSource
        report["authorization"] = round015EntitlementOptions.authorizationReportDictionary()
        report["aboutVersion"] = versionBuild.version
        report["aboutBuild"] = versionBuild.build
        report["statusMenu"] = statusMenuReportDictionary()
        report["mainMenu"] = mainMenuReportDictionary()
        report["settingsWindow"] = settingsWindowReportDictionary()

        do {
            let data = try JSONSerialization.data(withJSONObject: report, options: [.prettyPrinted, .sortedKeys])
            try data.write(to: URL(fileURLWithPath: reportPath), options: [.atomic])
            DevelopmentDiagnostics.log("app.quickSwitch.reportWritten", [
                "reportPathKind": reportPathSummary["locationKind"],
                "reportPathBasename": reportPathSummary["basename"],
                "reportPathHash": reportPathSummary["fingerprint"]
            ])
        } catch {
            var fields: [String: CustomStringConvertible?] = [
                "reportPathKind": reportPathSummary["locationKind"],
                "reportPathBasename": reportPathSummary["basename"],
                "reportPathHash": reportPathSummary["fingerprint"]
            ]
            DevelopmentDiagnostics.errorSummaryFields(error).forEach { fields[$0.key] = $0.value }
            DevelopmentDiagnostics.log("app.quickSwitch.reportFailed", fields)
            fputs("Round01 Quick Switch report failed: \(DevelopmentDiagnostics.errorSummaryString(error))\n", stderr)
        }
    }

    private func accessibilityStatus() -> PermissionStatus {
        if round1QuickSwitchOptions.simulateAccessibilityDenied {
            return .notGranted
        }

        return permissionChecker.status(for: .accessibility)
    }

    private func screenRecordingStatus() -> PermissionStatus {
        if round1QuickSwitchOptions.simulateScreenRecordingDenied {
            return .notGranted
        }

        return permissionChecker.status(for: .screenRecording)
    }

    private func quickSwitchSnapshotLoader() -> any QuickSwitchSnapshotLoading {
        if round1QuickSwitchOptions.fixtureActivation {
            return FixtureQuickSwitchSnapshotLoader(appCount: 2, windowsPerApp: 1, mode: .activation)
        }

        if round1QuickSwitchOptions.fixtureCandidateFiltering {
            return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .candidateFiltering)
        }

        if round1QuickSwitchOptions.fixtureSpaceFilter {
            return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .spaceFilter)
        }

        if round1QuickSwitchOptions.fixtureSplitView {
            return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .splitView)
        }

        if round1QuickSwitchOptions.fixtureMultiPageIdentity {
            return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .multiPageIdentity)
        }

        if let fixtureAppCount = round1QuickSwitchOptions.fixtureAppCount {
            return FixtureQuickSwitchSnapshotLoader(
                appCount: fixtureAppCount,
                windowsPerApp: round1QuickSwitchOptions.fixtureWindowsPerApp ?? 1,
                mode: .layout,
                usesMultipleDisplays: round1QuickSwitchOptions.fixtureMultiDisplay
            )
        }

        let liveLoader = LiveQuickSwitchSnapshotLoader()
        guard let delay = round1QuickSwitchOptions.snapshotLoadDelay else {
            return liveLoader
        }

        return DelayedQuickSwitchSnapshotLoader(base: liveLoader, delay: delay)
    }

    private func quickSwitchWindowActivationService() -> any WindowActivationServiceProtocol {
        if round1QuickSwitchOptions.debugWindowActivation {
            return DebugWindowActivationService()
        }

        let context = quickSwitchWindowSpaceContext()
        return CGWindowAXWindowService(
            spaceIDsByWindowIDProvider: context.spaceIDsByWindowIDProvider,
            fullscreenSpaceIDs: context.fullscreenSpaceIDs
        )
    }

    private func quickSwitchWaterfallViewMode() -> QuickSwitchWaterfallViewMode {
        round1QuickSwitchOptions.waterfallViewMode
            ?? preferenceStore.read().quickSwitchWaterfallViewMode
    }

    private func quickSwitchThemePreference() -> ThemePreference {
        let theme = preferenceStore.read().theme
        return theme == .dark ? .dark : .light
    }

    private func quickSwitchSpaceActivationService() -> any SpaceActivationServiceProtocol {
        if round1QuickSwitchOptions.debugWindowActivation {
            return DebugSpaceActivationService()
        }

        return PrivateSpaceActivationService()
    }

    private func quickSwitchWindowCloseService() -> any WindowCloseServiceProtocol {
        if round1QuickSwitchOptions.debugWindowClose {
            return DebugWindowCloseService()
        }

        let context = quickSwitchWindowSpaceContext()
        return CGWindowAXWindowService(
            spaceIDsByWindowIDProvider: context.spaceIDsByWindowIDProvider,
            fullscreenSpaceIDs: context.fullscreenSpaceIDs
        )
    }

    private func quickSwitchWindowSpaceContext() -> (
        spaceIDsByWindowIDProvider: ([UInt32]) -> [UInt32: [UInt64]],
        fullscreenSpaceIDs: Set<UInt64>
    ) {
        do {
            let spaceEnumerator = try SkyLightSpaceEnumerator()
            let displays = try spaceEnumerator.displays()
            let fullscreenSpaceIDs = Set(
                displays
                    .flatMap(\.spaces)
                    .filter { $0.type == .fullscreen }
                    .map(\.id)
            )
            return (
                spaceIDsByWindowIDProvider: spaceEnumerator.spaceIDsByWindowID(windowIDs:),
                fullscreenSpaceIDs: fullscreenSpaceIDs
            )
        } catch {
            DevelopmentDiagnostics.log("app.quickSwitch.windowSpaceContextUnavailable", [
                "error": DevelopmentDiagnostics.errorSummaryString(error)
            ])
            return (
                spaceIDsByWindowIDProvider: { _ in [:] },
                fullscreenSpaceIDs: []
            )
        }
    }

    private var quickSwitchConfirmBeforeClose: Bool {
        let value = UserDefaults.standard.object(forKey: Self.quickSwitchConfirmBeforeCloseDefaultsKey)
        return (value as? Bool) ?? true
    }

    private func setQuickSwitchConfirmBeforeClose(_ enabled: Bool) {
        UserDefaults.standard.set(enabled, forKey: Self.quickSwitchConfirmBeforeCloseDefaultsKey)
        DevelopmentDiagnostics.log("app.quickSwitch.closeConfirmationPreferenceChanged", [
            "confirmBeforeClose": enabled
        ])
    }

    private func setQuickSwitchWaterfallViewMode(_ mode: QuickSwitchWaterfallViewMode) {
        let preferences = preferenceStore.read()
        let updatedPreferences = AlignerPreferences(
            showMinimizedWindows: preferences.showMinimizedWindows,
            showFullscreenWindows: preferences.showFullscreenWindows,
            quickSwitchWaterfallViewMode: mode,
            theme: preferences.theme,
            language: preferences.language,
            spaceDisplayStrategy: preferences.spaceDisplayStrategy
        )
        preferenceStore.write(updatedPreferences)
        quickSwitchSessionController?.setWaterfallViewMode(mode)
        updateWaterfallViewModeMenuState()
        settingsWindowController?.refreshWaterfallViewMode()
        writeQuickSwitchReportIfNeeded()
        DevelopmentDiagnostics.log("app.quickSwitch.waterfallViewModePreferenceChanged", [
            "mode": mode.rawValue,
            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false
        ])
    }

    private func toggleQuickSwitchWaterfallViewModeFromQuickSwitch() {
        let currentMode = quickSwitchWaterfallViewMode()
        let nextMode: QuickSwitchWaterfallViewMode = currentMode == .horizontalMasonry
            ? .verticalColumns
            : .horizontalMasonry
        DevelopmentDiagnostics.log("app.quickSwitch.waterfallViewModeToggle", [
            "currentMode": currentMode.rawValue,
            "nextMode": nextMode.rawValue,
            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false
        ])
        setQuickSwitchWaterfallViewMode(nextMode)
    }

    private func setQuickSwitchThemePreference(_ theme: ThemePreference) {
        let normalizedTheme: ThemePreference = theme == .dark ? .dark : .light
        let preferences = preferenceStore.read()
        let updatedPreferences = AlignerPreferences(
            showMinimizedWindows: preferences.showMinimizedWindows,
            showFullscreenWindows: preferences.showFullscreenWindows,
            quickSwitchWaterfallViewMode: preferences.quickSwitchWaterfallViewMode,
            theme: normalizedTheme,
            language: preferences.language,
            spaceDisplayStrategy: preferences.spaceDisplayStrategy
        )
        preferenceStore.write(updatedPreferences)
        quickSwitchSessionController?.setTheme(normalizedTheme)
        writeQuickSwitchReportIfNeeded()
        DevelopmentDiagnostics.log("app.quickSwitch.themePreferenceChanged", [
            "theme": normalizedTheme.rawValue,
            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false
        ])
    }

    private func toggleQuickSwitchThemeFromQuickSwitch() {
        let currentTheme = quickSwitchThemePreference()
        let nextTheme: ThemePreference = currentTheme == .dark ? .light : .dark
        DevelopmentDiagnostics.log("app.quickSwitch.themeToggle", [
            "currentTheme": currentTheme.rawValue,
            "nextTheme": nextTheme.rawValue,
            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false
        ])
        setQuickSwitchThemePreference(nextTheme)
    }

    private func appVersionBuild() -> (version: String, build: String) {
        let info = Bundle.main.infoDictionary ?? [:]
        let version = info["CFBundleShortVersionString"] as? String ?? "Unknown"
        let build = info["CFBundleVersion"] as? String ?? "Unknown"
        return (version, build)
    }

    private func statusMenuReportDictionary() -> [String: Any] {
        updateWaterfallViewModeMenuState()
        let verticalItem = verticalWaterfallViewModeMenuItems.first
        let horizontalItem = horizontalWaterfallViewModeMenuItems.first
        return [
            "hasAboutItem": statusItem?.menu?.item(withTitle: "About Aligner...") != nil,
            "hasSettingsItem": statusItem?.menu?.item(withTitle: "Settings...") != nil,
            "hasExportPerformanceDiagnosticsItem": statusItem?.menu?.item(withTitle: "Export Performance Diagnostics...") != nil,
            "hasQuickSwitchViewSubmenu": statusItem?.menu?.item(withTitle: "Quick Switch View")?.submenu != nil,
            "hasVerticalColumnsItem": verticalItem != nil,
            "hasHorizontalWaterfallItem": horizontalItem != nil,
            "verticalColumnsState": verticalItem?.state == .on ? "on" : "off",
            "horizontalWaterfallState": horizontalItem?.state == .on ? "on" : "off"
        ]
    }

    private func mainMenuReportDictionary() -> [String: Any] {
        updateWaterfallViewModeMenuState()
        let appMenu = NSApp.mainMenu?.items.first?.submenu
        let viewMenu = NSApp.mainMenu?.item(withTitle: "View")?.submenu
        return [
            "hasMenu": NSApp.mainMenu != nil,
            "hasAppMenu": appMenu != nil,
            "hasAboutItem": appMenu?.item(withTitle: "About Aligner...") != nil,
            "hasSettingsItem": appMenu?.item(withTitle: "Settings...") != nil,
            "hasExportPerformanceDiagnosticsItem": appMenu?.item(withTitle: "Export Performance Diagnostics...") != nil,
            "settingsKeyEquivalent": appMenu?.item(withTitle: "Settings...")?.keyEquivalent ?? "",
            "hasQuickSwitchViewSubmenuInAppMenu": appMenu?.item(withTitle: "Quick Switch View")?.submenu != nil,
            "hasQuickSwitchViewSubmenuInViewMenu": viewMenu?.item(withTitle: "Quick Switch View")?.submenu != nil
        ]
    }

    private func settingsWindowReportDictionary() -> [String: Any] {
        settingsWindowController?.reportDictionary() ?? [
            "exists": false,
            "visible": false,
            "title": "",
            "selectedTabLabel": "",
            "selectedTabIdentifier": "",
            "waterfallViewMode": quickSwitchWaterfallViewMode().rawValue,
            "hasViewTab": false,
            "usesSwiftUI": false
        ]
    }

    @objc private func showAbout() {
        let versionBuild = appVersionBuild()
        DevelopmentDiagnostics.log("app.menu.showAbout", [
            "version": versionBuild.version,
            "build": versionBuild.build
        ])

        NSApp.setActivationPolicy(.regular)
        let alert = NSAlert()
        alert.messageText = "Aligner"
        alert.informativeText = "Version / Build: \(versionBuild.version) / \(versionBuild.build)"
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")
        alert.icon = NSApp.applicationIconImage
        NSApp.activate(ignoringOtherApps: true)
        alert.runModal()
    }

    @objc private func showSettingsFromMenu() {
        openSettingsWindow(source: "menu")
    }

    @objc private func exportPerformanceDiagnostics() {
        let versionBuild = appVersionBuild()
        let preferences = preferenceStore.read()
        let quickSwitch = quickSwitchSessionController?.performanceDiagnosticSummary() ?? [
            "snapshotLoaded": false,
            "quickSwitchVisible": false,
            "applicationCount": 0,
            "windowCount": 0,
            "applications": []
        ]
        performanceDiagnosticsExporter.export(
            version: versionBuild.version,
            build: versionBuild.build,
            preferences: preferences,
            accessibility: permissionStatusString(accessibilityStatus()),
            screenRecording: permissionStatusString(screenRecordingStatus()),
            quickSwitch: quickSwitch
        )
    }

    private func showSettingsFromQuickSwitch() {
        openSettingsWindow(source: "quickSwitch")
    }

    private func openSettingsWindow(source: String) {
        DevelopmentDiagnostics.log("app.settings.show", [
            "source": source,
            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false,
            "mode": quickSwitchWaterfallViewMode().rawValue
        ])
        hideQuickSwitchSession(reason: .userClosed)
        NSApp.setActivationPolicy(.regular)

        if settingsWindowController == nil {
            settingsWindowController = SettingsWindowController(
                modeProvider: { [weak self] in
                    self?.quickSwitchWaterfallViewMode() ?? .verticalColumns
                },
                onWaterfallViewModeChanged: { [weak self] mode in
                    self?.setQuickSwitchWaterfallViewMode(mode)
                }
            )
        }

        settingsWindowController?.showWindow(nil)
        NSApp.activate(ignoringOtherApps: true)
        writeQuickSwitchReportIfNeeded()
        DevelopmentDiagnostics.log("app.settings.windowShown", [
            "windowVisible": settingsWindowController?.window?.isVisible ?? false,
            "activationPolicy": NSApp.activationPolicy().rawValue
        ])
    }

    @objc private func useVerticalWaterfallViewMode() {
        setQuickSwitchWaterfallViewMode(.verticalColumns)
    }

    @objc private func useHorizontalWaterfallViewMode() {
        setQuickSwitchWaterfallViewMode(.horizontalMasonry)
    }

    @objc private func showPermissions() {
        DevelopmentDiagnostics.log("app.permissions.show", [
            "accessibility": permissionStatusString(accessibilityStatus()),
            "screenRecording": permissionStatusString(screenRecordingStatus())
        ])
        NSApp.setActivationPolicy(.regular)
        stopQuickSwitchTriggerService(reason: "show-permissions")
        hideQuickSwitchSession(reason: .userClosed)

        if permissionWindowController == nil {
            let controller = PermissionsWindowController(permissionManager: permissionChecker)
            controller.onPermissionStatusChanged = { [weak self] kind, status in
                DevelopmentDiagnostics.log("app.permissions.statusChanged", [
                    "kind": kind.rawValue,
                    "status": self?.permissionStatusString(status) ?? "\(status)"
                ])
                guard kind == .accessibility else { return }

                switch status {
                case .granted:
                    self?.startQuickSwitchTriggerServiceIfPossible()
                case .notGranted:
                    self?.stopQuickSwitchTriggerService(reason: "accessibility-status-not-granted")
                    self?.hideQuickSwitchSession(reason: .userClosed)
                }
            }
            controller.onPermissionRequestStarted = { [weak self] kind in
                DevelopmentDiagnostics.log("app.permissions.requestStarted", [
                    "kind": kind.rawValue
                ])
                if kind == .accessibility {
                    self?.stopQuickSwitchTriggerService(reason: "accessibility-request-started")
                    self?.hideQuickSwitchSession(reason: .userClosed)
                }
            }
            permissionWindowController = controller
        }

        permissionWindowController?.showWindow(nil)
        permissionWindowController?.window?.makeKeyAndOrderFront(nil)
        NSApp.activate(ignoringOtherApps: true)
        DevelopmentDiagnostics.log("app.permissions.windowShown", [
            "activationPolicy": NSApp.activationPolicy().rawValue,
            "windowVisible": permissionWindowController?.window?.isVisible ?? false
        ])
    }

    @objc private func showRound0DebugOverlay() {
        DevelopmentDiagnostics.log("app.menu.showRound0DebugOverlay")
        showDebugOverlay(includeSettingsChild: true)
    }

    @objc private func hideRound0DebugOverlay() {
        DevelopmentDiagnostics.log("app.menu.hideRound0DebugOverlay")
        hideDebugOverlay()
    }

    @objc private func showQuickSwitchFromMenu() {
        DevelopmentDiagnostics.log("app.menu.showQuickSwitch")
        openQuickSwitchFromTrigger()
    }

    @objc private func hideQuickSwitchFromMenu() {
        DevelopmentDiagnostics.log("app.menu.hideQuickSwitch")
        hideQuickSwitchSession()
    }

    private func hideRound1PerformancePoC() {
        round1PerformancePoCController?.hide()
    }

    private func writeRound1PerformancePoCReportIfNeeded() {
        guard let reportPath = round1PoCOptions.reportPath,
              let report = round1PerformancePoCController?.reportDictionary()
        else { return }
        let reportPathSummary = DevelopmentDiagnostics.pathSummary(reportPath)

        do {
            let data = try JSONSerialization.data(withJSONObject: report, options: [.prettyPrinted, .sortedKeys])
            try data.write(to: URL(fileURLWithPath: reportPath), options: [.atomic])
            DevelopmentDiagnostics.log("app.performancePoC.reportWritten", [
                "reportPathKind": reportPathSummary["locationKind"],
                "reportPathBasename": reportPathSummary["basename"],
                "reportPathHash": reportPathSummary["fingerprint"]
            ])
        } catch {
            var fields: [String: CustomStringConvertible?] = [
                "reportPathKind": reportPathSummary["locationKind"],
                "reportPathBasename": reportPathSummary["basename"],
                "reportPathHash": reportPathSummary["fingerprint"]
            ]
            DevelopmentDiagnostics.errorSummaryFields(error).forEach { fields[$0.key] = $0.value }
            DevelopmentDiagnostics.log("app.performancePoC.reportFailed", fields)
            fputs("Round01 performance PoC report failed: \(DevelopmentDiagnostics.errorSummaryString(error))\n", stderr)
        }
    }

    private func showDebugOverlay(includeSettingsChild: Bool) {
        DevelopmentDiagnostics.log("app.debugOverlay.show", [
            "includeSettingsChild": includeSettingsChild
        ])
        if debugOverlayController == nil {
            debugOverlayController = Round0DebugOverlayController()
        }

        debugOverlayController?.show(includeSettingsChild: includeSettingsChild)
    }

    private func hideDebugOverlay() {
        DevelopmentDiagnostics.log("app.debugOverlay.hide")
        debugOverlayController?.hide()
    }

    private func openRound1PerformancePoC(autoCycle: Bool) {
        DevelopmentDiagnostics.log("app.performancePoC.show", [
            "autoCycle": autoCycle
        ])
        if round1PerformancePoCController == nil {
            round1PerformancePoCController = Round1PerformancePoCController()
        }

        round1PerformancePoCController?.show(autoCycle: autoCycle)
    }

    @objc private func quit() {
        DevelopmentDiagnostics.logSync("app.menu.quit")
        NSApp.terminate(nil)
    }

    private func permissionStatusString(_ status: PermissionStatus) -> String {
        switch status {
        case .granted:
            return "granted"
        case .notGranted:
            return "notGranted"
        }
    }

    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"
        }
    }
}
