use clap::{App, Arg, ArgMatches, SubCommand};

use plume_models::{instance::Instance, users::*, Connection};
use rpassword;
use std::io::{self, Write};

pub fn command<'a, 'b>() -> App<'a, 'b> {
    SubCommand::with_name("users")
        .about("Manage users")
        .subcommand(
            SubCommand::with_name("new")
                .arg(
                    Arg::with_name("name")
                        .short("n")
                        .long("name")
                        .alias("username")
                        .takes_value(true)
                        .help("The username of the new user"),
                )
                .arg(
                    Arg::with_name("display-name")
                        .short("N")
                        .long("display-name")
                        .takes_value(true)
                        .help("The display name of the new user"),
                )
                .arg(
                    Arg::with_name("biography")
                        .short("b")
                        .long("bio")
                        .alias("biography")
                        .takes_value(true)
                        .help("The biography of the new user"),
                )
                .arg(
                    Arg::with_name("email")
                        .short("e")
                        .long("email")
                        .takes_value(true)
                        .help("Email address of the new user"),
                )
                .arg(
                    Arg::with_name("password")
                        .short("p")
                        .long("password")
                        .takes_value(true)
                        .help("The password of the new user"),
                )
                .arg(
                    Arg::with_name("admin")
                        .short("a")
                        .long("admin")
                        .help("Makes the user an administrator of the instance"),
                )
                .arg(
                    Arg::with_name("moderator")
                        .short("m")
                        .long("moderator")
                        .help("Makes the user a moderator of the instance"),
                )
                .about("Create a new user on this instance"),
        )
        .subcommand(
            SubCommand::with_name("reset-password")
                .arg(
                    Arg::with_name("name")
                        .short("u")
                        .long("user")
                        .alias("username")
                        .takes_value(true)
                        .help("The username of the user to reset password to"),
                )
                .arg(
                    Arg::with_name("password")
                        .short("p")
                        .long("password")
                        .takes_value(true)
                        .help("The password new for the user"),
                )
                .about("Reset user password"),
        )
}

pub fn run<'a>(args: &ArgMatches<'a>, conn: &Connection) {
    let conn = conn;
    match args.subcommand() {
        ("new", Some(x)) => new(x, conn),
        ("reset-password", Some(x)) => reset_password(x, conn),
        ("", None) => command().print_help().unwrap(),
        _ => println!("Unknown subcommand"),
    }
}

fn new<'a>(args: &ArgMatches<'a>, conn: &Connection) {
    let username = args
        .value_of("name")
        .map(String::from)
        .unwrap_or_else(|| super::ask_for("Username"));
    let display_name = args
        .value_of("display-name")
        .map(String::from)
        .unwrap_or_else(|| super::ask_for("Display name"));

    let admin = args.is_present("admin");
    let moderator = args.is_present("moderator");
    let role = if admin {
        Role::Admin
    } else if moderator {
        Role::Moderator
    } else {
        Role::Normal
    };

    let bio = args.value_of("biography").unwrap_or("").to_string();
    let email = args
        .value_of("email")
        .map(String::from)
        .unwrap_or_else(|| super::ask_for("Email address"));
    let password = args
        .value_of("password")
        .map(String::from)
        .unwrap_or_else(|| {
            print!("Password: ");
            io::stdout().flush().expect("Couldn't flush STDOUT");
            rpassword::read_password().expect("Couldn't read your password.")
        });

    NewUser::new_local(
        conn,
        username,
        display_name,
        role,
        &bio,
        email,
        User::hash_pass(&password).expect("Couldn't hash password"),
    )
    .expect("Couldn't save new user");
}

fn reset_password<'a>(args: &ArgMatches<'a>, conn: &Connection) {
    let username = args
        .value_of("name")
        .map(String::from)
        .unwrap_or_else(|| super::ask_for("Username"));
    let user = User::find_by_name(
        conn,
        &username,
        Instance::get_local()
            .expect("Failed to get local instance")
            .id,
    )
    .expect("Failed to get user");
    let password = args
        .value_of("password")
        .map(String::from)
        .unwrap_or_else(|| {
            print!("Password: ");
            io::stdout().flush().expect("Couldn't flush STDOUT");
            rpassword::read_password().expect("Couldn't read your password.")
        });
    user.reset_password(conn, &password)
        .expect("Failed to reset password");
}
