// File: crates/hyperswitch_connectors/src/connectors/nexixpay/transformers.rs
// Module: hyperswitch_connectors
// Public functions: 1
// Public structs: 36

use std::collections::HashMap;

use cards::CardNumber;
use common_enums::{
    AttemptStatus, CaptureMethod, CountryAlpha2, CountryAlpha3, Currency, RefundStatus,
};
use common_utils::{
    errors::CustomResult, ext_traits::ValueExt, request::Method, types::StringMinorUnit,
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        refunds::{Execute, RSync},
        SetupMandate,
    },
    router_request_types::{
        CompleteAuthorizeData, PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData,
        PaymentsPreProcessingData, PaymentsSyncData, ResponseId, SetupMandateRequestData,
    },
    router_response_types::{
        MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{consts::NO_ERROR_CODE, errors};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use strum::Display;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        get_unimplemented_payment_method_error_message, to_connector_meta,
        to_connector_meta_from_secret, CardData, PaymentsAuthorizeRequestData,
        PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingRequestData,
        PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RouterData as _,
    },
};

#[derive(Clone, Copy, Debug)]
enum AddressKind {
    Billing,
    Shipping,
}

trait AddressConstructor {
    fn new(
        name: Option<Secret<String>>,
        street: Option<Secret<String>>,
        city: Option<String>,
        post_code: Option<Secret<String>>,
        country: Option<CountryAlpha3>,
    ) -> Self;
}

impl AddressConstructor for BillingAddress {
    fn new(
        name: Option<Secret<String>>,
        street: Option<Secret<String>>,
        city: Option<String>,
        post_code: Option<Secret<String>>,
        country: Option<CountryAlpha3>,
    ) -> Self {
        Self {
            name,
            street,
            city,
            post_code,
            country,
        }
    }
}

impl AddressConstructor for ShippingAddress {
    fn new(
        name: Option<Secret<String>>,
        street: Option<Secret<String>>,
        city: Option<String>,
        post_code: Option<Secret<String>>,
        country: Option<CountryAlpha3>,
    ) -> Self {
        Self {
            name,
            street,
            city,
            post_code,
            country,
        }
    }
}

fn get_validated_address_details_generic<RouterContextDataAlias, AddressOutput>(
    data: &RouterContextDataAlias,
    address_kind: AddressKind,
) -> Result<Option<AddressOutput>, error_stack::Report<errors::ConnectorError>>
where
    RouterContextDataAlias: crate::utils::RouterData,
    AddressOutput: AddressConstructor + Sized,
{
    let (
        opt_line1,
        opt_line2,
        opt_full_name,
        opt_city,
        opt_zip,
        opt_country,
        has_address_details_check,
        address_type_str,
        max_name_len,
        max_street_len,
        max_city_len,
        max_post_code_len,
        max_country_len,
    ) = match address_kind {
        AddressKind::Billing => (
            data.get_optional_billing_line1(),
            data.get_optional_billing_line2(),
            data.get_optional_billing_full_name(),
            data.get_optional_billing_city(),
            data.get_optional_billing_zip(),
            data.get_optional_billing_country()
                .map(CountryAlpha2::from_alpha2_to_alpha3),
            data.get_optional_billing().is_some(),
            "billing",
            MAX_BILLING_ADDRESS_NAME_LENGTH,
            MAX_BILLING_ADDRESS_STREET_LENGTH,
            MAX_BILLING_ADDRESS_CITY_LENGTH,
            MAX_BILLING_ADDRESS_POST_CODE_LENGTH,
            MAX_BILLING_ADDRESS_COUNTRY_LENGTH,
        ),
        AddressKind::Shipping => (
            data.get_optional_shipping_line1(),
            data.get_optional_shipping_line2(),
            data.get_optional_shipping_full_name(),
            data.get_optional_shipping_city(),
            data.get_optional_shipping_zip(),
            data.get_optional_shipping_country()
                .map(CountryAlpha2::from_alpha2_to_alpha3),
            data.get_optional_shipping().is_some(),
            "shipping",
            MAX_BILLING_ADDRESS_NAME_LENGTH,
            MAX_BILLING_ADDRESS_STREET_LENGTH,
            MAX_BILLING_ADDRESS_CITY_LENGTH,
            MAX_BILLING_ADDRESS_POST_CODE_LENGTH,
            MAX_BILLING_ADDRESS_COUNTRY_LENGTH,
        ),
    };

    let street_val = match (opt_line1.clone(), opt_line2.clone()) {
        (Some(l1), Some(l2)) => Some(Secret::new(format!("{}, {}", l1.expose(), l2.expose()))),
        (Some(l1), None) => Some(l1),
        (None, Some(l2)) => Some(l2),
        (None, None) => None,
    };

    if has_address_details_check {
        let name_val = opt_full_name;
        if let Some(ref val) = name_val {
            let length = val.clone().expose().len();
            if length > max_name_len {
                return Err(error_stack::Report::from(
                    errors::ConnectorError::MaxFieldLengthViolated {
                        field_name: format!(
                            "{address_type_str}.address.first_name & {address_type_str}.address.last_name",

                        ),
                        connector: "Nexixpay".to_string(),
                        max_length: max_name_len,
                        received_length: length,
                    },
                ));
            }
        }

        if let Some(ref val) = street_val {
            let length = val.clone().expose().len();
            if length > max_street_len {
                return Err(error_stack::Report::from(
                    errors::ConnectorError::MaxFieldLengthViolated {
                        field_name: format!(
                            "{address_type_str}.address.line1 & {address_type_str}.address.line2",
                        ),
                        connector: "Nexixpay".to_string(),
                        max_length: max_street_len,
                        received_length: length,
                    },
                ));
            }
        }

        let city_val = opt_city;
        if let Some(ref val) = city_val {
            let length = val.len();
            if length > max_city_len {
                return Err(error_stack::Report::from(
                    errors::ConnectorError::MaxFieldLengthViolated {
                        field_name: format!("{address_type_str}.address.city"),
                        connector: "Nexixpay".to_string(),
                        max_length: max_city_len,
                        received_length: length,
                    },
                ));
            }
        }

        let post_code_val = opt_zip;
        if let Some(ref val) = post_code_val {
            let length = val.clone().expose().len();
            if length > max_post_code_len {
                return Err(error_stack::Report::from(
                    errors::ConnectorError::MaxFieldLengthViolated {
                        field_name: format!("{address_type_str}.address.zip"),
                        connector: "Nexixpay".to_string(),
                        max_length: max_post_code_len,
                        received_length: length,
                    },
                ));
            }
        }

        let country_val = opt_country;
        if let Some(ref val) = country_val {
            let length = val.to_string().len();
            if length > max_country_len {
                return Err(error_stack::Report::from(
                    errors::ConnectorError::MaxFieldLengthViolated {
                        field_name: format!("{address_type_str}.address.country"),
                        connector: "Nexixpay".to_string(),
                        max_length: max_country_len,
                        received_length: length,
                    },
                ));
            }
        }
        Ok(Some(AddressOutput::new(
            name_val,
            street_val,
            city_val,
            post_code_val,
            country_val,
        )))
    } else {
        Ok(None)
    }
}

const MAX_ORDER_ID_LENGTH: usize = 18;
const MAX_CARD_HOLDER_LENGTH: usize = 255;
const MAX_BILLING_ADDRESS_NAME_LENGTH: usize = 50;
const MAX_BILLING_ADDRESS_STREET_LENGTH: usize = 50;
const MAX_BILLING_ADDRESS_CITY_LENGTH: usize = 40;
const MAX_BILLING_ADDRESS_POST_CODE_LENGTH: usize = 16;
const MAX_BILLING_ADDRESS_COUNTRY_LENGTH: usize = 3;

pub struct NexixpayRouterData<T> {
    pub amount: StringMinorUnit,
    pub router_data: T,
}

impl<T> From<(StringMinorUnit, T)> for NexixpayRouterData<T> {
    fn from((amount, item): (StringMinorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum NexixpayRecurringAction {
    NoRecurring,
    SubsequentPayment,
    ContractCreation,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ContractType {
    MitUnscheduled,
    MitScheduled,
    Cit,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RecurrenceRequest {
    action: NexixpayRecurringAction,
    contract_id: Option<Secret<String>>,
    contract_type: Option<ContractType>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayNonMandatePaymentRequest {
    card: NexixpayCard,
    recurrence: RecurrenceRequest,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayMandatePaymentRequest {
    contract_id: Secret<String>,
    capture_type: Option<NexixpayCaptureType>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum NexixpayPaymentsRequestData {
    NexixpayNonMandatePaymentRequest(Box<NexixpayNonMandatePaymentRequest>),
    NexixpayMandatePaymentRequest(Box<NexixpayMandatePaymentRequest>),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayPaymentsRequest {
    order: Order,
    #[serde(flatten)]
    payment_data: NexixpayPaymentsRequestData,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum NexixpayCaptureType {
    Implicit,
    Explicit,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayCompleteAuthorizeRequest {
    order: Order,
    card: NexixpayCard,
    operation_id: String,
    capture_type: Option<NexixpayCaptureType>,
    three_d_s_auth_data: ThreeDSAuthData,
    recurrence: RecurrenceRequest,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OperationData {
    operation_id: String,
    operation_currency: Currency,
    operation_result: NexixpayPaymentStatus,
    operation_type: NexixpayOperationType,
    order_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayCompleteAuthorizeResponse {
    operation: OperationData,
}

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

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Order {
    order_id: String,
    amount: StringMinorUnit,
    currency: Currency,
    description: Option<String>,
    customer_info: CustomerInfo,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CustomerInfo {
    card_holder_name: Secret<String>,
    billing_address: Option<BillingAddress>,
    shipping_address: Option<ShippingAddress>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BillingAddress {
    name: Option<Secret<String>>,
    street: Option<Secret<String>>,
    city: Option<String>,
    post_code: Option<Secret<String>>,
    country: Option<CountryAlpha3>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ShippingAddress {
    name: Option<Secret<String>>,
    street: Option<Secret<String>>,
    city: Option<String>,
    post_code: Option<Secret<String>>,
    country: Option<CountryAlpha3>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayCard {
    pan: CardNumber,
    expiry_date: Secret<String>,
    cvv: Secret<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentsResponse {
    operation: Operation,
    three_d_s_auth_request: String,
    three_d_s_auth_url: Secret<url::Url>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayMandateResponse {
    operation: Operation,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum NexixpayPaymentsResponse {
    PaymentResponse(Box<PaymentsResponse>),
    MandateResponse(Box<NexixpayMandateResponse>),
}

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

#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub enum NexixpayPaymentIntent {
    Capture,
    Cancel,
    Authorize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayRedirectionRequest {
    pub three_d_s_auth_url: String,
    pub three_ds_request: String,
    pub return_url: String,
    pub transaction_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayConnectorMetaData {
    pub three_d_s_auth_result: Option<ThreeDSAuthResult>,
    pub three_d_s_auth_response: Option<Secret<String>>,
    pub authorization_operation_id: Option<String>,
    pub capture_operation_id: Option<String>,
    pub cancel_operation_id: Option<String>,
    pub psync_flow: NexixpayPaymentIntent,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateNexixpayConnectorMetaData {
    pub three_d_s_auth_result: Option<ThreeDSAuthResult>,
    pub three_d_s_auth_response: Option<Secret<String>>,
    pub authorization_operation_id: Option<String>,
    pub capture_operation_id: Option<String>,
    pub cancel_operation_id: Option<String>,
    pub psync_flow: Option<NexixpayPaymentIntent>,
    pub meta_data: serde_json::Value,
    pub is_auto_capture: bool,
}

fn update_nexi_meta_data(
    update_request: UpdateNexixpayConnectorMetaData,
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
    let nexixpay_meta_data =
        serde_json::from_value::<NexixpayConnectorMetaData>(update_request.meta_data)
            .change_context(errors::ConnectorError::ParsingFailed)?;

    Ok(serde_json::json!(NexixpayConnectorMetaData {
        three_d_s_auth_result: nexixpay_meta_data
            .three_d_s_auth_result
            .or(update_request.three_d_s_auth_result),
        three_d_s_auth_response: nexixpay_meta_data
            .three_d_s_auth_response
            .or(update_request.three_d_s_auth_response),
        authorization_operation_id: nexixpay_meta_data
            .authorization_operation_id
            .clone()
            .or(update_request.authorization_operation_id.clone()),
        capture_operation_id: {
            nexixpay_meta_data
                .capture_operation_id
                .or(if update_request.is_auto_capture {
                    nexixpay_meta_data
                        .authorization_operation_id
                        .or(update_request.authorization_operation_id.clone())
                } else {
                    update_request.capture_operation_id
                })
        },
        cancel_operation_id: nexixpay_meta_data
            .cancel_operation_id
            .or(update_request.cancel_operation_id),
        psync_flow: update_request
            .psync_flow
            .unwrap_or(nexixpay_meta_data.psync_flow)
    }))
}

pub fn get_error_response(
    operation_result: NexixpayPaymentStatus,
    status_code: u16,
) -> ErrorResponse {
    ErrorResponse {
        status_code,
        code: NO_ERROR_CODE.to_string(),
        message: operation_result.to_string(),
        reason: Some(operation_result.to_string()),
        attempt_status: None,
        connector_transaction_id: None,
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    }
}

fn get_nexi_order_id(payment_id: &str) -> CustomResult<String, errors::ConnectorError> {
    if payment_id.len() > MAX_ORDER_ID_LENGTH {
        if payment_id.starts_with("pay_") {
            Ok(payment_id
                .chars()
                .take(MAX_ORDER_ID_LENGTH)
                .collect::<String>())
        } else {
            Err(error_stack::Report::from(
                errors::ConnectorError::MaxFieldLengthViolated {
                    field_name: "payment_id".to_string(),
                    connector: "Nexixpay".to_string(),
                    max_length: MAX_ORDER_ID_LENGTH,
                    received_length: payment_id.len(),
                },
            ))
        }
    } else {
        Ok(payment_id.to_string())
    }
}

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

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayPreProcessingResponse {
    operation: Operation,
    three_d_s_auth_result: ThreeDSAuthResult,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Operation {
    additional_data: AdditionalData,
    channel: Option<Channel>,
    customer_info: CustomerInfo,
    operation_amount: StringMinorUnit,
    operation_currency: Currency,
    operation_id: String,
    operation_result: NexixpayPaymentStatus,
    operation_time: String,
    operation_type: NexixpayOperationType,
    order_id: String,
    payment_method: String,
    warnings: Option<Vec<DetailedWarnings>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum Channel {
    Ecommerce,
    Pos,
    Backoffice,
}

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

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdditionalData {
    masked_pan: String,
    card_id: Secret<String>,
    card_id4: Option<Secret<String>>,
    card_expiry_date: Option<Secret<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RedirectPayload {
    #[serde(rename = "PaRes")]
    pa_res: Option<Secret<String>>,

    #[serde(rename = "paymentId")]
    payment_id: Option<String>,
}

impl TryFrom<&PaymentsPreProcessingRouterData> for NexixpayPreProcessingRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> {
        let redirect_response = item.request.redirect_response.clone().ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "redirect_response",
            },
        )?;
        let redirect_payload = redirect_response
            .payload
            .ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload {
                field_name: "request.redirect_response.payload",
            })?
            .expose();
        let customer_details_encrypted: RedirectPayload =
            serde_json::from_value::<RedirectPayload>(redirect_payload.clone()).change_context(
                errors::ConnectorError::MissingConnectorRedirectionPayload {
                    field_name: "redirection_payload",
                },
            )?;
        Ok(Self {
            operation_id: customer_details_encrypted.payment_id,
            three_d_s_auth_response: customer_details_encrypted.pa_res,
        })
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            NexixpayPreProcessingResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsPreProcessingData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            NexixpayPreProcessingResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let three_ds_data = item.response.three_d_s_auth_result;
        let customer_details_encrypted: RedirectPayload = item
            .data
            .request
            .redirect_response
            .as_ref()
            .and_then(|res| res.payload.to_owned())
            .ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload {
                field_name: "request.redirect_response.payload",
            })?
            .expose()
            .parse_value("RedirectPayload")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        let is_auto_capture = item.data.request.is_auto_capture()?;
        let meta_data = to_connector_meta_from_secret(item.data.request.metadata.clone())?;
        let connector_metadata = Some(update_nexi_meta_data(UpdateNexixpayConnectorMetaData {
            three_d_s_auth_result: Some(three_ds_data),
            three_d_s_auth_response: customer_details_encrypted.pa_res,
            authorization_operation_id: None,
            capture_operation_id: None,
            cancel_operation_id: None,
            psync_flow: None,
            meta_data,
            is_auto_capture,
        })?);

        let status = AttemptStatus::from(item.response.operation.operation_result.clone());
        match status {
            AttemptStatus::Failure => {
                let response = Err(get_error_response(
                    item.response.operation.operation_result.clone(),
                    item.http_code,
                ));
                Ok(Self {
                    response,
                    ..item.data
                })
            }
            _ => Ok(Self {
                status,
                response: Ok(PaymentsResponseData::TransactionResponse {
                    resource_id: ResponseId::ConnectorTransactionId(
                        item.response.operation.order_id.clone(),
                    ),
                    redirection_data: Box::new(None),
                    mandate_reference: Box::new(None),
                    connector_metadata,
                    network_txn_id: None,
                    connector_response_reference_id: Some(item.response.operation.order_id),
                    incremental_authorization_allowed: None,
                    charges: None,
                }),
                ..item.data
            }),
        }
    }
}

impl TryFrom<&NexixpayRouterData<&PaymentsAuthorizeRouterData>> for NexixpayPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &NexixpayRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let order_id = get_nexi_order_id(&item.router_data.payment_id)?;

        let billing_address = get_validated_billing_address(item.router_data)?;
        let shipping_address = get_validated_shipping_address(item.router_data)?;

        let customer_info = CustomerInfo {
            card_holder_name: match item.router_data.get_billing_full_name()? {
                name if name.clone().expose().len() <= MAX_CARD_HOLDER_LENGTH => name,
                _ => {
                    return Err(error_stack::Report::from(
                        errors::ConnectorError::MaxFieldLengthViolated {
                            field_name: "billing.address.first_name & billing.address.last_name"
                                .to_string(),
                            connector: "Nexixpay".to_string(),
                            max_length: MAX_CARD_HOLDER_LENGTH,
                            received_length: item
                                .router_data
                                .get_billing_full_name()?
                                .expose()
                                .len(),
                        },
                    ))
                }
            },
            billing_address: billing_address.clone(),
            shipping_address: shipping_address.clone(),
        };
        let order = Order {
            order_id,
            amount: item.amount.clone(),
            currency: item.router_data.request.currency,
            description: item.router_data.description.clone(),
            customer_info,
        };
        let payment_data = NexixpayPaymentsRequestData::try_from(item)?;
        Ok(Self {
            order,
            payment_data,
        })
    }
}

impl TryFrom<&NexixpayRouterData<&PaymentsAuthorizeRouterData>> for NexixpayPaymentsRequestData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &NexixpayRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item
            .router_data
            .request
            .mandate_id
            .clone()
            .and_then(|mandate_id| mandate_id.mandate_reference_id)
        {
            None => {
                let recurrence_request_obj = if item.router_data.request.is_mandate_payment() {
                    let contract_id = item
                        .router_data
                        .connector_mandate_request_reference_id
                        .clone()
                        .ok_or_else(|| errors::ConnectorError::MissingRequiredField {
                            field_name: "connector_mandate_request_reference_id",
                        })?;
                    RecurrenceRequest {
                        action: NexixpayRecurringAction::ContractCreation,
                        contract_id: Some(Secret::new(contract_id)),
                        contract_type: Some(ContractType::MitUnscheduled),
                    }
                } else {
                    RecurrenceRequest {
                        action: NexixpayRecurringAction::NoRecurring,
                        contract_id: None,
                        contract_type: None,
                    }
                };

                match item.router_data.request.payment_method_data {
                    PaymentMethodData::Card(ref req_card) => {
                        if item.router_data.is_three_ds() {
                            Ok(Self::NexixpayNonMandatePaymentRequest(Box::new(
                                NexixpayNonMandatePaymentRequest {
                                    card: NexixpayCard {
                                        pan: req_card.card_number.clone(),
                                        expiry_date: req_card.get_expiry_date_as_mmyy()?,
                                        cvv: req_card.card_cvc.clone(),
                                    },
                                    recurrence: recurrence_request_obj,
                                },
                            )))
                        } else {
                            Err(errors::ConnectorError::NotSupported {
                                message: "No threeds is not supported".to_string(),
                                connector: "nexixpay",
                            }
                            .into())
                        }
                    }
                    PaymentMethodData::CardRedirect(_)
                    | PaymentMethodData::Wallet(_)
                    | PaymentMethodData::PayLater(_)
                    | PaymentMethodData::BankRedirect(_)
                    | PaymentMethodData::BankDebit(_)
                    | PaymentMethodData::BankTransfer(_)
                    | PaymentMethodData::Crypto(_)
                    | PaymentMethodData::MandatePayment
                    | PaymentMethodData::Reward
                    | PaymentMethodData::RealTimePayment(_)
                    | PaymentMethodData::Upi(_)
                    | PaymentMethodData::MobilePayment(_)
                    | PaymentMethodData::Voucher(_)
                    | PaymentMethodData::GiftCard(_)
                    | PaymentMethodData::OpenBanking(_)
                    | PaymentMethodData::CardToken(_)
                    | PaymentMethodData::CardDetailsForNetworkTransactionId(_)
                    | PaymentMethodData::NetworkToken(_) => {
                        Err(errors::ConnectorError::NotImplemented(
                            get_unimplemented_payment_method_error_message("nexixpay"),
                        ))?
                    }
                }
            }
            Some(api_models::payments::MandateReferenceId::ConnectorMandateId(mandate_data)) => {
                let contract_id = Secret::new(
                    mandate_data
                        .get_connector_mandate_request_reference_id()
                        .ok_or(errors::ConnectorError::MissingConnectorMandateID)?,
                );
                let capture_type =
                    get_nexixpay_capture_type(item.router_data.request.capture_method)?;
                Ok(Self::NexixpayMandatePaymentRequest(Box::new(
                    NexixpayMandatePaymentRequest {
                        contract_id,
                        capture_type,
                    },
                )))
            }
            Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_))
            | Some(api_models::payments::MandateReferenceId::NetworkMandateId(_)) => {
                Err(errors::ConnectorError::NotImplemented(
                    get_unimplemented_payment_method_error_message("nexixpay"),
                )
                .into())
            }
        }
    }
}

pub struct NexixpayAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for NexixpayAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Display, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum NexixpayPaymentStatus {
    Authorized,
    Executed,
    Declined,
    DeniedByRisk,
    ThreedsValidated,
    ThreedsFailed,
    Pending,
    Canceled,
    Voided,
    Refunded,
    Failed,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum NexixpayOperationType {
    Authorization,
    Capture,
    Void,
    Refund,
    CardVerification,
    Noshow,
    Incremental,
    DelayCharge,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum NexixpayRefundOperationType {
    Refund,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum NexixpayRefundResultStatus {
    Pending,
    Voided,
    Refunded,
    Failed,
    Executed,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayTransactionResponse {
    order_id: String,
    operation_id: String,
    operation_result: NexixpayPaymentStatus,
    operation_type: NexixpayOperationType,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayRSyncResponse {
    order_id: String,
    operation_id: String,
    operation_result: NexixpayRefundResultStatus,
    operation_type: NexixpayRefundOperationType,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayPaymentsCaptureRequest {
    amount: StringMinorUnit,
    currency: Currency,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayPaymentsCancelRequest {
    description: Option<String>,
    amount: StringMinorUnit,
    currency: Currency,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayOperationResponse {
    operation_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NexixpayRefundRequest {
    pub amount: StringMinorUnit,
    pub currency: Currency,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RefundResponse {
    operation_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayErrorBody {
    pub code: Option<String>,
    pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NexixpayErrorResponse {
    pub errors: Vec<NexixpayErrorBody>,
}

impl From<NexixpayPaymentStatus> for AttemptStatus {
    fn from(item: NexixpayPaymentStatus) -> Self {
        match item {
            NexixpayPaymentStatus::Declined
            | NexixpayPaymentStatus::DeniedByRisk
            | NexixpayPaymentStatus::ThreedsFailed
            | NexixpayPaymentStatus::Failed => Self::Failure,
            NexixpayPaymentStatus::Authorized => Self::Authorized,
            NexixpayPaymentStatus::ThreedsValidated => Self::AuthenticationSuccessful,
            NexixpayPaymentStatus::Executed => Self::Charged,
            NexixpayPaymentStatus::Pending => Self::AuthenticationPending, // this is being used in authorization calls only.
            NexixpayPaymentStatus::Canceled | NexixpayPaymentStatus::Voided => Self::Voided,
            NexixpayPaymentStatus::Refunded => Self::AutoRefunded,
        }
    }
}

fn get_nexixpay_capture_type(
    item: Option<CaptureMethod>,
) -> CustomResult<Option<NexixpayCaptureType>, errors::ConnectorError> {
    match item {
        Some(CaptureMethod::Manual) => Ok(Some(NexixpayCaptureType::Explicit)),
        Some(CaptureMethod::Automatic) | Some(CaptureMethod::SequentialAutomatic) | None => {
            Ok(Some(NexixpayCaptureType::Implicit))
        }
        Some(item) => Err(errors::ConnectorError::FlowNotSupported {
            flow: item.to_string(),
            connector: "Nexixpay".to_string(),
        }
        .into()),
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            NexixpayPaymentsResponse,
            PaymentsAuthorizeData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsAuthorizeData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            NexixpayPaymentsResponse,
            PaymentsAuthorizeData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        match item.response {
            NexixpayPaymentsResponse::PaymentResponse(ref response_body) => {
                let complete_authorize_url = item.data.request.get_complete_authorize_url()?;
                let operation_id: String = response_body.operation.operation_id.clone();
                let redirection_form = nexixpay_threeds_link(NexixpayRedirectionRequest {
                    three_d_s_auth_url: response_body
                        .three_d_s_auth_url
                        .clone()
                        .expose()
                        .to_string(),
                    three_ds_request: response_body.three_d_s_auth_request.clone(),
                    return_url: complete_authorize_url.clone(),
                    transaction_id: operation_id.clone(),
                })?;
                let is_auto_capture = item.data.request.is_auto_capture()?;
                let connector_metadata = Some(serde_json::json!(NexixpayConnectorMetaData {
                    three_d_s_auth_result: None,
                    three_d_s_auth_response: None,
                    authorization_operation_id: Some(operation_id.clone()),
                    cancel_operation_id: None,
                    capture_operation_id: {
                        if is_auto_capture {
                            Some(operation_id)
                        } else {
                            None
                        }
                    },
                    psync_flow: NexixpayPaymentIntent::Authorize
                }));
                let mandate_reference = if item.data.request.is_mandate_payment() {
                    Box::new(Some(MandateReference {
                        connector_mandate_id: item
                            .data
                            .connector_mandate_request_reference_id
                            .clone(),
                        payment_method_id: None,
                        mandate_metadata: None,
                        connector_mandate_request_reference_id: None,
                    }))
                } else {
                    Box::new(None)
                };
                let status = AttemptStatus::from(response_body.operation.operation_result.clone());
                match status {
                    AttemptStatus::Failure => {
                        let response = Err(get_error_response(
                            response_body.operation.operation_result.clone(),
                            item.http_code,
                        ));
                        Ok(Self {
                            response,
                            ..item.data
                        })
                    }
                    _ => Ok(Self {
                        status,
                        response: Ok(PaymentsResponseData::TransactionResponse {
                            resource_id: ResponseId::ConnectorTransactionId(
                                response_body.operation.order_id.clone(),
                            ),
                            redirection_data: Box::new(Some(redirection_form.clone())),
                            mandate_reference,
                            connector_metadata,
                            network_txn_id: None,
                            connector_response_reference_id: Some(
                                response_body.operation.order_id.clone(),
                            ),
                            incremental_authorization_allowed: None,
                            charges: None,
                        }),
                        ..item.data
                    }),
                }
            }
            NexixpayPaymentsResponse::MandateResponse(ref mandate_response) => {
                let status =
                    AttemptStatus::from(mandate_response.operation.operation_result.clone());
                let is_auto_capture = item.data.request.is_auto_capture()?;
                let operation_id = mandate_response.operation.operation_id.clone();
                let connector_metadata = Some(serde_json::json!(NexixpayConnectorMetaData {
                    three_d_s_auth_result: None,
                    three_d_s_auth_response: None,
                    authorization_operation_id: Some(operation_id.clone()),
                    cancel_operation_id: None,
                    capture_operation_id: {
                        if is_auto_capture {
                            Some(operation_id)
                        } else {
                            None
                        }
                    },
                    psync_flow: NexixpayPaymentIntent::Authorize
                }));


// Function: enum_nums
// File: crates/euclid_macros/src/lib.rs
// Module: euclid_macros

pub fn enum_nums(ts: TokenStream) -> TokenStream


// File: crates/router/tests/connectors/worldpayvantiv.rs
// Module: router

use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;

use crate::utils::{self, ConnectorActions};

#[derive(Clone, Copy)]
struct WorldpayvantivTest;
impl ConnectorActions for WorldpayvantivTest {}
impl utils::Connector for WorldpayvantivTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Worldpayxml;
        utils::construct_connector_data_old(
            Box::new(Worldpayxml::new()),
            types::Connector::Plaid,
            api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .worldpayxml
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

    fn get_name(&self) -> String {
        "Worldpayxml".to_string()
    }
}

static CONNECTOR: WorldpayvantivTest = WorldpayvantivTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


// Struct: CryptoData
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Implementations: 1
// Traits: GetPaymentMethodType

pub struct CryptoData


// Struct: FraudCheckSaleData
// File: crates/hyperswitch_domain_models/src/router_request_types/fraud_check.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct FraudCheckSaleData


// Implementation: impl ExternalToken
// File: crates/router/src/services/authentication.rs
// Module: router
// Methods: 2 total (1 public)

impl ExternalToken


// Function: add_entity
// File: crates/router/src/types/domain/user.rs
// Module: router

pub fn add_entity<T>(self, entity: T) -> NewUserRole<T>
    where
        T: Clone,


// Struct: PayoutsResponseData
// File: crates/hyperswitch_domain_models/src/router_response_types.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PayoutsResponseData


// Implementation: impl PaymentAttempt
// File: crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
// Module: hyperswitch_domain_models
// Methods: 5 total (5 public)

impl PaymentAttempt


// Struct: CnpOnlineRequest
// File: crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CnpOnlineRequest


// Function: should_perform_debit_routing_for_the_flow
// File: crates/router/src/core/debit_routing.rs
// Module: router

pub fn should_perform_debit_routing_for_the_flow<Op: Debug, F: Clone, D>(
    operation: &Op,
    payment_data: &D,
    debit_routing_config: &settings::DebitRoutingConfig,
) -> bool
where
    D: OperationSessionGetters<F> + Send + Sync + Clone,


// Struct: BillingAddress
// File: crates/hyperswitch_connectors/src/connectors/forte/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BillingAddress


// Function: get_connector_test_mode
// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs
// Module: hyperswitch_domain_models

pub fn get_connector_test_mode(&self) -> Option<bool>


// Struct: Authorization
// File: crates/router/src/core/webhooks/network_tokenization_incoming.rs
// Module: router
// Implementations: 1

pub struct Authorization


// Struct: GetnetRefundRequest
// File: crates/hyperswitch_connectors/src/connectors/getnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GetnetRefundRequest


// Struct: KafkaPaymentIntentEvent
// File: crates/router/src/services/kafka/payment_intent_event.rs
// Module: router
// Implementations: 4
// Traits: super::KafkaMessage

pub struct KafkaPaymentIntentEvent<'a>


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

impl api::PaymentVoid for for Paypal


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

// Documentation: Get a merchant id from String

pub fn wrap(merchant_id: String) -> CustomResult<Self, ValidationError>


// File: crates/analytics/src/payments/metrics/debit_routing.rs
// Module: analytics

use std::collections::HashSet;

use api_models::analytics::{
    payments::{PaymentDimensions, PaymentFilters, PaymentMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use diesel_models::enums as storage_enums;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

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

#[async_trait::async_trait]
impl<T> super::PaymentMetric<T> for DebitRouting
where
    T: AnalyticsDataSource + super::PaymentMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentDimensions],
        auth: &AuthInfo,
        filters: &PaymentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>> {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::Payment);

        for dim in dimensions.iter() {
            query_builder.add_select_column(dim).switch()?;
        }
        query_builder
            .add_select_column(Aggregate::Count {
                field: None,
                alias: Some("count"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Sum {
                field: "debit_routing_savings",
                alias: Some("total"),
            })
            .switch()?;
        query_builder.add_select_column("currency").switch()?;

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

        filters.set_filter_clause(&mut query_builder).switch()?;

        auth.set_filter_clause(&mut query_builder).switch()?;

        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;

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

        query_builder
            .add_group_by_clause("currency")
            .attach_printable("Error grouping by currency")
            .switch()?;

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

        query_builder
            .add_filter_clause(
                PaymentDimensions::PaymentStatus,
                storage_enums::AttemptStatus::Charged,
            )
            .switch()?;

        query_builder
            .execute_query::<PaymentMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    PaymentMetricsBucketIdentifier::new(
                        i.currency.as_ref().map(|i| i.0),
                        None,
                        i.connector.clone(),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.payment_method.clone(),
                        i.payment_method_type.clone(),
                        i.client_source.clone(),
                        i.client_version.clone(),
                        i.profile_id.clone(),
                        i.card_network.clone(),
                        i.merchant_id.clone(),
                        i.card_last_4.clone(),
                        i.card_issuer.clone(),
                        i.error_reason.clone(),
                        i.routing_approach.as_ref().map(|i| i.0.clone()),
                        i.signature_network.clone(),
                        i.is_issuer_regulated,
                        i.is_debit_routed,
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


// Struct: Refund
// File: crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Refund


// Struct: BanksVector
// File: crates/payment_methods/src/configs/settings.rs
// Module: payment_methods
// Implementations: 0

pub struct BanksVector


// Implementation: impl webhooks::IncomingWebhook for for Thunes
// File: crates/hyperswitch_connectors/src/connectors/thunes.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl webhooks::IncomingWebhook for for Thunes


// Struct: CardNetworkTokenizeExecutor
// File: crates/router/src/core/payment_methods/tokenize.rs
// Module: router
// Implementations: 0

pub struct CardNetworkTokenizeExecutor<'a, D>


// Module Structure
// File: crates/router/src/compatibility/stripe/payment_intents.rs
// Module: router

// Public submodules:
pub mod types;


// Implementation: impl api::PaymentAuthorize for for Tsys
// File: crates/hyperswitch_connectors/src/connectors/tsys.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Tsys


// Implementation: impl RoutableConnectors
// File: crates/router/src/core/routing.rs
// Module: router
// Methods: 2 total (1 public)

impl RoutableConnectors


// Function: update_redis_token_with_comprehensive_card_data
// File: crates/router/src/types/storage/revenue_recovery_redis_operation.rs
// Module: router

pub fn update_redis_token_with_comprehensive_card_data(
        state: &SessionState,
        customer_id: &str,
        token: &str,
        card_data: &api_models::revenue_recovery_data_backfill::ComprehensiveCardData,
        cutoff_datetime: Option<PrimitiveDateTime>,
    ) -> CustomResult<(), errors::StorageError>


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

pub fn server(state: AppState) -> Scope


// Struct: ServiceDetails
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ServiceDetails