import Foundation
import VMAgentCore

extension ConnectionHandler {
    /// Handle a **`shell-events`** request (docs/NASH.md §6.2, §7.5): drain nash's spooled shell-event
    /// batches out of the guest and hand them to the host (base64 NDJSON), then delete what was
    /// delivered. Non-streaming — unlike `exec` it does **not** take over the connection; it writes one
    /// reply line and the connection stays open for further ops (so ``ConnectionHandler/run`` `continue`s
    /// after calling it, rather than returning).
    ///
    /// At-least-once, matching the Linux agent: the drained files are removed only **after** the reply
    /// writes successfully, so a failed send re-delivers rather than loses (the host dedupes on
    /// `(shellId, seq)`). A guest with no spool yet drains nothing — never an error.
    func runShellEvents(requestLine: Data) {
        let request =
            ((try? JSONSerialization.jsonObject(with: requestLine)) as? [String: Any]) ?? [:]
        let dir = (request[AgentWire.ShellEvents.dirKey] as? String) ?? ShellSpool.defaultDir
        let maxBytes = (request[AgentWire.ShellEvents.maxBytesKey] as? Int) ?? ShellSpool.defaultCap
        let truncate = Self.truthy(request[AgentWire.ShellEvents.truncateKey])

        let result = ShellSpool.drain(dir: dir, maxBytes: maxBytes)
        let reply = AgentReply.ok([
            AgentWire.ShellEvents.filesKey: result.files,
            AgentWire.ShellEvents.bytesKey: result.data.count,
            AgentWire.ShellEvents.truncatedKey: result.truncated,
            AgentWire.ShellEvents.eventsKey: result.data.base64EncodedString(),
        ])
        guard let data = try? JSONSerialization.data(withJSONObject: reply) else { return }
        var line = data
        line.append(0x0A)
        if writeAll(line), truncate {
            let fm = FileManager.default
            for path in result.drainedPaths { try? fm.removeItem(atPath: path) }
        }
    }

    /// Coerce a JSON `truncate` value (bool or number) to a flag. `1`/`true` → delete drained files.
    static func truthy(_ value: Any?) -> Bool {
        if let b = value as? Bool { return b }
        if let n = value as? Int { return n != 0 }
        if let d = value as? Double { return d != 0 }
        return false
    }
}
