// File: crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs
// Module: hyperswitch_connectors
// Public functions: 1
// Public structs: 33

use common_enums::{enums, AttemptStatus, BankNames};
use common_utils::{
    errors::ParsingError,
    pii::{Email, IpAddress},
    request::Method,
    types::{FloatMajorUnit, MinorUnit},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankRedirectData, PayLaterData, PaymentMethodData, WalletData},
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{
        MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData,
    },
    types::{self},
};
use hyperswitch_interfaces::{
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    errors,
};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        self, AddressDetailsData, CardData as _, PaymentsAuthorizeRequestData, RouterData as _,
    },
};

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

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

#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Type {
    Direct,
    Redirect,
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum Gateway {
    Amex,
    CreditCard,
    Discover,
    Maestro,
    MasterCard,
    Visa,
    Klarna,
    Googlepay,
    Paypal,
    Ideal,
    Giropay,
    Trustly,
    Alipay,
    #[serde(rename = "WECHAT")]
    WeChatPay,
    Eps,
    MbWay,
    #[serde(rename = "DIRECTBANK")]
    Sofort,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Coupons {
    pub allow: Option<Vec<Secret<String>>>,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Mistercash {
    pub mobile_pay_button_position: Option<String>,
    pub disable_mobile_pay_button: Option<String>,
    pub qr_only: Option<String>,
    pub qr_size: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct Gateways {
    pub mistercash: Option<Mistercash>,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Settings {
    pub coupons: Option<Coupons>,
    pub gateways: Option<Gateways>,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct PaymentOptions {
    pub notification_url: Option<String>,
    pub notification_method: Option<String>,
    pub redirect_url: String,
    pub cancel_url: String,
    pub close_window: Option<bool>,
    pub settings: Option<Settings>,
    pub template_id: Option<String>,
    pub allowed_countries: Option<Vec<String>>,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Browser {
    pub javascript_enabled: Option<bool>,
    pub java_enabled: Option<bool>,
    pub cookies_enabled: Option<bool>,
    pub language: Option<String>,
    pub screen_color_depth: Option<i32>,
    pub screen_height: Option<i32>,
    pub screen_width: Option<i32>,
    pub time_zone: Option<i32>,
    pub user_agent: Option<String>,
    pub platform: Option<String>,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Customer {
    pub browser: Option<Browser>,
    pub locale: Option<String>,
    pub ip_address: Option<Secret<String, IpAddress>>,
    pub forward_ip: Option<Secret<String, IpAddress>>,
    pub first_name: Option<Secret<String>>,
    pub last_name: Option<Secret<String>>,
    pub gender: Option<Secret<String>>,
    pub birthday: Option<Secret<String>>,
    pub address1: Option<Secret<String>>,
    pub address2: Option<Secret<String>>,
    pub house_number: Option<Secret<String>>,
    pub zip_code: Option<Secret<String>>,
    pub city: Option<String>,
    pub state: Option<String>,
    pub country: Option<String>,
    pub phone: Option<Secret<String>>,
    pub email: Option<Email>,
    pub user_agent: Option<String>,
    pub referrer: Option<String>,
    pub reference: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct CardInfo {
    pub card_number: Option<cards::CardNumber>,
    pub card_holder_name: Option<Secret<String>>,
    pub card_expiry_date: Option<Secret<i32>>,
    pub card_cvc: Option<Secret<String>>,
    pub flexible_3d: Option<bool>,
    pub moto: Option<bool>,
    pub term_url: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct GpayInfo {
    pub payment_token: Option<Secret<String>>,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct PayLaterInfo {
    pub email: Option<Email>,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum GatewayInfo {
    Card(CardInfo),
    Wallet(WalletInfo),
    PayLater(PayLaterInfo),
    BankRedirect(BankRedirectInfo),
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum WalletInfo {
    GooglePay(GpayInfo),
    Alipay(AlipayInfo),
    WeChatPay(WeChatPayInfo),
    MbWay(MbWayInfo),
}

#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct MbWayInfo {}

#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct WeChatPayInfo {}
#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct AlipayInfo {}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum BankRedirectInfo {
    Ideal(IdealInfo),
    Trustly(TrustlyInfo),
    Eps(EpsInfo),
    Sofort(SofortInfo),
}

#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct SofortInfo {}

#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct EpsInfo {}
#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct TrustlyInfo {}
#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct IdealInfo {
    pub issuer_id: MultisafepayBankNames,
}

#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub enum MultisafepayBankNames {
    #[serde(rename = "0031")]
    AbnAmro,
    #[serde(rename = "0761")]
    AsnBank,
    #[serde(rename = "4371")]
    Bunq,
    #[serde(rename = "0721")]
    Ing,
    #[serde(rename = "0801")]
    Knab,
    #[serde(rename = "9926")]
    N26,
    #[serde(rename = "9927")]
    NationaleNederlanden,
    #[serde(rename = "0021")]
    Rabobank,
    #[serde(rename = "0771")]
    Regiobank,
    #[serde(rename = "1099")]
    Revolut,
    #[serde(rename = "0751")]
    SnsBank,
    #[serde(rename = "0511")]
    TriodosBank,
    #[serde(rename = "0161")]
    VanLanschot,
    #[serde(rename = "0806")]
    Yoursafe,
    #[serde(rename = "1235")]
    Handelsbanken,
}

impl TryFrom<&BankNames> for MultisafepayBankNames {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(bank: &BankNames) -> Result<Self, Self::Error> {
        match bank {
            BankNames::AbnAmro => Ok(Self::AbnAmro),
            BankNames::AsnBank => Ok(Self::AsnBank),
            BankNames::Bunq => Ok(Self::Bunq),
            BankNames::Ing => Ok(Self::Ing),
            BankNames::Knab => Ok(Self::Knab),
            BankNames::N26 => Ok(Self::N26),
            BankNames::NationaleNederlanden => Ok(Self::NationaleNederlanden),
            BankNames::Rabobank => Ok(Self::Rabobank),
            BankNames::Regiobank => Ok(Self::Regiobank),
            BankNames::Revolut => Ok(Self::Revolut),
            BankNames::SnsBank => Ok(Self::SnsBank),
            BankNames::TriodosBank => Ok(Self::TriodosBank),
            BankNames::VanLanschot => Ok(Self::VanLanschot),
            BankNames::Yoursafe => Ok(Self::Yoursafe),
            BankNames::Handelsbanken => Ok(Self::Handelsbanken),
            BankNames::AmericanExpress
            | BankNames::AffinBank
            | BankNames::AgroBank
            | BankNames::AllianceBank
            | BankNames::AmBank
            | BankNames::BankOfAmerica
            | BankNames::BankOfChina
            | BankNames::BankIslam
            | BankNames::BankMuamalat
            | BankNames::BankRakyat
            | BankNames::BankSimpananNasional
            | BankNames::Barclays
            | BankNames::BlikPSP
            | BankNames::CapitalOne
            | BankNames::Chase
            | BankNames::Citi
            | BankNames::CimbBank
            | BankNames::Discover
            | BankNames::NavyFederalCreditUnion
            | BankNames::PentagonFederalCreditUnion
            | BankNames::SynchronyBank
            | BankNames::WellsFargo
            | BankNames::HongLeongBank
            | BankNames::HsbcBank
            | BankNames::KuwaitFinanceHouse
            | BankNames::Moneyou
            | BankNames::ArzteUndApothekerBank
            | BankNames::AustrianAnadiBankAg
            | BankNames::BankAustria
            | BankNames::Bank99Ag
            | BankNames::BankhausCarlSpangler
            | BankNames::BankhausSchelhammerUndSchatteraAg
            | BankNames::BankMillennium
            | BankNames::BankPEKAOSA
            | BankNames::BawagPskAg
            | BankNames::BksBankAg
            | BankNames::BrullKallmusBankAg
            | BankNames::BtvVierLanderBank
            | BankNames::CapitalBankGraweGruppeAg
            | BankNames::CeskaSporitelna
            | BankNames::Dolomitenbank
            | BankNames::EasybankAg
            | BankNames::EPlatbyVUB
            | BankNames::ErsteBankUndSparkassen
            | BankNames::FrieslandBank
            | BankNames::HypoAlpeadriabankInternationalAg
            | BankNames::HypoNoeLbFurNiederosterreichUWien
            | BankNames::HypoOberosterreichSalzburgSteiermark
            | BankNames::HypoTirolBankAg
            | BankNames::HypoVorarlbergBankAg
            | BankNames::HypoBankBurgenlandAktiengesellschaft
            | BankNames::KomercniBanka
            | BankNames::MBank
            | BankNames::MarchfelderBank
            | BankNames::Maybank
            | BankNames::OberbankAg
            | BankNames::OsterreichischeArzteUndApothekerbank
            | BankNames::OcbcBank
            | BankNames::PayWithING
            | BankNames::PlaceZIPKO
            | BankNames::PlatnoscOnlineKartaPlatnicza
            | BankNames::PosojilnicaBankEGen
            | BankNames::PostovaBanka
            | BankNames::PublicBank
            | BankNames::RaiffeisenBankengruppeOsterreich
            | BankNames::RhbBank
            | BankNames::SchelhammerCapitalBankAg
            | BankNames::StandardCharteredBank
            | BankNames::SchoellerbankAg
            | BankNames::SpardaBankWien
            | BankNames::SporoPay
            | BankNames::SantanderPrzelew24
            | BankNames::TatraPay
            | BankNames::Viamo
            | BankNames::VolksbankGruppe
            | BankNames::VolkskreditbankAg
            | BankNames::VrBankBraunau
            | BankNames::UobBank
            | BankNames::PayWithAliorBank
            | BankNames::BankiSpoldzielcze
            | BankNames::PayWithInteligo
            | BankNames::BNPParibasPoland
            | BankNames::BankNowySA
            | BankNames::CreditAgricole
            | BankNames::PayWithBOS
            | BankNames::PayWithCitiHandlowy
            | BankNames::PayWithPlusBank
            | BankNames::ToyotaBank
            | BankNames::VeloBank
            | BankNames::ETransferPocztowy24
            | BankNames::PlusBank
            | BankNames::EtransferPocztowy24
            | BankNames::BankiSpbdzielcze
            | BankNames::BankNowyBfgSa
            | BankNames::GetinBank
            | BankNames::Blik
            | BankNames::NoblePay
            | BankNames::IdeaBank
            | BankNames::EnveloBank
            | BankNames::NestPrzelew
            | BankNames::MbankMtransfer
            | BankNames::Inteligo
            | BankNames::PbacZIpko
            | BankNames::BnpParibas
            | BankNames::BankPekaoSa
            | BankNames::VolkswagenBank
            | BankNames::AliorBank
            | BankNames::Boz
            | BankNames::BangkokBank
            | BankNames::KrungsriBank
            | BankNames::KrungThaiBank
            | BankNames::TheSiamCommercialBank
            | BankNames::KasikornBank
            | BankNames::OpenBankSuccess
            | BankNames::OpenBankFailure
            | BankNames::OpenBankCancelled
            | BankNames::Aib
            | BankNames::BankOfScotland
            | BankNames::DanskeBank
            | BankNames::FirstDirect
            | BankNames::FirstTrust
            | BankNames::Halifax
            | BankNames::Lloyds
            | BankNames::Monzo
            | BankNames::NatWest
            | BankNames::NationwideBank
            | BankNames::RoyalBankOfScotland
            | BankNames::Starling
            | BankNames::TsbBank
            | BankNames::TescoBank
            | BankNames::UlsterBank => Err(Into::into(errors::ConnectorError::NotSupported {
                message: String::from("BankRedirect"),
                connector: "Multisafepay",
            })),
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct DeliveryObject {
    first_name: Secret<String>,
    last_name: Secret<String>,
    address1: Secret<String>,
    house_number: Secret<String>,
    zip_code: Secret<String>,
    city: String,
    country: api_models::enums::CountryAlpha2,
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DefaultObject {
    shipping_taxed: bool,
    rate: f64,
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct TaxObject {
    pub default: DefaultObject,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct CheckoutOptions {
    pub validate_cart: Option<bool>,
    pub tax_tables: TaxObject,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Item {
    pub name: String,
    pub unit_price: FloatMajorUnit,
    pub description: Option<String>,
    pub quantity: i64,
}

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

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct MultisafepayPaymentsRequest {
    #[serde(rename = "type")]
    pub payment_type: Type,
    pub gateway: Option<Gateway>,
    pub order_id: String,
    pub currency: String,
    pub amount: MinorUnit,
    pub description: String,
    pub payment_options: Option<PaymentOptions>,
    pub customer: Option<Customer>,
    pub gateway_info: Option<GatewayInfo>,
    pub delivery: Option<DeliveryObject>,
    pub checkout_options: Option<CheckoutOptions>,
    pub shopping_cart: Option<ShoppingCart>,
    pub items: Option<String>,
    pub recurring_model: Option<MandateType>,
    pub recurring_id: Option<Secret<String>>,
    pub capture: Option<String>,
    pub days_active: Option<i32>,
    pub seconds_active: Option<i32>,
    pub var1: Option<String>,
    pub var2: Option<String>,
    pub var3: Option<String>,
}

impl TryFrom<utils::CardIssuer> for Gateway {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(issuer: utils::CardIssuer) -> Result<Self, Self::Error> {
        match issuer {
            utils::CardIssuer::AmericanExpress => Ok(Self::Amex),
            utils::CardIssuer::Master => Ok(Self::MasterCard),
            utils::CardIssuer::Maestro => Ok(Self::Maestro),
            utils::CardIssuer::Discover => Ok(Self::Discover),
            utils::CardIssuer::Visa => Ok(Self::Visa),
            utils::CardIssuer::DinersClub
            | utils::CardIssuer::JCB
            | utils::CardIssuer::CarteBlanche
            | utils::CardIssuer::UnionPay
            | utils::CardIssuer::CartesBancaires => Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Multisafe pay"),
            )
            .into()),
        }
    }
}

impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>>
    for MultisafepayPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let payment_type = match item.router_data.request.payment_method_data {
            PaymentMethodData::Card(ref _ccard) => Type::Direct,
            PaymentMethodData::MandatePayment => Type::Direct,
            PaymentMethodData::Wallet(ref wallet_data) => match wallet_data {
                WalletData::GooglePay(_) => Type::Direct,
                WalletData::PaypalRedirect(_) => Type::Redirect,
                WalletData::AliPayRedirect(_) => Type::Redirect,
                WalletData::WeChatPayRedirect(_) => Type::Redirect,
                WalletData::MbWayRedirect(_) => Type::Redirect,
                WalletData::AliPayQr(_)
                | WalletData::AliPayHkRedirect(_)
                | WalletData::AmazonPay(_)
                | WalletData::AmazonPayRedirect(_)
                | WalletData::Paysera(_)
                | WalletData::Skrill(_)
                | WalletData::BluecodeRedirect {}
                | WalletData::MomoRedirect(_)
                | WalletData::KakaoPayRedirect(_)
                | WalletData::GoPayRedirect(_)
                | WalletData::GcashRedirect(_)
                | WalletData::ApplePay(_)
                | WalletData::ApplePayRedirect(_)
                | WalletData::ApplePayThirdPartySdk(_)
                | WalletData::DanaRedirect {}
                | WalletData::GooglePayRedirect(_)
                | WalletData::GooglePayThirdPartySdk(_)
                | WalletData::MobilePayRedirect(_)
                | WalletData::PaypalSdk(_)
                | WalletData::Paze(_)
                | WalletData::SamsungPay(_)
                | WalletData::TwintRedirect {}
                | WalletData::VippsRedirect {}
                | WalletData::TouchNGoRedirect(_)
                | WalletData::WeChatPayQr(_)
                | WalletData::CashappQr(_)
                | WalletData::SwishQr(_)
                | WalletData::Mifinity(_)
                | WalletData::RevolutPay(_) => Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("multisafepay"),
                ))?,
            },
            PaymentMethodData::BankRedirect(ref bank_data) => match bank_data {
                BankRedirectData::Giropay { .. } => Type::Redirect,
                BankRedirectData::Ideal { .. } => Type::Direct,
                BankRedirectData::Trustly { .. } => Type::Redirect,
                BankRedirectData::Eps { .. } => Type::Redirect,
                BankRedirectData::Sofort { .. } => Type::Redirect,
                BankRedirectData::BancontactCard { .. }
                | BankRedirectData::Bizum { .. }
                | BankRedirectData::Blik { .. }
                | BankRedirectData::Eft { .. }
                | BankRedirectData::Interac { .. }
                | BankRedirectData::OnlineBankingCzechRepublic { .. }
                | BankRedirectData::OnlineBankingFinland { .. }
                | BankRedirectData::OnlineBankingPoland { .. }
                | BankRedirectData::OnlineBankingSlovakia { .. }
                | BankRedirectData::OpenBankingUk { .. }
                | BankRedirectData::Przelewy24 { .. }
                | BankRedirectData::OnlineBankingFpx { .. }
                | BankRedirectData::OnlineBankingThailand { .. }
                | BankRedirectData::LocalBankRedirect {} => {
                    Err(errors::ConnectorError::NotImplemented(
                        utils::get_unimplemented_payment_method_error_message("multisafepay"),
                    ))?
                }
            },
            PaymentMethodData::PayLater(ref _paylater) => Type::Redirect,
            _ => Type::Redirect,
        };

        let gateway = match item.router_data.request.payment_method_data {
            PaymentMethodData::Card(ref ccard) => {
                Some(Gateway::try_from(ccard.get_card_issuer()?)?)
            }
            PaymentMethodData::Wallet(ref wallet_data) => Some(match wallet_data {
                WalletData::GooglePay(_) => Gateway::Googlepay,
                WalletData::PaypalRedirect(_) => Gateway::Paypal,
                WalletData::AliPayRedirect(_) => Gateway::Alipay,
                WalletData::WeChatPayRedirect(_) => Gateway::WeChatPay,
                WalletData::MbWayRedirect(_) => Gateway::MbWay,
                WalletData::AliPayQr(_)
                | WalletData::AliPayHkRedirect(_)
                | WalletData::AmazonPay(_)
                | WalletData::AmazonPayRedirect(_)
                | WalletData::Paysera(_)
                | WalletData::Skrill(_)
                | WalletData::BluecodeRedirect {}
                | WalletData::MomoRedirect(_)
                | WalletData::KakaoPayRedirect(_)
                | WalletData::GoPayRedirect(_)
                | WalletData::GcashRedirect(_)
                | WalletData::ApplePay(_)
                | WalletData::ApplePayRedirect(_)
                | WalletData::ApplePayThirdPartySdk(_)
                | WalletData::DanaRedirect {}
                | WalletData::GooglePayRedirect(_)
                | WalletData::GooglePayThirdPartySdk(_)
                | WalletData::MobilePayRedirect(_)
                | WalletData::PaypalSdk(_)
                | WalletData::Paze(_)
                | WalletData::SamsungPay(_)
                | WalletData::TwintRedirect {}
                | WalletData::VippsRedirect {}
                | WalletData::TouchNGoRedirect(_)
                | WalletData::WeChatPayQr(_)
                | WalletData::CashappQr(_)
                | WalletData::SwishQr(_)
                | WalletData::Mifinity(_)
                | WalletData::RevolutPay(_) => Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("multisafepay"),
                ))?,
            }),
            PaymentMethodData::BankRedirect(ref bank_data) => Some(match bank_data {
                BankRedirectData::Giropay { .. } => Gateway::Giropay,
                BankRedirectData::Ideal { .. } => Gateway::Ideal,
                BankRedirectData::Trustly { .. } => Gateway::Trustly,
                BankRedirectData::Eps { .. } => Gateway::Eps,
                BankRedirectData::Sofort { .. } => Gateway::Sofort,
                BankRedirectData::BancontactCard { .. }
                | BankRedirectData::Bizum { .. }
                | BankRedirectData::Blik { .. }
                | BankRedirectData::Eft { .. }
                | BankRedirectData::Interac { .. }
                | BankRedirectData::OnlineBankingCzechRepublic { .. }
                | BankRedirectData::OnlineBankingFinland { .. }
                | BankRedirectData::OnlineBankingPoland { .. }
                | BankRedirectData::OnlineBankingSlovakia { .. }
                | BankRedirectData::OpenBankingUk { .. }
                | BankRedirectData::Przelewy24 { .. }
                | BankRedirectData::OnlineBankingFpx { .. }
                | BankRedirectData::OnlineBankingThailand { .. }
                | BankRedirectData::LocalBankRedirect {} => {
                    Err(errors::ConnectorError::NotImplemented(
                        utils::get_unimplemented_payment_method_error_message("multisafepay"),
                    ))?
                }
            }),
            PaymentMethodData::PayLater(PayLaterData::KlarnaRedirect {}) => Some(Gateway::Klarna),
            PaymentMethodData::MandatePayment => None,
            PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | 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("multisafepay"),
                ))?
            }
        };
        let description = item.router_data.get_description()?;
        let payment_options = PaymentOptions {
            notification_url: None,
            redirect_url: item.router_data.request.get_router_return_url()?,
            cancel_url: item.router_data.request.get_router_return_url()?,
            close_window: None,
            notification_method: None,
            settings: None,
            template_id: None,
            allowed_countries: None,
        };

        let customer = Customer {
            browser: None,
            locale: None,
            ip_address: None,
            forward_ip: None,
            first_name: None,
            last_name: None,
            gender: None,
            birthday: None,
            address1: None,
            address2: None,
            house_number: None,
            zip_code: None,
            city: None,
            state: None,
            country: None,
            phone: None,
            email: item.router_data.request.email.clone(),
            user_agent: None,
            referrer: None,
            reference: Some(item.router_data.connector_request_reference_id.clone()),
        };

        let billing_address = item
            .router_data
            .get_billing()?
            .address
            .as_ref()
            .ok_or_else(utils::missing_field_err("billing.address"))?;
        let first_name = billing_address.get_first_name()?;
        let delivery = DeliveryObject {
            first_name: first_name.clone(),
            last_name: billing_address
                .get_last_name()
                .unwrap_or(first_name)
                .clone(),
            address1: billing_address.get_line1()?.to_owned(),
            house_number: billing_address.get_line2()?.to_owned(),
            zip_code: billing_address.get_zip()?.to_owned(),
            city: billing_address.get_city()?.to_owned(),
            country: billing_address.get_country()?.to_owned(),
        };

        let gateway_info = match item.router_data.request.payment_method_data {
            PaymentMethodData::Card(ref ccard) => Some(GatewayInfo::Card(CardInfo {
                card_number: Some(ccard.card_number.clone()),
                card_expiry_date: Some(Secret::new(
                    (format!(
                        "{}{}",
                        ccard.get_card_expiry_year_2_digit()?.expose(),
                        ccard.card_exp_month.clone().expose()
                    ))
                    .parse::<i32>()
                    .unwrap_or_default(),
                )),
                card_cvc: Some(ccard.card_cvc.clone()),
                card_holder_name: None,
                flexible_3d: None,
                moto: None,
                term_url: None,
            })),
            PaymentMethodData::Wallet(ref wallet_data) => match wallet_data {
                WalletData::GooglePay(ref google_pay) => {
                    Some(GatewayInfo::Wallet(WalletInfo::GooglePay({
                        GpayInfo {
                            payment_token: Some(Secret::new(
                                google_pay
                                    .tokenization_data
                                    .get_encrypted_google_pay_token()
                                    .change_context(errors::ConnectorError::MissingRequiredField {
                                        field_name: "google_pay_token",
                                    })?
                                    .clone(),
                            )),
                        }
                    })))
                }
                WalletData::AliPayRedirect(_) => {
                    Some(GatewayInfo::Wallet(WalletInfo::Alipay(AlipayInfo {})))
                }
                WalletData::PaypalRedirect(_) => None,
                WalletData::WeChatPayRedirect(_) => {
                    Some(GatewayInfo::Wallet(WalletInfo::WeChatPay(WeChatPayInfo {})))
                }
                WalletData::MbWayRedirect(_) => {
                    Some(GatewayInfo::Wallet(WalletInfo::MbWay(MbWayInfo {})))
                }
                WalletData::AliPayQr(_)
                | WalletData::AliPayHkRedirect(_)
                | WalletData::AmazonPay(_)
                | WalletData::AmazonPayRedirect(_)
                | WalletData::Paysera(_)
                | WalletData::Skrill(_)
                | WalletData::BluecodeRedirect {}
                | WalletData::MomoRedirect(_)
                | WalletData::KakaoPayRedirect(_)
                | WalletData::GoPayRedirect(_)
                | WalletData::GcashRedirect(_)
                | WalletData::ApplePay(_)
                | WalletData::ApplePayRedirect(_)
                | WalletData::ApplePayThirdPartySdk(_)
                | WalletData::DanaRedirect {}
                | WalletData::GooglePayRedirect(_)
                | WalletData::GooglePayThirdPartySdk(_)
                | WalletData::MobilePayRedirect(_)
                | WalletData::PaypalSdk(_)
                | WalletData::Paze(_)
                | WalletData::SamsungPay(_)
                | WalletData::TwintRedirect {}
                | WalletData::VippsRedirect {}
                | WalletData::TouchNGoRedirect(_)
                | WalletData::WeChatPayQr(_)
                | WalletData::CashappQr(_)
                | WalletData::SwishQr(_)
                | WalletData::Mifinity(_)
                | WalletData::RevolutPay(_) => Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("multisafepay"),
                ))?,
            },
            PaymentMethodData::PayLater(ref paylater) => {
                Some(GatewayInfo::PayLater(PayLaterInfo {
                    email: Some(match paylater {
                        PayLaterData::KlarnaRedirect {} => item.router_data.get_billing_email()?,
                        PayLaterData::KlarnaSdk { token: _ }
                        | PayLaterData::AffirmRedirect {}
                        | PayLaterData::FlexitiRedirect {}
                        | PayLaterData::AfterpayClearpayRedirect {}
                        | PayLaterData::PayBrightRedirect {}
                        | PayLaterData::WalleyRedirect {}
                        | PayLaterData::AlmaRedirect {}
                        | PayLaterData::AtomeRedirect {}
                        | PayLaterData::BreadpayRedirect {} => {
                            Err(errors::ConnectorError::NotImplemented(
                                utils::get_unimplemented_payment_method_error_message(
                                    "multisafepay",
                                ),
                            ))?
                        }
                    }),
                }))
            }
            PaymentMethodData::BankRedirect(ref bank_redirect_data) => match bank_redirect_data {
                BankRedirectData::Ideal { bank_name, .. } => Some(GatewayInfo::BankRedirect(
                    BankRedirectInfo::Ideal(IdealInfo {
                        issuer_id: MultisafepayBankNames::try_from(&bank_name.ok_or(
                            errors::ConnectorError::MissingRequiredField {
                                field_name: "ideal.bank_name",
                            },
                        )?)?,
                    }),
                )),
                BankRedirectData::Trustly { .. } => Some(GatewayInfo::BankRedirect(
                    BankRedirectInfo::Trustly(TrustlyInfo {}),
                )),
                BankRedirectData::Eps { .. } => {
                    Some(GatewayInfo::BankRedirect(BankRedirectInfo::Eps(EpsInfo {})))
                }
                BankRedirectData::Sofort { .. } => Some(GatewayInfo::BankRedirect(
                    BankRedirectInfo::Sofort(SofortInfo {}),
                )),
                BankRedirectData::BancontactCard { .. }
                | BankRedirectData::Bizum { .. }
                | BankRedirectData::Blik { .. }
                | BankRedirectData::Eft { .. }
                | BankRedirectData::Giropay { .. }
                | BankRedirectData::Interac { .. }
                | BankRedirectData::OnlineBankingCzechRepublic { .. }
                | BankRedirectData::OnlineBankingFinland { .. }
                | BankRedirectData::OnlineBankingPoland { .. }
                | BankRedirectData::OnlineBankingSlovakia { .. }
                | BankRedirectData::OpenBankingUk { .. }
                | BankRedirectData::Przelewy24 { .. }
                | BankRedirectData::OnlineBankingFpx { .. }
                | BankRedirectData::OnlineBankingThailand { .. }
                | BankRedirectData::LocalBankRedirect {} => None,
            },
            PaymentMethodData::MandatePayment => None,
            PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("multisafepay"),
                ))?
            }
        };

        Ok(Self {
            payment_type,
            gateway,
            order_id: item.router_data.connector_request_reference_id.to_string(),
            currency: item.router_data.request.currency.to_string(),
            amount: item.amount,
            description,
            payment_options: Some(payment_options),
            customer: Some(customer),
            delivery: Some(delivery),
            gateway_info,
            checkout_options: None,
            shopping_cart: None,
            capture: None,
            items: None,
            recurring_model: if item.router_data.request.is_mandate_payment() {
                Some(MandateType::Unscheduled)
            } else {
                None
            },
            recurring_id: item
                .router_data
                .request
                .mandate_id
                .clone()
                .and_then(|mandate_ids| match mandate_ids.mandate_reference_id {
                    Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
                        connector_mandate_ids,
                    )) => connector_mandate_ids
                        .get_connector_mandate_id()
                        .map(Secret::new),
                    _ => None,
                }),


// Struct: PayoutData
// File: crates/router/src/core/payouts.rs
// Module: router
// Implementations: 0

pub struct PayoutData


// Implementation: impl Parse for for TryGetEnumMeta
// File: crates/router_derive/src/macros/try_get_enum.rs
// Module: router_derive
// Methods: 1 total (0 public)

impl Parse for for TryGetEnumMeta


// Implementation: impl api::PaymentToken for for Mpgs
// File: crates/hyperswitch_connectors/src/connectors/mpgs.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Mpgs


// Struct: PaymentRequestBankRedirect
// File: crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaymentRequestBankRedirect


// Implementation: impl DatabaseStore for for Store
// File: crates/storage_impl/src/database/store.rs
// Module: storage_impl
// Methods: 5 total (0 public)

impl DatabaseStore for for Store


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

impl api::RefundExecute for for Klarna


// Struct: PeachpaymentsPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/peachpayments/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PeachpaymentsPaymentsRequest


// Struct: ClientSecretTypeNew
// File: crates/diesel_models/src/ephemeral_key.rs
// Module: diesel_models
// Implementations: 0

pub struct ClientSecretTypeNew


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

// Documentation: Feature metadata for merchant connector account

pub struct MerchantConnectorAccountFeatureMetadata


// Struct: ErrorResponse
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models
// Implementations: 2
// Traits: Default

pub struct ErrorResponse


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

pub fn validate_payout_list_request(
    req: &payouts::PayoutListConstraints,
) -> CustomResult<(), errors::ApiErrorResponse>


// Implementation: impl Parse for for Derives
// File: crates/router_derive/src/macros/operation.rs
// Module: router_derive
// Methods: 1 total (0 public)

impl Parse for for Derives


// File: crates/diesel_models/src/payment_method.rs
// Module: diesel_models
// Public functions: 10
// Public structs: 15

use std::collections::HashMap;

use common_enums::MerchantStorageScheme;
use common_utils::{
    encryption::Encryption,
    errors::{CustomResult, ParsingError},
    pii,
};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use error_stack::ResultExt;
#[cfg(feature = "v1")]
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

#[cfg(feature = "v1")]
use crate::{enums as storage_enums, schema::payment_methods};
#[cfg(feature = "v2")]
use crate::{enums as storage_enums, schema_v2::payment_methods};

#[cfg(feature = "v1")]
#[derive(
    Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize,
)]
#[diesel(table_name = payment_methods, primary_key(payment_method_id), check_for_backend(diesel::pg::Pg))]
pub struct PaymentMethod {
    pub customer_id: common_utils::id_type::CustomerId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_method_id: String,
    #[diesel(deserialize_as = super::OptionalDieselArray<storage_enums::Currency>)]
    pub accepted_currency: Option<Vec<storage_enums::Currency>>,
    pub scheme: Option<String>,
    pub token: Option<String>,
    pub cardholder_name: Option<Secret<String>>,
    pub issuer_name: Option<String>,
    pub issuer_country: Option<String>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub payer_country: Option<Vec<String>>,
    pub is_stored: Option<bool>,
    pub swift_code: Option<String>,
    pub direct_debit_token: Option<String>,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub payment_method: Option<storage_enums::PaymentMethod>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub payment_method_issuer: Option<String>,
    pub payment_method_issuer_code: Option<storage_enums::PaymentMethodIssuerCode>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub payment_method_data: Option<Encryption>,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<serde_json::Value>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: Option<Encryption>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: Option<Encryption>,
    pub external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub vault_type: Option<storage_enums::VaultType>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Identifiable, Queryable, Selectable, Serialize, Deserialize)]
#[diesel(table_name = payment_methods, primary_key(id), check_for_backend(diesel::pg::Pg))]
pub struct PaymentMethod {
    pub customer_id: common_utils::id_type::GlobalCustomerId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub payment_method_data: Option<Encryption>,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<CommonMandateReference>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: Option<Encryption>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: Option<Encryption>,
    pub external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub vault_type: Option<storage_enums::VaultType>,
    pub locker_fingerprint_id: Option<String>,
    pub payment_method_type_v2: Option<storage_enums::PaymentMethod>,
    pub payment_method_subtype: Option<storage_enums::PaymentMethodType>,
    pub id: common_utils::id_type::GlobalPaymentMethodId,
    pub external_vault_token_data: Option<Encryption>,
}

impl PaymentMethod {
    #[cfg(feature = "v1")]
    pub fn get_id(&self) -> &String {
        &self.payment_method_id
    }

    #[cfg(feature = "v2")]
    pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId {
        &self.id
    }
}

#[cfg(feature = "v1")]
#[derive(
    Clone, Debug, Eq, PartialEq, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize,
)]
#[diesel(table_name = payment_methods)]
pub struct PaymentMethodNew {
    pub customer_id: common_utils::id_type::CustomerId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_method_id: String,
    pub payment_method: Option<storage_enums::PaymentMethod>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub payment_method_issuer: Option<String>,
    pub payment_method_issuer_code: Option<storage_enums::PaymentMethodIssuerCode>,
    pub accepted_currency: Option<Vec<storage_enums::Currency>>,
    pub scheme: Option<String>,
    pub token: Option<String>,
    pub cardholder_name: Option<Secret<String>>,
    pub issuer_name: Option<String>,
    pub issuer_country: Option<String>,
    pub payer_country: Option<Vec<String>>,
    pub is_stored: Option<bool>,
    pub swift_code: Option<String>,
    pub direct_debit_token: Option<String>,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub payment_method_data: Option<Encryption>,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<serde_json::Value>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: Option<Encryption>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: Option<Encryption>,
    pub external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub vault_type: Option<storage_enums::VaultType>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_methods)]
pub struct PaymentMethodNew {
    pub customer_id: common_utils::id_type::GlobalCustomerId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub payment_method_data: Option<Encryption>,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<CommonMandateReference>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: Option<Encryption>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: Option<Encryption>,
    pub external_vault_token_data: Option<Encryption>,
    pub locker_fingerprint_id: Option<String>,
    pub payment_method_type_v2: Option<storage_enums::PaymentMethod>,
    pub payment_method_subtype: Option<storage_enums::PaymentMethodType>,
    pub id: common_utils::id_type::GlobalPaymentMethodId,
    pub vault_type: Option<storage_enums::VaultType>,
}

impl PaymentMethodNew {
    pub fn update_storage_scheme(&mut self, storage_scheme: MerchantStorageScheme) {
        self.updated_by = Some(storage_scheme.to_string());
    }

    #[cfg(feature = "v1")]
    pub fn get_id(&self) -> &String {
        &self.payment_method_id
    }

    #[cfg(feature = "v2")]
    pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId {
        &self.id
    }
}

#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct TokenizeCoreWorkflow {
    pub lookup_key: String,
    pub pm: storage_enums::PaymentMethod,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize, Deserialize)]
pub enum PaymentMethodUpdate {
    MetadataUpdateAndLastUsed {
        metadata: Option<serde_json::Value>,
        last_used_at: PrimitiveDateTime,
    },
    UpdatePaymentMethodDataAndLastUsed {
        payment_method_data: Option<Encryption>,
        scheme: Option<String>,
        last_used_at: PrimitiveDateTime,
    },
    PaymentMethodDataUpdate {
        payment_method_data: Option<Encryption>,
    },
    LastUsedUpdate {
        last_used_at: PrimitiveDateTime,
    },
    NetworkTransactionIdAndStatusUpdate {
        network_transaction_id: Option<String>,
        status: Option<storage_enums::PaymentMethodStatus>,
    },
    StatusUpdate {
        status: Option<storage_enums::PaymentMethodStatus>,
    },
    AdditionalDataUpdate {
        payment_method_data: Option<Encryption>,
        status: Option<storage_enums::PaymentMethodStatus>,
        locker_id: Option<String>,
        payment_method: Option<storage_enums::PaymentMethod>,
        payment_method_type: Option<storage_enums::PaymentMethodType>,
        payment_method_issuer: Option<String>,
        network_token_requestor_reference_id: Option<String>,
        network_token_locker_id: Option<String>,
        network_token_payment_method_data: Option<Encryption>,
    },
    ConnectorMandateDetailsUpdate {
        connector_mandate_details: Option<serde_json::Value>,
    },
    NetworkTokenDataUpdate {
        network_token_requestor_reference_id: Option<String>,
        network_token_locker_id: Option<String>,
        network_token_payment_method_data: Option<Encryption>,
    },
    ConnectorNetworkTransactionIdAndMandateDetailsUpdate {
        connector_mandate_details: Option<pii::SecretSerdeValue>,
        network_transaction_id: Option<Secret<String>>,
    },
    ConnectorNetworkTransactionIdStatusAndMandateDetailsUpdate {
        connector_mandate_details: Option<pii::SecretSerdeValue>,
        network_transaction_id: Option<String>,
        status: Option<storage_enums::PaymentMethodStatus>,
    },
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize, Deserialize)]
pub enum PaymentMethodUpdate {
    UpdatePaymentMethodDataAndLastUsed {
        payment_method_data: Option<Encryption>,
        scheme: Option<String>,
        last_used_at: PrimitiveDateTime,
    },
    PaymentMethodDataUpdate {
        payment_method_data: Option<Encryption>,
    },
    LastUsedUpdate {
        last_used_at: PrimitiveDateTime,
    },
    NetworkTransactionIdAndStatusUpdate {
        network_transaction_id: Option<String>,
        status: Option<storage_enums::PaymentMethodStatus>,
    },
    StatusUpdate {
        status: Option<storage_enums::PaymentMethodStatus>,
    },
    GenericUpdate {
        payment_method_data: Option<Encryption>,
        status: Option<storage_enums::PaymentMethodStatus>,
        locker_id: Option<String>,
        payment_method_type_v2: Option<storage_enums::PaymentMethod>,
        payment_method_subtype: Option<storage_enums::PaymentMethodType>,
        network_token_requestor_reference_id: Option<String>,
        network_token_locker_id: Option<String>,
        network_token_payment_method_data: Option<Encryption>,
        locker_fingerprint_id: Option<String>,
        connector_mandate_details: Option<CommonMandateReference>,
        external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
    },
    ConnectorMandateDetailsUpdate {
        connector_mandate_details: Option<CommonMandateReference>,
    },
}

impl PaymentMethodUpdate {
    pub fn convert_to_payment_method_update(
        self,
        storage_scheme: MerchantStorageScheme,
    ) -> PaymentMethodUpdateInternal {
        let mut update_internal: PaymentMethodUpdateInternal = self.into();
        update_internal.updated_by = Some(storage_scheme.to_string());
        update_internal
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_methods)]
pub struct PaymentMethodUpdateInternal {
    payment_method_data: Option<Encryption>,
    last_used_at: Option<PrimitiveDateTime>,
    network_transaction_id: Option<String>,
    status: Option<storage_enums::PaymentMethodStatus>,
    locker_id: Option<String>,
    payment_method_type_v2: Option<storage_enums::PaymentMethod>,
    connector_mandate_details: Option<CommonMandateReference>,
    updated_by: Option<String>,
    payment_method_subtype: Option<storage_enums::PaymentMethodType>,
    last_modified: PrimitiveDateTime,
    network_token_requestor_reference_id: Option<String>,
    network_token_locker_id: Option<String>,
    network_token_payment_method_data: Option<Encryption>,
    locker_fingerprint_id: Option<String>,
    external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
}

#[cfg(feature = "v2")]
impl PaymentMethodUpdateInternal {
    pub fn apply_changeset(self, source: PaymentMethod) -> PaymentMethod {
        let Self {
            payment_method_data,
            last_used_at,
            network_transaction_id,
            status,
            locker_id,
            payment_method_type_v2,
            connector_mandate_details,
            updated_by,
            payment_method_subtype,
            last_modified,
            network_token_requestor_reference_id,
            network_token_locker_id,
            network_token_payment_method_data,
            locker_fingerprint_id,
            external_vault_source,
        } = self;

        PaymentMethod {
            customer_id: source.customer_id,
            merchant_id: source.merchant_id,
            created_at: source.created_at,
            last_modified,
            payment_method_data: payment_method_data.or(source.payment_method_data),
            locker_id: locker_id.or(source.locker_id),
            last_used_at: last_used_at.unwrap_or(source.last_used_at),
            connector_mandate_details: connector_mandate_details
                .or(source.connector_mandate_details),
            customer_acceptance: source.customer_acceptance,
            status: status.unwrap_or(source.status),
            network_transaction_id: network_transaction_id.or(source.network_transaction_id),
            client_secret: source.client_secret,
            payment_method_billing_address: source.payment_method_billing_address,
            updated_by: updated_by.or(source.updated_by),
            locker_fingerprint_id: locker_fingerprint_id.or(source.locker_fingerprint_id),
            payment_method_type_v2: payment_method_type_v2.or(source.payment_method_type_v2),
            payment_method_subtype: payment_method_subtype.or(source.payment_method_subtype),
            id: source.id,
            version: source.version,
            network_token_requestor_reference_id: network_token_requestor_reference_id
                .or(source.network_token_requestor_reference_id),
            network_token_locker_id: network_token_locker_id.or(source.network_token_locker_id),
            network_token_payment_method_data: network_token_payment_method_data
                .or(source.network_token_payment_method_data),
            external_vault_source: external_vault_source.or(source.external_vault_source),
            external_vault_token_data: source.external_vault_token_data,
            vault_type: source.vault_type,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_methods)]
pub struct PaymentMethodUpdateInternal {
    metadata: Option<serde_json::Value>,
    payment_method_data: Option<Encryption>,
    last_used_at: Option<PrimitiveDateTime>,
    network_transaction_id: Option<String>,
    status: Option<storage_enums::PaymentMethodStatus>,
    locker_id: Option<String>,
    network_token_requestor_reference_id: Option<String>,
    payment_method: Option<storage_enums::PaymentMethod>,
    connector_mandate_details: Option<serde_json::Value>,
    updated_by: Option<String>,
    payment_method_type: Option<storage_enums::PaymentMethodType>,
    payment_method_issuer: Option<String>,
    last_modified: PrimitiveDateTime,
    network_token_locker_id: Option<String>,
    network_token_payment_method_data: Option<Encryption>,
    scheme: Option<String>,
}

#[cfg(feature = "v1")]
impl PaymentMethodUpdateInternal {
    pub fn apply_changeset(self, source: PaymentMethod) -> PaymentMethod {
        let Self {
            metadata,
            payment_method_data,
            last_used_at,
            network_transaction_id,
            status,
            locker_id,
            network_token_requestor_reference_id,
            payment_method,
            connector_mandate_details,
            updated_by,
            payment_method_type,
            payment_method_issuer,
            last_modified,
            network_token_locker_id,
            network_token_payment_method_data,
            scheme,
        } = self;

        PaymentMethod {
            customer_id: source.customer_id,
            merchant_id: source.merchant_id,
            payment_method_id: source.payment_method_id,
            accepted_currency: source.accepted_currency,
            scheme: scheme.or(source.scheme),
            token: source.token,
            cardholder_name: source.cardholder_name,
            issuer_name: source.issuer_name,
            issuer_country: source.issuer_country,
            payer_country: source.payer_country,
            is_stored: source.is_stored,
            swift_code: source.swift_code,
            direct_debit_token: source.direct_debit_token,
            created_at: source.created_at,
            last_modified,
            payment_method: payment_method.or(source.payment_method),
            payment_method_type: payment_method_type.or(source.payment_method_type),
            payment_method_issuer: payment_method_issuer.or(source.payment_method_issuer),
            payment_method_issuer_code: source.payment_method_issuer_code,
            metadata: metadata.map_or(source.metadata, |v| Some(v.into())),
            payment_method_data: payment_method_data.or(source.payment_method_data),
            locker_id: locker_id.or(source.locker_id),
            last_used_at: last_used_at.unwrap_or(source.last_used_at),
            connector_mandate_details: connector_mandate_details
                .or(source.connector_mandate_details),
            customer_acceptance: source.customer_acceptance,
            status: status.unwrap_or(source.status),
            network_transaction_id: network_transaction_id.or(source.network_transaction_id),
            client_secret: source.client_secret,
            payment_method_billing_address: source.payment_method_billing_address,
            updated_by: updated_by.or(source.updated_by),
            version: source.version,
            network_token_requestor_reference_id: network_token_requestor_reference_id
                .or(source.network_token_requestor_reference_id),
            network_token_locker_id: network_token_locker_id.or(source.network_token_locker_id),
            network_token_payment_method_data: network_token_payment_method_data
                .or(source.network_token_payment_method_data),
            external_vault_source: source.external_vault_source,
            vault_type: source.vault_type,
        }
    }
}

#[cfg(feature = "v1")]
impl From<PaymentMethodUpdate> for PaymentMethodUpdateInternal {
    fn from(payment_method_update: PaymentMethodUpdate) -> Self {
        match payment_method_update {
            PaymentMethodUpdate::MetadataUpdateAndLastUsed {
                metadata,
                last_used_at,
            } => Self {
                metadata,
                payment_method_data: None,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
            },
            PaymentMethodUpdate::PaymentMethodDataUpdate {
                payment_method_data,
            } => Self {
                metadata: None,
                payment_method_data,
                last_used_at: None,
                network_transaction_id: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
            },
            PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
            },
            PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed {
                payment_method_data,
                scheme,
                last_used_at,
            } => Self {
                metadata: None,
                payment_method_data,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme,
            },
            PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate {
                network_transaction_id,
                status,
            } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                network_transaction_id,
                status,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
            },
            PaymentMethodUpdate::StatusUpdate { status } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                network_transaction_id: None,
                status,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
            },
            PaymentMethodUpdate::AdditionalDataUpdate {
                payment_method_data,
                status,
                locker_id,
                network_token_requestor_reference_id,
                payment_method,
                payment_method_type,
                payment_method_issuer,
                network_token_locker_id,
                network_token_payment_method_data,
            } => Self {
                metadata: None,
                payment_method_data,
                last_used_at: None,
                network_transaction_id: None,
                status,
                locker_id,
                network_token_requestor_reference_id,
                payment_method,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer,
                payment_method_type,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id,
                network_token_payment_method_data,
                scheme: None,
            },
            PaymentMethodUpdate::ConnectorMandateDetailsUpdate {
                connector_mandate_details,
            } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details,
                network_transaction_id: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
            },
            PaymentMethodUpdate::NetworkTokenDataUpdate {
                network_token_requestor_reference_id,
                network_token_locker_id,
                network_token_payment_method_data,
            } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                status: None,
                locker_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_transaction_id: None,
                network_token_requestor_reference_id,
                network_token_locker_id,
                network_token_payment_method_data,
                scheme: None,
            },
            PaymentMethodUpdate::ConnectorNetworkTransactionIdAndMandateDetailsUpdate {
                connector_mandate_details,
                network_transaction_id,
            } => Self {
                connector_mandate_details: connector_mandate_details
                    .map(|mandate_details| mandate_details.expose()),
                network_transaction_id: network_transaction_id.map(|txn_id| txn_id.expose()),
                last_modified: common_utils::date_time::now(),
                status: None,
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                locker_id: None,
                payment_method: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                network_token_requestor_reference_id: None,
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
            },
            PaymentMethodUpdate::ConnectorNetworkTransactionIdStatusAndMandateDetailsUpdate {
                connector_mandate_details,
                network_transaction_id,
                status,
            } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                status,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: connector_mandate_details
                    .map(|mandate_details| mandate_details.expose()),
                network_transaction_id,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl From<PaymentMethodUpdate> for PaymentMethodUpdateInternal {
    fn from(payment_method_update: PaymentMethodUpdate) -> Self {
        match payment_method_update {
            PaymentMethodUpdate::PaymentMethodDataUpdate {
                payment_method_data,
            } => Self {
                payment_method_data,
                last_used_at: None,
                network_transaction_id: None,
                status: None,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
            },
            PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self {
                payment_method_data: None,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
            },
            PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed {
                payment_method_data,
                last_used_at,
                ..
            } => Self {
                payment_method_data,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
            },
            PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate {
                network_transaction_id,
                status,
            } => Self {
                payment_method_data: None,
                last_used_at: None,
                network_transaction_id,
                status,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
            },
            PaymentMethodUpdate::StatusUpdate { status } => Self {
                payment_method_data: None,
                last_used_at: None,
                network_transaction_id: None,
                status,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
            },
            PaymentMethodUpdate::GenericUpdate {
                payment_method_data,
                status,
                locker_id,
                payment_method_type_v2,
                payment_method_subtype,
                network_token_requestor_reference_id,
                network_token_locker_id,
                network_token_payment_method_data,
                locker_fingerprint_id,
                connector_mandate_details,
                external_vault_source,
            } => Self {
                payment_method_data,
                last_used_at: None,
                network_transaction_id: None,
                status,
                locker_id,
                payment_method_type_v2,
                connector_mandate_details,
                updated_by: None,
                payment_method_subtype,
                last_modified: common_utils::date_time::now(),
                network_token_requestor_reference_id,
                network_token_locker_id,
                network_token_payment_method_data,
                locker_fingerprint_id,
                external_vault_source,
            },
            PaymentMethodUpdate::ConnectorMandateDetailsUpdate {
                connector_mandate_details,
            } => Self {
                payment_method_data: None,
                last_used_at: None,
                status: None,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details,
                network_transaction_id: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl From<&PaymentMethodNew> for PaymentMethod {
    fn from(payment_method_new: &PaymentMethodNew) -> Self {
        Self {
            customer_id: payment_method_new.customer_id.clone(),
            merchant_id: payment_method_new.merchant_id.clone(),
            payment_method_id: payment_method_new.payment_method_id.clone(),
            locker_id: payment_method_new.locker_id.clone(),
            network_token_requestor_reference_id: payment_method_new
                .network_token_requestor_reference_id
                .clone(),
            accepted_currency: payment_method_new.accepted_currency.clone(),
            scheme: payment_method_new.scheme.clone(),
            token: payment_method_new.token.clone(),
            cardholder_name: payment_method_new.cardholder_name.clone(),
            issuer_name: payment_method_new.issuer_name.clone(),
            issuer_country: payment_method_new.issuer_country.clone(),
            payer_country: payment_method_new.payer_country.clone(),
            is_stored: payment_method_new.is_stored,
            swift_code: payment_method_new.swift_code.clone(),
            direct_debit_token: payment_method_new.direct_debit_token.clone(),
            created_at: payment_method_new.created_at,
            last_modified: payment_method_new.last_modified,
            payment_method: payment_method_new.payment_method,
            payment_method_type: payment_method_new.payment_method_type,
            payment_method_issuer: payment_method_new.payment_method_issuer.clone(),
            payment_method_issuer_code: payment_method_new.payment_method_issuer_code,
            metadata: payment_method_new.metadata.clone(),
            payment_method_data: payment_method_new.payment_method_data.clone(),
            last_used_at: payment_method_new.last_used_at,
            connector_mandate_details: payment_method_new.connector_mandate_details.clone(),
            customer_acceptance: payment_method_new.customer_acceptance.clone(),
            status: payment_method_new.status,
            network_transaction_id: payment_method_new.network_transaction_id.clone(),
            client_secret: payment_method_new.client_secret.clone(),
            updated_by: payment_method_new.updated_by.clone(),
            payment_method_billing_address: payment_method_new
                .payment_method_billing_address
                .clone(),
            version: payment_method_new.version,
            network_token_locker_id: payment_method_new.network_token_locker_id.clone(),
            network_token_payment_method_data: payment_method_new
                .network_token_payment_method_data
                .clone(),
            external_vault_source: payment_method_new.external_vault_source.clone(),
            vault_type: payment_method_new.vault_type,
        }
    }
}

#[cfg(feature = "v2")]
impl From<&PaymentMethodNew> for PaymentMethod {
    fn from(payment_method_new: &PaymentMethodNew) -> Self {
        Self {
            customer_id: payment_method_new.customer_id.clone(),
            merchant_id: payment_method_new.merchant_id.clone(),
            locker_id: payment_method_new.locker_id.clone(),
            created_at: payment_method_new.created_at,
            last_modified: payment_method_new.last_modified,
            payment_method_data: payment_method_new.payment_method_data.clone(),
            last_used_at: payment_method_new.last_used_at,
            connector_mandate_details: payment_method_new.connector_mandate_details.clone(),
            customer_acceptance: payment_method_new.customer_acceptance.clone(),
            status: payment_method_new.status,
            network_transaction_id: payment_method_new.network_transaction_id.clone(),
            client_secret: payment_method_new.client_secret.clone(),
            updated_by: payment_method_new.updated_by.clone(),
            payment_method_billing_address: payment_method_new
                .payment_method_billing_address
                .clone(),
            locker_fingerprint_id: payment_method_new.locker_fingerprint_id.clone(),
            payment_method_type_v2: payment_method_new.payment_method_type_v2,
            payment_method_subtype: payment_method_new.payment_method_subtype,
            id: payment_method_new.id.clone(),
            version: payment_method_new.version,
            network_token_requestor_reference_id: payment_method_new
                .network_token_requestor_reference_id
                .clone(),
            network_token_locker_id: payment_method_new.network_token_locker_id.clone(),
            network_token_payment_method_data: payment_method_new
                .network_token_payment_method_data
                .clone(),
            external_vault_source: None,
            external_vault_token_data: payment_method_new.external_vault_token_data.clone(),
            vault_type: payment_method_new.vault_type,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsMandateReferenceRecord {
    pub connector_mandate_id: String,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<i64>,
    pub original_payment_authorized_currency: Option<common_enums::Currency>,
    pub mandate_metadata: Option<pii::SecretSerdeValue>,
    pub connector_mandate_status: Option<common_enums::ConnectorMandateStatus>,
    pub connector_mandate_request_reference_id: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConnectorTokenReferenceRecord {
    pub connector_token: String,
    pub payment_method_subtype: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<common_utils::types::MinorUnit>,
    pub original_payment_authorized_currency: Option<common_enums::Currency>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_token_status: common_enums::ConnectorTokenStatus,


// File: crates/router_env/src/logger/formatter.rs
// Module: router_env
// Public functions: 3
// Public structs: 1

//! Formatting [layer](https://docs.rs/tracing-subscriber/0.3.15/tracing_subscriber/layer/trait.Layer.html) for Router.

use std::{
    collections::{HashMap, HashSet},
    fmt,
    io::Write,
    sync::LazyLock,
};

use config::ConfigError;
use serde::ser::{SerializeMap, Serializer};
use serde_json::{ser::Formatter, Value};
// use time::format_description::well_known::Rfc3339;
use time::format_description::well_known::Iso8601;
use tracing::{Event, Metadata, Subscriber};
use tracing_subscriber::{
    fmt::MakeWriter,
    layer::Context,
    registry::{LookupSpan, SpanRef},
    Layer,
};

use crate::Storage;
// TODO: Documentation coverage for this crate

// Implicit keys

const MESSAGE: &str = "message";
const HOSTNAME: &str = "hostname";
const PID: &str = "pid";
const ENV: &str = "env";
const VERSION: &str = "version";
const BUILD: &str = "build";
const LEVEL: &str = "level";
const TARGET: &str = "target";
const SERVICE: &str = "service";
const LINE: &str = "line";
const FILE: &str = "file";
const FN: &str = "fn";
const FULL_NAME: &str = "full_name";
const TIME: &str = "time";

// Extra implicit keys. Keys that are provided during runtime but should be treated as
// implicit in the logs

const FLOW: &str = "flow";
const MERCHANT_AUTH: &str = "merchant_authentication";
const MERCHANT_ID: &str = "merchant_id";
const REQUEST_METHOD: &str = "request_method";
const REQUEST_URL_PATH: &str = "request_url_path";
const REQUEST_ID: &str = "request_id";
const WORKFLOW_ID: &str = "workflow_id";
const GLOBAL_ID: &str = "global_id";
const SESSION_ID: &str = "session_id";

/// Set of predefined implicit keys.
pub static IMPLICIT_KEYS: LazyLock<rustc_hash::FxHashSet<&str>> = LazyLock::new(|| {
    let mut set = rustc_hash::FxHashSet::default();

    set.insert(HOSTNAME);
    set.insert(PID);
    set.insert(ENV);
    set.insert(VERSION);
    set.insert(BUILD);
    set.insert(LEVEL);
    set.insert(TARGET);
    set.insert(SERVICE);
    set.insert(LINE);
    set.insert(FILE);
    set.insert(FN);
    set.insert(FULL_NAME);
    set.insert(TIME);

    set
});

/// Extra implicit keys. Keys that are not purely implicit but need to be logged alongside
/// other implicit keys in the log json.
pub static EXTRA_IMPLICIT_KEYS: LazyLock<rustc_hash::FxHashSet<&str>> = LazyLock::new(|| {
    let mut set = rustc_hash::FxHashSet::default();

    set.insert(MESSAGE);
    set.insert(FLOW);
    set.insert(MERCHANT_AUTH);
    set.insert(MERCHANT_ID);
    set.insert(REQUEST_METHOD);
    set.insert(REQUEST_URL_PATH);
    set.insert(REQUEST_ID);
    set.insert(GLOBAL_ID);
    set.insert(SESSION_ID);
    set.insert(WORKFLOW_ID);

    set
});

/// Describe type of record: entering a span, exiting a span, an event.
#[derive(Clone, Debug)]
pub enum RecordType {
    /// Entering a span.
    EnterSpan,
    /// Exiting a span.
    ExitSpan,
    /// Event.
    Event,
}

impl fmt::Display for RecordType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let repr = match self {
            Self::EnterSpan => "START",
            Self::ExitSpan => "END",
            Self::Event => "EVENT",
        };
        write!(f, "{repr}")
    }
}

/// Format log records.
/// `FormattingLayer` relies on the `tracing_bunyan_formatter::JsonStorageLayer` which is storage of entries.
#[derive(Debug)]
pub struct FormattingLayer<W, F>
where
    W: for<'a> MakeWriter<'a> + 'static,
    F: Formatter + Clone,
{
    dst_writer: W,
    pid: u32,
    hostname: String,
    env: String,
    service: String,
    #[cfg(feature = "vergen")]
    version: String,
    #[cfg(feature = "vergen")]
    build: String,
    default_fields: HashMap<String, Value>,
    formatter: F,
}

impl<W, F> FormattingLayer<W, F>
where
    W: for<'a> MakeWriter<'a> + 'static,
    F: Formatter + Clone,
{
    /// Constructor of `FormattingLayer`.
    ///
    /// A `name` will be attached to all records during formatting.
    /// A `dst_writer` to forward all records.
    ///
    /// ## Example
    /// ```rust
    /// let formatting_layer = router_env::FormattingLayer::new("my_service", std::io::stdout, serde_json::ser::CompactFormatter);
    /// ```
    pub fn new(
        service: &str,
        dst_writer: W,
        formatter: F,
    ) -> error_stack::Result<Self, ConfigError> {
        Self::new_with_implicit_entries(service, dst_writer, HashMap::new(), formatter)
    }

    /// Construct of `FormattingLayer with implicit default entries.
    pub fn new_with_implicit_entries(
        service: &str,
        dst_writer: W,
        default_fields: HashMap<String, Value>,
        formatter: F,
    ) -> error_stack::Result<Self, ConfigError> {
        let pid = std::process::id();
        let hostname = gethostname::gethostname().to_string_lossy().into_owned();
        let service = service.to_string();
        #[cfg(feature = "vergen")]
        let version = crate::version!().to_string();
        #[cfg(feature = "vergen")]
        let build = crate::build!().to_string();
        let env = crate::env::which().to_string();
        for key in default_fields.keys() {
            if IMPLICIT_KEYS.contains(key.as_str()) {
                return Err(ConfigError::Message(format!(
                    "A reserved key `{key}` was included in `default_fields` in the log formatting layer"
                ))
                .into());
            }
        }

        Ok(Self {
            dst_writer,
            pid,
            hostname,
            env,
            service,
            #[cfg(feature = "vergen")]
            version,
            #[cfg(feature = "vergen")]
            build,
            default_fields,
            formatter,
        })
    }

    /// Serialize common for both span and event entries.
    fn common_serialize<S>(
        &self,
        map_serializer: &mut impl SerializeMap<Error = serde_json::Error>,
        metadata: &Metadata<'_>,
        span: Option<&SpanRef<'_, S>>,
        storage: &Storage<'_>,
        name: &str,
    ) -> Result<(), std::io::Error>
    where
        S: Subscriber + for<'a> LookupSpan<'a>,
    {
        let is_extra = |s: &str| !IMPLICIT_KEYS.contains(s);
        let is_extra_implicit = |s: &str| is_extra(s) && EXTRA_IMPLICIT_KEYS.contains(s);

        map_serializer.serialize_entry(HOSTNAME, &self.hostname)?;
        map_serializer.serialize_entry(PID, &self.pid)?;
        map_serializer.serialize_entry(ENV, &self.env)?;
        #[cfg(feature = "vergen")]
        map_serializer.serialize_entry(VERSION, &self.version)?;
        #[cfg(feature = "vergen")]
        map_serializer.serialize_entry(BUILD, &self.build)?;
        map_serializer.serialize_entry(LEVEL, &format_args!("{}", metadata.level()))?;
        map_serializer.serialize_entry(TARGET, metadata.target())?;
        map_serializer.serialize_entry(SERVICE, &self.service)?;
        map_serializer.serialize_entry(LINE, &metadata.line())?;
        map_serializer.serialize_entry(FILE, &metadata.file())?;
        map_serializer.serialize_entry(FN, name)?;
        map_serializer
            .serialize_entry(FULL_NAME, &format_args!("{}::{}", metadata.target(), name))?;
        if let Ok(time) = &time::OffsetDateTime::now_utc().format(&Iso8601::DEFAULT) {
            map_serializer.serialize_entry(TIME, time)?;
        }

        // Write down implicit default entries.
        for (key, value) in self.default_fields.iter() {
            map_serializer.serialize_entry(key, value)?;
        }

        #[cfg(feature = "log_custom_entries_to_extra")]
        let mut extra = serde_json::Map::default();
        let mut explicit_entries_set: HashSet<&str> = HashSet::default();
        // Write down explicit event's entries.
        for (key, value) in storage.values.iter() {
            if is_extra_implicit(key) {
                #[cfg(feature = "log_extra_implicit_fields")]
                map_serializer.serialize_entry(key, value)?;
                explicit_entries_set.insert(key);
            } else if is_extra(key) {
                #[cfg(feature = "log_custom_entries_to_extra")]
                extra.insert(key.to_string(), value.clone());
                #[cfg(not(feature = "log_custom_entries_to_extra"))]
                map_serializer.serialize_entry(key, value)?;
                explicit_entries_set.insert(key);
            } else {
                tracing::warn!(
                    ?key,
                    ?value,
                    "Attempting to log a reserved entry. It won't be added to the logs"
                );
            }
        }

        // Write down entries from the span, if it exists.
        if let Some(span) = &span {
            let extensions = span.extensions();
            if let Some(visitor) = extensions.get::<Storage<'_>>() {
                for (key, value) in &visitor.values {
                    if is_extra_implicit(key) && !explicit_entries_set.contains(key) {
                        #[cfg(feature = "log_extra_implicit_fields")]
                        map_serializer.serialize_entry(key, value)?;
                    } else if is_extra(key) && !explicit_entries_set.contains(key) {
                        #[cfg(feature = "log_custom_entries_to_extra")]
                        extra.insert(key.to_string(), value.clone());
                        #[cfg(not(feature = "log_custom_entries_to_extra"))]
                        map_serializer.serialize_entry(key, value)?;
                    } else {
                        tracing::warn!(
                            ?key,
                            ?value,
                            "Attempting to log a reserved entry. It won't be added to the logs"
                        );
                    }
                }
            }
        }

        #[cfg(feature = "log_custom_entries_to_extra")]
        map_serializer.serialize_entry("extra", &extra)?;

        Ok(())
    }

    /// Flush memory buffer into an output stream trailing it with next line.
    ///
    /// Should be done by single `write_all` call to avoid fragmentation of log because of mutlithreading.
    fn flush(&self, mut buffer: Vec<u8>) -> Result<(), std::io::Error> {
        buffer.write_all(b"\n")?;
        self.dst_writer.make_writer().write_all(&buffer)
    }

    /// Serialize entries of span.
    fn span_serialize<S>(
        &self,
        span: &SpanRef<'_, S>,
        ty: RecordType,
    ) -> Result<Vec<u8>, std::io::Error>
    where
        S: Subscriber + for<'a> LookupSpan<'a>,
    {
        let mut buffer = Vec::new();
        let mut serializer =
            serde_json::Serializer::with_formatter(&mut buffer, self.formatter.clone());
        let mut map_serializer = serializer.serialize_map(None)?;
        let message = Self::span_message(span, ty);
        let mut storage = Storage::default();
        storage.record_value("message", message.into());

        self.common_serialize(
            &mut map_serializer,
            span.metadata(),
            Some(span),
            &storage,
            span.name(),
        )?;

        map_serializer.end()?;
        Ok(buffer)
    }

    /// Serialize event into a buffer of bytes using parent span.
    pub fn event_serialize<S>(
        &self,
        span: Option<&SpanRef<'_, S>>,
        event: &Event<'_>,
    ) -> std::io::Result<Vec<u8>>
    where
        S: Subscriber + for<'a> LookupSpan<'a>,
    {
        let mut buffer = Vec::new();
        let mut serializer =
            serde_json::Serializer::with_formatter(&mut buffer, self.formatter.clone());
        let mut map_serializer = serializer.serialize_map(None)?;

        let mut storage = Storage::default();
        event.record(&mut storage);

        let name = span.map_or("?", SpanRef::name);
        Self::event_message(span, event, &mut storage);

        self.common_serialize(&mut map_serializer, event.metadata(), span, &storage, name)?;

        map_serializer.end()?;
        Ok(buffer)
    }

    /// Format message of a span.
    ///
    /// Example: "[FN_WITHOUT_COLON - START]"
    fn span_message<S>(span: &SpanRef<'_, S>, ty: RecordType) -> String
    where
        S: Subscriber + for<'a> LookupSpan<'a>,
    {
        format!("[{} - {}]", span.metadata().name().to_uppercase(), ty)
    }

    /// Format message of an event.
    ///
    /// Examples: "[FN_WITHOUT_COLON - EVENT] Message"
    fn event_message<S>(span: Option<&SpanRef<'_, S>>, event: &Event<'_>, storage: &mut Storage<'_>)
    where
        S: Subscriber + for<'a> LookupSpan<'a>,
    {
        // Get value of kept "message" or "target" if does not exist.
        let message = storage
            .values
            .entry("message")
            .or_insert_with(|| event.metadata().target().into());

        // Prepend the span name to the message if span exists.
        if let (Some(span), Value::String(a)) = (span, message) {
            *a = format!("{} {}", Self::span_message(span, RecordType::Event), a,);
        }
    }
}

#[allow(clippy::expect_used)]
impl<S, W, F> Layer<S> for FormattingLayer<W, F>
where
    S: Subscriber + for<'a> LookupSpan<'a>,
    W: for<'a> MakeWriter<'a> + 'static,
    F: Formatter + Clone + 'static,
{
    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
        // Event could have no span.
        let span = ctx.lookup_current();

        let result: std::io::Result<Vec<u8>> = self.event_serialize(span.as_ref(), event);
        if let Ok(formatted) = result {
            let _ = self.flush(formatted);
        }
    }

    #[cfg(feature = "log_active_span_json")]
    fn on_enter(&self, id: &tracing::Id, ctx: Context<'_, S>) {
        let span = ctx.span(id).expect("No span");
        if let Ok(serialized) = self.span_serialize(&span, RecordType::EnterSpan) {
            let _ = self.flush(serialized);
        }
    }

    #[cfg(not(feature = "log_active_span_json"))]
    fn on_close(&self, id: tracing::Id, ctx: Context<'_, S>) {
        let span = ctx.span(&id).expect("No span");
        if span.parent().is_none() {
            if let Ok(serialized) = self.span_serialize(&span, RecordType::ExitSpan) {
                let _ = self.flush(serialized);
            }
        }
    }

    #[cfg(feature = "log_active_span_json")]
    fn on_close(&self, id: tracing::Id, ctx: Context<'_, S>) {
        let span = ctx.span(&id).expect("No span");
        if let Ok(serialized) = self.span_serialize(&span, RecordType::ExitSpan) {
            let _ = self.flush(serialized);
        }
    }
}


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

impl ConnectorCommon for for Affirm


// Struct: MandateRevokedResponse
// File: crates/api_models/src/mandates.rs
// Module: api_models
// Implementations: 0

pub struct MandateRevokedResponse


// Struct: PaymentMethodAuth
// File: crates/payment_methods/src/configs/settings.rs
// Module: payment_methods
// Implementations: 1
// Traits: SecretsHandler

pub struct PaymentMethodAuth


// Struct: GlobepayPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/globepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GlobepayPaymentsRequest


// Struct: ApplicationInformation
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ApplicationInformation


// Struct: BamboraapacMeta
// File: crates/hyperswitch_connectors/src/connectors/bamboraapac/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BamboraapacMeta


// Struct: JpmorganPSyncResponse
// File: crates/hyperswitch_connectors/src/connectors/jpmorgan/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct JpmorganPSyncResponse


// Function: generate_newman_command_for_users
// File: crates/test_utils/src/newman_runner.rs
// Module: test_utils

pub fn generate_newman_command_for_users() -> Result<ReturnArgs>