use anyhow::{bail, Context};
use clap::Parser;
use dialtone_common::rest::users::web_user::UserStatus;
use dialtone_common::utils::make_acct::make_acct;
use std::str::FromStr;
use validator::{Validate, ValidationError};

use dialtone_common::utils::version::DT_VERSION;
use dialtone_sqlx::constants::HOST_NAME_RE;
use dialtone_sqlx::constants::USER_NAME_RE;
use dialtone_sqlx::db::get_pooled_connection;
use dialtone_sqlx::db::user_principal::change_status::change_user_status;

#[derive(Parser, Debug, Validate)]
#[clap(name = "change_user_status", version = DT_VERSION)]
struct Opts {
    /// Users names (i.e. the local part of users@thefoo.example)
    #[clap(value_parser, short, long)]
    #[validate(regex = "USER_NAME_RE")]
    user_name: String,

    /// DNS host names of the sites (e.g. thefoo.example)
    #[clap(value_parser, short, long)]
    #[validate(regex = "HOST_NAME_RE")]
    host_name: String,

    /// One of 'Active', 'Suspended', or 'Pending Approval'.
    #[clap(value_parser, short, long)]
    #[validate(custom(function = "validate_user_status"))]
    status: String,
}

fn validate_user_status(value: &str) -> Result<(), ValidationError> {
    let user_status = UserStatus::from_str(value);
    match user_status {
        Ok(_) => Ok(()),
        Err(_) => Err(ValidationError::new("Unknown users status type.")),
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenv::dotenv().ok();
    let opts: Opts = Opts::parse();
    let v = opts.validate();
    match v {
        Err(errors) => bail!(errors),
        _ => {
            let pg_pool = get_pooled_connection().await?;
            let acct = make_acct(&opts.user_name, &opts.host_name);
            let user_status = UserStatus::from_str(&opts.status).unwrap();
            println!("Changing {} to {}", acct, user_status);
            change_user_status(&pg_pool, &acct, &user_status)
                .await
                .with_context(|| {
                    format!("Unable to change {} status. Does the users exist?", acct)
                })?;
            Ok(())
        }
    }
}
