use rocket::{post, State};
use rocket::serde::json::Json;
use serde::{Serialize, Deserialize};
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 MagicLinkRequest {
    pub email: String,
}

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

#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct MagicLinkResponse {
    pub data: Option<String>,
    pub metadata: MagicLinkResponseMetadata,
}

#[post("/v1/auth/magic-link", data = "<req>")]
pub async fn magiclink_request(req: Json<MagicLinkRequest>, pool: &State<PgPool>, config: &State<TFConfig>) -> Result<(ContentType, Json<MagicLinkResponse>), (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 {
        return Err((Status::Unauthorized, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", "ERR_UNAUTHORIZED", "authorization was provided but it is expired or invalid")))
    }

    // 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(MagicLinkResponse {
        data: None,
        metadata: MagicLinkResponseMetadata {},
    })))
}