import ClawdisIPC
import Foundation

enum ShellExecutor {
    static func run(command: [String], cwd: String?, env: [String: String]?, timeout: Double?) async -> Response {
        guard !command.isEmpty else { return Response(ok: false, message: "empty command") }

        let process = Process()
        process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
        process.arguments = command
        if let cwd { process.currentDirectoryURL = URL(fileURLWithPath: cwd) }
        if let env { process.environment = env }

        let stdoutPipe = Pipe()
        let stderrPipe = Pipe()
        process.standardOutput = stdoutPipe
        process.standardError = stderrPipe

        do {
            try process.run()
        } catch {
            return Response(ok: false, message: "failed to start: \(error.localizedDescription)")
        }

        let waitTask = Task { () -> Response in
            process.waitUntilExit()
            let out = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
            let err = stderrPipe.fileHandleForReading.readDataToEndOfFile()
            let status = process.terminationStatus
            let combined = out.isEmpty ? err : out
            return Response(ok: status == 0, message: status == 0 ? nil : "exit \(status)", payload: combined)
        }

        if let timeout, timeout > 0 {
            let nanos = UInt64(timeout * 1_000_000_000)
            let response = await withTaskGroup(of: Response.self) { group in
                group.addTask { await waitTask.value }
                group.addTask {
                    try? await Task.sleep(nanoseconds: nanos)
                    if process.isRunning { process.terminate() }
                    _ = await waitTask.value // drain pipes after termination
                    return Response(ok: false, message: "timeout")
                }
                // Whichever completes first (process exit or timeout) wins; cancel the other branch.
                let first = await group.next()!
                group.cancelAll()
                return first
            }
            return response
        }

        return await waitTask.value
    }
}
