// File: crates/euclid/src/dssa/types.rs
// Module: euclid
// Public functions: 5
// Public structs: 3

use std::{collections::HashMap, fmt};

use serde::Serialize;

use crate::{
    dssa::{self, graph},
    frontend::{ast, dir},
    types::{DataType, EuclidValue, Metadata},
};

pub trait EuclidAnalysable: Sized {
    fn get_dir_value_for_analysis(&self, rule_name: String) -> Vec<(dir::DirValue, Metadata)>;
}

#[derive(Debug, Clone)]
pub enum CtxValueKind<'a> {
    Assertion(&'a dir::DirValue),
    Negation(&'a [dir::DirValue]),
}

impl CtxValueKind<'_> {
    pub fn get_assertion(&self) -> Option<&dir::DirValue> {
        if let Self::Assertion(val) = self {
            Some(val)
        } else {
            None
        }
    }

    pub fn get_negation(&self) -> Option<&[dir::DirValue]> {
        if let Self::Negation(vals) = self {
            Some(vals)
        } else {
            None
        }
    }

    pub fn get_key(&self) -> Option<dir::DirKey> {
        match self {
            Self::Assertion(val) => Some(val.get_key()),
            Self::Negation(vals) => vals.first().map(|v| (*v).get_key()),
        }
    }
}

#[derive(Debug, Clone)]
pub struct ContextValue<'a> {
    pub value: CtxValueKind<'a>,
    pub metadata: &'a Metadata,
}

impl<'a> ContextValue<'a> {
    #[inline]
    pub fn assertion(value: &'a dir::DirValue, metadata: &'a Metadata) -> Self {
        Self {
            value: CtxValueKind::Assertion(value),
            metadata,
        }
    }

    #[inline]
    pub fn negation(values: &'a [dir::DirValue], metadata: &'a Metadata) -> Self {
        Self {
            value: CtxValueKind::Negation(values),
            metadata,
        }
    }
}

pub type ConjunctiveContext<'a> = Vec<ContextValue<'a>>;

#[derive(Clone, Serialize)]
pub enum AnalyzeResult {
    AllOk,
}

#[derive(Debug, Clone, Serialize, thiserror::Error)]
pub struct AnalysisError {
    #[serde(flatten)]
    pub error_type: AnalysisErrorType,
    pub metadata: Metadata,
}
impl fmt::Display for AnalysisError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.error_type.fmt(f)
    }
}
#[derive(Debug, Clone, Serialize)]
pub struct ValueData {
    pub value: dir::DirValue,
    pub metadata: Metadata,
}

#[derive(Debug, Clone, Serialize, thiserror::Error)]
#[serde(tag = "type", content = "info", rename_all = "snake_case")]
pub enum AnalysisErrorType {
    #[error("Invalid program key given: '{0}'")]
    InvalidKey(String),
    #[error("Invalid variant '{got}' received for key '{key}'")]
    InvalidVariant {
        key: String,
        expected: Vec<String>,
        got: String,
    },
    #[error(
        "Invalid data type for value '{}' (expected {expected}, got {got})",
        key
    )]
    InvalidType {
        key: String,
        expected: DataType,
        got: DataType,
    },
    #[error("Invalid comparison '{operator:?}' for value type {value_type}")]
    InvalidComparison {
        operator: ast::ComparisonType,
        value_type: DataType,
    },
    #[error("Invalid value received for length as '{value}: {:?}'", message)]
    InvalidValue {
        key: dir::DirKeyKind,
        value: String,
        message: Option<String>,
    },
    #[error("Conflicting assertions received for key '{}'", .key.kind)]
    ConflictingAssertions {
        key: dir::DirKey,
        values: Vec<ValueData>,
    },

    #[error("Key '{}' exhaustively negated", .key.kind)]
    ExhaustiveNegation {
        key: dir::DirKey,
        metadata: Vec<Metadata>,
    },
    #[error("The condition '{value}' was asserted and negated in the same condition")]
    NegatedAssertion {
        value: dir::DirValue,
        assertion_metadata: Metadata,
        negation_metadata: Metadata,
    },
    #[error("Graph analysis error: {0:#?}")]
    GraphAnalysis(
        graph::AnalysisError<dir::DirValue>,
        hyperswitch_constraint_graph::Memoization<dir::DirValue>,
    ),
    #[error("State machine error")]
    StateMachine(dssa::state_machine::StateMachineError),
    #[error("Unsupported program key '{0}'")]
    UnsupportedProgramKey(dir::DirKeyKind),
    #[error("Ran into an unimplemented feature")]
    NotImplemented,
    #[error("The payment method type is not supported under the payment method")]
    NotSupported,
}

#[derive(Debug, Clone)]
pub enum ValueType {
    EnumVariants(Vec<EuclidValue>),
    Number,
}

impl EuclidAnalysable for common_enums::AuthenticationType {
    fn get_dir_value_for_analysis(&self, rule_name: String) -> Vec<(dir::DirValue, Metadata)> {
        let auth = self.to_string();

        let dir_value = match self {
            Self::ThreeDs => dir::DirValue::AuthenticationType(Self::ThreeDs),
            Self::NoThreeDs => dir::DirValue::AuthenticationType(Self::NoThreeDs),
        };

        vec![(
            dir_value,
            HashMap::from_iter([(
                "AUTHENTICATION_TYPE".to_string(),
                serde_json::json!({
                    "rule_name": rule_name,
                    "Authentication_type": auth,
                }),
            )]),
        )]
    }
}


// Struct: CardAccountId
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CardAccountId


// Struct: Event
// File: crates/router/src/types/domain/event.rs
// Module: router
// Implementations: 1
// Traits: super::behaviour::Conversion

pub struct Event


// File: crates/router/src/types/storage/authentication.rs
// Module: router

pub use diesel_models::authentication::{Authentication, AuthenticationNew, AuthenticationUpdate};


// Struct: WorldpayConnectorMetadataObject
// File: crates/hyperswitch_connectors/src/connectors/worldpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct WorldpayConnectorMetadataObject


// File: crates/router/src/core/payments/access_token.rs
// Module: router
// Public functions: 4

use std::fmt::Debug;

use common_utils::ext_traits::AsyncExt;
use error_stack::ResultExt;
use hyperswitch_interfaces::api::ConnectorSpecifications;

use crate::{
    consts,
    core::{
        errors::{self, RouterResult},
        payments,
    },
    routes::{metrics, SessionState},
    services::{self, logger},
    types::{self, api as api_types, domain},
};

/// After we get the access token, check if there was an error and if the flow should proceed further
/// Returns bool
/// true - Everything is well, continue with the flow
/// false - There was an error, cannot proceed further
pub fn update_router_data_with_access_token_result<F, Req, Res>(
    add_access_token_result: &types::AddAccessTokenResult,
    router_data: &mut types::RouterData<F, Req, Res>,
    call_connector_action: &payments::CallConnectorAction,
) -> bool {
    // Update router data with access token or error only if it will be calling connector
    let should_update_router_data = matches!(
        (
            add_access_token_result.connector_supports_access_token,
            call_connector_action
        ),
        (true, payments::CallConnectorAction::Trigger)
    );

    if should_update_router_data {
        match add_access_token_result.access_token_result.as_ref() {
            Ok(access_token) => {
                router_data.access_token.clone_from(access_token);
                true
            }
            Err(connector_error) => {
                router_data.response = Err(connector_error.clone());
                false
            }
        }
    } else {
        true
    }
}

pub async fn add_access_token<
    F: Clone + 'static,
    Req: Debug + Clone + 'static,
    Res: Debug + Clone + 'static,
>(
    state: &SessionState,
    connector: &api_types::ConnectorData,
    merchant_context: &domain::MerchantContext,
    router_data: &types::RouterData<F, Req, Res>,
    creds_identifier: Option<&str>,
) -> RouterResult<types::AddAccessTokenResult> {
    if connector
        .connector_name
        .supports_access_token(router_data.payment_method)
    {
        let merchant_id = merchant_context.get_merchant_account().get_id();
        let store = &*state.store;

        // `merchant_connector_id` may not be present in the below cases
        // - when straight through routing is used without passing the `merchant_connector_id`
        // - when creds identifier is passed
        //
        // In these cases fallback to `connector_name`.
        // We cannot use multiple merchant connector account in these cases
        let merchant_connector_id_or_connector_name = connector
            .merchant_connector_id
            .clone()
            .map(|mca_id| mca_id.get_string_repr().to_string())
            .or(creds_identifier.map(|id| id.to_string()))
            .unwrap_or(connector.connector_name.to_string());

        let old_access_token = store
            .get_access_token(merchant_id, &merchant_connector_id_or_connector_name)
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("DB error when accessing the access token")?;

        let res = match old_access_token {
            Some(access_token) => {
                router_env::logger::debug!(
                    "Access token found in redis for merchant_id: {:?}, payment_id: {:?}, connector: {} which has expiry of: {} seconds",
                    merchant_context.get_merchant_account().get_id(),
                    router_data.payment_id,
                    connector.connector_name,
                    access_token.expires
                );
                metrics::ACCESS_TOKEN_CACHE_HIT.add(
                    1,
                    router_env::metric_attributes!((
                        "connector",
                        connector.connector_name.to_string()
                    )),
                );
                Ok(Some(access_token))
            }
            None => {
                metrics::ACCESS_TOKEN_CACHE_MISS.add(
                    1,
                    router_env::metric_attributes!((
                        "connector",
                        connector.connector_name.to_string()
                    )),
                );

                let authentication_token =
                    execute_authentication_token(state, connector, router_data).await?;

                let cloned_router_data = router_data.clone();

                let refresh_token_request_data = types::AccessTokenRequestData::try_from((
                    router_data.connector_auth_type.clone(),
                    authentication_token,
                ))
                .attach_printable(
                    "Could not create access token request, invalid connector account credentials",
                )?;

                let refresh_token_response_data: Result<types::AccessToken, types::ErrorResponse> =
                    Err(types::ErrorResponse::default());
                let refresh_token_router_data = payments::helpers::router_data_type_conversion::<
                    _,
                    api_types::AccessTokenAuth,
                    _,
                    _,
                    _,
                    _,
                >(
                    cloned_router_data,
                    refresh_token_request_data,
                    refresh_token_response_data,
                );
                refresh_connector_auth(
                    state,
                    connector,
                    merchant_context,
                    &refresh_token_router_data,
                )
                .await?
                .async_map(|access_token| async move {
                    let store = &*state.store;

                    // The expiry should be adjusted for network delays from the connector
                    // The access token might not have been expired when request is sent
                    // But once it reaches the connector, it might expire because of the network delay
                    // Subtract few seconds from the expiry in order to account for these network delays
                    // This will reduce the expiry time by `REDUCE_ACCESS_TOKEN_EXPIRY_TIME` seconds
                    let modified_access_token_with_expiry = types::AccessToken {
                        expires: access_token
                            .expires
                            .saturating_sub(consts::REDUCE_ACCESS_TOKEN_EXPIRY_TIME.into()),
                        ..access_token
                    };

                    logger::debug!(
                        access_token_expiry_after_modification =
                            modified_access_token_with_expiry.expires
                    );

                    if let Err(access_token_set_error) = store
                        .set_access_token(
                            merchant_id,
                            &merchant_connector_id_or_connector_name,
                            modified_access_token_with_expiry.clone(),
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("DB error when setting the access token")
                    {
                        // If we are not able to set the access token in redis, the error should just be logged and proceed with the payment
                        // Payments should not fail, once the access token is successfully created
                        // The next request will create new access token, if required
                        logger::error!(access_token_set_error=?access_token_set_error);
                    }
                    Some(modified_access_token_with_expiry)
                })
                .await
            }
        };

        Ok(types::AddAccessTokenResult {
            access_token_result: res,
            connector_supports_access_token: true,
        })
    } else {
        Ok(types::AddAccessTokenResult {
            access_token_result: Err(types::ErrorResponse::default()),
            connector_supports_access_token: false,
        })
    }
}

pub async fn refresh_connector_auth(
    state: &SessionState,
    connector: &api_types::ConnectorData,
    _merchant_context: &domain::MerchantContext,
    router_data: &types::RouterData<
        api_types::AccessTokenAuth,
        types::AccessTokenRequestData,
        types::AccessToken,
    >,
) -> RouterResult<Result<types::AccessToken, types::ErrorResponse>> {
    let connector_integration: services::BoxedAccessTokenConnectorIntegrationInterface<
        api_types::AccessTokenAuth,
        types::AccessTokenRequestData,
        types::AccessToken,
    > = connector.connector.get_connector_integration();

    let access_token_router_data_result = services::execute_connector_processing_step(
        state,
        connector_integration,
        router_data,
        payments::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await;

    let access_token_router_data = match access_token_router_data_result {
        Ok(router_data) => Ok(router_data.response),
        Err(connector_error) => {
            // If we receive a timeout error from the connector, then
            // the error has to be handled gracefully by updating the payment status to failed.
            // further payment flow will not be continued
            if connector_error.current_context().is_connector_timeout() {
                let error_response = types::ErrorResponse {
                    code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
                    message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
                    reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
                    status_code: 504,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                };

                Ok(Err(error_response))
            } else {
                Err(connector_error
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Could not refresh access token"))
            }
        }
    }?;

    metrics::ACCESS_TOKEN_CREATION.add(
        1,
        router_env::metric_attributes!(("connector", connector.connector_name.to_string())),
    );
    Ok(access_token_router_data)
}

pub async fn execute_authentication_token<
    F: Clone + 'static,
    Req: Debug + Clone + 'static,
    Res: Debug + Clone + 'static,
>(
    state: &SessionState,
    connector: &api_types::ConnectorData,
    router_data: &types::RouterData<F, Req, Res>,
) -> RouterResult<Option<types::AccessTokenAuthenticationResponse>> {
    let should_create_authentication_token = connector
        .connector
        .authentication_token_for_token_creation();

    if !should_create_authentication_token {
        return Ok(None);
    }

    let authentication_token_request_data = types::AccessTokenAuthenticationRequestData::try_from(
        router_data.connector_auth_type.clone(),
    )
    .attach_printable(
        "Could not create authentication token request, invalid connector account credentials",
    )?;

    let authentication_token_response_data: Result<
        types::AccessTokenAuthenticationResponse,
        types::ErrorResponse,
    > = Err(types::ErrorResponse::default());

    let auth_token_router_data = payments::helpers::router_data_type_conversion::<
        _,
        api_types::AccessTokenAuthentication,
        _,
        _,
        _,
        _,
    >(
        router_data.clone(),
        authentication_token_request_data,
        authentication_token_response_data,
    );

    let connector_integration: services::BoxedAuthenticationTokenConnectorIntegrationInterface<
        api_types::AccessTokenAuthentication,
        types::AccessTokenAuthenticationRequestData,
        types::AccessTokenAuthenticationResponse,
    > = connector.connector.get_connector_integration();

    let auth_token_router_data_result = services::execute_connector_processing_step(
        state,
        connector_integration,
        &auth_token_router_data,
        payments::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await;

    let auth_token_result = match auth_token_router_data_result {
        Ok(router_data) => router_data.response,
        Err(connector_error) => {
            // Handle timeout errors
            if connector_error.current_context().is_connector_timeout() {
                let error_response = types::ErrorResponse {
                    code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
                    message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
                    reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
                    status_code: 504,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                };
                Err(error_response)
            } else {
                return Err(connector_error
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Could not get authentication token"));
            }
        }
    };

    let authentication_token = auth_token_result
        .map_err(|_error| errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get authentication token")?;

    Ok(Some(authentication_token))
}


// Implementation: impl api::PaymentSync for for Aci
// File: crates/hyperswitch_connectors/src/connectors/aci.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Aci


// Struct: ThreeDSecureIoMetaData
// File: crates/hyperswitch_connectors/src/connectors/threedsecureio/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreeDSecureIoMetaData


// Implementation: impl RefundExecute for for Payone
// File: crates/hyperswitch_connectors/src/connectors/payone.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl RefundExecute for for Payone


// Implementation: impl api::PaymentVoid for for Dwolla
// File: crates/hyperswitch_connectors/src/connectors/dwolla.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentVoid for for Dwolla


// Function: construct_external_vault_proxy_payment_router_data
// File: crates/router/src/core/payments/transformers.rs
// Module: router

pub fn construct_external_vault_proxy_payment_router_data<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentConfirmData<api::ExternalVaultProxy>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::ExternalVaultProxyPaymentsRouterData>


// Implementation: impl api::PaymentVoid for for Adyen
// File: crates/hyperswitch_connectors/src/connectors/adyen.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentVoid for for Adyen


// Function: list_customer_payment_methods_core
// File: crates/router/src/core/payment_methods.rs
// Module: router

pub fn list_customer_payment_methods_core(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    customer_id: &id_type::GlobalCustomerId,
) -> RouterResult<Vec<payment_methods::CustomerPaymentMethodResponseItem>>


// Struct: InvoiceRecordBackRequest
// File: crates/hyperswitch_domain_models/src/router_request_types/revenue_recovery.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct InvoiceRecordBackRequest


impl TryFrom<&types::RefundSyncRouterData> for BraintreeRSyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::RefundSyncRouterData) -> Result<Self, Self::Error> {
        let metadata: BraintreeMeta = if let (
            Some(merchant_account_id),
            Some(merchant_config_currency),
        ) = (
            item.request.merchant_account_id.clone(),
            item.request.merchant_config_currency,
        ) {
            router_env::logger::info!(
                "BRAINTREE: Picking merchant_account_id and merchant_config_currency from payments request"
            );

            BraintreeMeta {
                merchant_account_id,
                merchant_config_currency,
            }
        } else {
            utils::to_connector_meta_from_secret(item.connector_meta_data.clone()).change_context(
                errors::ConnectorError::InvalidConnectorConfig { config: "metadata" },
            )?
        };
        utils::validate_currency(
            item.request.currency,
            Some(metadata.merchant_config_currency),
        )?;
        let refund_id = item.request.get_connector_refund_id()?;
        Ok(Self {
            query: REFUND_QUERY.to_string(),
            variables: RSyncInput {
                input: RefundSearchInput {
                    id: IdFilter { is: refund_id },
                },
            },
        })
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RSyncNodeData {
    id: String,
    status: BraintreeRefundStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RSyncEdgeData {
    node: RSyncNodeData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RefundData {
    edges: Vec<RSyncEdgeData>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RSyncSearchData {
    refunds: RefundData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RSyncResponseData {
    search: RSyncSearchData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RSyncResponse {
    data: RSyncResponseData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BraintreeRSyncResponse {
    RSyncResponse(Box<RSyncResponse>),
    ErrorResponse(Box<ErrorResponse>),
}

impl TryFrom<RefundsResponseRouterData<RSync, BraintreeRSyncResponse>>
    for RefundsRouterData<RSync>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, BraintreeRSyncResponse>,
    ) -> Result<Self, Self::Error> {
        match item.response {
            BraintreeRSyncResponse::ErrorResponse(error_response) => Ok(Self {
                response: build_error_response(&error_response.errors, item.http_code)
                    .map_err(|err| *err),
                ..item.data
            }),
            BraintreeRSyncResponse::RSyncResponse(rsync_response) => {
                let edge_data = rsync_response
                    .data
                    .search
                    .refunds
                    .edges
                    .first()
                    .ok_or(errors::ConnectorError::MissingConnectorRefundID)?;
                let connector_refund_id = &edge_data.node.id;
                let response = Ok(RefundsResponseData {
                    connector_refund_id: connector_refund_id.to_string(),
                    refund_status: enums::RefundStatus::from(edge_data.node.status.clone()),
                });
                Ok(Self {
                    response,
                    ..item.data
                })
            }
        }
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreditCardData {
    number: cards::CardNumber,
    expiration_year: Secret<String>,
    expiration_month: Secret<String>,
    cvv: Secret<String>,
    cardholder_name: Secret<String>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientTokenInput {
    merchant_account_id: Secret<String>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InputData {
    credit_card: CreditCardData,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InputClientTokenData {
    client_token: ClientTokenInput,
}

impl TryFrom<&types::TokenizationRouterData> for BraintreeTokenRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::TokenizationRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(card_data) => Ok(Self {
                query: TOKENIZE_CREDIT_CARD.to_string(),
                variables: VariableInput {
                    input: InputData {
                        credit_card: CreditCardData {
                            number: card_data.card_number,
                            expiration_year: card_data.card_exp_year,
                            expiration_month: card_data.card_exp_month,
                            cvv: card_data.card_cvc,
                            cardholder_name: item
                                .get_optional_billing_full_name()
                                .unwrap_or(Secret::new("".to_string())),
                        },
                    },
                },
            }),
            PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("braintree"),
                )
                .into())
            }
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TokenizePaymentMethodData {
    id: Secret<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizeCreditCardData {
    payment_method: TokenizePaymentMethodData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientToken {
    client_token: Secret<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizeCreditCard {
    tokenize_credit_card: TokenizeCreditCardData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientTokenData {
    create_client_token: ClientToken,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ClientTokenResponse {
    data: ClientTokenData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TokenResponse {
    data: TokenizeCreditCard,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ErrorResponse {
    errors: Vec<ErrorDetails>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BraintreeTokenResponse {
    TokenResponse(Box<TokenResponse>),
    ErrorResponse(Box<ErrorResponse>),
}

impl<F, T> TryFrom<ResponseRouterData<F, BraintreeTokenResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, BraintreeTokenResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: match item.response {
                BraintreeTokenResponse::ErrorResponse(error_response) => {
                    build_error_response(error_response.errors.as_ref(), item.http_code)
                        .map_err(|err| *err)
                }

                BraintreeTokenResponse::TokenResponse(token_response) => {
                    Ok(PaymentsResponseData::TokenizationResponse {
                        token: token_response
                            .data
                            .tokenize_credit_card
                            .payment_method
                            .id
                            .expose()
                            .clone(),
                    })
                }
            },
            ..item.data
        })
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CaptureTransactionBody {
    amount: StringMajorUnit,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CaptureInputData {
    transaction_id: String,
    transaction: CaptureTransactionBody,
}

impl TryFrom<&BraintreeRouterData<&types::PaymentsCaptureRouterData>> for BraintreeCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &BraintreeRouterData<&types::PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        let query = CAPTURE_TRANSACTION_MUTATION.to_string();
        let variables = VariableCaptureInput {
            input: CaptureInputData {
                transaction_id: item.router_data.request.connector_transaction_id.clone(),
                transaction: CaptureTransactionBody {
                    amount: item.amount.to_owned(),
                },
            },
        };
        Ok(Self { query, variables })
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CaptureResponseTransactionBody {
    id: String,
    status: BraintreePaymentStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CaptureTransactionData {
    transaction: CaptureResponseTransactionBody,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CaptureResponseData {
    capture_transaction: CaptureTransactionData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CaptureResponse {
    data: CaptureResponseData,
}

impl TryFrom<PaymentsCaptureResponseRouterData<BraintreeCaptureResponse>>
    for types::PaymentsCaptureRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsCaptureResponseRouterData<BraintreeCaptureResponse>,
    ) -> Result<Self, Self::Error> {
        match item.response {
            BraintreeCaptureResponse::SuccessResponse(capture_data) => {
                let transaction_data = capture_data.data.capture_transaction.transaction;
                let status = enums::AttemptStatus::from(transaction_data.status.clone());
                let response = if utils::is_payment_failure(status) {
                    Err(hyperswitch_domain_models::router_data::ErrorResponse {
                        code: transaction_data.status.to_string().clone(),
                        message: transaction_data.status.to_string().clone(),
                        reason: Some(transaction_data.status.to_string().clone()),
                        attempt_status: None,
                        connector_transaction_id: Some(transaction_data.id),
                        status_code: item.http_code,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(transaction_data.id),
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: None,
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                };
                Ok(Self {
                    status,
                    response,
                    ..item.data
                })
            }
            BraintreeCaptureResponse::ErrorResponse(error_data) => Ok(Self {
                response: build_error_response(&error_data.errors, item.http_code)
                    .map_err(|err| *err),
                ..item.data
            }),
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeletePaymentMethodFromVaultInputData {
    payment_method_id: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct VariableDeletePaymentMethodFromVaultInput {
    input: DeletePaymentMethodFromVaultInputData,
}

#[derive(Debug, Serialize)]
pub struct BraintreeRevokeMandateRequest {
    query: String,
    variables: VariableDeletePaymentMethodFromVaultInput,
}

impl TryFrom<&types::MandateRevokeRouterData> for BraintreeRevokeMandateRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::MandateRevokeRouterData) -> Result<Self, Self::Error> {
        let query = DELETE_PAYMENT_METHOD_FROM_VAULT_MUTATION.to_string();
        let variables = VariableDeletePaymentMethodFromVaultInput {
            input: DeletePaymentMethodFromVaultInputData {
                payment_method_id: Secret::new(
                    item.request
                        .connector_mandate_id
                        .clone()
                        .ok_or(errors::ConnectorError::MissingConnectorMandateID)?,
                ),
            },
        };
        Ok(Self { query, variables })
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            BraintreeRevokeMandateResponse,
            MandateRevokeRequestData,
            MandateRevokeResponseData,
        >,
    > for RouterData<F, MandateRevokeRequestData, MandateRevokeResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            BraintreeRevokeMandateResponse,
            MandateRevokeRequestData,
            MandateRevokeResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: match item.response {
                BraintreeRevokeMandateResponse::ErrorResponse(error_response) => {
                    build_error_response(error_response.errors.as_ref(), item.http_code)
                        .map_err(|err| *err)
                }
                BraintreeRevokeMandateResponse::RevokeMandateResponse(..) => {
                    Ok(MandateRevokeResponseData {
                        mandate_status: common_enums::MandateStatus::Revoked,
                    })
                }
            },
            ..item.data
        })
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BraintreeRevokeMandateResponse {
    RevokeMandateResponse(Box<RevokeMandateResponse>),
    ErrorResponse(Box<ErrorResponse>),
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RevokeMandateResponse {
    data: DeletePaymentMethodFromVault,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeletePaymentMethodFromVault {
    client_mutation_id: Option<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelInputData {
    transaction_id: String,
}

#[derive(Debug, Serialize)]
pub struct VariableCancelInput {
    input: CancelInputData,
}

#[derive(Debug, Serialize)]
pub struct BraintreeCancelRequest {
    query: String,
    variables: VariableCancelInput,
}

impl TryFrom<&types::PaymentsCancelRouterData> for BraintreeCancelRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
        let query = VOID_TRANSACTION_MUTATION.to_string();
        let variables = VariableCancelInput {
            input: CancelInputData {
                transaction_id: item.request.connector_transaction_id.clone(),
            },
        };
        Ok(Self { query, variables })
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CancelResponseTransactionBody {
    id: String,
    status: BraintreePaymentStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CancelTransactionData {
    reversal: CancelResponseTransactionBody,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelResponseData {
    reverse_transaction: CancelTransactionData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CancelResponse {
    data: CancelResponseData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BraintreeCancelResponse {
    CancelResponse(Box<CancelResponse>),
    ErrorResponse(Box<ErrorResponse>),
}

impl<F, T> TryFrom<ResponseRouterData<F, BraintreeCancelResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, BraintreeCancelResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        match item.response {
            BraintreeCancelResponse::ErrorResponse(error_response) => Ok(Self {
                response: build_error_response(&error_response.errors, item.http_code)
                    .map_err(|err| *err),
                ..item.data
            }),
            BraintreeCancelResponse::CancelResponse(void_response) => {
                let void_data = void_response.data.reverse_transaction.reversal;
                let status = enums::AttemptStatus::from(void_data.status.clone());
                let response = if utils::is_payment_failure(status) {
                    Err(hyperswitch_domain_models::router_data::ErrorResponse {
                        code: void_data.status.to_string().clone(),
                        message: void_data.status.to_string().clone(),
                        reason: Some(void_data.status.to_string().clone()),
                        attempt_status: None,
                        connector_transaction_id: None,
                        status_code: item.http_code,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::NoResponseId,
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: None,
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                };
                Ok(Self {
                    status,
                    response,
                    ..item.data
                })
            }
        }
    }
}

impl TryFrom<&types::PaymentsSyncRouterData> for BraintreePSyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::PaymentsSyncRouterData) -> Result<Self, Self::Error> {
        let transaction_id = item
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
        Ok(Self {
            query: TRANSACTION_QUERY.to_string(),
            variables: PSyncInput {
                input: TransactionSearchInput {
                    id: IdFilter { is: transaction_id },
                },
            },
        })
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NodeData {
    id: String,
    status: BraintreePaymentStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct EdgeData {
    node: NodeData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TransactionData {
    edges: Vec<EdgeData>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SearchData {
    transactions: TransactionData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PSyncResponseData {
    search: SearchData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PSyncResponse {
    data: PSyncResponseData,
}

impl<F, T> TryFrom<ResponseRouterData<F, BraintreePSyncResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, BraintreePSyncResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        match item.response {
            BraintreePSyncResponse::ErrorResponse(error_response) => Ok(Self {
                response: build_error_response(&error_response.errors, item.http_code)
                    .map_err(|err| *err),
                ..item.data
            }),
            BraintreePSyncResponse::SuccessResponse(psync_response) => {
                let edge_data = psync_response
                    .data
                    .search
                    .transactions
                    .edges
                    .first()
                    .ok_or(errors::ConnectorError::MissingConnectorTransactionID)?;
                let status = enums::AttemptStatus::from(edge_data.node.status.clone());
                let response = if utils::is_payment_failure(status) {
                    Err(hyperswitch_domain_models::router_data::ErrorResponse {
                        code: edge_data.node.status.to_string().clone(),
                        message: edge_data.node.status.to_string().clone(),
                        reason: Some(edge_data.node.status.to_string().clone()),
                        attempt_status: None,
                        connector_transaction_id: None,
                        status_code: item.http_code,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(edge_data.node.id.clone()),
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: None,
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                };
                Ok(Self {
                    status,
                    response,
                    ..item.data
                })
            }
        }
    }
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BraintreeThreeDsResponse {
    pub nonce: Secret<String>,
    pub liability_shifted: bool,
    pub liability_shift_possible: bool,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BraintreeThreeDsErrorResponse {
    pub code: String,
    pub message: String,
}

#[derive(Debug, Deserialize)]
pub struct BraintreeRedirectionResponse {
    pub authentication_response: String,
}

impl TryFrom<BraintreeMeta> for BraintreeClientTokenRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(metadata: BraintreeMeta) -> Result<Self, Self::Error> {
        Ok(Self {
            query: CLIENT_TOKEN_MUTATION.to_owned(),
            variables: VariableClientTokenInput {
                input: InputClientTokenData {
                    client_token: ClientTokenInput {
                        merchant_account_id: metadata.merchant_account_id,
                    },
                },
            },
        })
    }
}

impl
    TryFrom<(
        &BraintreeRouterData<&types::PaymentsAuthorizeRouterData>,
        BraintreeMeta,
    )> for CardPaymentRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (item, metadata): (
            &BraintreeRouterData<&types::PaymentsAuthorizeRouterData>,
            BraintreeMeta,
        ),
    ) -> Result<Self, Self::Error> {
        let (query, transaction_body) = if item.router_data.request.is_mandate_payment() {
            (
                match item.router_data.request.is_auto_capture()? {
                    true => CHARGE_AND_VAULT_TRANSACTION_MUTATION.to_string(),
                    false => AUTHORIZE_AND_VAULT_CREDIT_CARD_MUTATION.to_string(),
                },
                TransactionBody::Vault(VaultTransactionBody {
                    amount: item.amount.to_owned(),
                    merchant_account_id: metadata.merchant_account_id,
                    vault_payment_method_after_transacting: TransactionTiming {
                        when: "ALWAYS".to_string(),
                    },
                    customer_details: item
                        .router_data
                        .get_billing_email()
                        .ok()
                        .map(|email| CustomerBody { email }),
                    order_id: item.router_data.connector_request_reference_id.clone(),
                }),
            )
        } else {
            (
                match item.router_data.request.is_auto_capture()? {
                    true => CHARGE_CREDIT_CARD_MUTATION.to_string(),
                    false => AUTHORIZE_CREDIT_CARD_MUTATION.to_string(),
                },
                TransactionBody::Regular(RegularTransactionBody {
                    amount: item.amount.to_owned(),
                    merchant_account_id: metadata.merchant_account_id,
                    channel: CHANNEL_CODE.to_string(),
                    customer_details: item
                        .router_data
                        .get_billing_email()
                        .ok()
                        .map(|email| CustomerBody { email }),
                    order_id: item.router_data.connector_request_reference_id.clone(),
                }),
            )
        };
        Ok(Self {
            query,
            variables: VariablePaymentInput {
                input: PaymentInput {
                    payment_method_id: match item.router_data.get_payment_method_token()? {
                        PaymentMethodToken::Token(token) => token,
                        PaymentMethodToken::ApplePayDecrypt(_) => Err(
                            unimplemented_payment_method!("Apple Pay", "Simplified", "Braintree"),
                        )?,
                        PaymentMethodToken::PazeDecrypt(_) => {
                            Err(unimplemented_payment_method!("Paze", "Braintree"))?
                        }
                        PaymentMethodToken::GooglePayDecrypt(_) => {
                            Err(unimplemented_payment_method!("Google Pay", "Braintree"))?
                        }
                    },
                    transaction: transaction_body,
                },
            },
        })
    }
}

impl TryFrom<&BraintreeRouterData<&types::PaymentsCompleteAuthorizeRouterData>>
    for CardPaymentRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &BraintreeRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let metadata: BraintreeMeta = if let (
            Some(merchant_account_id),
            Some(merchant_config_currency),
        ) = (
            item.router_data.request.merchant_account_id.clone(),
            item.router_data.request.merchant_config_currency,
        ) {
            router_env::logger::info!(
                "BRAINTREE: Picking merchant_account_id and merchant_config_currency from payments request"
            );

            BraintreeMeta {
                merchant_account_id,
                merchant_config_currency,
            }
        } else {
            utils::to_connector_meta_from_secret(item.router_data.connector_meta_data.clone())
                .change_context(errors::ConnectorError::InvalidConnectorConfig {
                    config: "metadata",
                })?
        };
        utils::validate_currency(
            item.router_data.request.currency,
            Some(metadata.merchant_config_currency),
        )?;
        let payload_data = PaymentsCompleteAuthorizeRequestData::get_redirect_response_payload(
            &item.router_data.request,
        )?
        .expose();
        let redirection_response: BraintreeRedirectionResponse = serde_json::from_value(
            payload_data,
        )
        .change_context(errors::ConnectorError::MissingConnectorRedirectionPayload {
            field_name: "redirection_response",
        })?;
        let three_ds_data = serde_json::from_str::<BraintreeThreeDsResponse>(
            &redirection_response.authentication_response,
        )
        .change_context(errors::ConnectorError::MissingConnectorRedirectionPayload {
            field_name: "three_ds_data",
        })?;

        let (query, transaction_body) = if item.router_data.request.is_mandate_payment() {
            (
                match item.router_data.request.is_auto_capture()? {
                    true => CHARGE_AND_VAULT_TRANSACTION_MUTATION.to_string(),
                    false => AUTHORIZE_AND_VAULT_CREDIT_CARD_MUTATION.to_string(),
                },
                TransactionBody::Vault(VaultTransactionBody {
                    amount: item.amount.to_owned(),
                    merchant_account_id: metadata.merchant_account_id,
                    vault_payment_method_after_transacting: TransactionTiming {
                        when: "ALWAYS".to_string(),
                    },
                    customer_details: item
                        .router_data
                        .get_billing_email()
                        .ok()
                        .map(|email| CustomerBody { email }),
                    order_id: item.router_data.connector_request_reference_id.clone(),
                }),
            )
        } else {
            (
                match item.router_data.request.is_auto_capture()? {
                    true => CHARGE_CREDIT_CARD_MUTATION.to_string(),
                    false => AUTHORIZE_CREDIT_CARD_MUTATION.to_string(),
                },
                TransactionBody::Regular(RegularTransactionBody {
                    amount: item.amount.to_owned(),
                    merchant_account_id: metadata.merchant_account_id,
                    channel: CHANNEL_CODE.to_string(),
                    customer_details: item
                        .router_data
                        .get_billing_email()
                        .ok()
                        .map(|email| CustomerBody { email }),
                    order_id: item.router_data.connector_request_reference_id.clone(),
                }),
            )
        };
        Ok(Self {
            query,
            variables: VariablePaymentInput {
                input: PaymentInput {
                    payment_method_id: three_ds_data.nonce,
                    transaction: transaction_body,
                },
            },
        })
    }
}

fn get_braintree_redirect_form(
    client_token_data: ClientTokenResponse,
    payment_method_token: PaymentMethodToken,
    card_details: PaymentMethodData,
    complete_authorize_url: String,
) -> Result<RedirectForm, error_stack::Report<errors::ConnectorError>> {
    Ok(RedirectForm::Braintree {
        client_token: client_token_data
            .data
            .create_client_token
            .client_token
            .expose(),
        card_token: match payment_method_token {
            PaymentMethodToken::Token(token) => token.expose(),
            PaymentMethodToken::ApplePayDecrypt(_) => Err(unimplemented_payment_method!(
                "Apple Pay",
                "Simplified",
                "Braintree"
            ))?,
            PaymentMethodToken::PazeDecrypt(_) => {
                Err(unimplemented_payment_method!("Paze", "Braintree"))?
            }
            PaymentMethodToken::GooglePayDecrypt(_) => {
                Err(unimplemented_payment_method!("Google Pay", "Braintree"))?
            }
        },
        bin: match card_details {
            PaymentMethodData::Card(card_details) => card_details.card_number.get_card_isin(),
            PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => Err(
                errors::ConnectorError::NotImplemented("given payment method".to_owned()),
            )?,
        },
        acs_url: complete_authorize_url,
    })
}

#[derive(Debug, Deserialize)]
pub struct BraintreeWebhookResponse {
    pub bt_signature: String,
    pub bt_payload: String,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct Notification {
    pub kind: String, // xml parse only string to fields
    pub timestamp: String,
    pub dispute: Option<BraintreeDisputeData>,
}

pub(crate) fn get_status(status: &str) -> IncomingWebhookEvent {
    match status {
        "dispute_opened" => IncomingWebhookEvent::DisputeOpened,
        "dispute_lost" => IncomingWebhookEvent::DisputeLost,
        "dispute_won" => IncomingWebhookEvent::DisputeWon,
        "dispute_accepted" | "dispute_auto_accepted" => IncomingWebhookEvent::DisputeAccepted,
        "dispute_expired" => IncomingWebhookEvent::DisputeExpired,
        "dispute_disputed" => IncomingWebhookEvent::DisputeChallenged,
        _ => IncomingWebhookEvent::EventNotSupported,
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct BraintreeDisputeData {
    pub amount_disputed: MinorUnit,
    pub amount_won: Option<String>,
    pub case_number: Option<String>,
    pub chargeback_protection_level: Option<String>,
    pub currency_iso_code: enums::Currency,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created_at: Option<PrimitiveDateTime>,
    pub evidence: Option<DisputeEvidence>,
    pub id: String,
    pub kind: String, // xml parse only string to fields
    pub status: String,
    pub reason: Option<String>,
    pub reason_code: Option<String>,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub updated_at: Option<PrimitiveDateTime>,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub reply_by_date: Option<PrimitiveDateTime>,
    pub transaction: DisputeTransaction,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct DisputeTransaction {
    pub amount: StringMajorUnit,
    pub id: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct DisputeEvidence {
    pub comment: String,
    pub id: Secret<String>,
    pub created_at: Option<PrimitiveDateTime>,
    pub url: url::Url,
}

pub(crate) fn get_dispute_stage(code: &str) -> Result<enums::DisputeStage, errors::ConnectorError> {
    match code {
        "CHARGEBACK" => Ok(enums::DisputeStage::Dispute),
        "PRE_ARBITRATION" => Ok(enums::DisputeStage::PreArbitration),
        "RETRIEVAL" => Ok(enums::DisputeStage::PreDispute),
        _ => Err(errors::ConnectorError::WebhookBodyDecodingFailed),
    }
}


// Implementation: impl api::Payment for for Adyen
// File: crates/hyperswitch_connectors/src/connectors/adyen.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Payment for for Adyen


// Struct: Venmo
// File: crates/api_models/src/payouts.rs
// Module: api_models
// Implementations: 0

pub struct Venmo


// File: crates/analytics/src/auth_events/metrics/authentication_success_count.rs
// Module: analytics

use std::collections::HashSet;

use api_models::analytics::{
    auth_events::{AuthEventDimensions, AuthEventFilters, AuthEventMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_enums::AuthenticationStatus;
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::AuthEventMetricRow;
use crate::{
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
    AuthInfo,
};

#[derive(Default)]
pub(super) struct AuthenticationSuccessCount;

#[async_trait::async_trait]
impl<T> super::AuthEventMetric<T> for AuthenticationSuccessCount
where
    T: AnalyticsDataSource + super::AuthEventMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        auth: &AuthInfo,
        dimensions: &[AuthEventDimensions],
        filters: &AuthEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::Authentications);
        for dim in dimensions.iter() {
            query_builder.add_select_column(dim).switch()?;
        }
        query_builder
            .add_select_column(Aggregate::Count {
                field: None,
                alias: Some("count"),
            })
            .switch()?;

        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;

        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        query_builder
            .add_filter_clause("authentication_status", AuthenticationStatus::Success)
            .switch()?;
        filters.set_filter_clause(&mut query_builder).switch()?;
        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;
        auth.set_filter_clause(&mut query_builder).switch()?;

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<AuthEventMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    AuthEventMetricsBucketIdentifier::new(
                        i.authentication_status.as_ref().map(|i| i.0),
                        i.trans_status.as_ref().map(|i| i.0.clone()),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.error_message.clone(),
                        i.authentication_connector.as_ref().map(|i| i.0),
                        i.message_version.clone(),
                        i.acs_reference_number.clone(),
                        i.mcc.clone(),
                        i.currency.as_ref().map(|i| i.0),
                        i.merchant_country.clone(),
                        i.billing_country.clone(),
                        i.shipping_country.clone(),
                        i.issuer_country.clone(),
                        i.earliest_supported_version.clone(),
                        i.latest_supported_version.clone(),
                        i.whitelist_decision,
                        i.device_manufacturer.clone(),
                        i.device_type.clone(),
                        i.device_brand.clone(),
                        i.device_os.clone(),
                        i.device_display.clone(),
                        i.browser_name.clone(),
                        i.browser_version.clone(),
                        i.issuer_id.clone(),
                        i.scheme_name.clone(),
                        i.exemption_requested,
                        i.exemption_accepted,
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


// File: crates/diesel_models/src/query/subscription.rs
// Module: diesel_models
// Public functions: 3

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};
use error_stack::report;

use super::generics;
use crate::{
    errors,
    schema::subscription::dsl,
    subscription::{Subscription, SubscriptionNew, SubscriptionUpdate},
    PgPooledConn, StorageResult,
};

impl SubscriptionNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Subscription> {
        generics::generic_insert(conn, self).await
    }
}

impl Subscription {
    pub async fn find_by_merchant_id_subscription_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        id: String,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::id.eq(id.to_owned())),
        )
        .await
    }

    pub async fn update_subscription_entry(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        id: String,
        subscription_update: SubscriptionUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<
            <Self as HasTable>::Table,
            SubscriptionUpdate,
            _,
            _,
        >(
            conn,
            dsl::id
                .eq(id.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned())),
            subscription_update,
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(errors::DatabaseError::NotFound)
                .attach_printable("Error while updating subscription entry")
        })
    }
}


// Function: delete_theme
// File: crates/router/src/core/user/theme.rs
// Module: router

pub fn delete_theme(state: SessionState, theme_id: String) -> UserResponse<()>


// Struct: Wellsfargo
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo.rs
// Module: hyperswitch_connectors
// Implementations: 19
// Traits: ConnectorCommon, ConnectorValidation, api::Payment, api::PaymentAuthorize, api::PaymentSync, api::PaymentVoid, api::PaymentCapture, api::PaymentIncrementalAuthorization, api::MandateSetup, api::ConnectorAccessToken, api::PaymentToken, api::ConnectorMandateRevoke, api::PaymentSession, Refund, RefundExecute, RefundSync, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Wellsfargo

ConnectorCommon ConnectorValidation api::Payment api::PaymentAuthorize api::PaymentSync api::PaymentVoid api::PaymentCapture api::PaymentIncrementalAuthorization api::MandateSetup api::ConnectorAccessToken api::PaymentToken api::ConnectorMandateRevoke api::PaymentSession Refund RefundExecute RefundSync webhooks::IncomingWebhook ConnectorSpecifications


// Struct: HyperswitchVaultCreateResponse
// File: crates/hyperswitch_connectors/src/connectors/hyperswitch_vault/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct HyperswitchVaultCreateResponse


// Implementation: impl Responder
// File: crates/router/src/routes/routing.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// File: crates/analytics/src/auth_events/types.rs
// Module: analytics

use api_models::analytics::auth_events::{AuthEventDimensions, AuthEventFilters};
use error_stack::ResultExt;

use crate::{
    query::{QueryBuilder, QueryFilter, QueryResult, ToSql},
    types::{AnalyticsCollection, AnalyticsDataSource},
};

impl<T> QueryFilter<T> for AuthEventFilters
where
    T: AnalyticsDataSource,
    AnalyticsCollection: ToSql<T>,
{
    fn set_filter_clause(&self, builder: &mut QueryBuilder<T>) -> QueryResult<()> {
        if !self.authentication_status.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::AuthenticationStatus,
                    &self.authentication_status,
                )
                .attach_printable("Error adding authentication status filter")?;
        }

        if !self.trans_status.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::TransactionStatus,
                    &self.trans_status,
                )
                .attach_printable("Error adding transaction status filter")?;
        }

        if !self.error_message.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::ErrorMessage, &self.error_message)
                .attach_printable("Error adding error message filter")?;
        }

        if !self.authentication_connector.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::AuthenticationConnector,
                    &self.authentication_connector,
                )
                .attach_printable("Error adding authentication connector filter")?;
        }

        if !self.message_version.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::MessageVersion,
                    &self.message_version,
                )
                .attach_printable("Error adding message version filter")?;
        }

        if !self.platform.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::Platform, &self.platform)
                .attach_printable("Error adding platform filter")?;
        }

        if !self.acs_reference_number.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::AcsReferenceNumber,
                    &self.acs_reference_number,
                )
                .attach_printable("Error adding acs reference number filter")?;
        }

        if !self.mcc.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::Mcc, &self.mcc)
                .attach_printable("Failed to add MCC filter")?;
        }
        if !self.currency.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::Currency, &self.currency)
                .attach_printable("Failed to add currency filter")?;
        }
        if !self.merchant_country.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::MerchantCountry,
                    &self.merchant_country,
                )
                .attach_printable("Failed to add merchant country filter")?;
        }
        if !self.billing_country.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::BillingCountry,
                    &self.billing_country,
                )
                .attach_printable("Failed to add billing country filter")?;
        }
        if !self.shipping_country.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::ShippingCountry,
                    &self.shipping_country,
                )
                .attach_printable("Failed to add shipping country filter")?;
        }
        if !self.issuer_country.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::IssuerCountry,
                    &self.issuer_country,
                )
                .attach_printable("Failed to add issuer country filter")?;
        }
        if !self.earliest_supported_version.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::EarliestSupportedVersion,
                    &self.earliest_supported_version,
                )
                .attach_printable("Failed to add earliest supported version filter")?;
        }
        if !self.latest_supported_version.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::LatestSupportedVersion,
                    &self.latest_supported_version,
                )
                .attach_printable("Failed to add latest supported version filter")?;
        }
        if !self.whitelist_decision.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::WhitelistDecision,
                    &self.whitelist_decision,
                )
                .attach_printable("Failed to add whitelist decision filter")?;
        }
        if !self.device_manufacturer.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::DeviceManufacturer,
                    &self.device_manufacturer,
                )
                .attach_printable("Failed to add device manufacturer filter")?;
        }
        if !self.device_type.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::DeviceType, &self.device_type)
                .attach_printable("Failed to add device type filter")?;
        }
        if !self.device_brand.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::DeviceBrand, &self.device_brand)
                .attach_printable("Failed to add device brand filter")?;
        }
        if !self.device_os.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::DeviceOs, &self.device_os)
                .attach_printable("Failed to add device OS filter")?;
        }
        if !self.device_display.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::DeviceDisplay,
                    &self.device_display,
                )
                .attach_printable("Failed to add device display filter")?;
        }
        if !self.browser_name.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::BrowserName, &self.browser_name)
                .attach_printable("Failed to add browser name filter")?;
        }
        if !self.browser_version.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::BrowserVersion,
                    &self.browser_version,
                )
                .attach_printable("Failed to add browser version filter")?;
        }
        if !self.issuer_id.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::IssuerId, &self.issuer_id)
                .attach_printable("Failed to add issuer ID filter")?;
        }
        if !self.scheme_name.is_empty() {
            builder
                .add_filter_in_range_clause(AuthEventDimensions::SchemeName, &self.scheme_name)
                .attach_printable("Failed to add scheme name filter")?;
        }
        if !self.exemption_requested.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::ExemptionRequested,
                    &self.exemption_requested,
                )
                .attach_printable("Failed to add exemption requested filter")?;
        }
        if !self.exemption_accepted.is_empty() {
            builder
                .add_filter_in_range_clause(
                    AuthEventDimensions::ExemptionAccepted,
                    &self.exemption_accepted,
                )
                .attach_printable("Failed to add exemption accepted filter")?;
        }

        Ok(())
    }
}


// Struct: SankeyRow
// File: crates/analytics/src/auth_events/sankey.rs
// Module: analytics
// Implementations: 0

pub struct SankeyRow


// Implementation: impl Default for for PayoutAttemptUpdateInternal
// File: crates/diesel_models/src/payout_attempt.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl Default for for PayoutAttemptUpdateInternal


// Function: get_access_token_key
// File: crates/common_utils/src/id_type/merchant.rs
// Module: common_utils

// Documentation: get_access_token_key

pub fn get_access_token_key(
        &self,
        merchant_connector_id_or_connector_name: impl Display,
    ) -> String


// Function: decide_frm_flow
// File: crates/router/src/core/fraud_check/flows/sale_flow.rs
// Module: router

pub fn decide_frm_flow(
    router_data: &mut FrmSaleRouterData,
    state: &SessionState,
    connector: &FraudCheckConnectorData,
    call_connector_action: payments::CallConnectorAction,
    _merchant_context: &domain::MerchantContext,
) -> RouterResult<FrmSaleRouterData>


// Function: retrieve_profile
// File: crates/router/src/core/admin.rs
// Module: router

pub fn retrieve_profile(
    state: SessionState,
    profile_id: id_type::ProfileId,
    key_store: domain::MerchantKeyStore,
) -> RouterResponse<api_models::admin::ProfileResponse>


// Function: seed_forex
// File: crates/euclid_wasm/src/lib.rs
// Module: euclid_wasm

pub fn seed_forex(forex: JsValue) -> JsResult


// Implementation: impl api::MandateSetup for for Noon
// File: crates/hyperswitch_connectors/src/connectors/noon.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::MandateSetup for for Noon


// Struct: CurrencyCountryFlowFilter
// File: crates/kgraph_utils/src/types.rs
// Module: kgraph_utils
// Implementations: 0

pub struct CurrencyCountryFlowFilter


// Function: new
// File: crates/hyperswitch_connectors/src/connectors/bluecode.rs
// Module: hyperswitch_connectors

pub fn new() -> &'static Self


// Function: get_merchant_bank_data_for_open_banking_connectors
// File: crates/router/src/core/payments.rs
// Module: router

pub fn get_merchant_bank_data_for_open_banking_connectors(
    merchant_connector_account: &helpers::MerchantConnectorAccountType,
    merchant_context: &domain::MerchantContext,
    connector: &api::ConnectorData,
    state: &SessionState,
) -> RouterResult<Option<router_types::MerchantRecipientData>>


// Struct: NexinetsPreAuthOrDebitResponse
// File: crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NexinetsPreAuthOrDebitResponse


// Function: client_secret_create
// File: crates/router/src/routes/ephemeral_key.rs
// Module: router

pub fn client_secret_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<api_models::ephemeral_key::ClientSecretCreateRequest>,
) -> HttpResponse


// File: crates/router_derive/src/macros/diesel.rs
// Module: router_derive
// Public functions: 1

use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{parse::Parse, Data, DeriveInput, ItemEnum};

use crate::macros::helpers;

pub(crate) fn diesel_enum_text_derive_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    match &ast.data {
        Data::Enum(_) => (),
        _ => return Err(helpers::non_enum_error()),
    };

    Ok(quote! {

        #[automatically_derived]
        impl #impl_generics ::diesel::serialize::ToSql<::diesel::sql_types::Text, ::diesel::pg::Pg> for #name #ty_generics
        #where_clause
        {
            fn to_sql<'b>(&'b self, out: &mut ::diesel::serialize::Output<'b, '_, ::diesel::pg::Pg>) -> ::diesel::serialize::Result {
                use ::std::io::Write;

                out.write_all(self.to_string().as_bytes())?;
                Ok(::diesel::serialize::IsNull::No)
            }
        }

        #[automatically_derived]
        impl #impl_generics ::diesel::deserialize::FromSql<::diesel::sql_types::Text, ::diesel::pg::Pg> for #name #ty_generics
        #where_clause
        {
            fn from_sql(value: ::diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
                use ::core::str::FromStr;

                Self::from_str(::core::str::from_utf8(value.as_bytes())?)
                    .map_err(|_| "Unrecognized enum variant".into())
            }
        }
    })
}

pub(crate) fn diesel_enum_db_enum_derive_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    match &ast.data {
        Data::Enum(_) => (),
        _ => return Err(helpers::non_enum_error()),
    };

    let struct_name = format_ident!("Db{name}");
    let type_name = format!("{name}");

    Ok(quote! {

        #[derive(::core::clone::Clone, ::core::marker::Copy, ::core::fmt::Debug, ::diesel::QueryId, ::diesel::SqlType)]
        #[diesel(postgres_type(name = #type_name))]
        pub struct #struct_name;

        #[automatically_derived]
        impl #impl_generics ::diesel::serialize::ToSql<#struct_name, ::diesel::pg::Pg> for #name #ty_generics
        #where_clause
        {
            fn to_sql<'b>(&'b self, out: &mut ::diesel::serialize::Output<'b, '_, ::diesel::pg::Pg>) -> ::diesel::serialize::Result {
                use ::std::io::Write;

                out.write_all(self.to_string().as_bytes())?;
                Ok(::diesel::serialize::IsNull::No)
            }
        }

        #[automatically_derived]
        impl #impl_generics ::diesel::deserialize::FromSql<#struct_name, ::diesel::pg::Pg> for #name #ty_generics
        #where_clause
        {
            fn from_sql(value: ::diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
                use ::core::str::FromStr;

                Self::from_str(::core::str::from_utf8(value.as_bytes())?)
                    .map_err(|_| "Unrecognized enum variant".into())
            }
        }
    })
}

mod diesel_keyword {
    use syn::custom_keyword;

    custom_keyword!(storage_type);
    custom_keyword!(db_enum);
    custom_keyword!(text);
}

#[derive(Debug, strum::EnumString, strum::EnumIter, strum::Display)]
#[strum(serialize_all = "snake_case")]
pub enum StorageType {
    /// Store the Enum as Text value in the database
    Text,
    /// Store the Enum as Enum in the database. This requires a corresponding enum to be created
    /// in the database with the same name
    DbEnum,
}

#[derive(Debug)]
pub enum DieselEnumMeta {
    StorageTypeEnum {
        keyword: diesel_keyword::storage_type,
        value: StorageType,
    },
}

impl Parse for StorageType {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let text = input.parse::<syn::LitStr>()?;
        let value = text.value();

        value.as_str().parse().map_err(|_| {
            syn::Error::new_spanned(
                &text,
                format!(
                    "Unexpected value for storage_type: `{value}`. Possible values are `{}`",
                    helpers::get_possible_values_for_enum::<Self>()
                ),
            )
        })
    }
}

impl DieselEnumMeta {
    pub fn get_storage_type(&self) -> &StorageType {
        match self {
            Self::StorageTypeEnum { value, .. } => value,
        }
    }
}

impl Parse for DieselEnumMeta {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(diesel_keyword::storage_type) {
            let keyword = input.parse()?;
            input.parse::<syn::Token![=]>()?;
            let value = input.parse()?;
            Ok(Self::StorageTypeEnum { keyword, value })
        } else {
            Err(lookahead.error())
        }
    }
}

impl ToTokens for DieselEnumMeta {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::StorageTypeEnum { keyword, .. } => keyword.to_tokens(tokens),
        }
    }
}

trait DieselDeriveInputExt {
    /// Get all the error metadata associated with an enum.
    fn get_metadata(&self) -> syn::Result<Vec<DieselEnumMeta>>;
}

impl DieselDeriveInputExt for DeriveInput {
    fn get_metadata(&self) -> syn::Result<Vec<DieselEnumMeta>> {
        helpers::get_metadata_inner("storage_type", &self.attrs)
    }
}

pub(crate) fn diesel_enum_derive_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
    let storage_type = ast.get_metadata()?;

    match storage_type
        .first()
        .ok_or(syn::Error::new(
            Span::call_site(),
            "Storage type must be specified",
        ))?
        .get_storage_type()
    {
        StorageType::Text => diesel_enum_text_derive_inner(ast),
        StorageType::DbEnum => diesel_enum_db_enum_derive_inner(ast),
    }
}

/// Based on the storage type, derive appropriate diesel traits
/// This will add the appropriate #[diesel(sql_type)]
/// Since the `FromSql` and `ToSql` have to be derived for all the enums, this will add the
/// `DieselEnum` derive trait.
pub(crate) fn diesel_enum_attribute_macro(
    diesel_enum_meta: DieselEnumMeta,
    item: &ItemEnum,
) -> syn::Result<TokenStream> {
    let diesel_derives =
        quote!(#[derive(diesel::AsExpression, diesel::FromSqlRow, router_derive::DieselEnum) ]);

    match diesel_enum_meta {
        DieselEnumMeta::StorageTypeEnum {
            value: storage_type,
            ..
        } => match storage_type {
            StorageType::Text => Ok(quote! {
                #diesel_derives
                #[diesel(sql_type = ::diesel::sql_types::Text)]
                #[storage_type(storage_type = "text")]
                #item
            }),
            StorageType::DbEnum => {
                let name = &item.ident;
                let type_name = format_ident!("Db{name}");
                Ok(quote! {
                    #diesel_derives
                    #[diesel(sql_type = #type_name)]
                    #[storage_type(storage_type= "db_enum")]
                    #item
                })
            }
        },
    }
}


// Struct: CardDetailFromLocker
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 0

pub struct CardDetailFromLocker


// Implementation: impl api::PaymentSync for for Affirm
// File: crates/hyperswitch_connectors/src/connectors/affirm.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Affirm


// Implementation: impl Refund for for Cybersource
// File: crates/hyperswitch_connectors/src/connectors/cybersource.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl Refund for for Cybersource


// Implementation: impl api::PaymentVoid for for Worldpayvantiv
// File: crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentVoid for for Worldpayvantiv


// Function: retrieve_decision
// File: crates/diesel_models/src/query/gsm.rs
// Module: diesel_models

pub fn retrieve_decision(
        conn: &PgPooledConn,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> StorageResult<String>


// Implementation: impl RemoteStorageObject
// File: crates/hyperswitch_domain_models/src/lib.rs
// Module: hyperswitch_domain_models
// Methods: 1 total (1 public)

impl RemoteStorageObject


// Struct: BarclaycardThreeDSMetadata
// File: crates/hyperswitch_connectors/src/connectors/barclaycard/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BarclaycardThreeDSMetadata


// Function: confirm_intent_with_external_vault_proxy
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn confirm_intent_with_external_vault_proxy(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<api_models::payments::ExternalVaultProxyPaymentsRequest>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder


// File: crates/hyperswitch_domain_models/src/router_flow_types.rs
// Module: hyperswitch_domain_models

pub mod access_token_auth;
pub mod authentication;
pub mod dispute;
pub mod files;
pub mod fraud_check;
pub mod mandate_revoke;
pub mod payments;
pub mod payouts;
pub mod refunds;
pub mod revenue_recovery;
pub mod subscriptions;
pub mod unified_authentication_service;
pub mod vault;
pub mod webhooks;
pub use access_token_auth::*;
pub use dispute::*;
pub use files::*;
pub use fraud_check::*;
pub use payments::*;
pub use payouts::*;
pub use refunds::*;
pub use revenue_recovery::*;
pub use subscriptions::*;
pub use unified_authentication_service::*;
pub use vault::*;
pub use webhooks::*;


// Struct: ThreeDsDecisionRule
// File: crates/router/src/routes/app.rs
// Module: router
// Implementations: 1

pub struct ThreeDsDecisionRule


// Function: update_all_merchant_accounts
// File: crates/diesel_models/src/query/merchant_account.rs
// Module: diesel_models

pub fn update_all_merchant_accounts(
        conn: &PgPooledConn,
        merchant_account: MerchantAccountUpdateInternal,
    ) -> StorageResult<Vec<Self>>


// Function: list_saved_payment_methods_for_customer
// File: crates/router/src/core/payment_methods.rs
// Module: router

pub fn list_saved_payment_methods_for_customer(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    customer_id: id_type::GlobalCustomerId,
) -> RouterResponse<payment_methods::CustomerPaymentMethodsListResponse>


// Implementation: impl Profile
// File: crates/diesel_models/src/business_profile.rs
// Module: diesel_models
// Methods: 2 total (2 public)

impl Profile


// Implementation: impl InvoiceNew
// File: crates/diesel_models/src/query/invoice.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl InvoiceNew


// Function: server
// File: crates/router/src/routes/app.rs
// Module: router

pub fn server(state: AppState) -> Scope


// Struct: MerchantDetails
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 0

pub struct MerchantDetails


// Function: get_mca_from_profile_id
// File: crates/router/src/core/payouts.rs
// Module: router

pub fn get_mca_from_profile_id(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    profile_id: &id_type::ProfileId,
    connector_name: &str,
    merchant_connector_id: Option<&id_type::MerchantConnectorAccountId>,
) -> RouterResult<payment_helpers::MerchantConnectorAccountType>