// File: crates/hyperswitch_connectors/src/connectors/payme/transformers.rs
// Module: hyperswitch_connectors
// Public structs: 30

use std::collections::HashMap;

use api_models::enums::{AuthenticationType, PaymentMethod};
use common_enums::enums;
use common_utils::{
    pii,
    types::{MinorUnit, StringMajorUnit},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{PaymentMethodData, WalletData},
    router_data::{ConnectorAuthType, ErrorResponse, PaymentMethodToken, RouterData},
    router_flow_types::{Execute, Void},
    router_request_types::{PaymentsCancelData, PaymentsPreProcessingData, ResponseId},
    router_response_types::{
        MandateReference, PaymentsResponseData, PreprocessingResponseId, RedirectForm,
        RefundsResponseData,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData,
        PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, TokenizationRouterData,
    },
};
use hyperswitch_interfaces::{consts, errors};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
    types::{PaymentsCancelResponseRouterData, RefundsResponseRouterData, ResponseRouterData},
    unimplemented_payment_method,
    utils::{
        self, AddressDetailsData, CardData, PaymentsAuthorizeRequestData,
        PaymentsCancelRequestData, PaymentsCompleteAuthorizeRequestData,
        PaymentsPreProcessingRequestData, PaymentsSyncRequestData, RouterData as OtherRouterData,
    },
};

const LANGUAGE: &str = "en";

#[derive(Debug, Serialize)]
pub struct PaymeRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}

impl<T> TryFrom<(MinorUnit, T)> for PaymeRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from((amount, item): (MinorUnit, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount,
            router_data: item,
        })
    }
}

#[derive(Debug, Serialize)]
pub struct PayRequest {
    buyer_name: Secret<String>,
    buyer_email: pii::Email,
    payme_sale_id: String,
    #[serde(flatten)]
    card: PaymeCard,
    language: String,
}

#[derive(Debug, Serialize)]
pub struct MandateRequest {
    currency: enums::Currency,
    sale_price: MinorUnit,
    transaction_id: String,
    product_name: String,
    sale_return_url: String,
    seller_payme_id: Secret<String>,
    sale_callback_url: String,
    buyer_key: Secret<String>,
    language: String,
}

#[derive(Debug, Serialize)]
pub struct Pay3dsRequest {
    buyer_name: Secret<String>,
    buyer_email: pii::Email,
    buyer_key: Secret<String>,
    payme_sale_id: String,
    meta_data_jwt: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum PaymePaymentRequest {
    MandateRequest(MandateRequest),
    PayRequest(PayRequest),
}

#[derive(Debug, Serialize)]
pub struct PaymeQuerySaleRequest {
    sale_payme_id: String,
    seller_payme_id: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct PaymeQueryTransactionRequest {
    payme_transaction_id: String,
    seller_payme_id: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct PaymeCard {
    credit_card_cvv: Secret<String>,
    credit_card_exp: Secret<String>,
    credit_card_number: cards::CardNumber,
}

#[derive(Debug, Serialize)]
pub struct CaptureBuyerRequest {
    seller_payme_id: Secret<String>,
    #[serde(flatten)]
    card: PaymeCard,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct CaptureBuyerResponse {
    buyer_key: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct GenerateSaleRequest {
    currency: enums::Currency,
    sale_type: SaleType,
    sale_price: MinorUnit,
    transaction_id: String,
    product_name: String,
    sale_return_url: String,
    seller_payme_id: Secret<String>,
    sale_callback_url: String,
    sale_payment_method: SalePaymentMethod,
    services: Option<ThreeDs>,
    language: String,
}

#[derive(Debug, Serialize)]
pub struct ThreeDs {
    name: ThreeDsType,
    settings: ThreeDsSettings,
}

#[derive(Debug, Serialize)]
pub enum ThreeDsType {
    #[serde(rename = "3D Secure")]
    ThreeDs,
}

#[derive(Debug, Serialize)]
pub struct ThreeDsSettings {
    active: bool,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct GenerateSaleResponse {
    payme_sale_id: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, PaymePaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, PaymePaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        match item.response {
            // To handle webhook response
            PaymePaymentsResponse::PaymePaySaleResponse(response) => {
                Self::try_from(ResponseRouterData {
                    response,
                    data: item.data,
                    http_code: item.http_code,
                })
            }
            // To handle PSync response
            PaymePaymentsResponse::SaleQueryResponse(response) => {
                Self::try_from(ResponseRouterData {
                    response,
                    data: item.data,
                    http_code: item.http_code,
                })
            }
        }
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, PaymePaySaleResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, PaymePaySaleResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let status = enums::AttemptStatus::from(item.response.sale_status.clone());
        let response = if utils::is_payment_failure(status) {
            // To populate error message in case of failure
            Err(get_pay_sale_error_response((
                &item.response,
                item.http_code,
            )))
        } else {
            Ok(PaymentsResponseData::try_from(&item.response)?)
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

fn get_pay_sale_error_response(
    (pay_sale_response, http_code): (&PaymePaySaleResponse, u16),
) -> ErrorResponse {
    let code = pay_sale_response
        .status_error_code
        .map(|error_code| error_code.to_string())
        .unwrap_or(consts::NO_ERROR_CODE.to_string());
    ErrorResponse {
        code,
        message: pay_sale_response
            .status_error_details
            .clone()
            .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
        reason: pay_sale_response.status_error_details.to_owned(),
        status_code: http_code,
        attempt_status: None,
        connector_transaction_id: Some(pay_sale_response.payme_sale_id.clone()),
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    }
}

impl TryFrom<&PaymePaySaleResponse> for PaymentsResponseData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(value: &PaymePaySaleResponse) -> Result<Self, Self::Error> {
        let redirection_data = match value.sale_3ds {
            Some(true) => value.redirect_url.clone().map(|url| RedirectForm::Form {
                endpoint: url.to_string(),
                method: common_utils::request::Method::Get,
                form_fields: HashMap::<String, String>::new(),
            }),
            _ => None,
        };
        Ok(Self::TransactionResponse {
            resource_id: ResponseId::ConnectorTransactionId(value.payme_sale_id.clone()),
            redirection_data: Box::new(redirection_data),
            mandate_reference: Box::new(value.buyer_key.clone().map(|buyer_key| {
                MandateReference {
                    connector_mandate_id: Some(buyer_key.expose()),
                    payment_method_id: None,
                    mandate_metadata: None,
                    connector_mandate_request_reference_id: None,
                }
            })),
            connector_metadata: None,
            network_txn_id: None,
            connector_response_reference_id: None,
            incremental_authorization_allowed: None,
            charges: None,
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, SaleQueryResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, SaleQueryResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        // Only one element would be present since we are passing one transaction id in the PSync request
        let transaction_response = item
            .response
            .items
            .first()
            .cloned()
            .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
        let status = enums::AttemptStatus::from(transaction_response.sale_status.clone());
        let response = if utils::is_payment_failure(status) {
            // To populate error message in case of failure
            Err(get_sale_query_error_response((
                &transaction_response,
                item.http_code,
            )))
        } else {
            Ok(PaymentsResponseData::from(&transaction_response))
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

fn get_sale_query_error_response(
    (sale_query_response, http_code): (&SaleQuery, u16),
) -> ErrorResponse {
    ErrorResponse {
        code: sale_query_response
            .sale_error_code
            .clone()
            .unwrap_or(consts::NO_ERROR_CODE.to_string()),
        message: sale_query_response
            .sale_error_text
            .clone()
            .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
        reason: sale_query_response.sale_error_text.clone(),
        status_code: http_code,
        attempt_status: None,
        connector_transaction_id: Some(sale_query_response.sale_payme_id.clone()),
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    }
}

impl From<&SaleQuery> for PaymentsResponseData {
    fn from(value: &SaleQuery) -> Self {
        Self::TransactionResponse {
            resource_id: ResponseId::ConnectorTransactionId(value.sale_payme_id.clone()),
            redirection_data: Box::new(None),
            // mandate reference will be updated with webhooks only. That has been handled with PaymePaySaleResponse struct
            mandate_reference: Box::new(None),
            connector_metadata: None,
            network_txn_id: None,
            connector_response_reference_id: None,
            incremental_authorization_allowed: None,
            charges: None,
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum SaleType {
    Sale,
    Authorize,
    Token,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum SalePaymentMethod {
    CreditCard,
    ApplePay,
}

impl TryFrom<&PaymeRouterData<&PaymentsPreProcessingRouterData>> for GenerateSaleRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &PaymeRouterData<&PaymentsPreProcessingRouterData>,
    ) -> Result<Self, Self::Error> {
        let sale_type = SaleType::try_from(item.router_data)?;
        let seller_payme_id =
            PaymeAuthType::try_from(&item.router_data.connector_auth_type)?.seller_payme_id;
        let order_details = item.router_data.request.get_order_details()?;
        let services = get_services(item.router_data);
        let product_name = order_details
            .first()
            .ok_or_else(utils::missing_field_err("order_details"))?
            .product_name
            .clone();
        let pmd = item
            .router_data
            .request
            .payment_method_data
            .to_owned()
            .ok_or_else(utils::missing_field_err("payment_method_data"))?;
        Ok(Self {
            seller_payme_id,
            sale_price: item.amount.to_owned(),
            currency: item.router_data.request.get_currency()?,
            product_name,
            sale_payment_method: SalePaymentMethod::try_from(&pmd)?,
            sale_type,
            transaction_id: item.router_data.payment_id.clone(),
            sale_return_url: item.router_data.request.get_router_return_url()?,
            sale_callback_url: item.router_data.request.get_webhook_url()?,
            language: LANGUAGE.to_string(),
            services,
        })
    }
}

impl TryFrom<&PaymentMethodData> for SalePaymentMethod {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentMethodData) -> Result<Self, Self::Error> {
        match item {
            PaymentMethodData::Card(_) => Ok(Self::CreditCard),
            PaymentMethodData::Wallet(wallet_data) => match wallet_data {
                WalletData::ApplePayThirdPartySdk(_) => Ok(Self::ApplePay),
                WalletData::AliPayQr(_)
                | WalletData::AliPayRedirect(_)
                | WalletData::BluecodeRedirect {}
                | WalletData::AliPayHkRedirect(_)
                | WalletData::AmazonPay(_)
                | WalletData::AmazonPayRedirect(_)
                | WalletData::Paysera(_)
                | WalletData::Skrill(_)
                | WalletData::MomoRedirect(_)
                | WalletData::KakaoPayRedirect(_)
                | WalletData::GoPayRedirect(_)
                | WalletData::GcashRedirect(_)
                | WalletData::ApplePayRedirect(_)
                | WalletData::DanaRedirect {}
                | WalletData::GooglePay(_)
                | WalletData::GooglePayRedirect(_)
                | WalletData::GooglePayThirdPartySdk(_)
                | WalletData::MbWayRedirect(_)
                | WalletData::MobilePayRedirect(_)
                | WalletData::PaypalRedirect(_)
                | WalletData::PaypalSdk(_)
                | WalletData::Paze(_)
                | WalletData::SamsungPay(_)
                | WalletData::TwintRedirect {}
                | WalletData::VippsRedirect {}
                | WalletData::TouchNGoRedirect(_)
                | WalletData::WeChatPayRedirect(_)
                | WalletData::WeChatPayQr(_)
                | WalletData::CashappQr(_)
                | WalletData::ApplePay(_)
                | WalletData::SwishQr(_)
                | WalletData::Mifinity(_)
                | WalletData::RevolutPay(_) => Err(errors::ConnectorError::NotSupported {
                    message: "Wallet".to_string(),
                    connector: "payme",
                }
                .into()),
            },
            PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into())
            }
        }
    }
}

impl TryFrom<&PaymeRouterData<&PaymentsAuthorizeRouterData>> for PaymePaymentRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        value: &PaymeRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let payme_request = if value.router_data.request.mandate_id.is_some() {
            Self::MandateRequest(MandateRequest::try_from(value)?)
        } else {
            Self::PayRequest(PayRequest::try_from(value.router_data)?)
        };
        Ok(payme_request)
    }
}

impl TryFrom<&PaymentsSyncRouterData> for PaymeQuerySaleRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(value: &PaymentsSyncRouterData) -> Result<Self, Self::Error> {
        let seller_payme_id = PaymeAuthType::try_from(&value.connector_auth_type)?.seller_payme_id;
        Ok(Self {
            sale_payme_id: value.request.get_connector_transaction_id()?,
            seller_payme_id,
        })
    }
}

impl TryFrom<&RefundSyncRouterData> for PaymeQueryTransactionRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(value: &RefundSyncRouterData) -> Result<Self, Self::Error> {
        let seller_payme_id = PaymeAuthType::try_from(&value.connector_auth_type)?.seller_payme_id;
        Ok(Self {
            payme_transaction_id: value
                .request
                .connector_refund_id
                .clone()
                .ok_or(errors::ConnectorError::MissingConnectorRefundID)?,
            seller_payme_id,
        })
    }
}

impl<F>
    utils::ForeignTryFrom<(
        ResponseRouterData<
            F,
            GenerateSaleResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
        StringMajorUnit,
    )> for RouterData<F, PaymentsPreProcessingData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn foreign_try_from(
        (item, apple_pay_amount): (
            ResponseRouterData<
                F,
                GenerateSaleResponse,
                PaymentsPreProcessingData,
                PaymentsResponseData,
            >,
            StringMajorUnit,
        ),
    ) -> Result<Self, Self::Error> {
        match item.data.payment_method {
            PaymentMethod::Card => {
                match item.data.auth_type {
                    AuthenticationType::NoThreeDs => {
                        Ok(Self {
                            // We don't get any status from payme, so defaulting it to pending
                            // then move to authorize flow
                            status: enums::AttemptStatus::Pending,
                            preprocessing_id: Some(item.response.payme_sale_id.to_owned()),
                            response: Ok(PaymentsResponseData::PreProcessingResponse {
                                pre_processing_id: PreprocessingResponseId::ConnectorTransactionId(
                                    item.response.payme_sale_id,
                                ),
                                connector_metadata: None,
                                session_token: None,
                                connector_response_reference_id: None,
                            }),
                            ..item.data
                        })
                    }
                    AuthenticationType::ThreeDs => Ok(Self {
                        // We don't go to authorize flow in 3ds,
                        // Response is send directly after preprocessing flow
                        // redirection data is send to run script along
                        // status is made authentication_pending to show redirection
                        status: enums::AttemptStatus::AuthenticationPending,
                        preprocessing_id: Some(item.response.payme_sale_id.to_owned()),
                        response: Ok(PaymentsResponseData::TransactionResponse {
                            resource_id: ResponseId::ConnectorTransactionId(
                                item.response.payme_sale_id.to_owned(),
                            ),
                            redirection_data: Box::new(Some(RedirectForm::Payme)),
                            mandate_reference: Box::new(None),
                            connector_metadata: None,
                            network_txn_id: None,
                            connector_response_reference_id: None,
                            incremental_authorization_allowed: None,
                            charges: None,
                        }),
                        ..item.data
                    }),
                }
            }
            _ => {
                let currency_code = item.data.request.get_currency()?;
                let pmd = item.data.request.payment_method_data.to_owned();
                let payme_auth_type = PaymeAuthType::try_from(&item.data.connector_auth_type)?;

                let session_token = match pmd {
                    Some(PaymentMethodData::Wallet(WalletData::ApplePayThirdPartySdk(
                        _,
                    ))) => Some(api_models::payments::SessionToken::ApplePay(Box::new(
                        api_models::payments::ApplepaySessionTokenResponse {
                            session_token_data: Some(
                                api_models::payments::ApplePaySessionResponse::NoSessionResponse(api_models::payments::NullObject),
                            ),
                            payment_request_data: Some(
                                api_models::payments::ApplePayPaymentRequest {
                                    country_code: item.data.get_billing_country()?,
                                    currency_code,
                                    total: api_models::payments::AmountInfo {
                                        label: "Apple Pay".to_string(),
                                        total_type: None,
                                        amount: apple_pay_amount,
                                    },
                                    merchant_capabilities: None,
                                    supported_networks: None,
                                    merchant_identifier: None,
                                    required_billing_contact_fields: None,
                                    required_shipping_contact_fields: None,
                                    recurring_payment_request: None,
                                },
                            ),
                            connector: "payme".to_string(),
                            delayed_session_token: true,
                            sdk_next_action: api_models::payments::SdkNextAction {
                                next_action: api_models::payments::NextActionCall::Sync,
                            },
                            connector_reference_id: Some(item.response.payme_sale_id.to_owned()),
                            connector_sdk_public_key: Some(
                                payme_auth_type.payme_public_key.expose(),
                            ),
                            connector_merchant_id: payme_auth_type
                                .payme_merchant_id
                                .map(|mid| mid.expose()),
                        },
                    ))),
                    _ => None,
                };
                Ok(Self {
                    // We don't get any status from payme, so defaulting it to pending
                    status: enums::AttemptStatus::Pending,
                    preprocessing_id: Some(item.response.payme_sale_id.to_owned()),
                    response: Ok(PaymentsResponseData::PreProcessingResponse {
                        pre_processing_id: PreprocessingResponseId::ConnectorTransactionId(
                            item.response.payme_sale_id,
                        ),
                        connector_metadata: None,
                        session_token,
                        connector_response_reference_id: None,
                    }),
                    ..item.data
                })
            }
        }
    }
}

impl TryFrom<&PaymeRouterData<&PaymentsAuthorizeRouterData>> for MandateRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymeRouterData<&PaymentsAuthorizeRouterData>) -> Result<Self, Self::Error> {
        let seller_payme_id =
            PaymeAuthType::try_from(&item.router_data.connector_auth_type)?.seller_payme_id;
        let order_details = item.router_data.request.get_order_details()?;
        let product_name = order_details
            .first()
            .ok_or_else(utils::missing_field_err("order_details"))?
            .product_name
            .clone();
        Ok(Self {
            currency: item.router_data.request.currency,
            sale_price: item.amount.to_owned(),
            transaction_id: item.router_data.payment_id.clone(),
            product_name,
            sale_return_url: item.router_data.request.get_router_return_url()?,
            seller_payme_id,
            sale_callback_url: item.router_data.request.get_webhook_url()?,
            buyer_key: Secret::new(item.router_data.request.get_connector_mandate_id()?),
            language: LANGUAGE.to_string(),
        })
    }
}

impl TryFrom<&PaymentsAuthorizeRouterData> for PayRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = PaymeCard {
                    credit_card_cvv: req_card.card_cvc.clone(),
                    credit_card_exp: req_card
                        .get_card_expiry_month_year_2_digit_with_delimiter("".to_string())?,
                    credit_card_number: req_card.card_number,
                };
                let buyer_email = item.request.get_email()?;
                let buyer_name = item.get_billing_address()?.get_full_name()?;
                let payme_sale_id = item.preprocessing_id.to_owned().ok_or(
                    errors::ConnectorError::MissingConnectorRelatedTransactionID {
                        id: "payme_sale_id".to_string(),
                    },
                )?;
                Ok(Self {
                    card,
                    buyer_email,
                    buyer_name,
                    payme_sale_id,
                    language: LANGUAGE.to_string(),
                })
            }
            PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("payme"),
                ))?
            }
        }
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymeRedirectResponseData {
    meta_data: String,
}

impl TryFrom<&PaymentsCompleteAuthorizeRouterData> for Pay3dsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsCompleteAuthorizeRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            Some(PaymentMethodData::Card(_)) => {
                let buyer_email = item.request.get_email()?;
                let buyer_name = item.get_billing_address()?.get_full_name()?;

                let payload_data = item.request.get_redirect_response_payload()?.expose();

                let jwt_data: PaymeRedirectResponseData = serde_json::from_value(payload_data)
                    .change_context(errors::ConnectorError::MissingConnectorRedirectionPayload {
                        field_name: "meta_data_jwt",
                    })?;

                let payme_sale_id = item
                    .request
                    .connector_transaction_id
                    .clone()
                    .ok_or(errors::ConnectorError::MissingConnectorTransactionID)?;
                let pm_token = item.get_payment_method_token()?;
                let buyer_key =
                    match pm_token {
                        PaymentMethodToken::Token(token) => token,
                        PaymentMethodToken::ApplePayDecrypt(_) => Err(
                            unimplemented_payment_method!("Apple Pay", "Simplified", "Payme"),
                        )?,
                        PaymentMethodToken::PazeDecrypt(_) => {
                            Err(unimplemented_payment_method!("Paze", "Payme"))?
                        }
                        PaymentMethodToken::GooglePayDecrypt(_) => {
                            Err(unimplemented_payment_method!("Google Pay", "Payme"))?
                        }
                    };
                Ok(Self {
                    buyer_email,
                    buyer_key,
                    buyer_name,
                    payme_sale_id,
                    meta_data_jwt: Secret::new(jwt_data.meta_data),
                })
            }
            Some(PaymentMethodData::CardRedirect(_))
            | Some(PaymentMethodData::Wallet(_))
            | Some(PaymentMethodData::PayLater(_))
            | Some(PaymentMethodData::BankRedirect(_))
            | Some(PaymentMethodData::BankDebit(_))
            | Some(PaymentMethodData::BankTransfer(_))
            | Some(PaymentMethodData::Crypto(_))
            | Some(PaymentMethodData::MandatePayment)
            | Some(PaymentMethodData::Reward)
            | Some(PaymentMethodData::RealTimePayment(_))
            | Some(PaymentMethodData::MobilePayment(_))
            | Some(PaymentMethodData::Upi(_))
            | Some(PaymentMethodData::Voucher(_))
            | Some(PaymentMethodData::GiftCard(_))
            | Some(PaymentMethodData::OpenBanking(_))
            | Some(PaymentMethodData::CardToken(_))
            | Some(PaymentMethodData::NetworkToken(_))
            | Some(PaymentMethodData::CardDetailsForNetworkTransactionId(_))
            | None => {
                Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into())
            }
        }
    }
}

impl TryFrom<&TokenizationRouterData> for CaptureBuyerRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &TokenizationRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let seller_payme_id =
                    PaymeAuthType::try_from(&item.connector_auth_type)?.seller_payme_id;
                let card = PaymeCard {
                    credit_card_cvv: req_card.card_cvc.clone(),
                    credit_card_exp: req_card
                        .get_card_expiry_month_year_2_digit_with_delimiter("".to_string())?,
                    credit_card_number: req_card.card_number,
                };
                Ok(Self {
                    card,
                    seller_payme_id,
                })
            }
            PaymentMethodData::Wallet(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into())
            }
        }
    }
}

// Auth Struct
pub struct PaymeAuthType {
    #[allow(dead_code)]
    pub(super) payme_public_key: Secret<String>,
    pub(super) seller_payme_id: Secret<String>,
    pub(super) payme_merchant_id: Option<Secret<String>>,
}

impl TryFrom<&ConnectorAuthType> for PaymeAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                seller_payme_id: api_key.to_owned(),
                payme_public_key: key1.to_owned(),
                payme_merchant_id: None,
            }),
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Ok(Self {
                seller_payme_id: api_key.to_owned(),
                payme_public_key: key1.to_owned(),
                payme_merchant_id: Some(api_secret.to_owned()),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

impl TryFrom<&PaymentsPreProcessingRouterData> for SaleType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(value: &PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> {
        let sale_type = if value.request.setup_mandate_details.is_some() {
            // First mandate
            Self::Token
        } else {
            // Normal payments
            match value.request.is_auto_capture()? {
                true => Self::Sale,
                false => Self::Authorize,
            }
        };
        Ok(sale_type)
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, strum::Display)]
#[serde(rename_all = "kebab-case")]
pub enum SaleStatus {
    Initial,
    Completed,
    Refunded,
    PartialRefund,
    Authorized,
    Voided,
    PartialVoid,
    Failed,
    Chargeback,
}

impl From<SaleStatus> for enums::AttemptStatus {
    fn from(item: SaleStatus) -> Self {
        match item {
            SaleStatus::Initial => Self::Authorizing,
            SaleStatus::Completed => Self::Charged,
            SaleStatus::Refunded | SaleStatus::PartialRefund => Self::AutoRefunded,
            SaleStatus::Authorized => Self::Authorized,
            SaleStatus::Voided | SaleStatus::PartialVoid => Self::Voided,
            SaleStatus::Failed => Self::Failure,
            SaleStatus::Chargeback => Self::AutoRefunded,
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum PaymePaymentsResponse {
    PaymePaySaleResponse(PaymePaySaleResponse),
    SaleQueryResponse(SaleQueryResponse),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SaleQueryResponse {
    items: Vec<SaleQuery>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SaleQuery {
    sale_status: SaleStatus,
    sale_payme_id: String,
    sale_error_text: Option<String>,
    sale_error_code: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PaymePaySaleResponse {
    sale_status: SaleStatus,
    payme_sale_id: String,
    payme_transaction_id: Option<String>,
    buyer_key: Option<Secret<String>>,
    status_error_details: Option<String>,
    status_error_code: Option<u32>,
    sale_3ds: Option<bool>,
    redirect_url: Option<Url>,
}

#[derive(Serialize, Deserialize)]
pub struct PaymeMetadata {
    payme_transaction_id: Option<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, CaptureBuyerResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, CaptureBuyerResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_token: Some(PaymentMethodToken::Token(item.response.buyer_key.clone())),
            response: Ok(PaymentsResponseData::TokenizationResponse {
                token: item.response.buyer_key.expose(),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
pub struct PaymentCaptureRequest {
    payme_sale_id: String,
    sale_price: MinorUnit,
}

impl TryFrom<&PaymeRouterData<&PaymentsCaptureRouterData>> for PaymentCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymeRouterData<&PaymentsCaptureRouterData>) -> Result<Self, Self::Error> {
        if item.router_data.request.minor_amount_to_capture
            != item.router_data.request.minor_payment_amount
        {
            Err(errors::ConnectorError::NotSupported {
                message: "Partial Capture".to_string(),
                connector: "Payme",
            })?
        }
        Ok(Self {
            payme_sale_id: item.router_data.request.connector_transaction_id.clone(),
            sale_price: item.amount,
        })
    }
}

// REFUND :
// Type definition for RefundRequest
#[derive(Debug, Serialize)]
pub struct PaymeRefundRequest {
    sale_refund_amount: MinorUnit,
    payme_sale_id: String,
    seller_payme_id: Secret<String>,
    language: String,
}

impl<F> TryFrom<&PaymeRouterData<&RefundsRouterData<F>>> for PaymeRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymeRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let auth_type = PaymeAuthType::try_from(&item.router_data.connector_auth_type)?;
        Ok(Self {
            payme_sale_id: item.router_data.request.connector_transaction_id.clone(),
            seller_payme_id: auth_type.seller_payme_id,
            sale_refund_amount: item.amount.to_owned(),
            language: LANGUAGE.to_string(),
        })
    }
}

impl TryFrom<SaleStatus> for enums::RefundStatus {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(sale_status: SaleStatus) -> Result<Self, Self::Error> {
        match sale_status {
            SaleStatus::Refunded | SaleStatus::PartialRefund => Ok(Self::Success),
            SaleStatus::Failed => Ok(Self::Failure),
            SaleStatus::Initial
            | SaleStatus::Completed
            | SaleStatus::Authorized
            | SaleStatus::Voided
            | SaleStatus::PartialVoid
            | SaleStatus::Chargeback => Err(errors::ConnectorError::ResponseHandlingFailed)?,
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PaymeRefundResponse {
    sale_status: SaleStatus,
    payme_transaction_id: Option<String>,
    status_error_code: Option<u32>,
}

impl TryFrom<RefundsResponseRouterData<Execute, PaymeRefundResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, PaymeRefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::try_from(item.response.sale_status.clone())?;
        let response = if utils::is_refund_failure(refund_status) {
            let payme_response = &item.response;
            let status_error_code = payme_response
                .status_error_code
                .map(|error_code| error_code.to_string());
            Err(ErrorResponse {
                code: status_error_code


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

pub fn payments_capture(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsCaptureRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder


// Function: get_billing_address
// File: crates/api_models/src/subscription.rs
// Module: api_models

pub fn get_billing_address(&self) -> Result<Address, error_stack::Report<ValidationError>>


// Function: build_pm_collect_link_html
// File: crates/router/src/services/api/generic_link_response.rs
// Module: router

pub fn build_pm_collect_link_html(
    link_data: &GenericLinkFormData,
) -> CustomResult<String, errors::ApiErrorResponse>


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

use api_models::gsm as gsm_api_types;
use error_stack::ResultExt;
use router_env::{instrument, tracing};

use crate::{
    core::errors::{self, RouterResponse, StorageErrorExt},
    db::gsm::GsmInterface,
    services,
    types::transformers::{ForeignFrom, ForeignInto},
    SessionState,
};

#[instrument(skip_all)]
pub async fn create_gsm_rule(
    state: SessionState,
    gsm_rule: gsm_api_types::GsmCreateRequest,
) -> RouterResponse<gsm_api_types::GsmResponse> {
    let db = state.store.as_ref();
    GsmInterface::add_gsm_rule(db, gsm_rule.foreign_into())
        .await
        .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
            message: "GSM with given key already exists in our records".to_string(),
        })
        .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into()))
}

#[instrument(skip_all)]
pub async fn retrieve_gsm_rule(
    state: SessionState,
    gsm_request: gsm_api_types::GsmRetrieveRequest,
) -> RouterResponse<gsm_api_types::GsmResponse> {
    let db = state.store.as_ref();
    let gsm_api_types::GsmRetrieveRequest {
        connector,
        flow,
        sub_flow,
        code,
        message,
    } = gsm_request;
    GsmInterface::find_gsm_rule(db, connector.to_string(), flow, sub_flow, code, message)
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "GSM with given key does not exist in our records".to_string(),
        })
        .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into()))
}

#[instrument(skip_all)]
pub async fn update_gsm_rule(
    state: SessionState,
    gsm_request: gsm_api_types::GsmUpdateRequest,
) -> RouterResponse<gsm_api_types::GsmResponse> {
    let db = state.store.as_ref();
    let connector = gsm_request.connector.clone();
    let flow = gsm_request.flow.clone();
    let code = gsm_request.code.clone();
    let sub_flow = gsm_request.sub_flow.clone();
    let message = gsm_request.message.clone();

    let gsm_db_record =
        GsmInterface::find_gsm_rule(db, connector.to_string(), flow, sub_flow, code, message)
            .await
            .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
                message: "GSM with given key does not exist in our records".to_string(),
            })?;

    let inferred_feature_info = <(
        common_enums::GsmFeature,
        common_types::domain::GsmFeatureData,
    )>::foreign_from((&gsm_request, gsm_db_record));

    let gsm_api_types::GsmUpdateRequest {
        connector,
        flow,
        sub_flow,
        code,
        message,
        decision,
        status,
        router_error,
        step_up_possible,
        unified_code,
        unified_message,
        error_category,
        clear_pan_possible,
        feature,
        feature_data,
    } = gsm_request;
    GsmInterface::update_gsm_rule(
        db,
        connector.to_string(),
        flow,
        sub_flow,
        code,
        message,
        hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate {
            decision,
            status,
            router_error: Some(router_error),
            step_up_possible: feature_data
                .as_ref()
                .and_then(|feature_data| feature_data.get_retry_feature_data())
                .map(|retry_feature_data| retry_feature_data.is_step_up_possible())
                .or(step_up_possible),
            unified_code,
            unified_message,
            error_category,
            clear_pan_possible: feature_data
                .as_ref()
                .and_then(|feature_data| feature_data.get_retry_feature_data())
                .map(|retry_feature_data| retry_feature_data.is_clear_pan_possible())
                .or(clear_pan_possible),
            feature_data: feature_data.or(Some(inferred_feature_info.1)),
            feature: feature.or(Some(inferred_feature_info.0)),
        },
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
        message: "GSM with given key does not exist in our records".to_string(),
    })
    .attach_printable("Failed while updating Gsm rule")
    .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into()))
}

#[instrument(skip_all)]
pub async fn delete_gsm_rule(
    state: SessionState,
    gsm_request: gsm_api_types::GsmDeleteRequest,
) -> RouterResponse<gsm_api_types::GsmDeleteResponse> {
    let db = state.store.as_ref();
    let gsm_api_types::GsmDeleteRequest {
        connector,
        flow,
        sub_flow,
        code,
        message,
    } = gsm_request;
    match GsmInterface::delete_gsm_rule(
        db,
        connector.to_string(),
        flow.to_owned(),
        sub_flow.to_owned(),
        code.to_owned(),
        message.to_owned(),
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
        message: "GSM with given key does not exist in our records".to_string(),
    })
    .attach_printable("Failed while Deleting Gsm rule")
    {
        Ok(is_deleted) => {
            if is_deleted {
                Ok(services::ApplicationResponse::Json(
                    gsm_api_types::GsmDeleteResponse {
                        gsm_rule_delete: true,
                        connector,
                        flow,
                        sub_flow,
                        code,
                    },
                ))
            } else {
                Err(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while Deleting Gsm rule, got response as false")
            }
        }
        Err(err) => Err(err),
    }
}


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

pub fn validate_payout_status_against_not_allowed_statuses(
    payout_status: api_enums::PayoutStatus,
    not_allowed_statuses: &[api_enums::PayoutStatus],
    action: &'static str,
) -> Result<(), errors::ApiErrorResponse>


// Struct: RequestExtendedAuthorizationBool
// File: crates/common_types/src/primitive_wrappers.rs
// Module: common_types
// Implementations: 2
// Traits: Deref

pub struct RequestExtendedAuthorizationBool


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

pub struct MagicLink


// File: crates/router/src/core/payment_methods/tokenize/card_executor.rs
// Module: router
// Public functions: 15
// Public structs: 8

use std::str::FromStr;

use ::payment_methods::{controller::PaymentMethodsController, core::migration};
use api_models::{enums as api_enums, payment_methods as payment_methods_api};
use common_utils::{
    consts,
    ext_traits::OptionExt,
    generate_customer_id_of_default_length, id_type,
    pii::Email,
    type_name,
    types::keymanager::{Identifier, KeyManagerState, ToEncryptable},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::type_encryption::{crypto_operation, CryptoOperation};
use masking::{ExposeInterface, PeekInterface, SwitchStrategy};
use router_env::logger;

use super::{
    CardNetworkTokenizeExecutor, NetworkTokenizationBuilder, NetworkTokenizationProcess,
    NetworkTokenizationResponse, State, StoreLockerResponse, TransitionTo,
};
use crate::{
    core::payment_methods::{
        cards::{add_card_to_hs_locker, PmCards},
        transformers as pm_transformers,
    },
    errors::{self, RouterResult},
    types::{api, domain},
    utils,
};

// Available states for card tokenization
pub struct TokenizeWithCard;
pub struct CardRequestValidated;
pub struct CardDetailsAssigned;
pub struct CustomerAssigned;
pub struct CardTokenized;
pub struct CardStored;
pub struct CardTokenStored;
pub struct PaymentMethodCreated;

impl State for TokenizeWithCard {}
impl State for CustomerAssigned {}
impl State for CardRequestValidated {}
impl State for CardDetailsAssigned {}
impl State for CardTokenized {}
impl State for CardStored {}
impl State for CardTokenStored {}
impl State for PaymentMethodCreated {}

// State transitions for card tokenization
impl TransitionTo<CardRequestValidated> for TokenizeWithCard {}
impl TransitionTo<CardDetailsAssigned> for CardRequestValidated {}
impl TransitionTo<CustomerAssigned> for CardDetailsAssigned {}
impl TransitionTo<CardTokenized> for CustomerAssigned {}
impl TransitionTo<CardTokenStored> for CardTokenized {}
impl TransitionTo<PaymentMethodCreated> for CardTokenStored {}

impl Default for NetworkTokenizationBuilder<'_, TokenizeWithCard> {
    fn default() -> Self {
        Self::new()
    }
}

impl<'a> NetworkTokenizationBuilder<'a, TokenizeWithCard> {
    pub fn new() -> Self {
        Self {
            state: std::marker::PhantomData,
            customer: None,
            card: None,
            card_cvc: None,
            network_token: None,
            stored_card: None,
            stored_token: None,
            payment_method_response: None,
            card_tokenized: false,
            error_code: None,
            error_message: None,
        }
    }
    pub fn set_validate_result(self) -> NetworkTokenizationBuilder<'a, CardRequestValidated> {
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            customer: self.customer,
            card: self.card,
            card_cvc: self.card_cvc,
            network_token: self.network_token,
            stored_card: self.stored_card,
            stored_token: self.stored_token,
            payment_method_response: self.payment_method_response,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}

impl<'a> NetworkTokenizationBuilder<'a, CardRequestValidated> {
    pub fn set_card_details(
        self,
        card_req: &'a domain::TokenizeCardRequest,
        optional_card_info: Option<diesel_models::CardInfo>,
    ) -> NetworkTokenizationBuilder<'a, CardDetailsAssigned> {
        let card = domain::CardDetail {
            card_number: card_req.raw_card_number.clone(),
            card_exp_month: card_req.card_expiry_month.clone(),
            card_exp_year: card_req.card_expiry_year.clone(),
            bank_code: optional_card_info
                .as_ref()
                .and_then(|card_info| card_info.bank_code.clone()),
            nick_name: card_req.nick_name.clone(),
            card_holder_name: card_req.card_holder_name.clone(),
            card_issuer: optional_card_info
                .as_ref()
                .map_or(card_req.card_issuer.clone(), |card_info| {
                    card_info.card_issuer.clone()
                }),
            card_network: optional_card_info
                .as_ref()
                .map_or(card_req.card_network.clone(), |card_info| {
                    card_info.card_network.clone()
                }),
            card_type: optional_card_info.as_ref().map_or(
                card_req
                    .card_type
                    .as_ref()
                    .map(|card_type| card_type.to_string()),
                |card_info| card_info.card_type.clone(),
            ),
            card_issuing_country: optional_card_info
                .as_ref()
                .map_or(card_req.card_issuing_country.clone(), |card_info| {
                    card_info.card_issuing_country.clone()
                }),
            co_badged_card_data: None,
        };
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            card: Some(card),
            card_cvc: card_req.card_cvc.clone(),
            customer: self.customer,
            network_token: self.network_token,
            stored_card: self.stored_card,
            stored_token: self.stored_token,
            payment_method_response: self.payment_method_response,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}

impl<'a> NetworkTokenizationBuilder<'a, CardDetailsAssigned> {
    pub fn set_customer(
        self,
        customer: &'a api::CustomerDetails,
    ) -> NetworkTokenizationBuilder<'a, CustomerAssigned> {
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            customer: Some(customer),
            card: self.card,
            card_cvc: self.card_cvc,
            network_token: self.network_token,
            stored_card: self.stored_card,
            stored_token: self.stored_token,
            payment_method_response: self.payment_method_response,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}

impl<'a> NetworkTokenizationBuilder<'a, CustomerAssigned> {
    pub fn get_optional_card_and_cvc(
        &self,
    ) -> (Option<domain::CardDetail>, Option<masking::Secret<String>>) {
        (self.card.clone(), self.card_cvc.clone())
    }
    pub fn set_token_details(
        self,
        network_token: &'a NetworkTokenizationResponse,
    ) -> NetworkTokenizationBuilder<'a, CardTokenized> {
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            network_token: Some(&network_token.0),
            customer: self.customer,
            card: self.card,
            card_cvc: self.card_cvc,
            stored_card: self.stored_card,
            stored_token: self.stored_token,
            payment_method_response: self.payment_method_response,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}

impl<'a> NetworkTokenizationBuilder<'a, CardTokenized> {
    pub fn set_stored_card_response(
        self,
        store_card_response: &'a StoreLockerResponse,
    ) -> NetworkTokenizationBuilder<'a, CardStored> {
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            stored_card: Some(&store_card_response.store_card_resp),
            customer: self.customer,
            card: self.card,
            card_cvc: self.card_cvc,
            network_token: self.network_token,
            stored_token: self.stored_token,
            payment_method_response: self.payment_method_response,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}

impl<'a> NetworkTokenizationBuilder<'a, CardStored> {
    pub fn set_stored_token_response(
        self,
        store_token_response: &'a StoreLockerResponse,
    ) -> NetworkTokenizationBuilder<'a, CardTokenStored> {
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            card_tokenized: true,
            stored_token: Some(&store_token_response.store_token_resp),
            customer: self.customer,
            card: self.card,
            card_cvc: self.card_cvc,
            network_token: self.network_token,
            stored_card: self.stored_card,
            payment_method_response: self.payment_method_response,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}

impl<'a> NetworkTokenizationBuilder<'a, CardTokenStored> {
    pub fn set_payment_method_response(
        self,
        payment_method: &'a domain::PaymentMethod,
    ) -> NetworkTokenizationBuilder<'a, PaymentMethodCreated> {
        let card_detail_from_locker = self.card.as_ref().map(|card| api::CardDetailFromLocker {
            scheme: None,
            issuer_country: card.card_issuing_country.clone(),
            last4_digits: Some(card.card_number.clone().get_last4()),
            card_number: None,
            expiry_month: Some(card.card_exp_month.clone().clone()),
            expiry_year: Some(card.card_exp_year.clone().clone()),
            card_token: None,
            card_holder_name: card.card_holder_name.clone(),
            card_fingerprint: None,
            nick_name: card.nick_name.clone(),
            card_network: card.card_network.clone(),
            card_isin: Some(card.card_number.clone().get_card_isin()),
            card_issuer: card.card_issuer.clone(),
            card_type: card.card_type.clone(),
            saved_to_locker: true,
        });
        let payment_method_response = api::PaymentMethodResponse {
            merchant_id: payment_method.merchant_id.clone(),
            customer_id: Some(payment_method.customer_id.clone()),
            payment_method_id: payment_method.payment_method_id.clone(),
            payment_method: payment_method.payment_method,
            payment_method_type: payment_method.payment_method_type,
            card: card_detail_from_locker,
            recurring_enabled: Some(true),
            installment_payment_enabled: Some(false),
            metadata: payment_method.metadata.clone(),
            created: Some(payment_method.created_at),
            last_used_at: Some(payment_method.last_used_at),
            client_secret: payment_method.client_secret.clone(),
            bank_transfer: None,
            payment_experience: None,
        };
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            payment_method_response: Some(payment_method_response),
            customer: self.customer,
            card: self.card,
            card_cvc: self.card_cvc,
            network_token: self.network_token,
            stored_card: self.stored_card,
            stored_token: self.stored_token,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}

impl NetworkTokenizationBuilder<'_, PaymentMethodCreated> {
    pub fn build(self) -> api::CardNetworkTokenizeResponse {
        api::CardNetworkTokenizeResponse {
            payment_method_response: self.payment_method_response,
            customer: self.customer.cloned(),
            card_tokenized: self.card_tokenized,
            error_code: self.error_code.cloned(),
            error_message: self.error_message.cloned(),
            // Below field is mutated by caller functions for batched API operations
            tokenization_data: None,
        }
    }
}

// Specific executor for card tokenization
impl CardNetworkTokenizeExecutor<'_, domain::TokenizeCardRequest> {
    pub async fn validate_request_and_fetch_optional_customer(
        &self,
    ) -> RouterResult<Option<api::CustomerDetails>> {
        // Validate card's expiry
        migration::validate_card_expiry(&self.data.card_expiry_month, &self.data.card_expiry_year)?;

        // Validate customer ID
        let customer_id = self
            .customer
            .customer_id
            .as_ref()
            .get_required_value("customer_id")
            .change_context(errors::ApiErrorResponse::MissingRequiredField {
                field_name: "customer.customer_id",
            })?;

        // Fetch customer details if present
        let db = &*self.state.store;
        let key_manager_state: &KeyManagerState = &self.state.into();
        db.find_customer_optional_by_customer_id_merchant_id(
            key_manager_state,
            customer_id,
            self.merchant_account.get_id(),
            self.key_store,
            self.merchant_account.storage_scheme,
        )
        .await
        .inspect_err(|err| logger::info!("Error fetching customer: {:?}", err))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .map_or(
            // Validate if customer creation is feasible
            if self.customer.name.is_some()
                || self.customer.email.is_some()
                || self.customer.phone.is_some()
            {
                Ok(None)
            } else {
                Err(report!(errors::ApiErrorResponse::MissingRequiredFields {
                    field_names: vec!["customer.name", "customer.email", "customer.phone"],
                }))
            },
            // If found, send back CustomerDetails from DB
            |optional_customer| {
                Ok(optional_customer.map(|customer| api::CustomerDetails {
                    id: customer.customer_id.clone(),
                    name: customer.name.clone().map(|name| name.into_inner()),
                    email: customer.email.clone().map(Email::from),
                    phone: customer.phone.clone().map(|phone| phone.into_inner()),
                    phone_country_code: customer.phone_country_code.clone(),
                    tax_registration_id: customer
                        .tax_registration_id
                        .clone()
                        .map(|tax_registration_id| tax_registration_id.into_inner()),
                }))
            },
        )
    }

    pub async fn create_customer(&self) -> RouterResult<api::CustomerDetails> {
        let db = &*self.state.store;
        let customer_id = self
            .customer
            .customer_id
            .as_ref()
            .get_required_value("customer_id")
            .change_context(errors::ApiErrorResponse::MissingRequiredField {
                field_name: "customer_id",
            })?;
        let key_manager_state: &KeyManagerState = &self.state.into();

        let encrypted_data = crypto_operation(
            key_manager_state,
            type_name!(domain::Customer),
            CryptoOperation::BatchEncrypt(domain::FromRequestEncryptableCustomer::to_encryptable(
                domain::FromRequestEncryptableCustomer {
                    name: self.customer.name.clone(),
                    email: self
                        .customer
                        .email
                        .clone()
                        .map(|email| email.expose().switch_strategy()),
                    phone: self.customer.phone.clone(),
                    tax_registration_id: self.customer.tax_registration_id.clone(),
                },
            )),
            Identifier::Merchant(self.merchant_account.get_id().clone()),
            self.key_store.key.get_inner().peek(),
        )
        .await
        .inspect_err(|err| logger::info!("Error encrypting customer: {:?}", err))
        .and_then(|val| val.try_into_batchoperation())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to encrypt customer")?;

        let encryptable_customer =
            domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to form EncryptableCustomer")?;

        let new_customer_id = generate_customer_id_of_default_length();
        let domain_customer = domain::Customer {
            customer_id: new_customer_id.clone(),
            merchant_id: self.merchant_account.get_id().clone(),
            name: encryptable_customer.name,
            email: encryptable_customer.email.map(|email| {
                utils::Encryptable::new(
                    email.clone().into_inner().switch_strategy(),
                    email.into_encrypted(),
                )
            }),
            phone: encryptable_customer.phone,
            description: None,
            phone_country_code: self.customer.phone_country_code.to_owned(),
            metadata: None,
            connector_customer: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            address_id: None,
            default_payment_method_id: None,
            updated_by: None,
            version: common_types::consts::API_VERSION,
            tax_registration_id: encryptable_customer.tax_registration_id,
        };

        db.insert_customer(
            domain_customer,
            key_manager_state,
            self.key_store,
            self.merchant_account.storage_scheme,
        )
        .await
        .inspect_err(|err| logger::info!("Error creating a customer: {:?}", err))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Failed to insert customer [id - {:?}] for merchant [id - {:?}]",
                customer_id,
                self.merchant_account.get_id()
            )
        })?;

        Ok(api::CustomerDetails {
            id: new_customer_id,
            name: self.customer.name.clone(),
            email: self.customer.email.clone(),
            phone: self.customer.phone.clone(),
            phone_country_code: self.customer.phone_country_code.clone(),
            tax_registration_id: self.customer.tax_registration_id.clone(),
        })
    }

    pub async fn store_card_and_token_in_locker(
        &self,
        network_token: &NetworkTokenizationResponse,
        card: &domain::CardDetail,
        customer_id: &id_type::CustomerId,
    ) -> RouterResult<StoreLockerResponse> {
        let stored_card_resp = self.store_card_in_locker(card, customer_id).await?;
        let stored_token_resp = self
            .store_network_token_in_locker(
                network_token,
                customer_id,
                card.card_holder_name.clone(),
                card.nick_name.clone(),
            )
            .await?;
        let store_locker_response = StoreLockerResponse {
            store_card_resp: stored_card_resp,
            store_token_resp: stored_token_resp,
        };
        Ok(store_locker_response)
    }

    pub async fn store_card_in_locker(
        &self,
        card: &domain::CardDetail,
        customer_id: &id_type::CustomerId,
    ) -> RouterResult<pm_transformers::StoreCardRespPayload> {
        let merchant_id = self.merchant_account.get_id();
        let locker_req =
            pm_transformers::StoreLockerReq::LockerCard(pm_transformers::StoreCardReq {
                merchant_id: merchant_id.clone(),
                merchant_customer_id: customer_id.clone(),
                card: payment_methods_api::Card {
                    card_number: card.card_number.clone(),
                    card_exp_month: card.card_exp_month.clone(),
                    card_exp_year: card.card_exp_year.clone(),
                    card_isin: Some(card.card_number.get_card_isin().clone()),
                    name_on_card: card.card_holder_name.clone(),
                    nick_name: card
                        .nick_name
                        .as_ref()
                        .map(|nick_name| nick_name.clone().expose()),
                    card_brand: None,
                },
                requestor_card_reference: None,
                ttl: self.state.conf.locker.ttl_for_storage_in_secs,
            });

        let stored_resp = add_card_to_hs_locker(
            self.state,
            &locker_req,
            customer_id,
            api_enums::LockerChoice::HyperswitchCardVault,
        )
        .await
        .inspect_err(|err| logger::info!("Error adding card in locker: {:?}", err))
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

        Ok(stored_resp)
    }

    pub async fn create_payment_method(
        &self,
        stored_locker_resp: &StoreLockerResponse,
        network_token_details: &NetworkTokenizationResponse,
        card_details: &domain::CardDetail,
        customer_id: &id_type::CustomerId,
    ) -> RouterResult<domain::PaymentMethod> {
        let payment_method_id = common_utils::generate_id(consts::ID_LENGTH, "pm");

        // Form encrypted PM data (original card)
        let enc_pm_data = self.encrypt_card(card_details, true).await?;

        // Form encrypted network token data
        let enc_token_data = self
            .encrypt_network_token(network_token_details, card_details, true)
            .await?;

        // Form PM create entry
        let payment_method_create = api::PaymentMethodCreate {
            payment_method: Some(api_enums::PaymentMethod::Card),
            payment_method_type: card_details
                .card_type
                .as_ref()
                .and_then(|card_type| api_enums::PaymentMethodType::from_str(card_type).ok()),
            payment_method_issuer: card_details.card_issuer.clone(),
            payment_method_issuer_code: None,
            card: Some(api::CardDetail {
                card_number: card_details.card_number.clone(),
                card_exp_month: card_details.card_exp_month.clone(),
                card_exp_year: card_details.card_exp_year.clone(),
                card_holder_name: card_details.card_holder_name.clone(),
                nick_name: card_details.nick_name.clone(),
                card_issuing_country: card_details.card_issuing_country.clone(),
                card_network: card_details.card_network.clone(),
                card_issuer: card_details.card_issuer.clone(),
                card_type: card_details.card_type.clone(),
            }),
            metadata: None,
            customer_id: Some(customer_id.clone()),
            card_network: card_details
                .card_network
                .as_ref()
                .map(|network| network.to_string()),
            bank_transfer: None,
            wallet: None,
            client_secret: None,
            payment_method_data: None,
            billing: None,
            connector_mandate_details: None,
            network_transaction_id: None,
        };
        PmCards {
            state: self.state,
            merchant_context: &domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
                self.merchant_account.clone(),
                self.key_store.clone(),
            ))),
        }
        .create_payment_method(
            &payment_method_create,
            customer_id,
            &payment_method_id,
            Some(stored_locker_resp.store_card_resp.card_reference.clone()),
            self.merchant_account.get_id(),
            None,
            None,
            Some(enc_pm_data),
            None,
            None,
            None,
            None,
            None,
            network_token_details.1.clone(),
            Some(stored_locker_resp.store_token_resp.card_reference.clone()),
            Some(enc_token_data),
            Default::default(), // this method is used only for card bulk tokenization, and currently external vault is not supported for this hence passing Default i.e. InternalVault
        )
        .await
    }
}


// File: crates/router/src/services/authentication/cookies.rs
// Module: router
// Public functions: 4

use cookie::Cookie;
#[cfg(feature = "olap")]
use cookie::{
    time::{Duration, OffsetDateTime},
    SameSite,
};
use error_stack::{report, ResultExt};
#[cfg(feature = "olap")]
use masking::Mask;
#[cfg(feature = "olap")]
use masking::{ExposeInterface, Secret};

use crate::{
    consts::JWT_TOKEN_COOKIE_NAME,
    core::errors::{ApiErrorResponse, RouterResult},
};
#[cfg(feature = "olap")]
use crate::{
    consts::JWT_TOKEN_TIME_IN_SECS,
    core::errors::{UserErrors, UserResponse},
    services::ApplicationResponse,
};

#[cfg(feature = "olap")]
pub fn set_cookie_response<R>(response: R, token: Secret<String>) -> UserResponse<R> {
    let jwt_expiry_in_seconds = JWT_TOKEN_TIME_IN_SECS
        .try_into()
        .map_err(|_| UserErrors::InternalServerError)?;
    let (expiry, max_age) = get_expiry_and_max_age_from_seconds(jwt_expiry_in_seconds);

    let header_value = create_cookie(token, expiry, max_age)
        .to_string()
        .into_masked();
    let header_key = get_set_cookie_header();
    let header = vec![(header_key, header_value)];

    Ok(ApplicationResponse::JsonWithHeaders((response, header)))
}

#[cfg(feature = "olap")]
pub fn remove_cookie_response() -> UserResponse<()> {
    let (expiry, max_age) = get_expiry_and_max_age_from_seconds(0);

    let header_key = get_set_cookie_header();
    let header_value = create_cookie("".to_string().into(), expiry, max_age)
        .to_string()
        .into_masked();
    let header = vec![(header_key, header_value)];
    Ok(ApplicationResponse::JsonWithHeaders(((), header)))
}

pub fn get_jwt_from_cookies(cookies: &str) -> RouterResult<String> {
    Cookie::split_parse(cookies)
        .find_map(|cookie| {
            cookie
                .ok()
                .filter(|parsed_cookie| parsed_cookie.name() == JWT_TOKEN_COOKIE_NAME)
                .map(|parsed_cookie| parsed_cookie.value().to_owned())
        })
        .ok_or(report!(ApiErrorResponse::InvalidJwtToken))
        .attach_printable("Unable to find JWT token in cookies")
}

#[cfg(feature = "olap")]
fn create_cookie<'c>(
    token: Secret<String>,
    expires: OffsetDateTime,
    max_age: Duration,
) -> Cookie<'c> {
    Cookie::build((JWT_TOKEN_COOKIE_NAME, token.expose()))
        .http_only(true)
        .secure(true)
        .same_site(SameSite::Strict)
        .path("/")
        .expires(expires)
        .max_age(max_age)
        .build()
}

#[cfg(feature = "olap")]
fn get_expiry_and_max_age_from_seconds(seconds: i64) -> (OffsetDateTime, Duration) {
    let max_age = Duration::seconds(seconds);
    let expiry = OffsetDateTime::now_utc().saturating_add(max_age);
    (expiry, max_age)
}

#[cfg(feature = "olap")]
fn get_set_cookie_header() -> String {
    actix_http::header::SET_COOKIE.to_string()
}

pub fn get_cookie_header() -> String {
    actix_http::header::COOKIE.to_string()
}


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

pub struct PaysafeGiftCard


// Trait: AccountsStorageInterface
// File: crates/router/src/db.rs
// Module: router

pub trait AccountsStorageInterface: Send
    + Sync
    + dyn_clone::DynClone
    + OrganizationInterface
    + merchant_account::MerchantAccountInterface
    + business_profile::ProfileInterface
    + merchant_connector_account::MerchantConnectorAccountInterface
    + merchant_key_store::MerchantKeyStoreInterface
    + dashboard_metadata::DashboardMetadataInterface
    + 'static


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

pub struct GocardlessPaymentsResponse


// Function: build_unified_connector_service_grpc_headers
// File: crates/external_services/src/grpc_client/unified_connector_service.rs
// Module: external_services

// Documentation: Build the gRPC Headers for Unified Connector Service Request

pub fn build_unified_connector_service_grpc_headers(
    meta: ConnectorAuthMetadata,
    grpc_headers: GrpcHeadersUcs,
) -> Result<MetadataMap, UnifiedConnectorServiceError>


// Struct: EntityTypeQueryParam
// File: crates/api_models/src/user/theme.rs
// Module: api_models
// Implementations: 0

pub struct EntityTypeQueryParam


// Function: get_encrypted_apple_pay_payment_data_optional
// File: crates/common_types/src/payments.rs
// Module: common_types

// Documentation: Get the encrypted Apple Pay payment data if it exists

pub fn get_encrypted_apple_pay_payment_data_optional(&self) -> Option<&String>


// Implementation: impl KafkaConsolidatedEvent
// File: crates/router/src/services/kafka.rs
// Module: router
// Methods: 1 total (0 public)

impl KafkaConsolidatedEvent


// Struct: PayboxRsyncRequest
// File: crates/hyperswitch_connectors/src/connectors/paybox/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PayboxRsyncRequest


// Implementation: impl FrmMetricsBucketIdentifier
// File: crates/api_models/src/analytics/frm.rs
// Module: api_models
// Methods: 1 total (1 public)

impl FrmMetricsBucketIdentifier


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

pub fn new() -> &'static Self


// Implementation: impl ConnectorValidation for for Nomupay
// File: crates/hyperswitch_connectors/src/connectors/nomupay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorValidation for for Nomupay