//! [`LocalProcessSpawner`] — local [`ProcessSpawner`] over `tokio::process`
//! (ARCHITECTURE §5).
//!
//! Runs a **non-interactive** process to completion and captures
//! stdout/stderr/exit code. Used by `DetectProfiles` (via [`CliAgentRuntime`])
//! and any future short-lived command (git fallbacks, scripts). Interactive
//! processes go through the PTY port instead.

use async_trait::async_trait;
use tokio::process::Command;

use domain::ports::{ExitStatus, Output, ProcessError, ProcessSpawner, SpawnSpec};

/// Process spawner backed by the local OS via `tokio::process::Command`.
#[derive(Debug, Default, Clone, Copy)]
pub struct LocalProcessSpawner;

impl LocalProcessSpawner {
    /// Creates a new [`LocalProcessSpawner`].
    #[must_use]
    pub const fn new() -> Self {
        Self
    }
}

#[async_trait]
impl ProcessSpawner for LocalProcessSpawner {
    async fn run(&self, spec: SpawnSpec) -> Result<Output, ProcessError> {
        let mut cmd = Command::new(&spec.command);
        cmd.args(&spec.args);
        // `cwd` is "/" for detection probes; only set a real working directory
        // when the spec points at a concrete project path.
        if spec.cwd.as_str() != "/" {
            cmd.current_dir(spec.cwd.as_str());
        }
        for (key, value) in &spec.env {
            cmd.env(key, value);
        }

        let output = cmd
            .output()
            .await
            .map_err(|e| ProcessError::Spawn(format!("{}: {e}", spec.command)))?;

        Ok(Output {
            status: ExitStatus {
                code: output.status.code(),
            },
            stdout: output.stdout,
            stderr: output.stderr,
        })
    }
}
