use anyhow::{anyhow, bail};
use clap::Parser;
use dialtone_common::ap::pun::is_valid_pun;
use dialtone_common::rest::users::web_user::UserStatus;
use dialtone_common::utils::make_acct::make_acct;
use dialtone_sqlx::db::user_principal::change_status::change_user_status;
use validator::Validate;

use dialtone_common::utils::version::DT_VERSION;
use dialtone_sqlx::constants::HOST_NAME_RE;
use dialtone_sqlx::constants::USER_NAME_RE;
use dialtone_sqlx::control::user::check_name_availability::check_user_name_availability;
use dialtone_sqlx::control::user::create_with_actor::create_user_with_default_actor;
use dialtone_sqlx::db::get_pooled_connection;
use dialtone_sqlx::db::site_info::fetch_site;

#[derive(Parser, Debug, Validate)]
#[clap(name = "create_user", 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,

    /// Password for the users.
    #[clap(value_parser, short, long)]
    #[validate(length(min = 1))]
    password: String,

    /// Does not automatically set the user to Active status.
    #[clap(value_parser, short, long)]
    no_active: bool,
}

#[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?;
            // verify the sites exists first
            fetch_site(&pg_pool, &opts.host_name)
                .await?
                .ok_or(anyhow!(format!(
                    "Unable to create users for sites {}. Does it exist?",
                    opts.host_name
                )))?;

            let valid_name = is_valid_pun(&opts.user_name, false);
            if !valid_name {
                println!("{} contains characters that are not allowed actors names. A default actors is created when creating a users.", &opts.user_name);
                return Ok(());
            }

            //check users names
            let available =
                check_user_name_availability(&pg_pool, &opts.user_name, &opts.host_name).await?;

            if !available {
                println!("{} at {} is already used", &opts.user_name, &opts.host_name);
            } else {
                //create the users
                println!("Creating users {} at {}", &opts.user_name, &opts.host_name);
                create_user_with_default_actor(
                    &pg_pool,
                    &opts.user_name,
                    &opts.host_name,
                    &opts.password,
                )
                .await?;
            }

            if !opts.no_active {
                let acct = make_acct(&opts.user_name, &opts.host_name);
                println!("Changing {} to Active", acct);
                change_user_status(&pg_pool, &acct, &UserStatus::Active).await?;
            }

            Ok(())
        }
    }
}
