use crate::database::*;
use crate::util::result::{Error, Result};

use mongodb::bson::doc;
use mongodb::options::{Collation, FindOneOptions};
use rauth::auth::Session;
use regex::Regex;
use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize};
use validator::Validate;

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

#[derive(Validate, Serialize, Deserialize)]
pub struct Data {
    #[validate(length(min = 2, max = 32), regex = "RE_USERNAME")]
    username: String,
}

#[post("/complete", data = "<data>")]
pub async fn req(session: Session, user: Option<User>, data: Json<Data>) -> Result<()> {
    if user.is_some() {
        Err(Error::AlreadyOnboarded)?
    }

    data.validate()
        .map_err(|error| Error::FailedValidation { error })?;
    
    if data.username == "revolt" {
        Err(Error::UsernameTaken)?
    }

    let col = get_collection("users");
    if col
        .find_one(
            doc! {
                "username": &data.username
            },
            FindOneOptions::builder()
                .collation(Collation::builder().locale("en").strength(2).build())
                .build(),
        )
        .await
        .map_err(|_| Error::DatabaseError {
            operation: "find_one",
            with: "user",
        })?
        .is_some()
    {
        Err(Error::UsernameTaken)?
    }

    col.insert_one(
        doc! {
            "_id": session.user_id,
            "username": &data.username
        },
        None,
    )
    .await
    .map_err(|_| Error::DatabaseError {
        operation: "insert_one",
        with: "user",
    })?;

    Ok(())
}