use clap::Parser;
use dialtone_axum::media::create_media_dir;
use dialtone_common::media::{SYSTEM_BANNERS_PUN, SYSTEM_ICONS_PUN, SYSTEM_MEDIA_USER_NAME};
use dialtone_common::rest::sites::theme::Theme;
use dialtone_common::utils::make_acct::make_acct;
use dialtone_common::utils::version::DT_VERSION;
use dialtone_sqlx::constants::HOST_NAME_RE;
use dialtone_sqlx::control::actor::create_owned::create_credentialed_actor;
use dialtone_sqlx::db::get_pooled_connection;
use dialtone_sqlx::db::site_info::create_site;
use dialtone_sqlx::db::user_principal::create_user;
use dialtone_sqlx::logic::actor::new_actor::NewActor;
use validator::Validate;

/// Create a dialtone sites.
#[derive(Parser, Debug, Validate)]
#[clap(name = "create_site", 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,

    /// short names (e.g. "foo")
    #[clap(value_parser, short, long)]
    short_name: String,

    /// long names (e.g. "the Foos be Here")
    #[clap(value_parser, short, long)]
    long_name: Option<String>,

    /// default theme
    #[clap(value_parser, short, long)]
    default_theme: Option<Theme>,
}

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

    let v = opts.validate();
    match v {
        Err(_) => {
            println!("Host names '{}' is not properly formed.", opts.host_name)
        }
        _ => {
            let host_name = opts.host_name.as_str();
            println!("Creating site {}.", opts.host_name);

            // create directories
            println!("Creating media directories.");
            create_media_dir(host_name, *SYSTEM_BANNERS_PUN).await?;
            create_media_dir(host_name, *SYSTEM_ICONS_PUN).await?;

            println!("Registering {} in the database.", opts.host_name);
            let pg_pool = get_pooled_connection().await?;
            create_site(
                &pg_pool,
                &opts.host_name,
                opts.short_name,
                opts.long_name,
                opts.default_theme,
            )
            .await?;

            println!("Creating system users and actors.");
            let acct = make_acct(SYSTEM_MEDIA_USER_NAME, host_name);
            let password: String =
                rand::Rng::sample_iter(rand::thread_rng(), &rand::distributions::Alphanumeric)
                    .take(100)
                    .map(char::from)
                    .collect();
            create_user(&pg_pool, &acct, &password).await?;
            let create_new_actor = NewActor {
                preferred_user_name: *SYSTEM_BANNERS_PUN,
                actor_type: dialtone_common::ap::ActorType::Service,
                owner: &acct,
                name: None,
                summary: None,
                icon: None,
                image: None,
            };
            create_credentialed_actor(&pg_pool, create_new_actor, false, host_name).await?;
            let create_new_actor = NewActor {
                preferred_user_name: *SYSTEM_ICONS_PUN,
                actor_type: dialtone_common::ap::ActorType::Service,
                owner: &acct,
                name: None,
                summary: None,
                icon: None,
                image: None,
            };
            create_credentialed_actor(&pg_pool, create_new_actor, false, host_name).await?;
        }
    }

    Ok(())
}
