use std::path::Path;

use compliance_core::CoreError;

pub(super) struct AuditVuln {
    pub package: String,
    pub id: String,
    pub url: String,
}

#[tracing::instrument(skip_all)]
pub(super) async fn run_cargo_audit(
    repo_path: &Path,
    _repo_id: &str,
) -> Result<Vec<AuditVuln>, CoreError> {
    let cargo_lock = repo_path.join("Cargo.lock");
    if !cargo_lock.exists() {
        return Ok(Vec::new());
    }

    let output = tokio::process::Command::new("cargo")
        .args(["audit", "--json"])
        .current_dir(repo_path)
        .env("RUSTC_WRAPPER", "")
        .output()
        .await
        .map_err(|e| CoreError::Scanner {
            scanner: "cargo-audit".to_string(),
            source: Box::new(e),
        })?;

    let result: CargoAuditOutput =
        serde_json::from_slice(&output.stdout).unwrap_or_else(|_| CargoAuditOutput {
            vulnerabilities: CargoAuditVulns { list: Vec::new() },
        });

    let vulns = result
        .vulnerabilities
        .list
        .into_iter()
        .map(|v| AuditVuln {
            package: v.advisory.package,
            id: v.advisory.id,
            url: v.advisory.url,
        })
        .collect();

    Ok(vulns)
}

// Cargo audit types
#[derive(serde::Deserialize)]
struct CargoAuditOutput {
    vulnerabilities: CargoAuditVulns,
}

#[derive(serde::Deserialize)]
struct CargoAuditVulns {
    list: Vec<CargoAuditEntry>,
}

#[derive(serde::Deserialize)]
struct CargoAuditEntry {
    advisory: CargoAuditAdvisory,
}

#[derive(serde::Deserialize)]
struct CargoAuditAdvisory {
    id: String,
    package: String,
    url: String,
}
