use std::sync::Arc;

use async_trait::async_trait;

use crate::dagger_middleware::DaggerMiddleware;

use super::{
    architecture::{Architecture, Os},
    RustService,
};

pub struct MoldInstall {
    arch: Architecture,
    os: Os,
    version: String,
}

impl MoldInstall {
    pub fn new(arch: Architecture, os: Os, version: impl Into<String>) -> Self {
        Self {
            arch,
            os,
            version: version.into(),
        }
    }

    fn get_arch(&self) -> String {
        match self.arch {
            Architecture::Amd64 => "x86_64",
            Architecture::Arm64 => "arm",
        }
        .into()
    }
    fn get_os(&self) -> String {
        match &self.os {
            Os::Linux => "linux",
            o => todo!("os not implemented for mold: {:?}", o),
        }
        .into()
    }

    pub fn get_download_url(&self) -> String {
        format!(
            "https://github.com/rui314/mold/releases/download/v{}/mold-{}-{}-{}.tar.gz",
            self.version,
            self.version,
            self.get_arch(),
            self.get_os()
        )
    }

    pub fn get_folder(&self) -> String {
        format!(
            "mold-{}-{}-{}",
            self.version,
            self.get_arch(),
            self.get_os()
        )
    }

    pub fn get_archive_name(&self) -> String {
        format!(
            "mold-{}-{}-{}.tar.gz",
            self.version,
            self.get_arch(),
            self.get_os()
        )
    }
}

#[async_trait]
impl DaggerMiddleware for MoldInstall {
    async fn handle(
        &self,
        container: dagger_sdk::Container,
    ) -> eyre::Result<dagger_sdk::Container> {
        println!("installing mold");

        let c = container
            .with_exec(vec!["wget", &self.get_download_url()])
            .with_exec(vec!["tar", "-xvf", &self.get_archive_name()])
            .with_exec(vec![
                "mv",
                &format!("{}/bin/mold", self.get_folder()),
                "/usr/bin/mold",
            ]);

        Ok(c)
    }
}

pub trait MoldActionExt {
    fn with_mold(&mut self, version: impl Into<String>) -> &mut Self {
        self
    }
}

impl MoldActionExt for RustService {
    fn with_mold(&mut self, version: impl Into<String>) -> &mut Self {
        self.with_stage(super::RustServiceStage::AfterDeps(Arc::new(
            MoldInstall::new(self.get_arch(), self.get_os(), version),
        )))
    }
}
