// Native (C backend) implementation

///|
/// Process pool
pub struct ProcessPool {
  max_workers : Int
  semaphore : @async.Semaphore
  on_complete : OnJobComplete? // Callback on job completion
}

///|
/// Create a process pool
///
/// Parameters:
/// - `max_workers`: Maximum number of concurrent processes (default: 4)
/// - `on_complete`: Callback invoked on job completion (optional)
pub fn ProcessPool::new(
  max_workers? : Int = 4,
  on_complete? : OnJobComplete,
) -> ProcessPool {
  ProcessPool::{
    max_workers,
    semaphore: @async.Semaphore::new(max_workers),
    on_complete,
  }
}

///|
/// Execute a single job (internal function)
async fn run_job(job : Job) -> JobResult {
  let decoder = @encoding.decoder(UTF8)
  match job.timeout {
    Some(timeout) => {
      let result = @async.with_timeout_opt(timeout, () => {
        @process.collect_output(
          job.cmd,
          job.args,
          cwd?=job.cwd.map(fn(cwd) { cwd.to_string_view() }),
        )
      })
      match result {
        Some((exit_code, stdout, stderr)) => {
          let stdout_str = decoder.decode_lossy(stdout.binary())
          let stderr_str = decoder.decode_lossy(stderr.binary())
          JobResult::{
            job,
            exit_code,
            stdout: stdout_str,
            stderr: stderr_str,
            timed_out: false,
          }
        }
        None =>
          JobResult::{
            job,
            exit_code: -1,
            stdout: "",
            stderr: "Process timed out",
            timed_out: true,
          }
      }
    }
    None => {
      let (exit_code, stdout, stderr) = @process.collect_output(
        job.cmd,
        job.args,
        cwd?=job.cwd.map(fn(cwd) { cwd.to_string_view() }),
      )
      let stdout_str = decoder.decode_lossy(stdout.binary())
      let stderr_str = decoder.decode_lossy(stderr.binary())
      JobResult::{
        job,
        exit_code,
        stdout: stdout_str,
        stderr: stderr_str,
        timed_out: false,
      }
    }
  }
}

///|
/// Execute job with semaphore-limited concurrency (internal function)
async fn run_job_with_semaphore(
  semaphore : @async.Semaphore,
  job : Job,
  on_complete : OnJobComplete?,
) -> JobResult {
  semaphore.acquire()
  defer semaphore.release()
  let result = run_job(job)
  if on_complete is Some(callback) {
    (callback.0)(result)
  }
  result
}

///|
/// Run multiple jobs in parallel and return all results
///
/// Waits for all jobs to complete and returns the results array.
/// Job order is preserved matching the input order.
pub async fn ProcessPool::run_all(
  self : ProcessPool,
  jobs : Array[Job],
) -> Array[JobResult] {
  @async.with_task_group(group => {
    let tasks = jobs.map(job => {
      group.spawn(() => {
        run_job_with_semaphore(self.semaphore, job, self.on_complete)
      })
    })
    let results = []
    for task in tasks {
      results.push(task.wait())
    }
    results
  })
}

///|
/// Get current time in milliseconds
pub fn now() -> Int64 {
  @async.now()
}