use std::{
    io::{stdin, stdout, Write},
    process::exit,
};

use sqlx::{Pool, Postgres};

use crate::{
    auth::utils::generate_salt_and_hash_plain_password,
    models::{Function, Role, User},
};

pub enum Command {
    Migrate,
    CreateAdmin,
}

pub struct Args {
    pub command: Option<Command>,
}

pub fn parse_args() -> Result<Args, pico_args::Error> {
    let mut pargs = pico_args::Arguments::from_env();

    let command = pargs.free_from_str::<String>();

    let mut args = Args { command: None };

    if let Ok(parsed) = command {
        match parsed.trim() {
            "migrate" => args.command = Some(Command::Migrate),
            "createadmin" => args.command = Some(Command::CreateAdmin),
            _ => (),
        }
    }

    Ok(args)
}

fn prompt(prompt: &str) -> anyhow::Result<String> {
    print!("{}: ", prompt);
    stdout().flush()?;

    let mut input = String::new();
    stdin().read_line(&mut input)?;

    Ok(input.trim().to_string())
}

pub async fn handle_command(command: Option<Command>, pool: &Pool<Postgres>) -> anyhow::Result<()> {
    match command {
        Some(Command::Migrate) => {
            sqlx::migrate!("../migrations").run(pool).await?;

            exit(0);
        }
        Some(Command::CreateAdmin) => {
            let name = prompt("Full name of Admin")?;
            let email = prompt("E-Mail of Admin (for login)")?;
            let password = prompt("Password of Admin")?;
            let retyped_password = prompt("Retype Passsword of Admin")?;

            if password != retyped_password {
                eprintln!("Given passwords don't match!");
                exit(1)
            }

            let (hash, salt) = generate_salt_and_hash_plain_password(&password)?;

            User::create_with_password(
                &pool,
                &name,
                &email,
                &hash,
                &salt,
                Role::Admin,
                Function::Wachhabender,
                1,
            )
            .await?;

            exit(0);
        }
        None => (),
    };

    Ok(())
}
