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

#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct VerifyMagicLinkRequest {
    #[serde(rename = "magicLinkToken")]
    pub magic_link_token: String,
}


#[derive(Serialize, Deserialize)]
pub struct VerifyMagicLinkResponseMetadata {}

#[derive(Serialize, Deserialize)]
pub struct VerifyMagicLinkResponseData {
    #[serde(rename = "sessionToken")]
    pub session_token: String,
}

#[derive(Serialize, Deserialize)]
pub struct VerifyMagicLinkResponse {
    pub data: VerifyMagicLinkResponseData,
    pub metadata: VerifyMagicLinkResponseMetadata,
}

#[post("/v1/auth/verify-magic-link", data = "<req>")]
pub async fn verify_magic_link(req: Json<VerifyMagicLinkRequest>, db: &State<PgPool>, config: &State<TFConfig>) -> Result<(ContentType, Json<VerifyMagicLinkResponse>), (Status, String)> {
    // get the current time to check if the token is expired
    let (user_id, expired_at) = match sqlx::query!("SELECT user_id, expires_on FROM magic_links WHERE id = $1", req.0.magic_link_token).fetch_one(db.inner()).await {
        Ok(row) => (row.user_id, row.expires_on),
        Err(e) => {
            return Err((Status::Unauthorized, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_UNAUTHORIZED", "this token is invalid", e)))
        }
    };
    let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i32;
    println!("expired on {}, currently {}", expired_at, current_time);
    if expired_at < current_time {
        return Err((Status::Unauthorized, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", "ERR_UNAUTHORIZED", "valid authorization was provided but it is expired")))
    }

    // generate session token
    let token = match generate_session_token(user_id as i64, db.inner(), config.inner()).await {
        Ok(t) => t,
        Err(e) => {
            return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_UNABLE_TO_ISSUE", "an error occured trying to issue a session token, please try again later", e)))
        }
    };

    // delete the token
    match sqlx::query!("DELETE FROM magic_links WHERE id = $1", req.0.magic_link_token).execute(db.inner()).await {
        Ok(_) => (),
        Err(e) => {
            return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_UNABLE_TO_ISSUE", "an error occured trying to issue a session token, please try again later", e)))
        }
    }

    Ok((ContentType::JSON, Json(VerifyMagicLinkResponse {
        data: VerifyMagicLinkResponseData { session_token: token },
        metadata: VerifyMagicLinkResponseMetadata {},
    })))
}