import SwiftUI
import AppKit
import NucleicCore

/// Floating, read-only windows that show the live screen of running macOS VMs when the user has turned
/// on **"Always open VM guest display"** (Settings ▸ Virtual Machines ▸ Advanced).
///
/// Unlike the in-chat VM Monitor panel — which tracks whichever chat is open — these are **app-level
/// windows**, decoupled from the session UI: one per running guest, opened the instant a VM boots and
/// closed when it stops, regardless of which chat (if any) is in the foreground, and regardless of how
/// the VM was launched (`mac_vm_exec` or `mac_vm_computer`). That makes the guest screen visible even
/// from Home, a project overview, or with no chat open at all — the point of the setting, which exists
/// for Nucleic developers and for diagnosing a VM that won't behave. (Source of truth is
/// `AppStore.runningMacVMs()`, the engine's live per-session registry; the one-time base-image build
/// runs on a separate path with its own progress view + Recovery window, so it isn't mirrored here.)
///
/// A central watcher in `RootView` polls the running VMs and calls ``sync(running:enabled:store:)`` a
/// couple of times a second; this reconciles the open windows against that set. The window contents are
/// the same read-only, capture-polling `VMMonitorPanel`, pointed at a specific VM by name.
@MainActor
final class MacVMGuestDisplayController: NSObject, NSWindowDelegate {
    static let shared = MacVMGuestDisplayController()

    /// Open guest-display windows, keyed by VM name.
    private var windows: [String: NSWindow] = [:]
    /// VMs whose window the user manually closed while they were still running. We don't reopen these
    /// (that would fight the user), but a VM leaving the running set clears its dismissal so a later
    /// re-boot of the same VM opens fresh.
    private var dismissed: Set<String> = []

    private override init() { super.init() }

    /// Reconcile open windows against the running VMs. Opens a window for each running VM that lacks one
    /// (and wasn't manually dismissed); closes windows whose VM has stopped — or all of them when the
    /// setting is off (`enabled == false`, in which case `running` is empty).
    func sync(running: [MacVMEntry], enabled: Bool, store: AppStore) {
        let names = enabled ? Set(running.map(\.name)) : []

        // Tear down windows (and clear dismissals) for VMs that are no longer running — or for every VM
        // when the feature is off. `orderOut` hides without firing `windowWillClose`, so this never
        // mislabels a programmatic close as a user dismissal.
        for name in Set(windows.keys).union(dismissed) where !names.contains(name) {
            windows.removeValue(forKey: name)?.orderOut(nil)
            dismissed.remove(name)
        }

        // Surface a window for each newly running VM the user hasn't dismissed.
        for name in names where windows[name] == nil && !dismissed.contains(name) {
            windows[name] = makeWindow(vmName: name, store: store)
        }
    }

    /// A manual close (red button / ⌘W) is a dismissal: forget the window and remember not to reopen it
    /// for this VM until it stops. Programmatic teardown uses `orderOut`, which doesn't reach here.
    func windowWillClose(_ notification: Notification) {
        guard let window = notification.object as? NSWindow,
              let name = windows.first(where: { $0.value === window })?.key else { return }
        windows.removeValue(forKey: name)
        dismissed.insert(name)
    }

    private func makeWindow(vmName: String, store: AppStore) -> NSWindow {
        let content = VMMonitorPanel(session: nil, explicitVMName: vmName)
            .environment(store)
            .frame(minWidth: 480, minHeight: 320)

        let window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 960, height: 600),
            styleMask: [.titled, .closable, .resizable, .miniaturizable],
            backing: .buffered, defer: false)
        window.title = "macOS VM — \(Self.displayLabel(vmName))"
        window.contentViewController = NSHostingController(rootView: content)
        window.isReleasedWhenClosed = false
        window.delegate = self
        // Cascade so several guests don't stack exactly on top of each other, and surface without
        // stealing key focus from whatever the user is doing.
        window.center()
        window.setFrameOrigin(NSPoint(x: window.frame.origin.x + CGFloat(windows.count) * 28,
                                      y: window.frame.origin.y - CGFloat(windows.count) * 28))
        window.orderFront(nil)
        return window
    }

    /// Friendlier title-bar label: drop the internal `nucleic-mac-` prefix so the window reads as the
    /// session short-id (plus any channel suffix), not the full disk-clone name.
    private static func displayLabel(_ vmName: String) -> String {
        let stripped = vmName.hasPrefix("nucleic-mac-")
            ? String(vmName.dropFirst("nucleic-mac-".count)) : vmName
        return stripped.isEmpty ? vmName : stripped
    }
}
