import Foundation
import AlignerCore

protocol QuickSwitchSnapshotLoading: Sendable {
    func snapshot() throws -> QuickSwitchSnapshotLoad
}

struct QuickSwitchSnapshotLoad: Sendable {
    let snapshot: QuickSwitchSnapshot
    let currentSpaceIDs: Set<UInt64>
}

enum FixtureQuickSwitchMode: Sendable {
    case layout
    case candidateFiltering
    case activation
}

final class LiveQuickSwitchSnapshotLoader: QuickSwitchSnapshotLoading {
    func snapshot() throws -> QuickSwitchSnapshotLoad {
        let spaceEnumerator = try SkyLightSpaceEnumerator()
        let displays = try spaceEnumerator.displays()
        let currentSpaceIDs = try spaceEnumerator.currentSpaceIDs()
        let fullscreenSpaceIDs = Set(
            displays
                .flatMap(\.spaces)
                .filter { $0.type == .fullscreen }
                .map(\.id)
        )
        let windowService = CGWindowAXWindowService(
            spaceIDsByWindowIDProvider: spaceEnumerator.spaceIDsByWindowID(windowIDs:),
            fullscreenSpaceIDs: fullscreenSpaceIDs
        )
        let windows = try windowService.allWindows()

        return QuickSwitchSnapshotLoad(
            snapshot: QuickSwitchSnapshotBuilder.snapshot(displays: displays, windows: windows),
            currentSpaceIDs: currentSpaceIDs
        )
    }
}

final class DelayedQuickSwitchSnapshotLoader: QuickSwitchSnapshotLoading {
    private let base: any QuickSwitchSnapshotLoading
    private let delay: TimeInterval

    init(base: any QuickSwitchSnapshotLoading, delay: TimeInterval) {
        self.base = base
        self.delay = delay
    }

    func snapshot() throws -> QuickSwitchSnapshotLoad {
        if delay > 0 {
            Thread.sleep(forTimeInterval: delay)
        }

        return try base.snapshot()
    }
}

final class FixtureQuickSwitchSnapshotLoader: QuickSwitchSnapshotLoading {
    private let appCount: Int
    private let windowsPerApp: Int
    private let mode: FixtureQuickSwitchMode
    private let usesMultipleDisplays: Bool

    init(
        appCount: Int,
        windowsPerApp: Int = 1,
        mode: FixtureQuickSwitchMode = .layout,
        usesMultipleDisplays: Bool = false
    ) {
        self.appCount = max(1, appCount)
        self.windowsPerApp = max(1, windowsPerApp)
        self.mode = mode
        self.usesMultipleDisplays = usesMultipleDisplays
    }

    func snapshot() throws -> QuickSwitchSnapshotLoad {
        if mode == .candidateFiltering {
            return candidateFilteringSnapshot()
        }
        if mode == .activation {
            return activationSnapshot()
        }

        return layoutSnapshot()
    }

    private func layoutSnapshot() -> QuickSwitchSnapshotLoad {
        let displays = layoutDisplays()
        let spaceIDs: [UInt64] = usesMultipleDisplays ? [1, 2, 3, 4, 5] : [1, 2, 3]
        let windows = (0..<appCount).flatMap { index in
            let app = AlignerApp(
                bundleIdentifier: "com.example.fixture.\(String(format: "%03d", index))",
                name: "Fixture App \(String(format: "%03d", index))",
                category: .generic,
                processIdentifier: Int32(20_000 + index)
            )

            return (0..<windowsPerApp).map { windowIndex in
                let spaceSeed = index + windowIndex
                let spaceID = usesMultipleDisplays
                    ? spaceIDs[spaceSeed % spaceIDs.count]
                    : UInt64(spaceSeed % 5 == 0 ? 2 : spaceSeed % 2 == 0 ? 1 : 3)
                return AlignerWindow(
                    id: UInt32(10_000 + index * 100 + windowIndex),
                    app: app,
                    title: "Fixture Window \(String(format: "%03d", index))-\(String(format: "%02d", windowIndex))",
                    isFullscreen: spaceID == 2,
                    spaceIDs: [spaceID]
                )
            }
        }

        return QuickSwitchSnapshotLoad(
            snapshot: QuickSwitchSnapshotBuilder.snapshot(displays: displays, windows: windows),
            currentSpaceIDs: usesMultipleDisplays ? [2, 4] : [2]
        )
    }

    private func layoutDisplays() -> [AlignerDisplay] {
        let displayAUUID = "fixture-display-a"
        guard usesMultipleDisplays else {
            return [
                AlignerDisplay(
                    uuid: displayAUUID,
                    physical: true,
                    spaces: [
                        AlignerSpace(id: 1, type: .user, displayUUID: displayAUUID, index: 1),
                        AlignerSpace(id: 2, type: .fullscreen, displayUUID: displayAUUID, index: 2),
                        AlignerSpace(id: 3, type: .user, displayUUID: displayAUUID, index: 3)
                    ]
                )
            ]
        }

        let displayBUUID = "fixture-display-b"
        return [
            AlignerDisplay(
                uuid: displayAUUID,
                physical: true,
                spaces: [
                    AlignerSpace(id: 1, type: .user, displayUUID: displayAUUID, index: 1),
                    AlignerSpace(id: 2, type: .fullscreen, displayUUID: displayAUUID, index: 2),
                    AlignerSpace(id: 3, type: .user, displayUUID: displayAUUID, index: 3)
                ]
            ),
            AlignerDisplay(
                uuid: displayBUUID,
                physical: true,
                spaces: [
                    AlignerSpace(id: 4, type: .user, displayUUID: displayBUUID, index: 1),
                    AlignerSpace(id: 5, type: .user, displayUUID: displayBUUID, index: 2)
                ]
            )
        ]
    }

    private func candidateFilteringSnapshot() -> QuickSwitchSnapshotLoad {
        let displayUUID = "fixture-display-a"
        let displays = [
            AlignerDisplay(
                uuid: displayUUID,
                physical: true,
                spaces: [
                    AlignerSpace(id: 1, type: .user, displayUUID: displayUUID, index: 1),
                    AlignerSpace(id: 2, type: .fullscreen, displayUUID: displayUUID, index: 2)
                ]
            )
        ]

        let app = AlignerApp(
            bundleIdentifier: "com.example.candidate.standard",
            name: "Candidate Standard",
            category: .documentNotes,
            processIdentifier: 30_001
        )
        let hiddenApp = AlignerApp(
            bundleIdentifier: "com.example.candidate.hidden",
            name: "Candidate Hidden",
            category: .generic,
            isHidden: true,
            processIdentifier: 30_002
        )
        let systemApp = AlignerApp(
            bundleIdentifier: "com.apple.SecurityAgent",
            name: "SecurityAgent",
            category: .generic,
            processIdentifier: 30_003
        )
        let finderApp = AlignerApp(
            bundleIdentifier: "com.apple.finder",
            name: "Finder",
            category: .finder,
            processIdentifier: 30_004
        )

        let records = [
            WindowEnumerationRecord(
                id: 20_001,
                app: app,
                title: "Standard Candidate",
                size: CGSize(width: 900, height: 600),
                subrole: .standard,
                hasAXBacking: true,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_002,
                app: app,
                title: "Minimized Candidate",
                identifierSource: .syntheticAX,
                size: .zero,
                subrole: .standard,
                isMinimized: true,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_003,
                app: app,
                title: "Fullscreen Candidate",
                size: .zero,
                subrole: .standard,
                isFullscreen: true,
                spaceIDs: [2]
            ),
            WindowEnumerationRecord(
                id: 20_004,
                app: app,
                title: "Sheet Candidate",
                size: CGSize(width: 520, height: 320),
                subrole: .sheet,
                hasAXBacking: true,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_005,
                app: app,
                title: "Dialog Candidate",
                size: CGSize(width: 560, height: 360),
                subrole: .dialog,
                hasAXBacking: true,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_006,
                app: app,
                title: "",
                size: CGSize(width: 840, height: 540),
                subrole: .standard,
                hasAXBacking: true,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_007,
                app: app,
                activationPolicy: .accessory,
                title: "Accessory Rejected",
                size: CGSize(width: 900, height: 600),
                subrole: .standard,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_008,
                app: hiddenApp,
                title: "Hidden Rejected",
                size: CGSize(width: 900, height: 600),
                subrole: .standard,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_009,
                app: app,
                title: "Tiny Rejected",
                size: CGSize(width: 80, height: 40),
                subrole: .standard,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_010,
                app: app,
                title: "Ghost Rejected",
                size: CGSize(width: 900, height: 600),
                subrole: .standard,
                isGhost: true,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_011,
                app: app,
                title: "Popover Rejected",
                size: CGSize(width: 900, height: 600),
                subrole: .unknown("AXPopover"),
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_012,
                app: finderApp,
                title: "Desktop",
                size: CGSize(width: 1710, height: 1112),
                subrole: .standard,
                isDesktopElement: true,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_013,
                app: systemApp,
                title: "Security Confirmation",
                size: CGSize(width: 620, height: 420),
                subrole: .dialog,
                isSystemCritical: true,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_014,
                app: app,
                title: "Noninteractive Rejected",
                size: CGSize(width: 900, height: 600),
                subrole: .standard,
                isInteractable: false,
                spaceIDs: [1]
            ),
            WindowEnumerationRecord(
                id: 20_015,
                app: app,
                title: "CG-only Offscreen Rejected",
                size: CGSize(width: 900, height: 600),
                subrole: .standard,
                hasAXBacking: false,
                isOnscreen: false,
                spaceIDs: [1]
            )
        ]
        let windows = records.compactMap(WindowEnumerationPolicy.window)

        return QuickSwitchSnapshotLoad(
            snapshot: QuickSwitchSnapshotBuilder.snapshot(displays: displays, windows: windows),
            currentSpaceIDs: [1]
        )
    }

    private func activationSnapshot() -> QuickSwitchSnapshotLoad {
        let displayUUID = "fixture-display-a"
        let displays = [
            AlignerDisplay(
                uuid: displayUUID,
                physical: true,
                spaces: [
                    AlignerSpace(id: 1, type: .user, displayUUID: displayUUID, index: 1),
                    AlignerSpace(id: 2, type: .fullscreen, displayUUID: displayUUID, index: 2)
                ]
            )
        ]

        let normalApp = AlignerApp(
            bundleIdentifier: "com.example.activation.000",
            name: "Activation App 000",
            category: .generic,
            processIdentifier: 40_000
        )
        let minimizedApp = AlignerApp(
            bundleIdentifier: "com.example.activation.001",
            name: "Activation App 001",
            category: .generic,
            processIdentifier: 40_001
        )
        let windows = [
            AlignerWindow(
                id: 40_100,
                app: normalApp,
                title: "Normal Activation Window",
                isMinimized: false,
                spaceIDs: [1]
            ),
            AlignerWindow(
                id: 40_110,
                app: normalApp,
                title: "App Activated Only Window",
                isMinimized: false,
                spaceIDs: [1]
            ),
            AlignerWindow(
                id: 40_200,
                app: minimizedApp,
                title: "Minimized Activation Window",
                identifierSource: .syntheticAX,
                isMinimized: true,
                spaceIDs: [1]
            )
        ]

        return QuickSwitchSnapshotLoad(
            snapshot: QuickSwitchSnapshotBuilder.snapshot(displays: displays, windows: windows),
            currentSpaceIDs: [1]
        )
    }
}
