import AppKit
import ApplicationServices
import CoreGraphics
import Foundation
import VMAgentCore

// NucleicVMAgent — the native in-guest automation agent (docs/MACOS_VM_NATIVE_AGENT.md).
//
// Runs as a per-user LaunchAgent in the auto-login `agent` account's Aqua session (AX, SCK and
// CGEvent all need an active WindowServer session), listens on a vsock port, and executes the §4
// wire ops with Apple's frameworks directly: AXUIElement (observe + control), ScreenCaptureKit
// (capture), CGEvent (raw input). It is a dumb, well-scoped actuator — the host keeps ownership of
// VM lifecycle, gating, and the approval boundary.

// A peer that vanishes mid-write must not kill the process — writes surface EPIPE instead.
signal(SIGPIPE, SIG_IGN)

let port = ProcessInfo.processInfo.environment["NUCLEIC_AGENT_PORT"]
    .flatMap(UInt32.init) ?? AgentWire.port

agentLog("NucleicVMAgent protocol v\(AgentWire.version) starting on vsock port \(port)")

// Bind the vsock listener and start accepting BEFORE anything else — in particular before any TCC /
// WindowServer preflight (below). Those calls (`AXIsProcessTrusted`, `CGPreflightScreenCaptureAccess`,
// …) can block on the WindowServer / tccd while the Aqua session is still coming up — most visibly on
// the first heavy boot after the toolchain install, and only once the agent actually HOLDS Screen
// Recording / Accessibility grants (so tccd has real state to negotiate). If any of them stalled here,
// the listener would never bind and the host's connect would time out for its whole deadline even
// though the guest desktop is up. The readiness self-check is purely informational, so it must never
// gate the server. (The `ping` op reports the same readiness per-call for the host UI.)
let listener: AgentListener
do {
    listener = try AgentListener(port: port)
} catch {
    agentLog("FATAL: \(error) — is this running inside a macOS guest VM? (AF_VSOCK fails ENODEV on a host)")
    exit(1)
}
Thread.detachNewThread { listener.run() }
agentLog("vsock listener bound on port \(port); accepting connections")

// Now the informational self-check — off the accept path, so a slow WindowServer/tccd can't keep the
// agent unreachable. Run it on a background thread and bound each call, so even a hard hang here only
// costs the log line, never the server.
Thread.detachNewThread {
    let accessibility = AXIsProcessTrusted()
    let screenRecording = CGPreflightScreenCaptureAccess()
    let postEvent = CGPreflightPostEventAccess()
    agentLog(
        "readiness: accessibility=\(accessibility) "
            + "screenRecording=\(screenRecording) postEvent=\(postEvent)")
}
// Keep the main run loop alive: AppKit-adjacent calls (NSWorkspace, event posting) expect a main
// thread with a live dispatch queue in a GUI session.
dispatchMain()
