import AppKit
import CoreGraphics
import AlignerCore

@MainActor
final class QuickSwitchOverlayHost: OverlayHostProtocol {
    private let panel: QuickSwitchPanel
    private let screenProvider: (OverlayContext) -> NSScreen?
    private let debugOverlayWidth: CGFloat?
    private var currentScreen: NSScreen?

    init(
        screenProvider: ((OverlayContext) -> NSScreen?)? = nil,
        title: String = "Aligner Quick Switch Overlay",
        contentView: NSView? = nil,
        debugOverlayWidth: CGFloat? = nil
    ) {
        self.screenProvider = screenProvider ?? Self.screen(for:)
        self.debugOverlayWidth = debugOverlayWidth
        self.panel = QuickSwitchPanel()
        self.panel.title = title
        self.panel.contentView = contentView
        currentScreen = self.screenProvider(OverlayContext(entryPoint: .quickSwitch))
        applyWindowPolicy()
    }

    var currentLevel: CGWindowLevel {
        CGWindowLevel(panel.level.rawValue)
    }

    func show(context: OverlayContext) {
        currentScreen = screenProvider(context)
        applyWindowPolicy()
        panel.orderFrontRegardless()
    }

    func hide(reason: DismissReason) {
        detachAllChildren()
        panel.orderOut(nil)
    }

    func promoteForTextInput(allowAppActivation: Bool) {
        panel.makeKeyAndOrderFront(nil)
        if allowAppActivation {
            NSApp.activate(ignoringOtherApps: true)
        }
    }

    func attachChild(_ window: NSWindow) {
        if let panel = window as? NSPanel {
            OverlayWindowPolicy.apply(to: panel, on: currentScreen)
        } else {
            window.level = panel.level
            window.collectionBehavior = panel.collectionBehavior
            if let currentScreen {
                window.setFrame(currentScreen.frame, display: false)
            }
        }
        panel.addChildWindow(window, ordered: .above)
    }

    func detachChild(_ window: NSWindow) {
        panel.removeChildWindow(window)
    }

    func beginSheet(_ sheetPanel: NSPanel, completion: (() -> Void)?) {
        panel.beginSheet(sheetPanel) { _ in
            completion?()
        }
    }

    private func detachAllChildren() {
        for child in panel.childWindows ?? [] {
            panel.removeChildWindow(child)
            child.orderOut(nil)
        }
    }

    private func applyWindowPolicy() {
        OverlayWindowPolicy.apply(to: panel, on: currentScreen)

        guard
            let currentScreen,
            let debugOverlayWidth,
            debugOverlayWidth > 0
        else {
            return
        }

        let width = min(debugOverlayWidth, currentScreen.frame.width)
        let frame = CGRect(
            x: currentScreen.frame.midX - width / 2,
            y: currentScreen.frame.minY,
            width: width,
            height: currentScreen.frame.height
        )
        panel.setFrame(frame, display: false)
    }

    private static func screen(for context: OverlayContext) -> NSScreen? {
        screen(matching: context.targetDisplayUUID) ?? NSScreen.main
    }

    private static func screen(matching displayUUID: String?) -> NSScreen? {
        guard let displayUUID else { return nil }

        return NSScreen.screens.first { screen in
            guard
                let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber,
                let unmanagedUUID = CGDisplayCreateUUIDFromDisplayID(CGDirectDisplayID(number.uint32Value))
            else {
                return false
            }

            let uuid = unmanagedUUID.takeRetainedValue()
            let screenUUID = CFUUIDCreateString(nil, uuid) as String?
            return screenUUID?.caseInsensitiveCompare(displayUUID) == .orderedSame
        }
    }
}

@MainActor
private final class QuickSwitchPanel: NSPanel {
    init() {
        super.init(
            contentRect: .zero,
            styleMask: OverlayWindowPolicy.styleMask,
            backing: .buffered,
            defer: false
        )
    }

    override var canBecomeKey: Bool {
        true
    }

    override var canBecomeMain: Bool {
        true
    }
}
