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

use clap::Parser;

use super::{AmbientError, Config, Leaf};
use ambient_ci::{project::Projects, run::cmd_run, runlog::RunLog};

/// Run CI on projects.
#[derive(Debug, Parser)]
pub struct Run {
    /// Project specification file. May contain any number of projects.
    #[clap(long)]
    projects: Option<PathBuf>,

    /// Which projects to run CI for, from the ones in the PROJECTS
    /// file. Default is all of them.
    chosen: Option<Vec<String>>,

    /// Run log.
    #[clap(long)]
    log: Option<PathBuf>,

    /// rsync target for publishing artifacts with rsync program.
    #[clap(long, aliases = ["rsync", "target"])]
    rsync_target: Option<String>,

    /// dput target for publishing .deb package.
    #[clap(long, alias = "dput")]
    dput_target: Option<String>,

    /// Path to `ambient-execute-plan` binary to use to run CI in VM.
    #[clap(long)]
    executor: Option<PathBuf>,

    /// Only pretend to run CI.
    #[clap(long)]
    dry_run: bool,

    /// Run even if repository hasn't changed.
    #[clap(long)]
    force: bool,

    /// Use UEFI with VM.
    #[clap(long)]
    uefi: bool,
}

impl Leaf for Run {
    fn run(&self, config: &Config, runlog: &mut RunLog) -> Result<(), AmbientError> {
        let mut config = config.clone();

        if let Some(executor) = &self.executor {
            config.set_executor(Path::new(executor));
        };

        if let Some(s) = &self.rsync_target {
            config.set_rsync_target(s);
        }

        if let Some(s) = &self.dput_target {
            config.set_dput_target(s);
        };

        let projects = if let Some(filename) = &self.projects {
            filename.to_path_buf()
        } else {
            config.projects().into()
        };
        let projects = Projects::from_file(&projects).map_err(RunError::Projects)?;

        config.lint(&projects)?;

        let result = cmd_run(
            &config,
            runlog,
            &projects,
            self.chosen.as_deref(),
            self.dry_run,
            self.force,
            self.uefi || config.uefi(),
        );
        match result {
            Ok(_) => (),
            // Err(ambient_ci::run::RunError::RunFailed) => (),
            Err(_) => result.map_err(RunError::Run)?,
        }

        Ok(())
    }
}

#[derive(Debug, thiserror::Error)]
pub enum RunError {
    #[error(transparent)]
    Run(#[from] ambient_ci::run::RunError),

    #[error("failed to load projects file")]
    Projects(#[source] ambient_ci::project::ProjectError),
}
