//! `sprouts deploy` — build the sprout to wasm and upload it as a new version.

use std::path::{Path, PathBuf};
use std::process::Command;

use anyhow::{Context, bail};
use clap::Args as ClapArgs;
use serde::Deserialize;

use crate::commands::session;
use crate::{api, config};

#[derive(ClapArgs)]
pub struct Args {
    /// The sprout directory (defaults to the current directory).
    #[arg(long, default_value = ".")]
    path: PathBuf,
    /// Skip `cargo build` and upload the existing artifact.
    #[arg(long)]
    no_build: bool,
}

pub fn run(args: Args) -> anyhow::Result<()> {
    let dir = args.path;
    let manifest_text = std::fs::read_to_string(dir.join("sprout.toml"))
        .context("reading sprout.toml (run from a sprout directory)")?;
    let name = manifest_name(&dir)?;

    if !args.no_build {
        println!("Building {name} for wasm32-wasip1…");
        let status = Command::new("cargo")
            .args(["build", "--release", "--target", "wasm32-wasip1"])
            .current_dir(&dir)
            .status()
            .context("running cargo build")?;
        if !status.success() {
            bail!("cargo build failed");
        }
    }

    let wasm_path = artifact_path(&dir)?;
    let wasm = std::fs::read(&wasm_path)
        .with_context(|| format!("reading artifact {}", wasm_path.display()))?;
    println!("Uploading {} ({:.1} KB)…", wasm_path.display(), wasm.len() as f64 / 1024.0);

    let sprout = session::authed(|u, t| api::create_sprout(u, t, &name))?;
    let result = session::authed(|u, t| api::deploy(u, t, &sprout.id, &wasm, &manifest_text))?;

    let api_url = config::load()?.api_url();
    println!("Deployed {name} v{}", result.version);
    println!("  sprout id: {}", sprout.id);
    for webhook in &result.webhooks {
        println!("  webhook [{}]: {api_url}{}", webhook.trigger, webhook.path);
    }
    Ok(())
}

/// Read the sprout's name from its `sprout.toml`.
pub fn manifest_name(dir: &Path) -> anyhow::Result<String> {
    #[derive(Deserialize)]
    struct Manifest {
        name: String,
    }
    let text = std::fs::read_to_string(dir.join("sprout.toml")).context("reading sprout.toml")?;
    let manifest: Manifest = toml::from_str(&text).context("parsing sprout.toml")?;
    Ok(manifest.name)
}

/// Locate the built wasm artifact from the crate's package name.
fn artifact_path(dir: &Path) -> anyhow::Result<PathBuf> {
    #[derive(Deserialize)]
    struct CargoToml {
        package: Package,
    }
    #[derive(Deserialize)]
    struct Package {
        name: String,
    }
    let text = std::fs::read_to_string(dir.join("Cargo.toml")).context("reading Cargo.toml")?;
    let cargo: CargoToml = toml::from_str(&text).context("parsing Cargo.toml")?;
    let artifact = format!("{}.wasm", cargo.package.name.replace('-', "_"));
    Ok(dir.join("target/wasm32-wasip1/release").join(artifact))
}
