use rocket::{post, State};
use rocket::serde::json::Json;
use serde::{Serialize, Deserialize};
use std::time::{SystemTime, UNIX_EPOCH};
use rocket::http::{ContentType, Status};
use sqlx::PgPool;
use crate::config::TFConfig;
use crate::tokens::send_magic_link;

#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct SignupRequest {
    pub email: String,
}

#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct SignupResponseMetadata {}

#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct SignupResponse {
    pub data: Option<String>,
    pub metadata: SignupResponseMetadata,
}
/*
created_on TIMESTAMP NOT NULL,

    banned INTEGER NOT NULL,
    ban_reason VARCHAR(1024) NOT NULL
 */
#[post("/v1/signup", data = "<req>")]
pub async fn signup_request(req: Json<SignupRequest>, pool: &State<PgPool>, config: &State<TFConfig>) -> Result<(ContentType, Json<SignupResponse>), (Status, String)> {
    // figure out if the user already exists
    let mut id = -1;
    match sqlx::query!("SELECT id FROM users WHERE email = $1", req.email.clone()).fetch_optional(pool.inner()).await {
        Ok(res) => if let Some(r) = res { id = r.id as i64 },
        Err(e) => {
            return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))
        }
    }

    if id == -1 {
        let id_res = match sqlx::query!("INSERT INTO users (email, created_on, banned, ban_reason, totp_secret, totp_otpurl, totp_verified) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT DO NOTHING RETURNING id;", req.email, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64, 0, "", "", "", 0).fetch_one(pool.inner()).await {
            Ok(row) => row.id,
            Err(e) => {
                return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))
            }
        };
        id = id_res as i64;
    }

    // send magic link to email
    match send_magic_link(id, req.email.clone(), pool.inner(), config.inner()).await {
        Ok(_) => (),
        Err(e) => {
            return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))
        }
    };

    // this endpoint doesn't actually ever return an error? it will send you the magic link no matter what
    // this appears to do the exact same thing as /v1/auth/magic-link, but it doesn't check if you have an account (magic-link does)
    Ok((ContentType::JSON, Json(SignupResponse {
        data: None,
        metadata: SignupResponseMetadata {},
    })))
}