use std::fmt::Display;

use async_trait::async_trait;
use eyre::Context;

use crate::{cli, cuddle_file::CuddleFile, MainAction};

#[derive(Clone)]
pub struct CuddleReleaser {
    client: dagger_sdk::Query,
    env: Option<String>,
    cuddle_file: CuddleFile,

    folder: String,
}

pub struct CuddleReleaserOptions {
    upstream: String,
    cluster: String,
    namespace: String,
    app: String,
}

pub enum CuddleEnv {
    Prod,
    Dev,
}

impl Display for CuddleEnv {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CuddleEnv::Prod => f.write_str("prod"),
            CuddleEnv::Dev => f.write_str("dev"),
        }
    }
}

impl TryInto<CuddleEnv> for String {
    type Error = eyre::Error;

    fn try_into(self) -> Result<CuddleEnv, Self::Error> {
        let env = match self.as_str() {
            "prod" => CuddleEnv::Prod,
            "dev" => CuddleEnv::Dev,
            _ => eyre::bail!("was not a valid env: {}", self),
        };

        Ok(env)
    }
}

impl CuddleReleaser {
    pub async fn new(client: dagger_sdk::Query) -> eyre::Result<Self> {
        let cuddle_file = CuddleFile::from_cuddle_file().await?;

        let env = std::env::var("CUDDLE_ENV").ok();

        Ok(Self {
            client,
            cuddle_file,
            env,
            folder: ".cuddle/tmp".into(),
        })
    }

    pub async fn releaser(&self, env: CuddleEnv) -> eyre::Result<()> {
        let client = self.client.clone();

        if self.cuddle_file.deployment.is_none() {
            return Ok(());
        }

        let chosen_cluster = match self
            .cuddle_file
            .deployment
            .as_ref()
            .unwrap()
            .env
            .0
            .get(&self.env.as_ref().unwrap_or(&env.to_string()).to_string())
        {
            Some(c) => match c.clusters.first().take() {
                Some(c) => c,
                None => return Ok(()),
            },
            None => return Ok(()),
        };

        if let Some(clusters) = &self.cuddle_file.vars.clusters {
            let cluster = match clusters.0.get(chosen_cluster) {
                Some(c) => c,
                None => eyre::bail!("no cluster found for: {}", chosen_cluster),
            };

            let options = CuddleReleaserOptions {
                cluster: chosen_cluster.clone(),
                namespace: cluster.namespace.clone(),
                app: self.cuddle_file.vars.service.clone(),
                upstream: self
                    .cuddle_file
                    .deployment
                    .as_ref()
                    .unwrap()
                    .registry
                    .clone(),
            };

            let cuddle_releaser_image = "docker.io/kasperhermansen/cuddle-releaser:main-1706726858";

            let folder = client.host().directory(&self.folder);

            let ssh_sock = std::env::var("SSH_AUTH_SOCK").context("SSH_AUTH_SOCK not set")?;

            let cuddle_releaser = client
                .container()
                .from(cuddle_releaser_image)
                .with_env_variable("RUST_LOG", "trace")
                .with_directory("/mnt/templates", folder)
                .with_unix_socket(
                    ssh_sock.clone(),
                    client.host().unix_socket(ssh_sock.clone()),
                );

            let time = chrono::Local::now();

            cuddle_releaser
                .with_exec(vec!["echo", &time.to_rfc3339()])
                .with_exec(vec![
                    "cuddle-releaser",
                    "release",
                    &format!("--upstream={}", options.upstream),
                    &format!("--folder={}", "/mnt/templates/k8s"),
                    &format!("--cluster={}", options.cluster),
                    &format!("--namespace={}", options.namespace),
                    &format!("--app={}", options.app),
                ])
                .sync()
                .await?;
        }

        Ok(())
    }
}

#[async_trait]
impl MainAction for CuddleReleaser {
    async fn execute_main(&self, _ctx: &mut cli::Context) -> eyre::Result<()> {
        self.releaser(CuddleEnv::Prod).await?;

        Ok(())
    }
}
