// File: crates/router/src/core/blocklist/utils.rs
// Module: router
// Public functions: 7

use api_models::blocklist as api_blocklist;
use common_enums::MerchantDecision;
use common_utils::errors::CustomResult;
use diesel_models::configs;
use error_stack::ResultExt;
use masking::StrongSecret;

use super::{errors, transformers::generate_fingerprint, SessionState};
use crate::{
    consts,
    core::{
        errors::{RouterResult, StorageErrorExt},
        payments::PaymentData,
    },
    logger,
    types::{domain, storage, transformers::ForeignInto},
    utils,
};

pub async fn delete_entry_from_blocklist(
    state: &SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    request: api_blocklist::DeleteFromBlocklistRequest,
) -> RouterResult<api_blocklist::DeleteFromBlocklistResponse> {
    let blocklist_entry = match request {
        api_blocklist::DeleteFromBlocklistRequest::CardBin(bin) => {
            delete_card_bin_blocklist_entry(state, &bin, merchant_id).await?
        }

        api_blocklist::DeleteFromBlocklistRequest::ExtendedCardBin(xbin) => {
            delete_card_bin_blocklist_entry(state, &xbin, merchant_id).await?
        }

        api_blocklist::DeleteFromBlocklistRequest::Fingerprint(fingerprint_id) => state
            .store
            .delete_blocklist_entry_by_merchant_id_fingerprint_id(merchant_id, &fingerprint_id)
            .await
            .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
                message: "no blocklist record for the given fingerprint id was found".to_string(),
            })?,
    };

    Ok(blocklist_entry.foreign_into())
}

pub async fn toggle_blocklist_guard_for_merchant(
    state: &SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    query: api_blocklist::ToggleBlocklistQuery,
) -> CustomResult<api_blocklist::ToggleBlocklistResponse, errors::ApiErrorResponse> {
    let key = merchant_id.get_blocklist_guard_key();
    let maybe_guard = state.store.find_config_by_key(&key).await;
    let new_config = configs::ConfigNew {
        key: key.clone(),
        config: query.status.to_string(),
    };
    match maybe_guard {
        Ok(_config) => {
            let updated_config = configs::ConfigUpdate::Update {
                config: Some(query.status.to_string()),
            };
            state
                .store
                .update_config_by_key(&key, updated_config)
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error enabling the blocklist guard")?;
        }
        Err(e) if e.current_context().is_db_not_found() => {
            state
                .store
                .insert_config(new_config)
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error enabling the blocklist guard")?;
        }
        Err(error) => {
            logger::error!(?error);
            Err(error)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error enabling the blocklist guard")?;
        }
    };
    let guard_status = if query.status { "enabled" } else { "disabled" };
    Ok(api_blocklist::ToggleBlocklistResponse {
        blocklist_guard_status: guard_status.to_string(),
    })
}

pub async fn list_blocklist_entries_for_merchant(
    state: &SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    query: api_blocklist::ListBlocklistQuery,
) -> RouterResult<Vec<api_blocklist::BlocklistResponse>> {
    state
        .store
        .list_blocklist_entries_by_merchant_id_data_kind(
            merchant_id,
            query.data_kind,
            query.limit.into(),
            query.offset.into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "no blocklist records found".to_string(),
        })
        .map(|v| v.into_iter().map(ForeignInto::foreign_into).collect())
}

fn validate_card_bin(bin: &str) -> RouterResult<()> {
    if bin.len() == 6 && bin.chars().all(|c| c.is_ascii_digit()) {
        Ok(())
    } else {
        Err(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "data".to_string(),
            expected_format: "a 6 digit number".to_string(),
        }
        .into())
    }
}

fn validate_extended_card_bin(bin: &str) -> RouterResult<()> {
    if bin.len() == 8 && bin.chars().all(|c| c.is_ascii_digit()) {
        Ok(())
    } else {
        Err(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "data".to_string(),
            expected_format: "an 8 digit number".to_string(),
        }
        .into())
    }
}

pub async fn insert_entry_into_blocklist(
    state: &SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    to_block: api_blocklist::AddToBlocklistRequest,
) -> RouterResult<api_blocklist::AddToBlocklistResponse> {
    let blocklist_entry = match &to_block {
        api_blocklist::AddToBlocklistRequest::CardBin(bin) => {
            validate_card_bin(bin)?;
            duplicate_check_insert_bin(
                bin,
                state,
                merchant_id,
                common_enums::BlocklistDataKind::CardBin,
            )
            .await?
        }

        api_blocklist::AddToBlocklistRequest::ExtendedCardBin(bin) => {
            validate_extended_card_bin(bin)?;
            duplicate_check_insert_bin(
                bin,
                state,
                merchant_id,
                common_enums::BlocklistDataKind::ExtendedCardBin,
            )
            .await?
        }

        api_blocklist::AddToBlocklistRequest::Fingerprint(fingerprint_id) => {
            let blocklist_entry_result = state
                .store
                .find_blocklist_entry_by_merchant_id_fingerprint_id(merchant_id, fingerprint_id)
                .await;

            match blocklist_entry_result {
                Ok(_) => {
                    return Err(errors::ApiErrorResponse::PreconditionFailed {
                        message: "data associated with the given fingerprint is already blocked"
                            .to_string(),
                    }
                    .into());
                }

                // if it is a db not found error, we can proceed as normal
                Err(inner) if inner.current_context().is_db_not_found() => {}

                err @ Err(_) => {
                    err.change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("error fetching blocklist entry from table")?;
                }
            }

            state
                .store
                .insert_blocklist_entry(storage::BlocklistNew {
                    merchant_id: merchant_id.to_owned(),
                    fingerprint_id: fingerprint_id.clone(),
                    data_kind: api_models::enums::enums::BlocklistDataKind::PaymentMethod,
                    metadata: None,
                    created_at: common_utils::date_time::now(),
                })
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("failed to add fingerprint to blocklist")?
        }
    };
    Ok(blocklist_entry.foreign_into())
}

pub async fn get_merchant_fingerprint_secret(
    state: &SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
) -> RouterResult<String> {
    let key = merchant_id.get_merchant_fingerprint_secret_key();
    let config_fetch_result = state.store.find_config_by_key(&key).await;

    match config_fetch_result {
        Ok(config) => Ok(config.config),

        Err(e) if e.current_context().is_db_not_found() => {
            let new_fingerprint_secret =
                utils::generate_id(consts::FINGERPRINT_SECRET_LENGTH, "fs");
            let new_config = storage::ConfigNew {
                key,
                config: new_fingerprint_secret.clone(),
            };

            state
                .store
                .insert_config(new_config)
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("unable to create new fingerprint secret for merchant")?;

            Ok(new_fingerprint_secret)
        }

        Err(e) => Err(e)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("error fetching merchant fingerprint secret"),
    }
}

async fn duplicate_check_insert_bin(
    bin: &str,
    state: &SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    data_kind: common_enums::BlocklistDataKind,
) -> RouterResult<storage::Blocklist> {
    let blocklist_entry_result = state
        .store
        .find_blocklist_entry_by_merchant_id_fingerprint_id(merchant_id, bin)
        .await;

    match blocklist_entry_result {
        Ok(_) => {
            return Err(errors::ApiErrorResponse::PreconditionFailed {
                message: "provided bin is already blocked".to_string(),
            }
            .into());
        }

        Err(e) if e.current_context().is_db_not_found() => {}

        err @ Err(_) => {
            return err
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("unable to fetch blocklist entry");
        }
    }

    state
        .store
        .insert_blocklist_entry(storage::BlocklistNew {
            merchant_id: merchant_id.to_owned(),
            fingerprint_id: bin.to_string(),
            data_kind,
            metadata: None,
            created_at: common_utils::date_time::now(),
        })
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("error inserting pm blocklist item")
}

async fn delete_card_bin_blocklist_entry(
    state: &SessionState,
    bin: &str,
    merchant_id: &common_utils::id_type::MerchantId,
) -> RouterResult<storage::Blocklist> {
    state
        .store
        .delete_blocklist_entry_by_merchant_id_fingerprint_id(merchant_id, bin)
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "could not find a blocklist entry for the given bin".to_string(),
        })
}

pub async fn validate_data_for_blocklist<F>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: &mut PaymentData<F>,
) -> CustomResult<bool, errors::ApiErrorResponse>
where
    F: Send + Clone,
{
    let db = &state.store;
    let merchant_id = merchant_context.get_merchant_account().get_id();
    let merchant_fingerprint_secret = get_merchant_fingerprint_secret(state, merchant_id).await?;

    // Hashed Fingerprint to check whether or not this payment should be blocked.
    let card_number_fingerprint = if let Some(domain::PaymentMethodData::Card(card)) =
        payment_data.payment_method_data.as_ref()
    {
        generate_fingerprint(
            state,
            StrongSecret::new(card.card_number.get_card_no()),
            StrongSecret::new(merchant_fingerprint_secret.clone()),
            api_models::enums::LockerChoice::HyperswitchCardVault,
        )
        .await
        .attach_printable("error in pm fingerprint creation")
        .map_or_else(
            |error| {
                logger::error!(?error);
                None
            },
            Some,
        )
        .map(|payload| payload.card_fingerprint)
    } else {
        None
    };

    // Hashed Cardbin to check whether or not this payment should be blocked.
    let card_bin_fingerprint = payment_data
        .payment_method_data
        .as_ref()
        .and_then(|pm_data| match pm_data {
            domain::PaymentMethodData::Card(card) => Some(card.card_number.get_card_isin()),
            _ => None,
        });

    // Hashed Extended Cardbin to check whether or not this payment should be blocked.
    let extended_card_bin_fingerprint =
        payment_data
            .payment_method_data
            .as_ref()
            .and_then(|pm_data| match pm_data {
                domain::PaymentMethodData::Card(card) => {
                    Some(card.card_number.get_extended_card_bin())
                }
                _ => None,
            });

    //validating the payment method.
    let mut blocklist_futures = Vec::new();
    if let Some(card_number_fingerprint) = card_number_fingerprint.as_ref() {
        blocklist_futures.push(db.find_blocklist_entry_by_merchant_id_fingerprint_id(
            merchant_id,
            card_number_fingerprint,
        ));
    }

    if let Some(card_bin_fingerprint) = card_bin_fingerprint.as_ref() {
        blocklist_futures.push(
            db.find_blocklist_entry_by_merchant_id_fingerprint_id(
                merchant_id,
                card_bin_fingerprint,
            ),
        );
    }

    if let Some(extended_card_bin_fingerprint) = extended_card_bin_fingerprint.as_ref() {
        blocklist_futures.push(db.find_blocklist_entry_by_merchant_id_fingerprint_id(
            merchant_id,
            extended_card_bin_fingerprint,
        ));
    }

    let blocklist_lookups = futures::future::join_all(blocklist_futures).await;

    let mut should_payment_be_blocked = false;
    for lookup in blocklist_lookups {
        match lookup {
            Ok(_) => {
                should_payment_be_blocked = true;
            }
            Err(e) => {
                logger::error!(blocklist_db_error=?e, "failed db operations for blocklist");
            }
        }
    }
    if should_payment_be_blocked {
        // Update db for attempt and intent status.
        db.update_payment_intent(
            &state.into(),
            payment_data.payment_intent.clone(),
            storage::PaymentIntentUpdate::RejectUpdate {
                status: common_enums::IntentStatus::Failed,
                merchant_decision: Some(MerchantDecision::Rejected.to_string()),
                updated_by: merchant_context
                    .get_merchant_account()
                    .storage_scheme
                    .to_string(),
            },
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable(
            "Failed to update status in Payment Intent to failed due to it being blocklisted",
        )?;

        // If payment is blocked not showing connector details
        let attempt_update = storage::PaymentAttemptUpdate::BlocklistUpdate {
            status: common_enums::AttemptStatus::Failure,
            error_code: Some(Some("HE-03".to_string())),
            error_message: Some(Some("This payment method is blocked".to_string())),
            updated_by: merchant_context
                .get_merchant_account()
                .storage_scheme
                .to_string(),
        };
        db.update_payment_attempt_with_attempt_id(
            payment_data.payment_attempt.clone(),
            attempt_update,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable(
            "Failed to update status in Payment Attempt to failed, due to it being blocklisted",
        )?;

        Err(errors::ApiErrorResponse::PaymentBlockedError {
            code: 200,
            message: "This payment method is blocked".to_string(),
            status: "Failed".to_string(),
            reason: "Blocked".to_string(),
        }
        .into())
    } else {
        payment_data.payment_attempt.fingerprint_id = generate_payment_fingerprint(
            state,
            payment_data.payment_attempt.merchant_id.clone(),
            payment_data.payment_method_data.clone(),
        )
        .await?;
        Ok(false)
    }
}

pub async fn generate_payment_fingerprint(
    state: &SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    payment_method_data: Option<domain::PaymentMethodData>,
) -> CustomResult<Option<String>, errors::ApiErrorResponse> {
    let merchant_fingerprint_secret = get_merchant_fingerprint_secret(state, &merchant_id).await?;

    Ok(
        if let Some(domain::PaymentMethodData::Card(card)) = payment_method_data.as_ref() {
            generate_fingerprint(
                state,
                StrongSecret::new(card.card_number.get_card_no()),
                StrongSecret::new(merchant_fingerprint_secret),
                api_models::enums::LockerChoice::HyperswitchCardVault,
            )
            .await
            .attach_printable("error in pm fingerprint creation")
            .map_or_else(
                |error| {
                    logger::error!(?error);
                    None
                },
                Some,
            )
            .map(|payload| payload.card_fingerprint)
        } else {
            logger::error!("failed to retrieve card fingerprint");
            None
        },
    )
}


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

pub struct HyperswitchVaultCustomerCreateRequest


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

pub fn new() -> &'static Self


// Function: insert
// File: crates/diesel_models/src/query/payment_intent.rs
// Module: diesel_models

pub fn insert(self, conn: &PgPooledConn) -> StorageResult<PaymentIntent>


// Function: new
// File: crates/drainer/src/settings.rs
// Module: drainer

// Documentation: Panics if secret or encryption management client cannot be initiated

pub fn new(conf: Settings<SecuredSecret>) -> Self


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

impl Responder


// Struct: SingleChargeData
// File: crates/hyperswitch_connectors/src/connectors/boku/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SingleChargeData


// Function: get_count_by_merchant_id_status
// File: crates/diesel_models/src/query/payment_method.rs
// Module: diesel_models

pub fn get_count_by_merchant_id_status(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        status: common_enums::PaymentMethodStatus,
    ) -> StorageResult<i64>


// Struct: ProFeatureRequest
// File: crates/router/src/services/email/types.rs
// Module: router
// Implementations: 1
// Traits: EmailData

pub struct ProFeatureRequest


    }]"#)]
    pub order_details: Option<Vec<Secret<OrderDetailsWithAmount>>>,

    /// The URL to redirect after the completion of the operation
    #[schema(value_type = String, example = "https://hyperswitch.io")]
    pub return_url: Option<common_utils::types::Url>,

    /// For non-card charges, you can use this value as the complete description that appears on your customers’ statements. Must contain at least one letter, maximum 22 characters.
    #[schema(value_type = Option<String>, max_length = 255, example = "Hyperswitch Router")]
    pub statement_descriptor: Option<common_utils::types::StatementDescriptor>,

    /// Allowed Payment Method Types for a given PaymentIntent
    #[schema(value_type = Option<Vec<PaymentMethodType>>)]
    pub allowed_payment_method_types: Option<Vec<common_enums::PaymentMethodType>>,

    /// Total number of authorizations happened in an incremental_authorization payment
    pub authorization_count: Option<i32>,

    /// Date time at which payment was updated
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub modified_at: Option<PrimitiveDateTime>,
}

// Serialize is implemented because, this will be serialized in the api events.
// Usually request types should not have serialize implemented.
//
/// Request for Payment Intent Confirm
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentsConfirmIntentRequest {
    /// The URL to which you want the user to be redirected after the completion of the payment operation
    /// If this url is not passed, the url configured in the business profile will be used
    #[schema(value_type = Option<String>, example = "https://hyperswitch.io")]
    pub return_url: Option<common_utils::types::Url>,

    /// The payment instrument data to be used for the payment
    pub payment_method_data: PaymentMethodDataRequest,

    /// The payment instrument data to be used for the payment in case of split payments
    pub split_payment_method_data: Option<Vec<SplitPaymentMethodDataRequest>>,

    /// The payment method type to be used for the payment. This should match with the `payment_method_data` provided
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// The payment method subtype to be used for the payment. This should match with the `payment_method_data` provided
    #[schema(value_type = PaymentMethodType, example = "apple_pay")]
    pub payment_method_subtype: api_enums::PaymentMethodType,

    /// The shipping address for the payment. This will override the shipping address provided in the create-intent request
    pub shipping: Option<Address>,

    /// This "CustomerAcceptance" object is passed during Payments-Confirm request, it enlists the type, time, and mode of acceptance properties related to an acceptance done by the customer. The customer_acceptance sub object is usually passed by the SDK or client.
    #[schema(value_type = Option<CustomerAcceptance>)]
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,

    /// Additional details required by 3DS 2.0
    #[schema(value_type = Option<BrowserInformation>)]
    pub browser_info: Option<common_utils::types::BrowserInformation>,

    /// The payment_method_id to be associated with the payment
    #[schema(value_type = Option<String>)]
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,

    #[schema(example = "187282ab-40ef-47a9-9206-5099ba31e432")]
    pub payment_token: Option<String>,

    /// Merchant connector details used to make payments.
    #[schema(value_type = Option<MerchantConnectorAuthDetails>)]
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,

    /// If true, returns stringified connector raw response body
    pub return_raw_connector_response: Option<bool>,
}

// Serialize is implemented because, this will be serialized in the api events.
// Usually request types should not have serialize implemented.
//
/// Request for Gift Card balance check
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentsGiftCardBalanceCheckRequest {
    pub gift_card_data: GiftCardData,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct ProxyPaymentsRequest {
    /// The URL to which you want the user to be redirected after the completion of the payment operation
    /// If this url is not passed, the url configured in the business profile will be used
    #[schema(value_type = Option<String>, example = "https://hyperswitch.io")]
    pub return_url: Option<common_utils::types::Url>,

    pub amount: AmountDetails,

    pub recurring_details: mandates::ProcessorPaymentToken,

    pub shipping: Option<Address>,

    /// Additional details required by 3DS 2.0
    #[schema(value_type = Option<BrowserInformation>)]
    pub browser_info: Option<common_utils::types::BrowserInformation>,

    #[schema(example = "stripe")]
    pub connector: String,

    #[schema(value_type = String)]
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(deny_unknown_fields)]
#[cfg(feature = "v2")]
pub struct ExternalVaultProxyPaymentsRequest {
    /// The URL to which you want the user to be redirected after the completion of the payment operation
    /// If this url is not passed, the url configured in the business profile will be used
    #[schema(value_type = Option<String>, example = "https://hyperswitch.io")]
    pub return_url: Option<common_utils::types::Url>,

    /// The payment instrument data to be used for the payment
    pub payment_method_data: ProxyPaymentMethodDataRequest,

    /// The payment method type to be used for the payment. This should match with the `payment_method_data` provided
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// The payment method subtype to be used for the payment. This should match with the `payment_method_data` provided
    #[schema(value_type = PaymentMethodType, example = "apple_pay")]
    pub payment_method_subtype: api_enums::PaymentMethodType,

    /// The shipping address for the payment. This will override the shipping address provided in the create-intent request
    pub shipping: Option<Address>,

    /// This "CustomerAcceptance" object is passed during Payments-Confirm request, it enlists the type, time, and mode of acceptance properties related to an acceptance done by the customer. The customer_acceptance sub object is usually passed by the SDK or client.
    #[schema(value_type = Option<CustomerAcceptance>)]
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,

    /// Additional details required by 3DS 2.0
    #[schema(value_type = Option<BrowserInformation>)]
    pub browser_info: Option<common_utils::types::BrowserInformation>,

    /// The payment_method_id to be associated with the payment
    #[schema(value_type = Option<String>)]
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,

    #[schema(example = "187282ab-40ef-47a9-9206-5099ba31e432")]
    pub payment_token: Option<String>,

    /// Merchant connector details used to make payments.
    #[schema(value_type = Option<MerchantConnectorAuthDetails>)]
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,

    /// If true, returns stringified connector raw response body
    pub return_raw_connector_response: Option<bool>,
}

// This struct contains the union of fields in `PaymentsCreateIntentRequest` and
// `PaymentsConfirmIntentRequest`
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(deny_unknown_fields)]
#[cfg(feature = "v2")]
pub struct PaymentsRequest {
    /// The amount details for the payment
    pub amount_details: AmountDetails,

    /// Unique identifier for the payment. This ensures idempotency for multiple payments
    /// that have been done by a single merchant.
    #[schema(
        value_type = Option<String>,
        min_length = 30,
        max_length = 30,
        example = "pay_mbabizu24mvu3mela5njyhpit4"
    )]
    pub merchant_reference_id: Option<id_type::PaymentReferenceId>,

    /// The routing algorithm id to be used for the payment
    #[schema(value_type = Option<String>)]
    pub routing_algorithm_id: Option<id_type::RoutingId>,

    #[schema(value_type = Option<CaptureMethod>, example = "automatic")]
    pub capture_method: Option<api_enums::CaptureMethod>,

    #[schema(value_type = Option<AuthenticationType>, example = "no_three_ds", default = "no_three_ds")]
    pub authentication_type: Option<api_enums::AuthenticationType>,

    /// The billing details of the payment. This address will be used for invoicing.
    pub billing: Option<Address>,

    /// The shipping address for the payment
    pub shipping: Option<Address>,

    /// The identifier for the customer
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "12345_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: Option<id_type::GlobalCustomerId>,

    /// Set to `present` to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be `absent` when merchant's doing merchant initiated payments and customer is not present while doing the payment.
    #[schema(example = "present", value_type = Option<PresenceOfCustomerDuringPayment>)]
    pub customer_present: Option<common_enums::PresenceOfCustomerDuringPayment>,

    /// A description for the payment
    #[schema(example = "It's my first payment request", value_type = Option<String>)]
    pub description: Option<common_utils::types::Description>,

    /// The URL to which you want the user to be redirected after the completion of the payment operation
    #[schema(value_type = Option<String>, example = "https://hyperswitch.io")]
    pub return_url: Option<common_utils::types::Url>,

    #[schema(value_type = Option<FutureUsage>, example = "off_session")]
    pub setup_future_usage: Option<api_enums::FutureUsage>,

    /// Apply MIT exemption for a payment
    #[schema(value_type = Option<MitExemptionRequest>)]
    pub apply_mit_exemption: Option<common_enums::MitExemptionRequest>,

    /// For non-card charges, you can use this value as the complete description that appears on your customers’ statements. Must contain at least one letter, maximum 22 characters.
    #[schema(max_length = 22, example = "Hyperswitch Router", value_type = Option<String>)]
    pub statement_descriptor: Option<common_utils::types::StatementDescriptor>,

    /// Use this object to capture the details about the different products for which the payment is being made. The sum of amount across different products here should be equal to the overall payment amount
    #[schema(value_type = Option<Vec<OrderDetailsWithAmount>>, example = r#"[{
        "product_name": "Apple iPhone 16",
        "quantity": 1,
        "amount" : 69000
        "product_img_link" : "https://dummy-img-link.com"
    }]"#)]
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,

    /// Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent
    #[schema(value_type = Option<Vec<PaymentMethodType>>)]
    pub allowed_payment_method_types: Option<Vec<api_enums::PaymentMethodType>>,

    /// Metadata is useful for storing additional, unstructured information on an object.
    #[schema(value_type = Option<Object>, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)]
    pub metadata: Option<pii::SecretSerdeValue>,

    /// Some connectors like Apple pay, Airwallex and Noon might require some additional information, find specific details in the child attributes below.
    pub connector_metadata: Option<ConnectorMetadata>,

    /// Additional data that might be required by hyperswitch based on the requested features by the merchants.
    pub feature_metadata: Option<FeatureMetadata>,

    /// Whether to generate the payment link for this payment or not (if applicable)
    #[schema(value_type = Option<EnablePaymentLinkRequest>)]
    pub payment_link_enabled: Option<common_enums::EnablePaymentLinkRequest>,

    /// Configure a custom payment link for the particular payment
    #[schema(value_type = Option<PaymentLinkConfigRequest>)]
    pub payment_link_config: Option<admin::PaymentLinkConfigRequest>,

    ///Request an incremental authorization, i.e., increase the authorized amount on a confirmed payment before you capture it.
    #[schema(value_type = Option<RequestIncrementalAuthorization>)]
    pub request_incremental_authorization: Option<common_enums::RequestIncrementalAuthorization>,

    ///Will be used to expire client secret after certain amount of time to be supplied in seconds, if not sent it will be taken from profile config
    ///(900) for 15 mins
    #[schema(example = 900)]
    pub session_expiry: Option<u32>,

    /// Additional data related to some frm(Fraud Risk Management) connectors
    #[schema(value_type = Option<Object>, example = r#"{ "coverage_request" : "fraud", "fulfillment_method" : "delivery" }"#)]
    pub frm_metadata: Option<pii::SecretSerdeValue>,

    /// Whether to perform external authentication (if applicable)
    #[schema(value_type = Option<External3dsAuthenticationRequest>)]
    pub request_external_three_ds_authentication:
        Option<common_enums::External3dsAuthenticationRequest>,

    /// The payment instrument data to be used for the payment
    pub payment_method_data: PaymentMethodDataRequest,

    /// The payment method type to be used for the payment. This should match with the `payment_method_data` provided
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// The payment method subtype to be used for the payment. This should match with the `payment_method_data` provided
    #[schema(value_type = PaymentMethodType, example = "apple_pay")]
    pub payment_method_subtype: api_enums::PaymentMethodType,

    /// This "CustomerAcceptance" object is passed during Payments-Confirm request, it enlists the type, time, and mode of acceptance properties related to an acceptance done by the customer. The customer_acceptance sub object is usually passed by the SDK or client.
    #[schema(value_type = Option<CustomerAcceptance>)]
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,

    /// Additional details required by 3DS 2.0
    #[schema(value_type = Option<BrowserInformation>)]
    pub browser_info: Option<common_utils::types::BrowserInformation>,

    /// The payment_method_id to be associated with the payment
    #[schema(value_type = Option<String>)]
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,

    /// Indicates if 3ds challenge is forced
    pub force_3ds_challenge: Option<bool>,

    /// Indicates if the redirection has to open in the iframe
    pub is_iframe_redirection_enabled: Option<bool>,

    /// Merchant connector details used to make payments.
    #[schema(value_type = Option<MerchantConnectorAuthDetails>)]
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,

    /// Stringified connector raw response body. Only returned if `return_raw_connector_response` is true
    pub return_raw_connector_response: Option<bool>,

    /// Allow partial authorization for this payment
    #[schema(value_type = Option<bool>, default = false)]
    pub enable_partial_authorization: Option<primitive_wrappers::EnablePartialAuthorizationBool>,
}

#[cfg(feature = "v2")]
impl From<&PaymentsRequest> for PaymentsCreateIntentRequest {
    fn from(request: &PaymentsRequest) -> Self {
        Self {
            amount_details: request.amount_details.clone(),
            merchant_reference_id: request.merchant_reference_id.clone(),
            routing_algorithm_id: request.routing_algorithm_id.clone(),
            capture_method: request.capture_method,
            authentication_type: request.authentication_type,
            billing: request.billing.clone(),
            shipping: request.shipping.clone(),
            customer_id: request.customer_id.clone(),
            customer_present: request.customer_present,
            description: request.description.clone(),
            return_url: request.return_url.clone(),
            setup_future_usage: request.setup_future_usage,
            apply_mit_exemption: request.apply_mit_exemption,
            statement_descriptor: request.statement_descriptor.clone(),
            order_details: request.order_details.clone(),
            allowed_payment_method_types: request.allowed_payment_method_types.clone(),
            metadata: request.metadata.clone(),
            connector_metadata: request.connector_metadata.clone(),
            feature_metadata: request.feature_metadata.clone(),
            payment_link_enabled: request.payment_link_enabled,
            payment_link_config: request.payment_link_config.clone(),
            request_incremental_authorization: request.request_incremental_authorization,
            session_expiry: request.session_expiry,
            frm_metadata: request.frm_metadata.clone(),
            request_external_three_ds_authentication: request
                .request_external_three_ds_authentication,
            force_3ds_challenge: request.force_3ds_challenge,
            merchant_connector_details: request.merchant_connector_details.clone(),
            enable_partial_authorization: request.enable_partial_authorization,
        }
    }
}

#[cfg(feature = "v2")]
impl From<&PaymentsRequest> for PaymentsConfirmIntentRequest {
    fn from(request: &PaymentsRequest) -> Self {
        Self {
            return_url: request.return_url.clone(),
            payment_method_data: request.payment_method_data.clone(),
            payment_method_type: request.payment_method_type,
            payment_method_subtype: request.payment_method_subtype,
            shipping: request.shipping.clone(),
            customer_acceptance: request.customer_acceptance.clone(),
            browser_info: request.browser_info.clone(),
            payment_method_id: request.payment_method_id.clone(),
            payment_token: None,
            merchant_connector_details: request.merchant_connector_details.clone(),
            return_raw_connector_response: request.return_raw_connector_response,
            split_payment_method_data: None,
        }
    }
}

// Serialize is implemented because, this will be serialized in the api events.
// Usually request types should not have serialize implemented.
//
/// Request body for Payment Status
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentsRetrieveRequest {
    /// A boolean used to indicate if the payment status should be fetched from the connector
    /// If this is set to true, the status will be fetched from the connector
    #[serde(default)]
    pub force_sync: bool,
    /// A boolean used to indicate if all the attempts needs to be fetched for the intent.
    /// If this is set to true, attempts list will be available in the response.
    #[serde(default)]
    pub expand_attempts: bool,
    /// These are the query params that are sent in case of redirect response.
    /// These can be ingested by the connector to take necessary actions.
    pub param: Option<String>,
    /// If true, returns stringified connector raw response body
    pub return_raw_connector_response: Option<bool>,
    /// Merchant connector details used to make payments.
    #[schema(value_type = Option<MerchantConnectorAuthDetails>)]
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
/// Request for Payment Status
pub struct PaymentsStatusRequest {
    /// A boolean used to indicate if the payment status should be fetched from the connector
    /// If this is set to true, the status will be fetched from the connector
    #[serde(default)]
    pub force_sync: bool,
    /// A boolean used to indicate if all the attempts needs to be fetched for the intent.
    /// If this is set to true, attempts list will be available in the response.
    #[serde(default)]
    pub expand_attempts: bool,
    /// These are the query params that are sent in case of redirect response.
    /// These can be ingested by the connector to take necessary actions.
    pub param: Option<String>,
    /// If true, returns stringified connector raw response body
    pub return_raw_connector_response: Option<bool>,
}

/// Error details for the payment
#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, ToSchema)]
pub struct ErrorDetails {
    /// The error code
    pub code: String,
    /// The error message
    pub message: String,
    /// The unified error code across all connectors.
    /// This can be relied upon for taking decisions based on the error.
    pub unified_code: Option<String>,
    /// The unified error message across all connectors.
    /// If there is a translation available, this will have the translated message
    pub unified_message: Option<String>,
    /// This field can be returned for both approved and refused Mastercard payments.
    /// This code provides additional information about the type of transaction or the reason why the payment failed.
    /// If the payment failed, the network advice code gives guidance on if and when you can retry the payment.
    pub network_advice_code: Option<String>,
    /// For card errors resulting from a card issuer decline, a brand specific 2, 3, or 4 digit code which indicates the reason the authorization failed.
    pub network_decline_code: Option<String>,
    /// A string indicating how to proceed with an network error if payment gateway provide one. This is used to understand the network error code better.
    pub network_error_message: Option<String>,
}

/// Token information that can be used to initiate transactions by the merchant.
#[cfg(feature = "v2")]
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct ConnectorTokenDetails {
    /// A token that can be used to make payments directly with the connector.
    #[schema(example = "pm_9UhMqBMEOooRIvJFFdeW")]
    pub token: String,

    /// The reference id sent to the connector when creating the token
    pub connector_token_request_reference_id: Option<String>,
}

/// Response for Payment Intent Confirm
/// Few fields should be expandable, we need not return these in the normal response
/// But when explicitly requested for expanded objects, these can be returned
/// For example
/// shipping, billing, customer, payment_method
#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct PaymentsResponse {
    /// Unique identifier for the payment. This ensures idempotency for multiple payments
    /// that have been done by a single merchant.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "12345_pay_01926c58bc6e77c09e809964e72af8c8",
        value_type = String,
    )]
    pub id: id_type::GlobalPaymentId,

    #[schema(value_type = IntentStatus, example = "succeeded")]
    pub status: api_enums::IntentStatus,

    /// Amount related information for this payment and attempt
    pub amount: PaymentAmountDetailsResponse,

    /// The identifier for the customer
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "12345_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: Option<id_type::GlobalCustomerId>,

    /// The connector used for the payment
    #[schema(example = "stripe")]
    pub connector: Option<String>,

    /// Time when the payment was created
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created: PrimitiveDateTime,

    /// Time when the payment was last modified
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,

    /// The payment method information provided for making a payment
    #[schema(value_type = Option<PaymentMethodDataResponseWithBilling>)]
    #[serde(serialize_with = "serialize_payment_method_data_response")]
    pub payment_method_data: Option<PaymentMethodDataResponseWithBilling>,

    /// The payment method type for this payment attempt
    #[schema(value_type = Option<PaymentMethod>, example = "wallet")]
    pub payment_method_type: Option<api_enums::PaymentMethod>,

    #[schema(value_type = Option<PaymentMethodType>, example = "apple_pay")]
    pub payment_method_subtype: Option<api_enums::PaymentMethodType>,

    /// A unique identifier for a payment provided by the connector
    #[schema(value_type = Option<String>, example = "993672945374576J")]
    pub connector_transaction_id: Option<String>,

    /// reference(Identifier) to the payment at connector side
    #[schema(value_type = Option<String>, example = "993672945374576J")]
    pub connector_reference_id: Option<String>,

    /// Identifier of the connector ( merchant connector account ) which was chosen to make the payment
    #[schema(value_type = Option<String>)]
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,

    /// The browser information used for this payment
    #[schema(value_type = Option<BrowserInformation>)]
    pub browser_info: Option<common_utils::types::BrowserInformation>,

    /// Error details for the payment if any
    pub error: Option<ErrorDetails>,

    /// The shipping address associated with the payment intent
    pub shipping: Option<Address>,

    /// The billing address associated with the payment intent
    pub billing: Option<Address>,

    /// List of payment attempts associated with payment intent
    pub attempts: Option<Vec<PaymentAttemptResponse>>,

    /// Connector token information that can be used to make payments directly by the merchant.
    pub connector_token_details: Option<ConnectorTokenDetails>,

    /// The payment_method_id associated with the payment
    #[schema(value_type = Option<String>)]
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,

    /// Additional information required for redirection
    pub next_action: Option<NextActionData>,

    /// The url to which user must be redirected to after completion of the purchase
    #[schema(value_type = Option<String>)]
    pub return_url: Option<common_utils::types::Url>,

    /// The authentication type that was requested for this order
    #[schema(value_type = Option<AuthenticationType>, example = "no_three_ds", default = "no_three_ds")]
    pub authentication_type: Option<api_enums::AuthenticationType>,

    /// The authentication type that was appliced for this order
    /// This depeneds on the 3DS rules configured, If not a default authentication type will be applied
    #[schema(value_type = Option<AuthenticationType>, example = "no_three_ds", default = "no_three_ds")]
    pub authentication_type_applied: Option<api_enums::AuthenticationType>,

    /// Indicates if the redirection has to open in the iframe
    pub is_iframe_redirection_enabled: Option<bool>,

    /// Unique identifier for the payment. This ensures idempotency for multiple payments
    /// that have been done by a single merchant.
    #[schema(
        value_type = Option<String>,
        min_length = 30,
        max_length = 30,
        example = "pay_mbabizu24mvu3mela5njyhpit4"
    )]
    pub merchant_reference_id: Option<id_type::PaymentReferenceId>,

    /// Stringified connector raw response body. Only returned if `return_raw_connector_response` is true
    #[schema(value_type = Option<String>)]
    pub raw_connector_response: Option<Secret<String>>,

    /// Additional data that might be required by hyperswitch based on the additional features.
    pub feature_metadata: Option<FeatureMetadata>,

    /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
    #[schema(value_type = Option<Object>, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)]
    pub metadata: Option<pii::SecretSerdeValue>,
}

#[cfg(feature = "v2")]
impl PaymentAttemptListResponse {
    pub fn find_attempt_in_attempts_list_using_connector_transaction_id(
        &self,
        connector_transaction_id: &common_utils::types::ConnectorTransactionId,
    ) -> Option<PaymentAttemptResponse> {
        self.payment_attempt_list.iter().find_map(|attempt| {
            attempt
                .connector_payment_id
                .as_ref()
                .filter(|txn_id| *txn_id == connector_transaction_id)
                .map(|_| attempt.clone())
        })
    }
    pub fn find_attempt_in_attempts_list_using_charge_id(
        &self,
        charge_id: String,
    ) -> Option<PaymentAttemptResponse> {
        self.payment_attempt_list.iter().find_map(|attempt| {
            attempt.feature_metadata.as_ref().and_then(|metadata| {
                metadata.revenue_recovery.as_ref().and_then(|recovery| {
                    recovery
                        .charge_id
                        .as_ref()
                        .filter(|id| **id == charge_id)
                        .map(|_| attempt.clone())
                })
            })
        })
    }
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg(feature = "v2")]
pub struct PaymentStartRedirectionRequest {
    /// Global Payment ID
    pub id: id_type::GlobalPaymentId,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg(feature = "v2")]
pub struct PaymentStartRedirectionParams {
    /// The identifier for the Merchant Account.
    pub publishable_key: String,
    /// The identifier for business profile
    pub profile_id: id_type::ProfileId,
}

/// Details of external authentication
#[derive(Setter, Clone, Default, Debug, PartialEq, serde::Serialize, ToSchema)]
pub struct ExternalAuthenticationDetailsResponse {
    /// Authentication Type - Challenge / Frictionless
    #[schema(value_type = Option<DecoupledAuthenticationType>)]
    pub authentication_flow: Option<enums::DecoupledAuthenticationType>,
    /// Electronic Commerce Indicator (eci)
    pub electronic_commerce_indicator: Option<String>,
    /// Authentication Status
    #[schema(value_type = AuthenticationStatus)]
    pub status: enums::AuthenticationStatus,
    /// DS Transaction ID
    pub ds_transaction_id: Option<String>,
    /// Message Version
    pub version: Option<String>,
    /// Error Code
    pub error_code: Option<String>,
    /// Error Message
    pub error_message: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)]
#[serde(deny_unknown_fields)]
pub struct PaymentListConstraints {
    /// The identifier for customer
    #[schema(
        max_length = 64,
        min_length = 1,
        example = "cus_y3oqhf46pyzuxjbcn2giaqnb44",
        value_type = Option<String>,
    )]
    pub customer_id: Option<id_type::CustomerId>,

    /// A cursor for use in pagination, fetch the next list after some object
    #[schema(example = "pay_fafa124123", value_type = Option<String>)]
    pub starting_after: Option<id_type::PaymentId>,

    /// A cursor for use in pagination, fetch the previous list before some object
    #[schema(example = "pay_fafa124123", value_type = Option<String>)]
    pub ending_before: Option<id_type::PaymentId>,

    /// limit on the number of objects to return
    #[schema(default = 10, maximum = 100)]
    #[serde(default = "default_payments_list_limit")]
    pub limit: u32,

    /// The time at which payment is created
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<PrimitiveDateTime>,

    /// Time less than the payment created time
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.lt"
    )]
    pub created_lt: Option<PrimitiveDateTime>,

    /// Time greater than the payment created time
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.gt"
    )]
    pub created_gt: Option<PrimitiveDateTime>,

    /// Time less than or equals to the payment created time
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.lte"
    )]
    pub created_lte: Option<PrimitiveDateTime>,

    /// Time greater than or equals to the payment created time
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    #[serde(rename = "created.gte")]
    pub created_gte: Option<PrimitiveDateTime>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, utoipa::IntoParams)]
#[serde(deny_unknown_fields)]
pub struct PaymentListConstraints {
    /// The identifier for payment
    #[param(example = "pay_fafa124123", value_type = Option<String>)]
    pub payment_id: Option<id_type::GlobalPaymentId>,

    /// The identifier for business profile
    #[param(example = "pay_fafa124123", value_type = Option<String>)]
    pub profile_id: Option<id_type::ProfileId>,

    /// The identifier for customer
    #[param(
        max_length = 64,
        min_length = 1,
        example = "cus_y3oqhf46pyzuxjbcn2giaqnb44",
        value_type = Option<String>,
    )]
    pub customer_id: Option<id_type::GlobalCustomerId>,

    /// A cursor for use in pagination, fetch the next list after some object
    #[param(example = "pay_fafa124123", value_type = Option<String>)]
    pub starting_after: Option<id_type::GlobalPaymentId>,

    /// A cursor for use in pagination, fetch the previous list before some object
    #[param(example = "pay_fafa124123", value_type = Option<String>)]
    pub ending_before: Option<id_type::GlobalPaymentId>,

    /// limit on the number of objects to return
    #[param(default = 10, maximum = 100)]
    #[serde(default = "default_payments_list_limit")]
    pub limit: u32,

    /// The starting point within a list of objects
    pub offset: Option<u32>,

    /// The time at which payment is created
    #[param(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<PrimitiveDateTime>,

    /// Time less than the payment created time
    #[param(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.lt"
    )]
    pub created_lt: Option<PrimitiveDateTime>,

    /// Time greater than the payment created time
    #[param(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,


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

impl api::PaymentVoid for for Xendit


// Function: profile_list
// File: crates/openapi/src/routes/profile.rs
// Module: openapi

pub fn profile_list()


// File: crates/external_services/src/grpc_client/dynamic_routing.rs
// Module: external_services
// Public functions: 1
// Public structs: 1

/// Module for Contract based routing
pub mod contract_routing_client;

use std::fmt::Debug;

use common_utils::errors::CustomResult;
use router_env::logger;
use serde;
/// Elimination Routing Client Interface Implementation
pub mod elimination_based_client;
/// Success Routing Client Interface Implementation
pub mod success_rate_client;

pub use contract_routing_client::ContractScoreCalculatorClient;
pub use elimination_based_client::EliminationAnalyserClient;
pub use success_rate_client::SuccessRateCalculatorClient;

use super::Client;
/// Result type for Dynamic Routing
pub type DynamicRoutingResult<T> = CustomResult<T, DynamicRoutingError>;

/// Dynamic Routing Errors
#[derive(Debug, Clone, thiserror::Error)]
pub enum DynamicRoutingError {
    /// The required input is missing
    #[error("Missing Required Field : {field} for building the Dynamic Routing Request")]
    MissingRequiredField {
        /// The required field name
        field: String,
    },
    /// Error from Dynamic Routing Server while performing success_rate analysis
    #[error("Error from Dynamic Routing Server while perfrming success_rate analysis : {0}")]
    SuccessRateBasedRoutingFailure(String),

    /// Generic Error from Dynamic Routing Server while performing contract based routing
    #[error("Error from Dynamic Routing Server while performing contract based routing: {0}")]
    ContractBasedRoutingFailure(String),
    /// Generic Error from Dynamic Routing Server while performing contract based routing
    #[error("Contract not found in the dynamic routing service")]
    ContractNotFound,
    /// Error from Dynamic Routing Server while perfrming elimination
    #[error("Error from Dynamic Routing Server while perfrming elimination : {0}")]
    EliminationRateRoutingFailure(String),
}

/// Type that consists of all the services provided by the client
#[derive(Debug, Clone)]
pub struct RoutingStrategy {
    /// success rate service for Dynamic Routing
    pub success_rate_client: SuccessRateCalculatorClient<Client>,
    /// contract based routing service for Dynamic Routing
    pub contract_based_client: ContractScoreCalculatorClient<Client>,
    /// elimination service for Dynamic Routing
    pub elimination_based_client: EliminationAnalyserClient<Client>,
}

/// Contains the Dynamic Routing Client Config
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, Default)]
#[serde(untagged)]
pub enum DynamicRoutingClientConfig {
    /// If the dynamic routing client config has been enabled
    Enabled {
        /// The host for the client
        host: String,
        /// The port of the client
        port: u16,
        /// Service name
        service: String,
    },
    #[default]
    /// If the dynamic routing client config has been disabled
    Disabled,
}

impl DynamicRoutingClientConfig {
    /// establish connection with the server
    pub fn get_dynamic_routing_connection(
        self,
        client: Client,
    ) -> Result<Option<RoutingStrategy>, Box<dyn std::error::Error>> {
        match self {
            Self::Enabled { host, port, .. } => {
                let uri = format!("http://{host}:{port}").parse::<tonic::transport::Uri>()?;
                logger::info!("Connection established with dynamic routing gRPC Server");

                let (success_rate_client, contract_based_client, elimination_based_client) = (
                    SuccessRateCalculatorClient::with_origin(client.clone(), uri.clone()),
                    ContractScoreCalculatorClient::with_origin(client.clone(), uri.clone()),
                    EliminationAnalyserClient::with_origin(client, uri),
                );

                Ok(Some(RoutingStrategy {
                    success_rate_client,
                    contract_based_client,
                    elimination_based_client,
                }))
            }
            Self::Disabled => Ok(None),
        }
    }
}


// Struct: OrderFulfilmentResponse
// File: crates/hyperswitch_connectors/src/connectors/riskified/transformers/api.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct OrderFulfilmentResponse


// Implementation: impl std::error::Error for for ApiErrorResponse
// File: crates/api_models/src/errors/types.rs
// Module: api_models
// Methods: 0 total (0 public)

impl std::error::Error for for ApiErrorResponse


// Struct: PaymentMetricsAccumulator
// File: crates/analytics/src/payments/accumulator.rs
// Module: analytics
// Implementations: 1

pub struct PaymentMetricsAccumulator


// File: crates/euclid/src/frontend/dir.rs
// Module: euclid
// Public functions: 8
// Public structs: 5

//! Domain Intermediate Representation
pub mod enums;
pub mod lowering;
pub mod transformers;

use strum::IntoEnumIterator;

// use common_utils::types::MinorUnit;
use crate::{enums as euclid_enums, frontend::ast, types};

#[macro_export]
macro_rules! dirval {
    (Connector = $name:ident) => {
        $crate::frontend::dir::DirValue::Connector(Box::new(
            $crate::frontend::ast::ConnectorChoice {
                connector: $crate::enums::RoutableConnectors::$name,
            },
        ))
    };

    ($key:ident = $val:ident) => {{
        pub use $crate::frontend::dir::enums::*;

        $crate::frontend::dir::DirValue::$key($key::$val)
    }};

    ($key:ident = $num:literal) => {{
        $crate::frontend::dir::DirValue::$key($crate::types::NumValue {
            number: common_utils::types::MinorUnit::new($num),
            refinement: None,
        })
    }};

    ($key:ident s= $str:literal) => {{
        $crate::frontend::dir::DirValue::$key($crate::types::StrValue {
            value: $str.to_string(),
        })
    }};

    ($key:literal = $str:literal) => {{
        $crate::frontend::dir::DirValue::MetaData($crate::types::MetadataValue {
            key: $key.to_string(),
            value: $str.to_string(),
        })
    }};
}

#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize)]
pub struct DirKey {
    pub kind: DirKeyKind,
    pub value: Option<String>,
}

impl DirKey {
    pub fn new(kind: DirKeyKind, value: Option<String>) -> Self {
        Self { kind, value }
    }
}

#[derive(
    Debug,
    Clone,
    Hash,
    PartialEq,
    Eq,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::VariantNames,
    strum::EnumString,
    strum::EnumMessage,
    strum::EnumProperty,
)]
pub enum DirKeyKind {
    #[strum(
        serialize = "payment_method",
        detailed_message = "Different modes of payment - eg. cards, wallets, banks",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "payment_method")]
    PaymentMethod,
    #[strum(
        serialize = "card_bin",
        detailed_message = "First 4 to 6 digits of a payment card number",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "card_bin")]
    CardBin,
    #[strum(
        serialize = "card_type",
        detailed_message = "Type of the payment card - eg. credit, debit",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "card_type")]
    CardType,
    #[strum(
        serialize = "card_network",
        detailed_message = "Network that facilitates payment card transactions",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "card_network")]
    CardNetwork,
    #[strum(
        serialize = "pay_later",
        detailed_message = "Supported types of Pay Later payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "pay_later")]
    PayLaterType,
    #[strum(
        serialize = "gift_card",
        detailed_message = "Supported types of Gift Card payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "gift_card")]
    GiftCardType,
    #[strum(
        serialize = "mandate_acceptance_type",
        detailed_message = "Mode of customer acceptance for mandates - online and offline",
        props(Category = "Payments")
    )]
    #[serde(rename = "mandate_acceptance_type")]
    MandateAcceptanceType,
    #[strum(
        serialize = "mandate_type",
        detailed_message = "Type of mandate acceptance - single use and multi use",
        props(Category = "Payments")
    )]
    #[serde(rename = "mandate_type")]
    MandateType,
    #[strum(
        serialize = "payment_type",
        detailed_message = "Indicates if a payment is mandate or non-mandate",
        props(Category = "Payments")
    )]
    #[serde(rename = "payment_type")]
    PaymentType,
    #[strum(
        serialize = "wallet",
        detailed_message = "Supported types of Wallet payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "wallet")]
    WalletType,
    #[strum(
        serialize = "upi",
        detailed_message = "Supported types of UPI payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "upi")]
    UpiType,
    #[strum(
        serialize = "voucher",
        detailed_message = "Supported types of Voucher payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "voucher")]
    VoucherType,
    #[strum(
        serialize = "bank_transfer",
        detailed_message = "Supported types of Bank Transfer payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "bank_transfer")]
    BankTransferType,
    #[strum(
        serialize = "bank_redirect",
        detailed_message = "Supported types of Bank Redirect payment methods",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "bank_redirect")]
    BankRedirectType,
    #[strum(
        serialize = "bank_debit",
        detailed_message = "Supported types of Bank Debit payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "bank_debit")]
    BankDebitType,
    #[strum(
        serialize = "crypto",
        detailed_message = "Supported types of Crypto payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "crypto")]
    CryptoType,
    #[strum(
        serialize = "metadata",
        detailed_message = "Aribitrary Key and value pair",
        props(Category = "Metadata")
    )]
    #[serde(rename = "metadata")]
    MetaData,
    #[strum(
        serialize = "reward",
        detailed_message = "Supported types of Reward payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "reward")]
    RewardType,
    #[strum(
        serialize = "amount",
        detailed_message = "Value of the transaction",
        props(Category = "Payments")
    )]
    #[serde(rename = "amount")]
    PaymentAmount,
    #[strum(
        serialize = "currency",
        detailed_message = "Currency used for the payment",
        props(Category = "Payments")
    )]
    #[serde(rename = "currency")]
    PaymentCurrency,
    #[strum(
        serialize = "authentication_type",
        detailed_message = "Type of authentication for the payment",
        props(Category = "Payments")
    )]
    #[serde(rename = "authentication_type")]
    AuthenticationType,
    #[strum(
        serialize = "capture_method",
        detailed_message = "Modes of capturing a payment",
        props(Category = "Payments")
    )]
    #[serde(rename = "capture_method")]
    CaptureMethod,
    #[strum(
        serialize = "country",
        serialize = "business_country",
        detailed_message = "Country of the business unit",
        props(Category = "Merchant")
    )]
    #[serde(rename = "business_country", alias = "country")]
    BusinessCountry,
    #[strum(
        serialize = "billing_country",
        detailed_message = "Country of the billing address of the customer",
        props(Category = "Customer")
    )]
    #[serde(rename = "billing_country")]
    BillingCountry,
    #[serde(skip_deserializing, rename = "connector")]
    Connector,
    #[strum(
        serialize = "business_label",
        detailed_message = "Identifier for business unit",
        props(Category = "Merchant")
    )]
    #[serde(rename = "business_label")]
    BusinessLabel,
    #[strum(
        serialize = "setup_future_usage",
        detailed_message = "Identifier for recurring payments",
        props(Category = "Payments")
    )]
    #[serde(rename = "setup_future_usage")]
    SetupFutureUsage,
    #[strum(
        serialize = "card_redirect",
        detailed_message = "Supported types of Card Redirect payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "card_redirect")]
    CardRedirectType,
    #[serde(rename = "real_time_payment")]
    #[strum(
        serialize = "real_time_payment",
        detailed_message = "Supported types of real time payment method",
        props(Category = "Payment Method Types")
    )]
    RealTimePaymentType,
    #[serde(rename = "open_banking")]
    #[strum(
        serialize = "open_banking",
        detailed_message = "Supported types of open banking payment method",
        props(Category = "Payment Method Types")
    )]
    OpenBankingType,
    #[serde(rename = "mobile_payment")]
    #[strum(
        serialize = "mobile_payment",
        detailed_message = "Supported types of mobile payment method",
        props(Category = "Payment Method Types")
    )]
    MobilePaymentType,
    #[strum(
        serialize = "issuer_name",
        detailed_message = "Name of the card issuing bank",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "issuer_name")]
    IssuerName,
    #[strum(
        serialize = "issuer_country",
        detailed_message = "Country of the card issuing bank",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "issuer_country")]
    IssuerCountry,
    #[strum(
        serialize = "customer_device_platform",
        detailed_message = "Platform of the customer's device (Web, Android, iOS)",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "customer_device_platform")]
    CustomerDevicePlatform,
    #[strum(
        serialize = "customer_device_type",
        detailed_message = "Type of the customer's device (Mobile, Tablet, Desktop, Gaming Console)",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "customer_device_type")]
    CustomerDeviceType,
    #[strum(
        serialize = "customer_device_display_size",
        detailed_message = "Display size of the customer's device (e.g., 500x600)",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "customer_device_display_size")]
    CustomerDeviceDisplaySize,
    #[strum(
        serialize = "acquirer_country",
        detailed_message = "Country of the acquiring bank",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "acquirer_country")]
    AcquirerCountry,
    #[strum(
        serialize = "acquirer_fraud_rate",
        detailed_message = "Fraud rate of the acquiring bank",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "acquirer_fraud_rate")]
    AcquirerFraudRate,
}

pub trait EuclidDirFilter: Sized
where
    Self: 'static,
{
    const ALLOWED: &'static [DirKeyKind];
    fn get_allowed_keys() -> &'static [DirKeyKind] {
        Self::ALLOWED
    }

    fn is_key_allowed(key: &DirKeyKind) -> bool {
        Self::ALLOWED.contains(key)
    }
}

impl DirKeyKind {
    pub fn get_type(&self) -> types::DataType {
        match self {
            Self::PaymentMethod => types::DataType::EnumVariant,
            Self::CardBin => types::DataType::StrValue,
            Self::CardType => types::DataType::EnumVariant,
            Self::CardNetwork => types::DataType::EnumVariant,
            Self::MetaData => types::DataType::MetadataValue,
            Self::MandateType => types::DataType::EnumVariant,
            Self::PaymentType => types::DataType::EnumVariant,
            Self::MandateAcceptanceType => types::DataType::EnumVariant,
            Self::PayLaterType => types::DataType::EnumVariant,
            Self::WalletType => types::DataType::EnumVariant,
            Self::UpiType => types::DataType::EnumVariant,
            Self::VoucherType => types::DataType::EnumVariant,
            Self::BankTransferType => types::DataType::EnumVariant,
            Self::GiftCardType => types::DataType::EnumVariant,
            Self::BankRedirectType => types::DataType::EnumVariant,
            Self::CryptoType => types::DataType::EnumVariant,
            Self::RewardType => types::DataType::EnumVariant,
            Self::PaymentAmount => types::DataType::Number,
            Self::PaymentCurrency => types::DataType::EnumVariant,
            Self::AuthenticationType => types::DataType::EnumVariant,
            Self::CaptureMethod => types::DataType::EnumVariant,
            Self::BusinessCountry => types::DataType::EnumVariant,
            Self::BillingCountry => types::DataType::EnumVariant,
            Self::Connector => types::DataType::EnumVariant,
            Self::BankDebitType => types::DataType::EnumVariant,
            Self::BusinessLabel => types::DataType::StrValue,
            Self::SetupFutureUsage => types::DataType::EnumVariant,
            Self::CardRedirectType => types::DataType::EnumVariant,
            Self::RealTimePaymentType => types::DataType::EnumVariant,
            Self::OpenBankingType => types::DataType::EnumVariant,
            Self::MobilePaymentType => types::DataType::EnumVariant,
            Self::IssuerName => types::DataType::StrValue,
            Self::IssuerCountry => types::DataType::EnumVariant,
            Self::CustomerDevicePlatform => types::DataType::EnumVariant,
            Self::CustomerDeviceType => types::DataType::EnumVariant,
            Self::CustomerDeviceDisplaySize => types::DataType::EnumVariant,
            Self::AcquirerCountry => types::DataType::EnumVariant,
            Self::AcquirerFraudRate => types::DataType::Number,
        }
    }
    pub fn get_value_set(&self) -> Option<Vec<DirValue>> {
        match self {
            Self::PaymentMethod => Some(
                enums::PaymentMethod::iter()
                    .map(DirValue::PaymentMethod)
                    .collect(),
            ),
            Self::CardBin => None,
            Self::CardType => Some(enums::CardType::iter().map(DirValue::CardType).collect()),
            Self::MandateAcceptanceType => Some(
                euclid_enums::MandateAcceptanceType::iter()
                    .map(DirValue::MandateAcceptanceType)
                    .collect(),
            ),
            Self::PaymentType => Some(
                euclid_enums::PaymentType::iter()
                    .map(DirValue::PaymentType)
                    .collect(),
            ),
            Self::MandateType => Some(
                euclid_enums::MandateType::iter()
                    .map(DirValue::MandateType)
                    .collect(),
            ),
            Self::CardNetwork => Some(
                enums::CardNetwork::iter()
                    .map(DirValue::CardNetwork)
                    .collect(),
            ),
            Self::PayLaterType => Some(
                enums::PayLaterType::iter()
                    .map(DirValue::PayLaterType)
                    .collect(),
            ),
            Self::MetaData => None,
            Self::WalletType => Some(
                enums::WalletType::iter()
                    .map(DirValue::WalletType)
                    .collect(),
            ),
            Self::UpiType => Some(enums::UpiType::iter().map(DirValue::UpiType).collect()),
            Self::VoucherType => Some(
                enums::VoucherType::iter()
                    .map(DirValue::VoucherType)
                    .collect(),
            ),
            Self::BankTransferType => Some(
                enums::BankTransferType::iter()
                    .map(DirValue::BankTransferType)
                    .collect(),
            ),
            Self::GiftCardType => Some(
                enums::GiftCardType::iter()
                    .map(DirValue::GiftCardType)
                    .collect(),
            ),
            Self::BankRedirectType => Some(
                enums::BankRedirectType::iter()
                    .map(DirValue::BankRedirectType)
                    .collect(),
            ),
            Self::CryptoType => Some(
                enums::CryptoType::iter()
                    .map(DirValue::CryptoType)
                    .collect(),
            ),
            Self::RewardType => Some(
                enums::RewardType::iter()
                    .map(DirValue::RewardType)
                    .collect(),
            ),
            Self::PaymentAmount => None,
            Self::PaymentCurrency => Some(
                enums::PaymentCurrency::iter()
                    .map(DirValue::PaymentCurrency)
                    .collect(),
            ),
            Self::AuthenticationType => Some(
                enums::AuthenticationType::iter()
                    .map(DirValue::AuthenticationType)
                    .collect(),
            ),
            Self::CaptureMethod => Some(
                enums::CaptureMethod::iter()
                    .map(DirValue::CaptureMethod)
                    .collect(),
            ),
            Self::BankDebitType => Some(
                enums::BankDebitType::iter()
                    .map(DirValue::BankDebitType)
                    .collect(),
            ),
            Self::BusinessCountry => Some(
                enums::Country::iter()
                    .map(DirValue::BusinessCountry)
                    .collect(),
            ),
            Self::BillingCountry => Some(
                enums::Country::iter()
                    .map(DirValue::BillingCountry)
                    .collect(),
            ),
            Self::Connector => Some(
                common_enums::RoutableConnectors::iter()
                    .map(|connector| {
                        DirValue::Connector(Box::new(ast::ConnectorChoice { connector }))
                    })
                    .collect(),
            ),
            Self::BusinessLabel => None,
            Self::SetupFutureUsage => Some(
                enums::SetupFutureUsage::iter()
                    .map(DirValue::SetupFutureUsage)
                    .collect(),
            ),
            Self::CardRedirectType => Some(
                enums::CardRedirectType::iter()
                    .map(DirValue::CardRedirectType)
                    .collect(),
            ),
            Self::RealTimePaymentType => Some(
                enums::RealTimePaymentType::iter()
                    .map(DirValue::RealTimePaymentType)
                    .collect(),
            ),
            Self::OpenBankingType => Some(
                enums::OpenBankingType::iter()
                    .map(DirValue::OpenBankingType)
                    .collect(),
            ),
            Self::MobilePaymentType => Some(
                enums::MobilePaymentType::iter()
                    .map(DirValue::MobilePaymentType)
                    .collect(),
            ),
            Self::IssuerName => None,
            Self::IssuerCountry => Some(
                enums::Country::iter()
                    .map(DirValue::IssuerCountry)
                    .collect(),
            ),
            Self::CustomerDevicePlatform => Some(
                enums::CustomerDevicePlatform::iter()
                    .map(DirValue::CustomerDevicePlatform)
                    .collect(),
            ),
            Self::CustomerDeviceType => Some(
                enums::CustomerDeviceType::iter()
                    .map(DirValue::CustomerDeviceType)
                    .collect(),
            ),
            Self::CustomerDeviceDisplaySize => Some(
                enums::CustomerDeviceDisplaySize::iter()
                    .map(DirValue::CustomerDeviceDisplaySize)
                    .collect(),
            ),
            Self::AcquirerCountry => Some(
                enums::Country::iter()
                    .map(DirValue::AcquirerCountry)
                    .collect(),
            ),
            Self::AcquirerFraudRate => None,
        }
    }
}

#[derive(
    Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, strum::Display, strum::VariantNames,
)]
#[serde(tag = "key", content = "value")]
pub enum DirValue {
    #[serde(rename = "payment_method")]
    PaymentMethod(enums::PaymentMethod),
    #[serde(rename = "card_bin")]
    CardBin(types::StrValue),
    #[serde(rename = "card_type")]
    CardType(enums::CardType),
    #[serde(rename = "card_network")]
    CardNetwork(enums::CardNetwork),
    #[serde(rename = "metadata")]
    MetaData(types::MetadataValue),
    #[serde(rename = "pay_later")]
    PayLaterType(enums::PayLaterType),
    #[serde(rename = "wallet")]
    WalletType(enums::WalletType),
    #[serde(rename = "acceptance_type")]
    MandateAcceptanceType(euclid_enums::MandateAcceptanceType),
    #[serde(rename = "mandate_type")]
    MandateType(euclid_enums::MandateType),
    #[serde(rename = "payment_type")]
    PaymentType(euclid_enums::PaymentType),
    #[serde(rename = "upi")]
    UpiType(enums::UpiType),
    #[serde(rename = "voucher")]
    VoucherType(enums::VoucherType),
    #[serde(rename = "bank_transfer")]
    BankTransferType(enums::BankTransferType),
    #[serde(rename = "bank_redirect")]
    BankRedirectType(enums::BankRedirectType),
    #[serde(rename = "bank_debit")]
    BankDebitType(enums::BankDebitType),
    #[serde(rename = "crypto")]
    CryptoType(enums::CryptoType),
    #[serde(rename = "reward")]
    RewardType(enums::RewardType),
    #[serde(rename = "gift_card")]
    GiftCardType(enums::GiftCardType),
    #[serde(rename = "amount")]
    PaymentAmount(types::NumValue),
    #[serde(rename = "currency")]
    PaymentCurrency(enums::PaymentCurrency),
    #[serde(rename = "authentication_type")]
    AuthenticationType(enums::AuthenticationType),
    #[serde(rename = "capture_method")]
    CaptureMethod(enums::CaptureMethod),
    #[serde(rename = "business_country", alias = "country")]
    BusinessCountry(enums::Country),
    #[serde(rename = "billing_country")]
    BillingCountry(enums::Country),
    #[serde(skip_deserializing, rename = "connector")]
    Connector(Box<ast::ConnectorChoice>),
    #[serde(rename = "business_label")]
    BusinessLabel(types::StrValue),
    #[serde(rename = "setup_future_usage")]
    SetupFutureUsage(enums::SetupFutureUsage),
    #[serde(rename = "card_redirect")]
    CardRedirectType(enums::CardRedirectType),
    #[serde(rename = "real_time_payment")]
    RealTimePaymentType(enums::RealTimePaymentType),
    #[serde(rename = "open_banking")]
    OpenBankingType(enums::OpenBankingType),
    #[serde(rename = "mobile_payment")]
    MobilePaymentType(enums::MobilePaymentType),
    #[serde(rename = "issuer_name")]
    IssuerName(types::StrValue),
    #[serde(rename = "issuer_country")]
    IssuerCountry(enums::Country),
    #[serde(rename = "customer_device_platform")]
    CustomerDevicePlatform(enums::CustomerDevicePlatform),
    #[serde(rename = "customer_device_type")]
    CustomerDeviceType(enums::CustomerDeviceType),
    #[serde(rename = "customer_device_display_size")]
    CustomerDeviceDisplaySize(enums::CustomerDeviceDisplaySize),
    #[serde(rename = "acquirer_country")]
    AcquirerCountry(enums::Country),
    #[serde(rename = "acquirer_fraud_rate")]
    AcquirerFraudRate(types::NumValue),
}

impl DirValue {
    pub fn get_key(&self) -> DirKey {
        let (kind, data) = match self {
            Self::PaymentMethod(_) => (DirKeyKind::PaymentMethod, None),
            Self::CardBin(_) => (DirKeyKind::CardBin, None),
            Self::RewardType(_) => (DirKeyKind::RewardType, None),
            Self::BusinessCountry(_) => (DirKeyKind::BusinessCountry, None),
            Self::BillingCountry(_) => (DirKeyKind::BillingCountry, None),
            Self::BankTransferType(_) => (DirKeyKind::BankTransferType, None),
            Self::UpiType(_) => (DirKeyKind::UpiType, None),
            Self::CardType(_) => (DirKeyKind::CardType, None),
            Self::CardNetwork(_) => (DirKeyKind::CardNetwork, None),
            Self::MetaData(met) => (DirKeyKind::MetaData, Some(met.key.clone())),
            Self::PayLaterType(_) => (DirKeyKind::PayLaterType, None),
            Self::WalletType(_) => (DirKeyKind::WalletType, None),
            Self::BankRedirectType(_) => (DirKeyKind::BankRedirectType, None),
            Self::CryptoType(_) => (DirKeyKind::CryptoType, None),
            Self::AuthenticationType(_) => (DirKeyKind::AuthenticationType, None),
            Self::CaptureMethod(_) => (DirKeyKind::CaptureMethod, None),
            Self::PaymentAmount(_) => (DirKeyKind::PaymentAmount, None),
            Self::PaymentCurrency(_) => (DirKeyKind::PaymentCurrency, None),
            Self::Connector(_) => (DirKeyKind::Connector, None),
            Self::BankDebitType(_) => (DirKeyKind::BankDebitType, None),
            Self::MandateAcceptanceType(_) => (DirKeyKind::MandateAcceptanceType, None),
            Self::MandateType(_) => (DirKeyKind::MandateType, None),
            Self::PaymentType(_) => (DirKeyKind::PaymentType, None),
            Self::BusinessLabel(_) => (DirKeyKind::BusinessLabel, None),
            Self::SetupFutureUsage(_) => (DirKeyKind::SetupFutureUsage, None),
            Self::CardRedirectType(_) => (DirKeyKind::CardRedirectType, None),
            Self::VoucherType(_) => (DirKeyKind::VoucherType, None),
            Self::GiftCardType(_) => (DirKeyKind::GiftCardType, None),
            Self::RealTimePaymentType(_) => (DirKeyKind::RealTimePaymentType, None),
            Self::OpenBankingType(_) => (DirKeyKind::OpenBankingType, None),
            Self::MobilePaymentType(_) => (DirKeyKind::MobilePaymentType, None),
            Self::IssuerName(_) => (DirKeyKind::IssuerName, None),
            Self::IssuerCountry(_) => (DirKeyKind::IssuerCountry, None),
            Self::CustomerDevicePlatform(_) => (DirKeyKind::CustomerDevicePlatform, None),
            Self::CustomerDeviceType(_) => (DirKeyKind::CustomerDeviceType, None),
            Self::CustomerDeviceDisplaySize(_) => (DirKeyKind::CustomerDeviceDisplaySize, None),
            Self::AcquirerCountry(_) => (DirKeyKind::AcquirerCountry, None),
            Self::AcquirerFraudRate(_) => (DirKeyKind::AcquirerFraudRate, None),
        };

        DirKey::new(kind, data)
    }
    pub fn get_metadata_val(&self) -> Option<types::MetadataValue> {
        match self {
            Self::MetaData(val) => Some(val.clone()),
            Self::PaymentMethod(_) => None,
            Self::CardBin(_) => None,
            Self::CardType(_) => None,
            Self::CardNetwork(_) => None,
            Self::PayLaterType(_) => None,
            Self::WalletType(_) => None,
            Self::BankRedirectType(_) => None,
            Self::CryptoType(_) => None,
            Self::AuthenticationType(_) => None,
            Self::CaptureMethod(_) => None,
            Self::GiftCardType(_) => None,
            Self::PaymentAmount(_) => None,
            Self::PaymentCurrency(_) => None,
            Self::BusinessCountry(_) => None,
            Self::BillingCountry(_) => None,
            Self::Connector(_) => None,
            Self::BankTransferType(_) => None,
            Self::UpiType(_) => None,
            Self::BankDebitType(_) => None,
            Self::RewardType(_) => None,
            Self::VoucherType(_) => None,
            Self::MandateAcceptanceType(_) => None,
            Self::MandateType(_) => None,
            Self::PaymentType(_) => None,
            Self::BusinessLabel(_) => None,
            Self::SetupFutureUsage(_) => None,
            Self::CardRedirectType(_) => None,
            Self::RealTimePaymentType(_) => None,
            Self::OpenBankingType(_) => None,
            Self::MobilePaymentType(_) => None,
            Self::IssuerName(_) => None,
            Self::IssuerCountry(_) => None,
            Self::CustomerDevicePlatform(_) => None,
            Self::CustomerDeviceType(_) => None,
            Self::CustomerDeviceDisplaySize(_) => None,
            Self::AcquirerCountry(_) => None,
            Self::AcquirerFraudRate(_) => None,
        }
    }

    pub fn get_str_val(&self) -> Option<types::StrValue> {
        match self {
            Self::CardBin(val) => Some(val.clone()),
            Self::IssuerName(val) => Some(val.clone()),
            _ => None,
        }
    }

    pub fn get_num_value(&self) -> Option<types::NumValue> {
        match self {
            Self::PaymentAmount(val) => Some(val.clone()),
            Self::AcquirerFraudRate(val) => Some(val.clone()),
            _ => None,
        }
    }

    pub fn check_equality(v1: &Self, v2: &Self) -> bool {
        match (v1, v2) {
            (Self::PaymentMethod(pm1), Self::PaymentMethod(pm2)) => pm1 == pm2,
            (Self::CardType(ct1), Self::CardType(ct2)) => ct1 == ct2,
            (Self::CardNetwork(cn1), Self::CardNetwork(cn2)) => cn1 == cn2,
            (Self::MetaData(md1), Self::MetaData(md2)) => md1 == md2,
            (Self::PayLaterType(plt1), Self::PayLaterType(plt2)) => plt1 == plt2,
            (Self::WalletType(wt1), Self::WalletType(wt2)) => wt1 == wt2,
            (Self::BankDebitType(bdt1), Self::BankDebitType(bdt2)) => bdt1 == bdt2,
            (Self::BankRedirectType(brt1), Self::BankRedirectType(brt2)) => brt1 == brt2,
            (Self::BankTransferType(btt1), Self::BankTransferType(btt2)) => btt1 == btt2,
            (Self::GiftCardType(gct1), Self::GiftCardType(gct2)) => gct1 == gct2,
            (Self::CryptoType(ct1), Self::CryptoType(ct2)) => ct1 == ct2,
            (Self::AuthenticationType(at1), Self::AuthenticationType(at2)) => at1 == at2,
            (Self::CaptureMethod(cm1), Self::CaptureMethod(cm2)) => cm1 == cm2,
            (Self::PaymentCurrency(pc1), Self::PaymentCurrency(pc2)) => pc1 == pc2,
            (Self::BusinessCountry(c1), Self::BusinessCountry(c2)) => c1 == c2,
            (Self::BillingCountry(c1), Self::BillingCountry(c2)) => c1 == c2,
            (Self::PaymentType(pt1), Self::PaymentType(pt2)) => pt1 == pt2,
            (Self::MandateType(mt1), Self::MandateType(mt2)) => mt1 == mt2,
            (Self::MandateAcceptanceType(mat1), Self::MandateAcceptanceType(mat2)) => mat1 == mat2,
            (Self::RewardType(rt1), Self::RewardType(rt2)) => rt1 == rt2,
            (Self::RealTimePaymentType(rtp1), Self::RealTimePaymentType(rtp2)) => rtp1 == rtp2,
            (Self::Connector(c1), Self::Connector(c2)) => c1 == c2,
            (Self::BusinessLabel(bl1), Self::BusinessLabel(bl2)) => bl1 == bl2,
            (Self::SetupFutureUsage(sfu1), Self::SetupFutureUsage(sfu2)) => sfu1 == sfu2,
            (Self::UpiType(ut1), Self::UpiType(ut2)) => ut1 == ut2,
            (Self::VoucherType(vt1), Self::VoucherType(vt2)) => vt1 == vt2,
            (Self::CardRedirectType(crt1), Self::CardRedirectType(crt2)) => crt1 == crt2,
            (Self::IssuerName(n1), Self::IssuerName(n2)) => n1 == n2,
            (Self::IssuerCountry(c1), Self::IssuerCountry(c2)) => c1 == c2,
            (Self::CustomerDevicePlatform(p1), Self::CustomerDevicePlatform(p2)) => p1 == p2,
            (Self::CustomerDeviceType(t1), Self::CustomerDeviceType(t2)) => t1 == t2,
            (Self::CustomerDeviceDisplaySize(s1), Self::CustomerDeviceDisplaySize(s2)) => s1 == s2,
            (Self::AcquirerCountry(c1), Self::AcquirerCountry(c2)) => c1 == c2,
            (Self::AcquirerFraudRate(r1), Self::AcquirerFraudRate(r2)) => r1 == r2,
            _ => false,
        }
    }
}

#[cfg(feature = "payouts")]
#[derive(
    Debug,
    Clone,
    Hash,
    PartialEq,
    Eq,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::VariantNames,
    strum::EnumString,
    strum::EnumMessage,
    strum::EnumProperty,
)]
pub enum PayoutDirKeyKind {
    #[strum(
        serialize = "country",
        serialize = "business_country",
        detailed_message = "Country of the business unit",
        props(Category = "Merchant")
    )]
    #[serde(rename = "business_country", alias = "country")]
    BusinessCountry,

    #[strum(
        serialize = "billing_country",
        detailed_message = "Country of the billing address of the customer",
        props(Category = "Customer")
    )]
    #[serde(rename = "billing_country")]
    BillingCountry,

    #[strum(
        serialize = "business_label",
        detailed_message = "Identifier for business unit",
        props(Category = "Merchant")
    )]
    #[serde(rename = "business_label")]
    BusinessLabel,

    #[strum(
        serialize = "amount",
        detailed_message = "Value of the transaction",
        props(Category = "Order details")
    )]
    #[serde(rename = "amount")]
    PayoutAmount,

    #[strum(
        serialize = "currency",
        detailed_message = "Currency used for the payout",
        props(Category = "Order details")
    )]
    #[serde(rename = "currency")]
    PayoutCurrency,

    #[strum(
        serialize = "payment_method",
        detailed_message = "Different modes of payout - eg. cards, wallets, banks",
        props(Category = "Payout Methods")
    )]
    #[serde(rename = "payment_method")]
    PayoutType,

    #[strum(
        serialize = "wallet",
        detailed_message = "Supported types of Wallets for payouts",
        props(Category = "Payout Methods Type")
    )]
    #[serde(rename = "wallet")]
    WalletType,

    #[strum(
        serialize = "bank_transfer",
        detailed_message = "Supported types of Bank transfer types for payouts",
        props(Category = "Payout Methods Type")
    )]
    #[serde(rename = "bank_transfer")]
    BankTransferType,
}

#[cfg(feature = "payouts")]
#[derive(
    Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, strum::Display, strum::VariantNames,
)]
pub enum PayoutDirValue {
    #[serde(rename = "business_country", alias = "country")]
    BusinessCountry(enums::Country),
    #[serde(rename = "billing_country")]
    BillingCountry(enums::Country),
    #[serde(rename = "business_label")]
    BusinessLabel(types::StrValue),
    #[serde(rename = "amount")]
    PayoutAmount(types::NumValue),
    #[serde(rename = "currency")]
    PayoutCurrency(enums::PaymentCurrency),
    #[serde(rename = "payment_method")]
    PayoutType(common_enums::PayoutType),
    #[serde(rename = "wallet")]
    WalletType(enums::PayoutWalletType),
    #[serde(rename = "bank_transfer")]
    BankTransferType(enums::PayoutBankTransferType),
}

#[derive(Debug, Clone)]
pub enum DirComparisonLogic {
    NegativeConjunction,
    PositiveDisjunction,
}

#[derive(Debug, Clone)]
pub struct DirComparison {
    pub values: Vec<DirValue>,
    pub logic: DirComparisonLogic,
    pub metadata: types::Metadata,
}

pub type DirIfCondition = Vec<DirComparison>;

#[derive(Debug, Clone)]
pub struct DirIfStatement {
    pub condition: DirIfCondition,
    pub nested: Option<Vec<DirIfStatement>>,
}

#[derive(Debug, Clone)]
pub struct DirRule<O> {
    pub name: String,
    pub connector_selection: O,
    pub statements: Vec<DirIfStatement>,
}

#[derive(Debug, Clone)]
pub struct DirProgram<O> {
    pub default_selection: O,
    pub rules: Vec<DirRule<O>>,
    pub metadata: types::Metadata,
}

#[cfg(test)]
mod test {
    #![allow(clippy::expect_used)]
    use rustc_hash::FxHashMap;
    use strum::IntoEnumIterator;

    use super::*;

    #[test]
    fn test_consistent_dir_key_naming() {
        let mut key_names: FxHashMap<DirKeyKind, String> = FxHashMap::default();

        for key in DirKeyKind::iter() {
            if matches!(key, DirKeyKind::Connector) {
                continue;
            }

            let json_str = if let DirKeyKind::MetaData = key {
                r#""metadata""#.to_string()
            } else {


// Function: routing_create_config
// File: crates/openapi/src/routes/routing.rs
// Module: openapi

pub fn routing_create_config()


// Function: new_token
// File: crates/router/src/services/authentication.rs
// Module: router

pub fn new_token(
        user_id: String,
        purpose: TokenPurpose,
        origin: domain::Origin,
        settings: &Settings,
        path: Vec<TokenPurpose>,
        tenant_id: Option<id_type::TenantId>,
    ) -> UserResult<String>


// Function: get_resources_set
// File: crates/router/src/services/authorization/roles.rs
// Module: router

pub fn get_resources_set(&self) -> HashSet<Resource>


// Struct: NuveiMandateMeta
// File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NuveiMandateMeta


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

pub struct MerchantConnectorDeleteResponse


// Implementation: impl Flexiti
// File: crates/hyperswitch_connectors/src/connectors/flexiti.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl Flexiti


// Implementation: impl Default for for RequiredFields
// File: crates/payment_methods/src/configs/payment_connector_required_fields.rs
// Module: payment_methods
// Methods: 1 total (0 public)

impl Default for for RequiredFields


// Struct: GocardlessRefundRequest
// File: crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GocardlessRefundRequest