pub struct CuddleX {
    command: String,
    args: Vec<String>,
}

impl CuddleX {
    pub fn command(command: impl Into<String>) -> Self {
        Self {
            command: command.into(),
            args: Vec::new(),
        }
    }
    pub fn arg(&mut self, arg: impl Into<String>) -> &mut Self {
        self.args.push(arg.into());

        self
    }

    pub async fn run(&mut self) -> eyre::Result<(String, String, i32)> {
        let mut cmd = tokio::process::Command::new("cuddle");

        let cmd = cmd.arg("x").arg(&self.command).args(&self.args);

        let process = cmd.spawn()?;

        let output = process.wait_with_output().await?;

        Ok((
            std::str::from_utf8(&output.stdout)?.to_string(),
            std::str::from_utf8(&output.stderr)?.to_string(),
            output.status.code().unwrap_or(0),
        ))
    }
}

pub mod well_known {
    use super::CuddleX;

    pub async fn render(args: impl IntoIterator<Item = impl Into<String>>) -> eyre::Result<()> {
        tracing::info!("running render");

        let mut cmd = CuddleX::command("render");

        for arg in args.into_iter() {
            let arg = arg.into();

            cmd.arg(arg);
        }

        let (stdout, stderr, status) = cmd.run().await?;

        for line in stdout.lines() {
            tracing::trace!("render: {}", line);
        }

        for line in stderr.lines() {
            tracing::trace!("render: {}", line);
        }

        if status != 0 {
            tracing::warn!("finished render with non-zero exit code: {}", status);
        }

        tracing::info!("finished running render");

        Ok(())
    }
}
