/*
 * Authenticator
 *
 * REST API and web server providing authentication for a Tapis v3 instance.
 *
 * The version of the OpenAPI document: 1
 * Contact: cicsupport@tacc.utexas.edu
 * Generated by: https://openapi-generator.tech
 */

use super::{configuration, ContentType, Error};
use crate::{apis::ResponseContent, models};
use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};

/// struct for typed errors of method [`create_token`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CreateTokenError {
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`create_v2_token`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CreateV2TokenError {
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`generate_device_code`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GenerateDeviceCodeError {
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`revoke_token`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RevokeTokenError {
    UnknownValue(serde_json::Value),
}

/// Generate a Tapis JWT using some OAuth2 grant type. Typically, a request to this endpoint is the last step in the token generation process. The fields required in the request payload depend on the grant type being used (see details below).
pub async fn create_token(
    configuration: &configuration::Configuration,
    new_token: models::NewToken,
) -> Result<models::CreateToken201Response, Error<CreateTokenError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_body_new_token = new_token;

    let uri_str = format!("{}/v3/oauth2/tokens", configuration.base_path);
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    req_builder = req_builder.json(&p_body_new_token);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CreateToken201Response`"))),
            ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::CreateToken201Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<CreateTokenError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Create a v2 token from a Tapis v3 JWT. Note that only some tenants for both v2 and v3 are supported.
pub async fn create_v2_token(
    configuration: &configuration::Configuration,
    v2_token: models::V2Token,
) -> Result<models::CreateV2Token200Response, Error<CreateV2TokenError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_body_v2_token = v2_token;

    let uri_str = format!("{}/v3/oauth2/v2/token", configuration.base_path);
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref apikey) = configuration.api_key {
        let key = apikey.key.clone();
        let value = match apikey.prefix {
            Some(ref prefix) => format!("{} {}", prefix, key),
            None => key,
        };
        req_builder = req_builder.header("X-Tapis-Token", value);
    };
    req_builder = req_builder.json(&p_body_v2_token);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CreateV2Token200Response`"))),
            ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::CreateV2Token200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<CreateV2TokenError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Generate a device code; this is the first step in the device_code grant type. See the OAuth2 documentation for details.
pub async fn generate_device_code(
    configuration: &configuration::Configuration,
    new_device_code: models::NewDeviceCode,
) -> Result<models::GenerateDeviceCode200Response, Error<GenerateDeviceCodeError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_body_new_device_code = new_device_code;

    let uri_str = format!("{}/v3/oauth2/device/code", configuration.base_path);
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    req_builder = req_builder.json(&p_body_new_device_code);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GenerateDeviceCode200Response`"))),
            ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GenerateDeviceCode200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<GenerateDeviceCodeError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Revoke a Tapis JWT. Pass the token to revoke in the body of the request. Once revoked, a token cannot be unrevoked. Only user tokens generated by this Authenticator can be revoked with this endpoint.
pub async fn revoke_token(
    configuration: &configuration::Configuration,
    revoke_token_request: models::RevokeTokenRequest,
) -> Result<models::BasicResponse, Error<RevokeTokenError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_body_revoke_token_request = revoke_token_request;

    let uri_str = format!("{}/v3/oauth2/tokens/revoke", configuration.base_path);
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    req_builder = req_builder.json(&p_body_revoke_token_request);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::BasicResponse`"))),
            ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::BasicResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<RevokeTokenError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}
