import AppKit
import ApplicationServices
import CoreGraphics
import Foundation
import VMAgentCore

extension ConnectionHandler {
    /// `ping` — capability negotiation: protocol version, displays, and TCC readiness, so the host
    /// can distinguish "agent present" from "agent present but not authorized" (§5).
    func opPing() -> [String: Any] {
        var displayIDs = [CGDirectDisplayID](repeating: 0, count: 8)
        var displayCount: UInt32 = 0
        CGGetActiveDisplayList(UInt32(displayIDs.count), &displayIDs, &displayCount)
        let displays: [[String: Any]] = (0..<Int(displayCount)).map { i in
            let id = displayIDs[i]
            return [
                "id": Int(id),
                "width": CGDisplayPixelsWide(id),
                "height": CGDisplayPixelsHigh(id),
                "main": CGDisplayIsMain(id) != 0,
            ]
        }
        return AgentReply.ok([
            "version": AgentWire.version,
            "displays": displays,
            "readiness": [
                "accessibility": AXIsProcessTrusted(),
                "screenRecording": CGPreflightScreenCaptureAccess(),
                "postEvent": CGPreflightPostEventAccess(),
            ],
        ])
    }

    /// `cursor_position` — the pointer's current screen location (top-left-origin pixels).
    func opCursorPosition() -> [String: Any] {
        guard let event = CGEvent(source: nil) else {
            return AgentReply.err("could not read the cursor position (no event source)")
        }
        let p = event.location
        return AgentReply.ok(["x": Int(p.x.rounded()), "y": Int(p.y.rounded())])
    }

    /// `launch_app` — open an application by name, `NSWorkspace` when the bundle is findable in the
    /// standard app directories, else `/usr/bin/open -a` (which searches LaunchServices).
    func opLaunchApp(_ request: [String: Any]) -> [String: Any] {
        guard let name = request["name"] as? String, !name.isEmpty else {
            return AgentReply.err("launch_app requires a non-empty \"name\"")
        }
        let leaf = name.hasSuffix(".app") ? name : name + ".app"
        let candidates = [
            "/Applications/\(leaf)",
            "/System/Applications/\(leaf)",
            "/System/Applications/Utilities/\(leaf)",
        ]
        if let path = candidates.first(where: { FileManager.default.fileExists(atPath: $0) }) {
            let semaphore = DispatchSemaphore(value: 0)
            var failure: String?
            NSWorkspace.shared.openApplication(
                at: URL(fileURLWithPath: path), configuration: NSWorkspace.OpenConfiguration()
            ) { _, error in
                failure = error.map { String(describing: $0) }
                semaphore.signal()
            }
            _ = semaphore.wait(timeout: .now() + 15)
            if let failure { return AgentReply.err("could not launch \(name): \(failure)") }
            return AgentReply.ok()
        }
        // Not in the standard directories — let LaunchServices resolve it.
        let open = Process()
        open.executableURL = URL(fileURLWithPath: "/usr/bin/open")
        open.arguments = ["-a", name]
        do {
            try open.run()
        } catch {
            return AgentReply.err("could not launch \(name): \(error)")
        }
        open.waitUntilExit()
        guard open.terminationStatus == 0 else {
            return AgentReply.err("no application named \"\(name)\" (open -a exited \(open.terminationStatus))")
        }
        return AgentReply.ok()
    }
}
