use crate::release_common::{
    CmdRunner, ReleaseChannel, ReleaseContext, default_repo, default_site_base_url, default_target,
    gh, verify_copied_release_assets, verify_github_release,
};
use anyhow::Result;

/// Create or update a GitHub Release and optionally publish the draft.
#[derive(clap::Parser)]
pub struct Command {
    /// Release version, for example 2.0.1 or 2.1.0-beta.1.
    #[arg(long)]
    version: String,

    /// Release channel.
    #[arg(long, value_enum, default_value_t = ReleaseChannel::Stable)]
    channel: ReleaseChannel,

    /// GitHub repository in OWNER/REPO form.
    #[arg(long, default_value_t = default_repo())]
    repo: String,

    /// Public website base URL.
    #[arg(long, default_value_t = default_site_base_url())]
    site_base_url: String,

    /// Build target.
    #[arg(long, default_value_t = default_target())]
    target: String,

    /// Replace assets on an existing release draft instead of creating a new release.
    #[arg(long)]
    replace_assets: bool,

    /// Publish the release draft after creating or updating it.
    #[arg(long)]
    publish: bool,

    /// Print planned commands without executing them.
    #[arg(long)]
    dry_run: bool,
}

impl crate::Command for Command {
    fn run(self) -> Result<i32> {
        let ctx = ReleaseContext::new(
            self.version,
            self.channel,
            Some(self.repo),
            Some(self.site_base_url),
            Some(self.target),
        )?;
        let runner = CmdRunner::new(self.dry_run);

        if !self.dry_run {
            verify_copied_release_assets(&ctx)?;
        }

        if self.replace_assets {
            upload_assets(&runner, &ctx)?;
        } else {
            create_release(&runner, &ctx)?;
        }

        verify_github_release(&ctx, &runner)?;

        if self.publish {
            let mut cmd = gh();
            cmd.arg("release")
                .arg("edit")
                .arg(&ctx.tag)
                .arg("--repo")
                .arg(&ctx.repo)
                .arg("--draft=false");
            runner.run(cmd, "publishing GitHub Release draft")?;
            verify_github_release(&ctx, &runner)?;
        }

        Ok(0)
    }
}

fn create_release(runner: &CmdRunner, ctx: &ReleaseContext) -> Result<()> {
    let mut cmd = gh();
    cmd.arg("release")
        .arg("create")
        .arg(&ctx.tag)
        .arg(ctx.artifact_installer())
        .arg(ctx.artifact_signature())
        .arg(ctx.artifact_zip())
        .arg("--repo")
        .arg(&ctx.repo)
        .arg("--target")
        .arg("main")
        .arg("--title")
        .arg(format!("ALCOMD3 {}", ctx.tag))
        .arg("--notes-file")
        .arg(&ctx.release_notes)
        .arg("--draft");

    if ctx.channel.is_prerelease() {
        cmd.arg("--prerelease");
    }

    runner.run(cmd, "creating GitHub Release draft")
}

fn upload_assets(runner: &CmdRunner, ctx: &ReleaseContext) -> Result<()> {
    let mut cmd = gh();
    cmd.arg("release")
        .arg("upload")
        .arg(&ctx.tag)
        .arg(ctx.artifact_installer())
        .arg(ctx.artifact_signature())
        .arg(ctx.artifact_zip())
        .arg("--repo")
        .arg(&ctx.repo)
        .arg("--clobber");
    runner.run(cmd, "uploading GitHub Release assets")
}
