use std::{path::PathBuf};

use crate::{build::RustVersion, source::RustSource};

pub struct RustTest {
    client: dagger_sdk::Query,
    registry: Option<String>,
}

impl RustTest {
    pub fn new(client: dagger_sdk::Query) -> Self {
        Self {
            client,
            registry: None,
        }
    }

    pub async fn test(
        &self,
        source_path: Option<impl Into<PathBuf>>,
        rust_version: impl AsRef<RustVersion>,
        crates: &[&str],
        extra_deps: &[&str],
    ) -> eyre::Result<()> {
        let rust_version = rust_version.as_ref();
        let source_path = source_path.map(|s| s.into());
        let source = source_path.clone().unwrap_or(PathBuf::from("."));

        let rust_source = RustSource::new(self.client.clone());
        let (src, dep_src) = rust_source
            .get_rust_src(source_path, crates.to_vec())
            .await?;
        let mut deps = vec!["apt", "install", "-y"];
        deps.extend(extra_deps);

        let rust_build_image = self
            .client
            .container()
            .from(rust_version.to_string())
            .with_exec(vec!["apt", "update"])
            .with_exec(deps);

        let target_cache = self.client.cache_volume("rust_target_test".to_string());

        let build_options = vec!["cargo", "build", "--workspace"];
        let rust_prebuild = rust_build_image
            .with_workdir("/mnt/src")
            .with_directory("/mnt/src", dep_src)
            .with_exec(build_options)
            .with_mounted_cache("/mnt/src/target/", target_cache);

        let incremental_dir = rust_source
            .get_rust_target_src(&source, rust_prebuild.clone(), crates.to_vec())
            .await?;

        let rust_with_src = rust_build_image
            .with_workdir("/mnt/src")
            .with_directory(
                "/usr/local/cargo",
                rust_prebuild.directory("/usr/local/cargo"),
            )
            .with_directory("/mnt/src/target", incremental_dir)
            .with_directory("/mnt/src/", src);

        let test = rust_with_src.with_exec(vec!["cargo", "test"]);

        let stdout = test.stdout().await?;
        let stderr = test.stderr().await?;
        println!("stdout: {}, stderr: {}", stdout, stderr);

        test.sync().await?;

        Ok(())
    }
}
