use crate::coauthor_repo::CoauthorRepo;
use crate::commands::{coauthor::Coauthor, mob::Mob, setup::Setup};
use clap::{Parser, Subcommand};
use std::error::Error;
use std::io::Write;
use std::str;
#[derive(Parser)]
#[command(
    author,
    version,
    about,
    long_about,
    bin_name = "git mob",
    override_usage = "git mob [COMMAND] [OPTIONS]"
)]
#[command(propagate_version = true)]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,
    #[command(flatten)]
    mob: Mob,
}
#[derive(Subcommand)]
enum Commands {
    Setup(Setup),
    Coauthor(Coauthor),
}
pub fn run(coauthor_repo: &impl CoauthorRepo, out: &mut impl Write) -> Result<(), Box<dyn Error>> {
    let cli = Cli::parse();
    run_inner(&cli, coauthor_repo, out)
}
fn run_inner(
    cli: &Cli,
    coauthor_repo: &impl CoauthorRepo,
    out: &mut impl Write,
) -> Result<(), Box<dyn Error>> {
    match &cli.command {
        None => cli.mob.handle(coauthor_repo, out)?,
        Some(Commands::Setup(setup)) => setup.handle(out)?,
        Some(Commands::Coauthor(coauthor)) => coauthor.handle(coauthor_repo, out)?,
    }
    Ok(())
}
#[cfg(test)]
mod tests {
    use std::error::Error;
    use super::*;
    use crate::coauthor_repo::MockCoauthorRepo;
    use mockall::predicate;
    #[test]
    fn test_clear_mob_session() -> Result<(), Box<dyn Error>> {
        let mut mock_coauthor_repo = MockCoauthorRepo::new();
        mock_coauthor_repo
            .expect_clear_mob()
            .once()
            .returning(|| Ok(()));
        let cli = Cli {
            command: None,
            mob: Mob {
                with: None,
                clear: true,
                list: false,
                trailers: false,
            },
        };
        let mut out = Vec::new();
        run_inner(&cli, &mock_coauthor_repo, &mut out)?;
        Ok(())
    }
    #[test]
    fn test_delete_coauthor() -> Result<(), Box<dyn Error>> {
        let key = "lm";
        let mut mock_coauthor_repo = MockCoauthorRepo::new();
        mock_coauthor_repo
            .expect_get()
            .with(predicate::eq(key))
            .once()
            .returning(|_| Ok(Some("Leo Messi <leo.messi@example.com>".to_owned())));
        mock_coauthor_repo
            .expect_remove()
            .with(predicate::eq(key))
            .once()
            .returning(|_| Ok(()));
        let cli = Cli {
            command: Some(Commands::Coauthor(Coauthor {
                delete: Some(key.to_owned()),
                add: None,
                list: false,
            })),
            mob: Mob {
                with: None,
                clear: false,
                list: false,
                trailers: false,
            },
        };
        let mut out = Vec::new();
        run_inner(&cli, &mock_coauthor_repo, &mut out)?;
        Ok(())
    }
}