use cuddle_ci::{Context, MainAction, PullRequestAction};
use dagger_sdk::{Container, HostDirectoryOptsBuilder, PortForward, ServiceUpOptsBuilder};

const UPDATE_DEPLOYMENT_IMAGE: &str = "docker.io/kasperhermansen/update-deployment:1690401410";

const CADDY_IMAGE: &str = "caddy:2.10.0";

const ZOLA_PLATFORM: &str = "x86_64";
const ZOLA_VERSION: &str = "v0.21.0";

const DEBIAN_EDITION: &str = "bookworm";
const DEBIAN_PLATFORM: &str = "amd64";

#[derive(Clone)]
pub struct BlogComponent {
    client: dagger_sdk::Query,
}

impl BlogComponent {
    pub async fn run(&self) -> eyre::Result<()> {
        let (_, container) = build(self.client.clone()).await?;

        container
            .as_service()
            .up_opts(
                ServiceUpOptsBuilder::default()
                    .ports(vec![PortForward {
                        backend: 80,
                        frontend: 8000,
                        protocol: dagger_sdk::NetworkProtocol::Tcp,
                    }])
                    .build()?,
            )
            .await?;

        Ok(())
    }
}

#[async_trait::async_trait]
impl PullRequestAction for BlogComponent {
    async fn execute_pull_request(&self, _ctx: &mut Context) -> eyre::Result<()> {
        let (tag, container) = build(self.client.clone()).await?;
        deploy(self.client.clone(), tag, container).await?;

        Ok(())
    }
}

#[async_trait::async_trait]
impl MainAction for BlogComponent {
    async fn execute_main(&self, _ctx: &mut Context) -> eyre::Result<()> {
        let (tag, container) = build(self.client.clone()).await?;
        deploy(self.client.clone(), tag, container).await?;

        Ok(())
    }
}

async fn build(client: dagger_sdk::Query) -> eyre::Result<(i64, Container)> {
    let src = client.host().directory_opts(
        ".",
        HostDirectoryOptsBuilder::default()
            .exclude(vec![".git", "node_modules", "public", "ci", "target"])
            .build()?,
    );

    // let github_zola_download: String = format!("https://github.com/barnumbirr/zola-debian/releases/download/v{ZOLA_VERSION}/zola_{ZOLA_VERSION}_{DEBIAN_PLATFORM}_{DEBIAN_EDITION}.deb");
    let node_cache = client.cache_volume("node_cache");
    let debian_cache = client.cache_volume("debian_cache");

    let styles_file = client
        .container()
        .from("node:16")
        .with_workdir("/app")
        .with_directory(".", src.clone())
        .with_mounted_cache("node_modules", node_cache)
        .with_exec(vec!["yarn"])
        .with_exec(vec!["yarn", "compile"])
        .file("static/styles/styles.css");

    let tag = chrono::Utc::now().timestamp();

    let zola_download_link  = format!("https://github.com/getzola/zola/releases/download/{ZOLA_VERSION}/zola-{ZOLA_VERSION}-{ZOLA_PLATFORM}-unknown-linux-gnu.tar.gz");

    let dist_dir = client
        .container_opts(
            dagger_sdk::QueryContainerOptsBuilder::default()
                .platform(format!("linux/{DEBIAN_PLATFORM}"))
                .build()?,
        )
        .from(format!("debian:{DEBIAN_EDITION}"))
        // .with_exec(vec!["apt", "update"])
        // .with_exec(vec!["apt", "install", "wget", "-y"])
        .with_workdir("/mnt")
        .with_file("/mnt/zola.tar.gz", client.http(zola_download_link))
        .with_exec(vec!["tar", "-xvf", "/mnt/zola.tar.gz"])
        .with_exec(vec!["mv", "/mnt/zola", "/usr/local/bin/zola"])
        // .with_mounted_cache("/mnt", debian_cache)
        // .with_exec(vec!["wget", &github_zola_download])
        // .with_exec(vec![
        //     "dpkg",
        //     "-i",
        //     format!("zola_{ZOLA_VERSION}_{DEBIAN_PLATFORM}_{DEBIAN_EDITION}.deb").as_str(),
        // ])
        .with_workdir("/app")
        .with_directory(".", src)
        .with_file("static/styles/styles.css", styles_file)
        .with_exec(vec!["zola", "build"])
        .directory("public");

    let caddy_file = client.host().directory("deployment").file("Caddyfile");

    let dep_image = client
        .container()
        .from(CADDY_IMAGE)
        .with_directory("/srv", dist_dir)
        .with_file("/etc/caddy/Caddyfile", caddy_file);

    Ok((tag, dep_image))
}

async fn deploy(client: dagger_sdk::Query, tag: i64, container: Container) -> eyre::Result<()> {
    container
        .publish(format!("kasperhermansen/kasperhermansen-blog:{tag}"))
        .await?;

    client
        .container()
        .from(UPDATE_DEPLOYMENT_IMAGE)
        .with_env_variable("GIT_USERNAME", "kjuulh")
        .with_env_variable("GIT_PASSWORD", std::env::var("GIT_PASSWORD").unwrap())
        .with_exec(vec![
            "update-deployment",
            "--repo",
            "https://git.front.kjuulh.io/kjuulh/blog-deployment.git",
            "--service",
            "blog",
            "--image",
            &format!("kasperhermansen/kasperhermansen-blog:{tag}"),
        ])
        .exit_code()
        .await?;

    Ok(())
}

#[tokio::main]
async fn main() -> eyre::Result<()> {
    let _ = dotenvy::dotenv();

    dagger_sdk::connect(|client| async move {
        let blog_component = &BlogComponent { client };

        let run = std::env::var("RUN").unwrap_or_default();
        if !run.is_empty() {
            blog_component.run().await?;

            return Ok(());
        }

        cuddle_ci::CuddleCI::default()
            .with_main(blog_component)
            .execute(std::env::args())
            .await?;

        Ok(())
    })
    .await?;

    Ok(())
}
