use anyhow::Context;
use clap::Parser;
use dialtone_common::rest::sites::site_data::{PublicSiteInfo, RegistrationMethod, SiteData};
use dialtone_common::utils::version::DT_VERSION;
use dialtone_sqlx::constants::HOST_NAME_RE;
use dialtone_sqlx::db::get_pooled_connection;
use dialtone_sqlx::db::site_info::fetch_site;
use dialtone_sqlx::db::site_info::update::update_site;
use lazy_static::*;
use regex::Regex;
use validator::Validate;

lazy_static! {
    static ref SIMPLE_CODE_RE: Regex = Regex::new(r"[a-zA-Z0-9]+$").unwrap();
}

/// Sets allowed registration methods.
#[derive(Parser, Debug, Validate)]
#[clap(name = "set_registration", version = DT_VERSION)]
struct Opts {
    /// DNS host names (e.g. thefoo.example)
    #[clap(value_parser, short, long)]
    #[validate(regex = "HOST_NAME_RE")]
    host_name: String,

    /// Enable open registration.
    #[clap(value_parser, short, long)]
    open_registration: bool,

    /// Enable registration with a simple code.
    #[clap(value_parser, short, long)]
    #[validate(regex = "SIMPLE_CODE_RE")]
    simple_code_registration: Option<String>,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenv::dotenv().ok();
    let opts: Opts = Opts::parse();

    let v = opts.validate();
    match v {
        Err(_) => {
            println!("Host name '{}' is not properly formed.", opts.host_name);
        }
        _ => {
            let mut registration_methods: Vec<RegistrationMethod> = vec![];
            if opts.open_registration {
                registration_methods.push(RegistrationMethod::OpenRegistration);
            }

            let mut simple_code_for_registration: Option<String> = None;
            if opts.simple_code_registration.is_some() {
                simple_code_for_registration = Some(opts.simple_code_registration.unwrap());
                registration_methods.push(RegistrationMethod::SimpleCode);
            }

            let pg_pool = get_pooled_connection().await?;
            let site_info = fetch_site(&pg_pool, &opts.host_name)
                .await?
                .with_context(|| "host_name is not a valid site")?;

            if registration_methods.is_empty() {
                println!("Closing registration.")
            } else {
                println!("Setting registration methods: {:?}.", registration_methods);
            }

            let new_site_data = SiteData {
                public: PublicSiteInfo {
                    registration_methods,
                    ..site_info.site_data.public
                },
                simple_code_for_registration,
                ..site_info.site_data
            };
            update_site(&pg_pool, &opts.host_name, new_site_data).await?;
            println!("Registrations set");
        }
    }
    Ok(())
}
