//! # API tokens management

use crate::api_tokens;
use crate::api_tokens::{NewToken, TokenID, TokenRights};
use crate::controllers::HttpResult;
use crate::controllers::api_tokens_controller::rest_token::RestToken;
use actix_web::{HttpResponse, web};
use basic_jwt::JWTPrivateKey;

/// Create a special module for REST token to enforce usage of constructor function
mod rest_token {
    use crate::api_tokens::Token;

    #[derive(serde::Serialize)]
    pub struct RestToken {
        #[serde(flatten)]
        token: Token,
    }

    impl RestToken {
        pub fn new(mut token: Token) -> Self {
            token.pub_key = None;
            Self { token }
        }
    }
}

#[derive(serde::Serialize)]
struct CreateTokenResult {
    token: RestToken,
    priv_key: JWTPrivateKey,
}

/// Create a new API token
pub async fn create(new_token: web::Json<NewToken>) -> HttpResult {
    if let Some(err) = new_token.check_error() {
        log::error!("Failed to validate new API token information! {err}");
        return Ok(HttpResponse::BadRequest().json(format!(
            "Failed to validate new API token information! {err}"
        )));
    }

    let (token, priv_key) = api_tokens::create(&new_token).await?;

    Ok(HttpResponse::Ok().json(CreateTokenResult {
        token: RestToken::new(token),
        priv_key,
    }))
}

/// Get the list of API tokens
pub async fn list() -> HttpResult {
    let list = api_tokens::full_list()
        .await?
        .into_iter()
        .map(RestToken::new)
        .collect::<Vec<_>>();

    Ok(HttpResponse::Ok().json(list))
}

#[derive(serde::Deserialize)]
pub struct TokenIDInPath {
    uid: TokenID,
}

/// Get the information about a single token
pub async fn get_single(path: web::Path<TokenIDInPath>) -> HttpResult {
    let token = api_tokens::get_single(path.uid).await?;

    Ok(HttpResponse::Ok().json(RestToken::new(token)))
}

#[derive(serde::Deserialize)]
pub struct UpdateTokenBody {
    rights: TokenRights,
}

/// Update a token
pub async fn update(
    path: web::Path<TokenIDInPath>,
    body: web::Json<UpdateTokenBody>,
) -> HttpResult {
    if let Some(err) = body.rights.check_error() {
        log::error!("Failed to validate updated API token information! {err}");
        return Ok(HttpResponse::BadRequest()
            .json(format!("Failed to validate API token information! {err}")));
    }

    api_tokens::update_rights(path.uid, body.0.rights).await?;

    Ok(HttpResponse::Accepted().finish())
}

/// Delete a token
pub async fn delete(path: web::Path<TokenIDInPath>) -> HttpResult {
    api_tokens::delete(path.uid).await?;

    Ok(HttpResponse::Accepted().finish())
}