use ecow::EcoString;

use super::*;

/// A package in the git.
#[derive(Clone)]
pub struct GitClPack<P> {
    /// The namespace to mount.
    pub namespace: EcoString,
    /// The URL of the git.
    pub url: P,
}

impl<P: AsRef<str>> GitClPack<P> {
    /// Creates a new `GitClPack` instance.
    pub fn new(namespace: EcoString, url: P) -> Self {
        Self { namespace, url }
    }
}

impl<P: AsRef<str>> fmt::Debug for GitClPack<P> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "GitClPack({})", self.url.as_ref())
    }
}

impl<P: AsRef<str>> PackFs for GitClPack<P> {
    fn read_all(
        &mut self,
        f: &mut (dyn FnMut(&str, PackFile) -> PackageResult<()> + Send + Sync),
    ) -> PackageResult<()> {
        let temp_dir = std::env::temp_dir();
        let temp_dir = temp_dir.join("tinymist/package-gitcl");

        tinymist_std::fs::paths::temp_dir_in(temp_dir, |temp_dir| {
            let package_path = temp_dir.join("package");
            clone(self.url.as_ref(), &package_path)?;

            Ok(DirPack::new(package_path).read_all(f))
        })
        .map_err(other)?
    }
}

impl<P: AsRef<str>> Pack for GitClPack<P> {}
impl<P: AsRef<str>> PackExt for GitClPack<P> {}

fn clone(url: &str, dst: &Path) -> io::Result<()> {
    let mut cmd = gitcl();
    cmd.arg("clone").arg(url).arg(dst);
    let status = cmd.status()?;
    if !status.success() {
        return Err(io::Error::other(format!("git clone failed: {status}")));
    }
    Ok(())
}

fn gitcl() -> std::process::Command {
    std::process::Command::new("git")
}
