use super::*;
use crate::workspace::Workspace;
use std::{
    env,
    fs::File,
    io::{self, Write},
    path,
};

const TMPDIR: &str = "TMPDIR";
pub fn exec() -> Result<()> {
    eprintln!(
        "Checking for new resource - cepler v{}",
        clap::crate_version!()
    );
    let resource: ResourceConfig<()> =
        serde_json::from_reader(io::stdin()).context("Deserializing stdin")?;
    let ResourceConfig {
        source, version, ..
    }: ResourceConfig<()> = resource.clone();
    if let Some(ref version) = version {
        eprintln!(
            "Last trigger: '{}', checking if we can deploy a newer version",
            version.trigger
        );
    } else {
        eprintln!("No previous deployments - checking if there is one");
    }

    let clone_dir = format!(
        "{}/cepler-repo-cache",
        env::var(TMPDIR).unwrap_or_else(|_| "/tmp".to_string())
    );
    let mut file = File::create(format!(
        "{}/cepler-check-input",
        env::var(TMPDIR).unwrap_or_else(|_| "/tmp".to_string())
    ))?;
    file.write_all(&serde_json::to_vec(&resource)?)?;
    let conf = GitConfig {
        url: source.uri,
        branch: source.branch.clone(),
        gates_branch: source.gates_branch.clone(),
        private_key: source.private_key,
        dir: clone_dir.clone(),
        // `check` keeps a persistent on-worker cache and pulls into it; a
        // shallow boundary would make subsequent walks unreliable, so always
        // do a full clone here regardless of the source-level `depth`.
        depth: None,
    };
    let path = path::Path::new(&clone_dir);
    let repo = if !path.exists() || path.read_dir()?.next().is_none() {
        eprintln!("Cloning repo");
        let repo = Repo::clone(conf).context("Couldn't clone repo")?;
        std::env::set_current_dir(path)?;
        repo
    } else {
        eprintln!("Pulling latest state");
        std::env::set_current_dir(path)?;
        let repo = Repo::open(None)?;
        repo.pull(conf)?;
        repo
    };
    let (hash, summary) = repo.head_commit_summary()?;
    eprintln!(
        "HEAD of branch '{}' is now at: [{}] - {}",
        source.branch, hash, summary
    );

    // Idempotent on the pull path (where `Repo::pull`'s Hard reset has
    // already populated the WT) and load-bearing on the no-checkout
    // clone path (where the WT is still empty).
    let config = populate_workspace_metadata(
        &repo,
        &source.config,
        source.gates_file.as_ref(),
        source.gates_branch.as_ref(),
    )?;
    let ws = Workspace::new(&config.scope, source.config.clone(), source.ignore_queue)?;
    let mut res = Vec::new();
    let environment = if let Some(environment) = source.environment {
        environment
    } else {
        eprintln!("No environment specified - ignoring");
        println!("{}", serde_json::to_string(&res)?);
        return Ok(());
    };
    let env = config.environments.get(&environment).context(format!(
        "Environment '{}' not found in config '{}'",
        environment, source.config
    ))?;
    eprintln!("Checking equivalence with last deployed state...");
    let gate = get_gate(
        source.gates_file.as_ref(),
        source.gates_branch.as_ref(),
        &environment,
        &repo,
    )?;
    match (version, ws.check(env, gate)?) {
        (None, Some((state_id, _))) => {
            eprintln!("Found new state to deploy");
            res.push(Version::from(state_id))
        }
        (Some(last), Some((state_id, _))) if last.trigger != state_id.head_commit => {
            eprintln!("Found new state to deploy");
            res.push(last);
            res.push(Version::from(state_id))
        }
        (Some(last), ret) => {
            match ret {
                Some((state_id, _)) if last.trigger == state_id.head_commit => {
                    eprintln!("Last trigger is still up to date")
                }
                _ => eprintln!("Nothing new to deploy"),
            }
            res.push(last);
        }
        _ => {
            eprintln!("Nothing new to deploy");
        }
    }
    println!("{}", serde_json::to_string(&res)?);
    Ok(())
}
