use anyhow::{bail, Context, Result};
use std::path::Path;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;

/// Render a .puml file to SVG by calling `java -jar plantuml.jar -tsvg -pipe`.
///
/// The plantuml source is piped via stdin so no temp files are needed.
pub async fn render_svg(plantuml_jar: &Path, puml_file: &Path) -> Result<String> {
    let content = tokio::fs::read(puml_file)
        .await
        .with_context(|| format!("reading {}", puml_file.display()))?;

    let mut child = Command::new("java")
        .args([
            "-jar",
            plantuml_jar.to_string_lossy().as_ref(),
            "-tsvg",
            "-pipe",
            "-charset",
            "UTF-8",
        ])
        .stdin(std::process::Stdio::piped())
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped())
        .spawn()
        .context("failed to spawn java — is a JRE installed and in PATH?")?;

    if let Some(mut stdin) = child.stdin.take() {
        stdin
            .write_all(&content)
            .await
            .context("writing to plantuml stdin")?;
    }

    let output = child
        .wait_with_output()
        .await
        .context("waiting for plantuml process")?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        bail!("plantuml exited {}: {}", output.status, stderr.trim());
    }

    let svg = String::from_utf8(output.stdout).context("plantuml SVG output is not UTF-8")?;

    if svg.trim().is_empty() {
        bail!("plantuml produced empty output for {}", puml_file.display());
    }

    Ok(svg)
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;

    #[tokio::test]
    async fn returns_error_when_jar_missing() {
        let dir = TempDir::new().unwrap();
        let puml = dir.path().join("test.puml");
        std::fs::write(&puml, "@startuml\nA -> B\n@enduml").unwrap();

        let result = render_svg(Path::new("/nonexistent/plantuml.jar"), &puml).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn returns_error_when_puml_missing() {
        let result = render_svg(
            Path::new("/opt/plantuml.jar"),
            Path::new("/nonexistent/diagram.puml"),
        )
        .await;
        assert!(result.is_err());
    }
}
