#[cfg(feature = "v2")]
use std::str::FromStr;
use std::{
    collections::{HashMap, HashSet},
    num::NonZeroU8,
};

use cards::CardNumber;
use common_types::payments::{
    ApplePayPredecryptData, BillingFrequency, GPayPredecryptData, InstallmentInterestRate,
};
#[cfg(feature = "v1")]
use common_utils::crypto::OptionalEncryptableName;
use common_utils::{
    consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH,
    errors,
    ext_traits::OptionExt,
    id_type, link_utils, pii,
    types::{FloatMajorUnit, MinorUnit, Percentage, Surcharge},
};
use error_stack::ResultExt;
use hyperswitch_masking::PeekInterface;
use rust_decimal::{
    prelude::{FromPrimitive, ToPrimitive},
    Decimal,
};
use serde::de;
use utoipa::ToSchema;

#[cfg(feature = "v1")]
use crate::payments::BankCodeResponse;
#[cfg(feature = "payouts")]
use crate::payouts;
use crate::{admin, enums as api_enums, open_router, payments};

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentMethodCreate {
    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method: Option<api_enums::PaymentMethod>,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>,example = "credit")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,

    /// The name of the bank/ provider issuing the payment method to the end user
    #[schema(example = "Citibank")]
    pub payment_method_issuer: Option<String>,

    /// A standard code representing the issuer of payment method
    #[schema(value_type = Option<PaymentMethodIssuerCode>,example = "jp_applepay")]
    pub payment_method_issuer_code: Option<api_enums::PaymentMethodIssuerCode>,

    /// Card Details
    #[schema(example = json!({
    "card_number": "4111111145551142",
    "card_exp_month": "10",
    "card_exp_year": "25",
    "card_holder_name": "John Doe"}))]
    pub card: Option<CardDetail>,

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

    /// The unique identifier of the customer.
    #[schema(value_type = Option<String>, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::CustomerId>,

    /// The card network
    #[schema(example = "Visa")]
    pub card_network: Option<String>,

    /// Payment method details from locker. Deprecated - use `bank_transfer_data` instead.
    #[cfg(feature = "payouts")]
    #[schema(value_type = Option<Bank>, deprecated)]
    pub bank_transfer: Option<payouts::Bank>,

    /// Payment method details from locker
    #[cfg(feature = "payouts")]
    #[schema(value_type = Option<BankTransfer>)]
    pub bank_transfer_data: Option<payouts::BankTransfer>,

    /// Payment method details from locker
    #[cfg(feature = "payouts")]
    #[schema(value_type = Option<Wallet>)]
    pub wallet: Option<payouts::Wallet>,

    /// For Client based calls, SDK will use the client_secret
    /// in order to call /payment_methods
    /// Client secret will be generated whenever a new
    /// payment method is created
    pub client_secret: Option<String>,

    /// Payment method data to be passed in case of client
    /// based flow
    pub payment_method_data: Option<PaymentMethodCreateData>,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    #[serde(skip_deserializing)]
    /// The connector mandate details of the payment method, this is added only for cards migration
    /// api and is skipped during deserialization of the payment method create request as this
    /// it should not be passed in the request
    pub connector_mandate_details: Option<PaymentsMandateReference>,

    #[serde(skip_deserializing)]
    /// The transaction id of a CIT (customer initiated transaction) associated with the payment method,
    /// this is added only for cards migration api and is skipped during deserialization of the
    /// payment method create request as it should not be passed in the request
    pub network_transaction_id: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentMethodRetrieveRequest {
    #[serde(default)]
    pub fetch_raw_detail: bool,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentMethodCreate {
    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>,example = "credit")]
    pub payment_method_subtype: Option<api_enums::PaymentMethodType>,

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

    /// The unique identifier of the customer.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "0a_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: Option<id_type::GlobalCustomerId>,

    /// Payment method data to be passed
    pub payment_method_data: PaymentMethodCreateData,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    /// The network tokenization configuration if applicable
    #[schema(value_type = Option<NetworkTokenization>)]
    pub network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,

    /// The storage type for the payment method
    #[schema(value_type = StorageType)]
    pub storage_type: common_enums::StorageType,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentMethodIntentCreate {
    /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
    #[schema(value_type = Option<Object>,example = json!({ "city": "NY", "unit": "245" }))]
    pub metadata: Option<pii::SecretSerdeValue>,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    /// The unique identifier of the customer.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "0a_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: id_type::GlobalCustomerId,
}

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

    /// Payment method data to be passed
    pub payment_method_data: PaymentMethodCreateData,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// This is a sub-category of payment method.
    #[schema(value_type = PaymentMethodType,example = "credit")]
    pub payment_method_subtype: api_enums::PaymentMethodType,
}

#[cfg(feature = "v2")]
impl PaymentMethodIntentConfirm {
    pub fn validate_payment_method_data_against_payment_method(
        payment_method_type: api_enums::PaymentMethod,
        payment_method_data: PaymentMethodCreateData,
    ) -> bool {
        match payment_method_type {
            api_enums::PaymentMethod::Card => {
                matches!(
                    payment_method_data,
                    PaymentMethodCreateData::Card(_) | PaymentMethodCreateData::ProxyCard(_)
                )
            }
            api_enums::PaymentMethod::Wallet => {
                matches!(payment_method_data, PaymentMethodCreateData::Wallet(_))
            }
            _ => false,
        }
    }
}

/// This struct is used internally only
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct PaymentMethodIntentConfirmInternal {
    pub id: id_type::GlobalPaymentMethodId,
    pub request: PaymentMethodIntentConfirm,
}

#[cfg(feature = "v2")]
impl From<PaymentMethodIntentConfirmInternal> for PaymentMethodIntentConfirm {
    fn from(item: PaymentMethodIntentConfirmInternal) -> Self {
        item.request
    }
}
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
/// This struct is only used by and internal api to migrate payment method
pub struct PaymentMethodMigrate {
    /// Merchant id
    pub merchant_id: id_type::MerchantId,

    /// The type of payment method use for the payment.
    pub payment_method: Option<api_enums::PaymentMethod>,

    /// This is a sub-category of payment method.
    pub payment_method_type: Option<api_enums::PaymentMethodType>,

    /// The name of the bank/ provider issuing the payment method to the end user
    pub payment_method_issuer: Option<String>,

    /// A standard code representing the issuer of payment method
    pub payment_method_issuer_code: Option<api_enums::PaymentMethodIssuerCode>,

    /// Card Details
    pub card: Option<MigrateCardDetail>,

    /// Network token details
    pub network_token: Option<MigrateNetworkTokenDetail>,

    /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
    pub metadata: Option<pii::SecretSerdeValue>,

    /// The unique identifier of the customer.
    pub customer_id: Option<id_type::CustomerId>,

    /// The card network
    pub card_network: Option<String>,

    /// Payment method details from locker. Deprecated, use bank_transfer_data instead
    #[cfg(feature = "payouts")]
    pub bank_transfer: Option<payouts::Bank>,

    // Payment method details from locker
    #[cfg(feature = "payouts")]
    pub bank_transfer_data: Option<payouts::BankTransfer>,

    /// Payment method details from locker
    #[cfg(feature = "payouts")]
    pub wallet: Option<payouts::Wallet>,

    /// Payment method data to be passed in case of client
    /// based flow
    pub payment_method_data: Option<PaymentMethodCreateData>,

    /// The billing details of the payment method
    pub billing: Option<payments::Address>,

    /// The connector mandate details of the payment method
    #[serde(deserialize_with = "deserialize_connector_mandate_details")]
    pub connector_mandate_details: Option<CommonMandateReference>,

    // The CIT (customer initiated transaction) transaction id associated with the payment method
    pub network_transaction_id: Option<String>,
}

#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodMigrateResponse {
    //payment method response when payment method entry is created
    pub payment_method_response: PaymentMethodResponse,

    //card data migration status
    pub card_migrated: Option<bool>,

    //payment method data migration status (bank debit, wallet, etc.)
    pub payment_method_migrated: Option<bool>,

    //network token data migration status
    pub network_token_migrated: Option<bool>,

    //connector mandate details migration status
    pub connector_mandate_details_migrated: Option<bool>,

    //network transaction id migration status
    pub network_transaction_id_migrated: Option<bool>,
}

#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodRecordUpdateResponse {
    pub payment_method_id: String,
    pub status: common_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub connector_mandate_details: Option<pii::SecretSerdeValue>,
    pub updated_payment_method_data: Option<bool>,
}

#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsMandateReference(
    pub HashMap<id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>,
);

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PayoutsMandateReference(
    pub HashMap<id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>,
);

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PayoutsMandateReferenceRecord {
    pub transfer_method_id: Option<String>,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
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 connector_customer_id: Option<String>,
    pub connector_mandate_request_reference_id: Option<String>,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsMandateReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

impl From<CommonMandateReference> for PaymentsMandateReference {
    fn from(common_mandate: CommonMandateReference) -> Self {
        common_mandate.payments.unwrap_or_default()
    }
}

impl From<PaymentsMandateReference> for CommonMandateReference {
    fn from(payments_reference: PaymentsMandateReference) -> Self {
        Self {
            payments: Some(payments_reference),
            payouts: None,
        }
    }
}

fn deserialize_connector_mandate_details<'de, D>(
    deserializer: D,
) -> Result<Option<CommonMandateReference>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let value: Option<serde_json::Value> =
        <Option<serde_json::Value> as de::Deserialize>::deserialize(deserializer)?;

    let payments_data = value
        .clone()
        .map(|mut mandate_details| {
            mandate_details
                .as_object_mut()
                .map(|obj| obj.remove("payouts"));

            serde_json::from_value::<PaymentsMandateReference>(mandate_details)
        })
        .transpose()
        .map_err(|err| {
            let err_msg = format!("{err:?}");
            de::Error::custom(format_args!(
                "Failed to deserialize PaymentsMandateReference `{err_msg}`",
            ))
        })?;

    let payouts_data = value
        .clone()
        .map(|mandate_details| {
            serde_json::from_value::<Option<CommonMandateReference>>(mandate_details).map(
                |optional_common_mandate_details| {
                    optional_common_mandate_details
                        .and_then(|common_mandate_details| common_mandate_details.payouts)
                },
            )
        })
        .transpose()
        .map_err(|err| {
            let err_msg = format!("{err:?}");
            de::Error::custom(format_args!(
                "Failed to deserialize CommonMandateReference `{err_msg}`",
            ))
        })?
        .flatten();

    Ok(Some(CommonMandateReference {
        payments: payments_data,
        payouts: payouts_data,
    }))
}

#[cfg(feature = "v1")]
impl PaymentMethodCreate {
    pub fn get_payment_method_create_from_payment_method_migrate(
        card_number: CardNumber,
        payment_method_migrate: &PaymentMethodMigrate,
    ) -> Self {
        let card_details =
            payment_method_migrate
                .card
                .as_ref()
                .map(|payment_method_migrate_card| CardDetail {
                    card_number,
                    card_exp_month: payment_method_migrate_card.card_exp_month.clone(),
                    card_exp_year: payment_method_migrate_card.card_exp_year.clone(),
                    card_holder_name: payment_method_migrate_card.card_holder_name.clone(),
                    nick_name: payment_method_migrate_card.nick_name.clone(),
                    card_issuing_country: payment_method_migrate_card.card_issuing_country.clone(),
                    card_issuing_country_code: payment_method_migrate_card
                        .card_issuing_country_code
                        .clone(),
                    card_network: payment_method_migrate_card.card_network.clone(),
                    card_issuer: payment_method_migrate_card.card_issuer.clone(),
                    card_type: payment_method_migrate_card.card_type.clone(),
                    card_cvc: None,
                });

        Self {
            customer_id: payment_method_migrate.customer_id.clone(),
            payment_method: payment_method_migrate.payment_method,
            payment_method_type: payment_method_migrate.payment_method_type,
            payment_method_issuer: payment_method_migrate.payment_method_issuer.clone(),
            payment_method_issuer_code: payment_method_migrate.payment_method_issuer_code,
            metadata: payment_method_migrate.metadata.clone(),
            payment_method_data: payment_method_migrate.payment_method_data.clone(),
            connector_mandate_details: payment_method_migrate
                .connector_mandate_details
                .clone()
                .map(|common_mandate_reference| {
                    PaymentsMandateReference::from(common_mandate_reference)
                }),
            client_secret: None,
            billing: payment_method_migrate.billing.clone(),
            card: card_details,
            card_network: payment_method_migrate.card_network.clone(),
            #[cfg(feature = "payouts")]
            bank_transfer: payment_method_migrate.bank_transfer.clone(),
            #[cfg(feature = "payouts")]
            bank_transfer_data: payment_method_migrate.bank_transfer_data.clone(),
            #[cfg(feature = "payouts")]
            wallet: payment_method_migrate.wallet.clone(),
            network_transaction_id: payment_method_migrate.network_transaction_id.clone(),
        }
    }

    pub fn get_payment_method_create_from_payment_method_data_migrate(
        payment_method_data: PaymentMethodCreateData,
        payment_method_migrate: &PaymentMethodMigrate,
    ) -> Self {
        Self {
            customer_id: payment_method_migrate.customer_id.clone(),
            payment_method: payment_method_migrate.payment_method,
            payment_method_type: payment_method_migrate.payment_method_type,
            payment_method_issuer: payment_method_migrate.payment_method_issuer.clone(),
            payment_method_issuer_code: payment_method_migrate.payment_method_issuer_code,
            metadata: payment_method_migrate.metadata.clone(),
            payment_method_data: Some(payment_method_data),
            connector_mandate_details: payment_method_migrate
                .connector_mandate_details
                .clone()
                .map(PaymentsMandateReference::from),
            client_secret: None,
            billing: payment_method_migrate.billing.clone(),
            card: None,
            card_network: None,
            #[cfg(feature = "payouts")]
            bank_transfer: None,
            #[cfg(feature = "payouts")]
            bank_transfer_data: None,
            #[cfg(feature = "payouts")]
            wallet: None,
            network_transaction_id: payment_method_migrate.network_transaction_id.clone(),
        }
    }
}

#[cfg(feature = "v2")]
impl PaymentMethodCreate {
    pub fn validate_payment_method_data_against_payment_method(
        payment_method_type: api_enums::PaymentMethod,
        payment_method_data: PaymentMethodCreateData,
    ) -> bool {
        match payment_method_type {
            api_enums::PaymentMethod::Card => {
                matches!(
                    payment_method_data,
                    PaymentMethodCreateData::Card(_) | PaymentMethodCreateData::ProxyCard(_)
                )
            }
            api_enums::PaymentMethod::BankDebit => {
                matches!(payment_method_data, PaymentMethodCreateData::BankDebit(_))
            }
            api_enums::PaymentMethod::Wallet => {
                matches!(payment_method_data, PaymentMethodCreateData::Wallet(_))
            }
            api_enums::PaymentMethod::BankRedirect => {
                matches!(
                    payment_method_data,
                    PaymentMethodCreateData::BankRedirect(_)
                )
            }
            _ => false,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentMethodUpdate {
    /// Card Details
    #[schema(example = json!({
    "card_number": "4111111145551142",
    "card_exp_month": "10",
    "card_exp_year": "25",
    "card_holder_name": "John Doe"}))]
    pub card: Option<CardDetailUpdate>,

    /// Wallet Details
    pub wallet: Option<PaymentMethodDataWalletInfo>,

    /// This is a 15 minute expiry token which shall be used from the client to authenticate and perform sessions from the SDK
    #[schema(max_length = 30, min_length = 30, example = "secret_k2uj3he2893eiu2d")]
    pub client_secret: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentMethodUpdate {
    /// Payment method details to be updated for the payment_method
    pub payment_method_data: Option<PaymentMethodUpdateData>,

    /// The connector token details to be updated for the payment_method
    pub connector_token_details: Option<ConnectorTokenDetails>,

    /// The network transaction ID provided by the card network during a Customer Initiated Transaction (CIT)
    /// when `setup_future_usage` is set to `off_session`.
    #[schema(value_type = Option<String>)]
    pub network_transaction_id: Option<hyperswitch_masking::Secret<String>>,

    /// The acknowledgement status of the payment method update, this is used to determine the status of the payment method update
    #[schema(value_type = Option<AcknowledgementStatus>)]
    pub acknowledgement_status: Option<common_enums::AcknowledgementStatus>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
#[serde(rename = "payment_method_data")]
pub enum PaymentMethodUpdateData {
    Card(CardDetailUpdate),
    BankDebit(BankDebitDetailUpdate),
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
pub enum BankDebitDetailUpdate {
    Ach {
        #[schema(value_type = Option<String>)]
        bank_account_holder_name: Option<hyperswitch_masking::Secret<String>>,
    },
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
pub enum WalletPaymentMethodData {
    ApplePay(Box<PaymentMethodDataWalletInfo>),
    GooglePay(Box<PaymentMethodDataWalletInfo>),
    #[schema(value_type = PaypalRedirection)]
    PayPal(Box<payments::PaypalRedirection>),
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
pub enum BankRedirectDetail {
    BancontactCard {},
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
#[serde(rename = "payment_method_data")]
pub enum PaymentMethodCreateData {
    Card(CardDetail),
    ProxyCard(ProxyCardDetails),
    BankDebit(BankDebitDetail),
    Wallet(WalletPaymentMethodData),
    BankRedirect(BankRedirectDetail),
}

#[cfg(feature = "v2")]
impl PaymentMethodCreateData {
    pub fn get_card(&self) -> Option<CardDetail> {
        match self {
            Self::Card(card_detail) => Some(card_detail.clone()),
            _ => None,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
#[serde(rename = "payment_method_data")]
pub enum PaymentMethodCreateData {
    Card(CardDetail),
    BankDebit(BankDebitDetail),
    Wallet(WalletDetail),
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
pub enum BankDebitDetail {
    Ach {
        #[schema(value_type = String)]
        account_number: hyperswitch_masking::Secret<String>,
        #[schema(value_type = String)]
        routing_number: hyperswitch_masking::Secret<String>,
        #[schema(value_type = Option<String>)]
        #[serde(default)]
        bank_account_holder_name: Option<hyperswitch_masking::Secret<String>>,
        #[schema(value_type = Option<BankType>)]
        #[serde(default)]
        bank_type: Option<common_enums::BankType>,
        #[schema(value_type = Option<BankHolderType>)]
        #[serde(default)]
        bank_holder_type: Option<common_enums::BankHolderType>,
    },
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
pub enum WalletDetail {
    ApplePayDecryptedData {
        /// The application primary account number associated with the card
        #[schema(value_type = String, example = "4242424242424242")]
        application_primary_account_number: CardNumber,

        /// The card's expiry month
        #[schema(value_type = String)]
        expiry_month: hyperswitch_masking::Secret<String>,

        /// The card's expiry year
        #[schema(value_type = String)]
        expiry_year: hyperswitch_masking::Secret<String>,
    },
    GooglePayDecryptedData {
        /// The application primary account number (PAN) associated with the card
        #[schema(value_type = String, example = "4242424242424242")]
        application_primary_account_number: CardNumber,

        /// The card's expiry month
        #[schema(value_type = String)]
        expiry_month: hyperswitch_masking::Secret<String>,

        /// The card's expiry year
        #[schema(value_type = String)]
        expiry_year: hyperswitch_masking::Secret<String>,
    },
}

#[cfg(feature = "v1")]
impl From<ApplePayPredecryptData> for WalletDetail {
    fn from(data: ApplePayPredecryptData) -> Self {
        Self::ApplePayDecryptedData {
            application_primary_account_number: data.application_primary_account_number,
            expiry_month: data.application_expiration_month,
            expiry_year: data.application_expiration_year,
        }
    }
}

#[cfg(feature = "v1")]
impl From<GPayPredecryptData> for WalletDetail {
    fn from(data: GPayPredecryptData) -> Self {
        Self::GooglePayDecryptedData {
            application_primary_account_number: data.application_primary_account_number,
            expiry_month: data.card_exp_month,
            expiry_year: data.card_exp_year,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
pub enum BankDebitDetail {
    Ach {
        #[schema(value_type = String)]
        account_number: hyperswitch_masking::Secret<String>,
        #[schema(value_type = String)]
        routing_number: hyperswitch_masking::Secret<String>,
        #[schema(value_type = Option<String>)]
        #[serde(default)]
        bank_account_holder_name: Option<hyperswitch_masking::Secret<String>>,
        #[schema(value_type = Option<BankType>)]
        #[serde(default)]
        bank_type: Option<common_enums::BankType>,
        #[schema(value_type = Option<BankHolderType>)]
        #[serde(default)]
        bank_holder_type: Option<common_enums::BankHolderType>,
        #[schema(value_type = Option<BankNames>)]
        #[serde(default)]
        bank_name: Option<common_enums::BankNames>,
    },
}

impl BankDebitDetail {
    pub fn get_masked_account_number(&self) -> String {
        match self {
            Self::Ach { account_number, .. } => account_number
                .peek()
                .chars()
                .rev()
                .take(4)
                .collect::<String>()
                .chars()
                .rev()
                .collect::<String>(),
        }
    }

    pub fn get_masked_routing_number(&self) -> String {
        match self {
            Self::Ach { routing_number, .. } => routing_number
                .peek()
                .chars()
                .rev()
                .take(4)
                .collect::<String>()
                .chars()
                .rev()
                .collect::<String>(),
        }
    }

    pub fn get_bank_account_holder_name(&self) -> Option<hyperswitch_masking::Secret<String>> {
        match self {
            Self::Ach {
                bank_account_holder_name,
                ..
            } => bank_account_holder_name.clone(),
        }
    }

    pub fn get_bank_type(&self) -> Option<common_enums::BankType> {
        match self {
            Self::Ach { bank_type, .. } => *bank_type,
        }
    }

    pub fn get_bank_holder_type(&self) -> Option<common_enums::BankHolderType> {
        match self {
            Self::Ach {
                bank_holder_type, ..
            } => *bank_holder_type,
        }
    }
}

#[cfg(feature = "v2")]
impl From<BankDebitDetail> for BankDebitDetailsPaymentMethod {
    fn from(bank_debit: BankDebitDetail) -> Self {
        let account_number_last4_digits = bank_debit.get_masked_account_number();
        let routing_number_last4_digits = bank_debit.get_masked_routing_number();
        match bank_debit {
            BankDebitDetail::Ach {
                bank_account_holder_name,
                bank_type,
                bank_holder_type,
                bank_name,
                ..
            } => Self::AchBankDebit {
                account_number_last4_digits,
                routing_number_last4_digits,
                bank_account_holder_name,
                bank_name,
                bank_type,
                bank_holder_type,
            },
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct CardDetail {
    /// Card Number
    #[schema(value_type = String,example = "4111111145551142")]
    pub card_number: CardNumber,

    /// Card Expiry Month
    #[schema(value_type = String,example = "10")]
    pub card_exp_month: hyperswitch_masking::Secret<String>,

    /// Card Expiry Year
    #[schema(value_type = String,example = "25")]
    pub card_exp_year: hyperswitch_masking::Secret<String>,

    /// Card CVC for Volatile Storage
    #[schema(value_type = Option<String>,example = "123")]
    pub card_cvc: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder Name
    #[schema(value_type = String,example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder's Nick Name
    #[schema(value_type = Option<String>,example = "John Doe")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Issuing Country
    pub card_issuing_country: Option<String>,

    /// Card Issuing Country Code
    pub card_issuing_country_code: Option<String>,

    /// Card's Network
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<api_enums::CardNetwork>,

    /// Issuer Bank for Card
    pub card_issuer: Option<String>,

    /// Card Type
    pub card_type: Option<String>,
}

#[derive(
    Debug,
    serde::Deserialize,
    serde::Serialize,
    Clone,
    ToSchema,
    strum::EnumString,
    strum::Display,
    Eq,
    PartialEq,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "UPPERCASE")]
pub enum CardType {
    Credit,
    Debit,
}

// We cannot use the card struct that we have for payments for the following reason
// The card struct used for payments has card_cvc as mandatory
// but when vaulting the card, we do not need cvc to be collected from the user
// This is because, the vaulted payment method can be used for future transactions in the presence of the customer
// when the customer is on_session again, the cvc can be collected from the customer
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
// #[serde(deny_unknown_fields)]
pub struct CardDetail {
    /// Card Number
    #[schema(value_type = String,example = "4111111145551142")]
    pub card_number: CardNumber,

    /// Card Expiry Month
    #[schema(value_type = String,example = "10")]
    pub card_exp_month: hyperswitch_masking::Secret<String>,

    /// Card Expiry Year
    #[schema(value_type = String,example = "25")]
    pub card_exp_year: hyperswitch_masking::Secret<String>,

    /// Card Holder Name
    #[schema(value_type = String,example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder's Nick Name
    #[schema(value_type = Option<String>,example = "John Doe")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Issuing Country
    #[schema(value_type = CountryAlpha2)]
    pub card_issuing_country: Option<api_enums::CountryAlpha2>,

    /// Card's Network
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<api_enums::CardNetwork>,

    /// Issuer Bank for Card
    pub card_issuer: Option<String>,

    /// Card Type
    pub card_type: Option<CardType>,

    /// The CVC number for the card
    /// This is optional in case the card needs to be vaulted
    #[schema(value_type = String, example = "242")]
    pub card_cvc: Option<hyperswitch_masking::Secret<String>>,
}

// This struct is for collecting Proxy Card Data
// All card related data present in this struct are tokenized
// No strict type is present to accept tokenized data
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct ProxyCardDetails {
    /// Tokenized Card Number
    #[schema(value_type = String,example = "tok_sjfowhoejsldj")]
    pub card_number: hyperswitch_masking::Secret<String>,

    /// Card Expiry Month
    #[schema(value_type = String,example = "10")]
    pub card_exp_month: hyperswitch_masking::Secret<String>,

    /// Card Expiry Year
    #[schema(value_type = String,example = "25")]
    pub card_exp_year: hyperswitch_masking::Secret<String>,

    /// First Six Digit of Card Number
    pub bin_number: Option<String>,

    ///Last Four Digit of Card Number
    pub last_four: Option<String>,

    /// Issuer Bank for Card
    pub card_issuer: Option<String>,

    /// Card's Network
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<common_enums::CardNetwork>,

    /// Card Type
    pub card_type: Option<String>,

    /// Issuing Country of the Card
    pub card_issuing_country: Option<String>,

    /// Card Holder's Nick Name
    #[schema(value_type = Option<String>,example = "John Doe")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder Name
    #[schema(value_type = String,example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// The CVC number for the card
    /// This is optional in case the card needs to be vaulted
    #[schema(value_type = String, example = "242")]
    pub card_cvc: Option<hyperswitch_masking::Secret<String>>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct MigrateCardDetail {
    /// Card Number
    #[schema(value_type = String,example = "4111111145551142")]
    pub card_number: hyperswitch_masking::Secret<String>,

    /// Card Expiry Month
    #[schema(value_type = String,example = "10")]
    pub card_exp_month: hyperswitch_masking::Secret<String>,

    /// Card Expiry Year
    #[schema(value_type = String,example = "25")]
    pub card_exp_year: hyperswitch_masking::Secret<String>,

    /// Card Holder Name
    #[schema(value_type = String,example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder's Nick Name
    #[schema(value_type = Option<String>,example = "John Doe")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Issuing Country
    pub card_issuing_country: Option<String>,

    /// Card Issuing Country Code
    pub card_issuing_country_code: Option<String>,

    /// Card's Network
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<api_enums::CardNetwork>,

    /// Issuer Bank for Card
    pub card_issuer: Option<String>,

    /// Card Type
    pub card_type: Option<String>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct MigrateNetworkTokenData {
    /// Network Token Number
    #[schema(value_type = String,example = "4111111145551142")]
    pub network_token_number: CardNumber,

    /// Network Token Expiry Month
    #[schema(value_type = String,example = "10")]
    pub network_token_exp_month: hyperswitch_masking::Secret<String>,

    /// Network Token Expiry Year
    #[schema(value_type = String,example = "25")]
    pub network_token_exp_year: hyperswitch_masking::Secret<String>,

    /// Card Holder Name
    #[schema(value_type = String,example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder's Nick Name
    #[schema(value_type = Option<String>,example = "John Doe")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Issuing Country
    pub card_issuing_country: Option<String>,

    /// Card Issuing Country
    pub card_issuing_country_code: Option<String>,

    /// Card's Network
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<api_enums::CardNetwork>,

    /// Issuer Bank for Card
    pub card_issuer: Option<String>,

    /// Card Type
    pub card_type: Option<String>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct MigrateNetworkTokenDetail {
    /// Network token details
    pub network_token_data: MigrateNetworkTokenData,

    /// Network token requestor reference id
    pub network_token_requestor_ref_id: String,
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct CardDetailUpdate {
    /// Card Expiry Month
    #[schema(value_type = String, example = "10")]
    pub card_exp_month: Option<hyperswitch_masking::Secret<String>>,

    /// Card Expiry Year
    #[schema(value_type = String, example = "25")]
    pub card_exp_year: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder Name
    #[schema(value_type = String, example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder's Nick Name
    #[schema(value_type = Option<String>, example = "John Doe")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card's Last 4 Digits
    #[schema(value_type = Option<String>, example = "1111")]
    pub last4_digits: Option<String>,

    /// Issuing Bank of the Particular Card
    #[schema(value_type = Option<String>, example = "Bank of America")]
    pub card_issuer: Option<String>,

    /// The country where that particular card was issued
    #[schema(value_type = Option<String>, example = "US")]
    pub issuer_country: Option<String>,

    /// The country code where that particular card was issued
    #[schema(value_type = Option<String>, example = "US")]
    pub issuer_country_code: Option<String>,

    /// The card network
    #[schema(value_type = Option<String>, example = "VISA")]
    pub card_network: Option<common_enums::CardNetwork>,
}

#[cfg(feature = "v1")]
impl CardDetailUpdate {
    pub fn apply(&self, card_data_from_locker: Card) -> CardDetail {
        CardDetail {
            card_number: card_data_from_locker.card_number,
            card_exp_month: self
                .card_exp_month
                .clone()
                .unwrap_or(card_data_from_locker.card_exp_month),
            card_exp_year: self
                .card_exp_year
                .clone()
                .unwrap_or(card_data_from_locker.card_exp_year),
            card_holder_name: self
                .card_holder_name
                .clone()
                .or(card_data_from_locker.name_on_card),
            nick_name: self.nick_name.clone().or(card_data_from_locker
                .nick_name
                .map(hyperswitch_masking::Secret::new)),
            card_cvc: None,
            card_issuing_country: None,
            card_issuing_country_code: None,
            card_network: None,
            card_issuer: None,
            card_type: None,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct CardDetailUpdate {
    /// Card Holder Name
    #[schema(value_type = String,example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder's Nick Name
    #[schema(value_type = Option<String>,example = "John Doe")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// The CVC number for the card
    #[schema(value_type = Option<String>,  example = "242")]
    pub card_cvc: Option<hyperswitch_masking::Secret<String>>,
}

#[cfg(feature = "v2")]
impl CardDetailUpdate {
    pub fn apply(&self, card_data_from_locker: Card) -> CardDetail {
        CardDetail {
            card_number: card_data_from_locker.card_number,
            card_exp_month: card_data_from_locker.card_exp_month,
            card_exp_year: card_data_from_locker.card_exp_year,
            card_holder_name: self
                .card_holder_name
                .clone()
                .or(card_data_from_locker.name_on_card),
            nick_name: self.nick_name.clone().or(card_data_from_locker
                .nick_name
                .map(hyperswitch_masking::Secret::new)),
            card_issuing_country: None,
            card_network: None,
            card_issuer: None,
            card_type: None,
            card_cvc: None,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
#[serde(rename = "payment_method_data")]
pub enum PaymentMethodResponseData {
    Card(Box<CardDetailFromLocker>),
    BankDebit(BankDebitDetailsPaymentMethod),
    Wallet(WalletPaymentMethodData),
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct RawCardWithNTDetails {
    /// Raw card details associated with the payment method
    #[schema(value_type = CardDetail)]
    pub card_details: CardDetail,

    /// Raw network token details associated with the payment method
    #[schema(value_type = CardDetail)]
    pub network_token_details: CardDetail,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct RawProxyCardDataResponse {
    #[schema(value_type = String, example = "4111111145551142")]
    pub card_number: hyperswitch_masking::Secret<String>,

    #[schema(value_type = Option<String>, example = "25")]
    pub card_exp_year: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type = Option<String>, example = "10")]
    pub card_exp_month: Option<hyperswitch_masking::Secret<String>>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "snake_case")]
pub enum RawPaymentMethodData {
    Card(CardDetail),
    CardWithNT(RawCardWithNTDetails),
    BankDebit(BankDebitDetail),
    ProxyCard(RawProxyCardDataResponse),
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentMethodResponse {
    /// Unique identifier for a merchant
    #[schema(example = "merchant_1671528864", value_type = String)]
    pub merchant_id: id_type::MerchantId,

    /// The unique identifier of the customer.
    #[schema(value_type = Option<String>, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::CustomerId>,

    /// The unique identifier of the Payment method
    #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")]
    pub payment_method_id: String,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method: Option<api_enums::PaymentMethod>,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>, example = "credit")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,

    /// Card details from card locker
    #[schema(example = json!({"last4": "1142","exp_month": "03","exp_year": "2030"}))]
    pub card: Option<CardDetailFromLocker>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for installment payments (e.g., EMI, BNPL). Optional.
    #[schema(example = true)]
    pub installment_payment_enabled: Option<bool>,

    /// Type of payment experience enabled with the connector
    #[schema(value_type = Option<Vec<PaymentExperience>>, example = json!(["redirect_to_url"]))]
    pub payment_experience: Option<Vec<api_enums::PaymentExperience>>,

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

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2023-01-18T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,

    /// Payment method details from locker
    #[cfg(feature = "payouts")]
    #[schema(value_type = Option<Bank>)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bank_transfer: Option<payouts::Bank>,

    #[schema(value_type = Option<PrimitiveDateTime>, example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_used_at: Option<time::PrimitiveDateTime>,

    /// For Client based calls
    pub client_secret: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethodUpdateResponse {
    /// Unique identifier for a merchant
    #[schema(example = "merchant_1671528864", value_type = String)]
    pub merchant_id: id_type::MerchantId,

    /// The unique identifier of the customer.
    #[schema(value_type = Option<String>, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::CustomerId>,

    /// The unique identifier of the Payment method
    #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")]
    pub payment_method_id: String,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method: Option<api_enums::PaymentMethod>,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>, example = "credit")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,

    /// Card details from card locker
    #[schema(example = json!({"last4": "1142","exp_month": "03","exp_year": "2030"}))]
    pub card: Option<CardDetailFromLocker>,

    /// Updated Wallet Details
    pub wallet: Option<PaymentMethodDataWalletInfo>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for installment payments (e.g., EMI, BNPL). Optional.
    #[schema(example = true)]
    pub installment_payment_enabled: Option<bool>,

    /// Type of payment experience enabled with the connector
    #[schema(value_type = Option<Vec<PaymentExperience>>, example = json!(["redirect_to_url"]))]
    pub payment_experience: Option<Vec<api_enums::PaymentExperience>>,

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

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2023-01-18T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,

    /// Payment method details from locker
    #[cfg(feature = "payouts")]
    #[schema(value_type = Option<Bank>)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bank_transfer: Option<payouts::Bank>,

    #[schema(value_type = Option<PrimitiveDateTime>, example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_used_at: Option<time::PrimitiveDateTime>,

    /// For Client based calls
    pub client_secret: Option<String>,
}
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema, Clone)]
pub struct ConnectorTokenDetails {
    /// The unique identifier of the connector account through which the token was generated
    #[schema(value_type = String, example = "mca_")]
    pub connector_id: id_type::MerchantConnectorAccountId,

    #[schema(value_type = TokenizationType)]
    pub token_type: common_enums::TokenizationType,

    /// The status of connector token if it is active or inactive
    #[schema(value_type = ConnectorTokenStatus)]
    pub status: common_enums::ConnectorTokenStatus,

    /// The reference id of the connector token
    /// This is the reference that was passed to connector when creating the token
    pub connector_token_request_reference_id: Option<String>,

    pub original_payment_authorized_amount: Option<MinorUnit>,

    /// The currency of the original payment authorized amount
    #[schema(value_type = Currency)]
    pub original_payment_authorized_currency: Option<common_enums::Currency>,

    /// Metadata associated with the connector token
    pub metadata: Option<pii::SecretSerdeValue>,

    /// The connector customer identifier associated with this tokenized payment method
    pub connector_customer_id: Option<String>,

    /// The value of the connector token. This token can be used to make merchant initiated payments ( MIT ), directly with the connector.
    pub token: hyperswitch_masking::Secret<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema, Clone)]
pub struct PaymentMethodResponse {
    /// The unique identifier of the Payment method
    #[schema(value_type = String, example = "0a_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodId,

    /// Unique identifier for a merchant
    #[schema(value_type = String, example = "merchant_1671528864")]
    pub merchant_id: id_type::MerchantId,

    /// The unique identifier of the customer.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "0a_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: Option<id_type::GlobalCustomerId>,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method_type: Option<api_enums::PaymentMethod>,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>, example = "credit")]
    pub payment_method_subtype: Option<api_enums::PaymentMethodType>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2023-01-18T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_used_at: Option<time::PrimitiveDateTime>,

    /// The payment method details related to the payment method
    pub payment_method_data: Option<PaymentMethodResponseData>,

    /// The connector token details if available
    pub connector_tokens: Option<Vec<ConnectorTokenDetails>>,

    /// Network token details if available
    pub network_token: Option<NetworkTokenResponse>,

    /// The storage type for the payment method
    #[schema(value_type = StorageType)]
    pub storage_type: common_enums::StorageType,

    /// Card CVC token storage details
    pub card_cvc_token_storage: Option<CardCVCTokenStorageDetails>,

    /// The network transaction ID provided by the card network during a Customer Initiated Transaction (CIT)
    /// when `setup_future_usage` is set to `off_session`.
    #[schema(value_type = String)]
    pub network_transaction_id: Option<hyperswitch_masking::Secret<String>>,

    /// The raw data associated with the payment method
    #[schema(value_type = RawPaymentMethodData)]
    pub raw_payment_method_data: Option<RawPaymentMethodData>,

    /// Billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    /// The acknowledgement status of the payment method update
    #[schema(value_type = Option<AcknowledgementStatus>)]
    pub acknowledgement_status: Option<common_enums::AcknowledgementStatus>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema, Clone)]
pub struct PaymentMethodDetailsResponse {
    /// The unique identifier of the Payment method
    #[schema(value_type = String, example = "0a_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodId,

    /// Unique identifier for a merchant
    #[schema(value_type = String, example = "merchant_1671528864")]
    pub merchant_id: id_type::MerchantId,

    /// The unique identifier of the customer.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "0a_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: Option<id_type::GlobalCustomerId>,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method_type: Option<api_enums::PaymentMethod>,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>, example = "credit")]
    pub payment_method_subtype: Option<api_enums::PaymentMethodType>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2023-01-18T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_used_at: Option<time::PrimitiveDateTime>,

    /// The payment method details related to the payment method
    pub payment_method_data: Option<PaymentMethodResponseData>,

    /// The connector token details if available
    pub connector_tokens: Option<Vec<ConnectorTokenDetails>>,

    /// Network token details if available
    pub network_token: Option<NetworkTokenResponse>,

    /// The storage type for the payment method
    #[schema(value_type = StorageType)]
    pub storage_type: common_enums::StorageType,

    /// The network transaction ID provided by the card network during a Customer Initiated Transaction (CIT)
    /// when `setup_future_usage` is set to `off_session`.
    #[schema(value_type = String)]
    pub network_transaction_id: Option<hyperswitch_masking::Secret<String>>,

    /// Billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    /// The acknowledgement status of the payment method update
    #[schema(value_type = Option<AcknowledgementStatus>)]
    pub acknowledgement_status: Option<common_enums::AcknowledgementStatus>,
}

#[cfg(feature = "v2")]
impl From<PaymentMethodResponse> for PaymentMethodDetailsResponse {
    fn from(resp: PaymentMethodResponse) -> Self {
        Self {
            id: resp.id,
            merchant_id: resp.merchant_id,
            customer_id: resp.customer_id,
            payment_method_type: resp.payment_method_type,
            payment_method_subtype: resp.payment_method_subtype,
            recurring_enabled: resp.recurring_enabled,
            created: resp.created,
            last_used_at: resp.last_used_at,
            payment_method_data: resp.payment_method_data,
            connector_tokens: resp.connector_tokens,
            network_token: resp.network_token,
            storage_type: resp.storage_type,
            network_transaction_id: resp.network_transaction_id,
            billing: resp.billing,
            acknowledgement_status: resp.acknowledgement_status,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct CardCVCTokenStorageDetails {
    /// Indicates whether the card cvc is stored or not
    #[schema(example = true)]
    pub is_stored: bool,

    /// A timestamp (ISO 8601 code) that determines expiry for stored card cvc token
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub expires_at: Option<time::PrimitiveDateTime>,
}

#[cfg(feature = "v2")]
impl CardCVCTokenStorageDetails {
    pub fn generate_expiry_timestamp(duration_in_secs: i64) -> Self {
        let current_time = common_utils::date_time::now();
        let expiry_time = current_time + time::Duration::seconds(duration_in_secs);
        Self {
            is_stored: true,
            expires_at: Some(expiry_time),
        }
    }
}

#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum PaymentMethodsData {
    Card(CardDetailsPaymentMethod),
    BankDetails(PaymentMethodDataBankCreds),
    WalletDetails(PaymentMethodDataWalletInfo),
    BankDebit(BankDebitDetailsPaymentMethod),
}

impl PaymentMethodsData {
    pub fn get_card_details(&self) -> Option<CardDetailsPaymentMethod> {
        match self {
            Self::Card(card_details) => Some(card_details.clone()),
            _ => None,
        }
    }
}

#[cfg(feature = "v2")]
impl From<WalletPaymentMethodData> for PaymentMethodsData {
    fn from(wallet_data: WalletPaymentMethodData) -> Self {
        match wallet_data {
            WalletPaymentMethodData::ApplePay(data) => Self::WalletDetails(*data),
            WalletPaymentMethodData::GooglePay(data) => Self::WalletDetails(*data),
            WalletPaymentMethodData::PayPal(data) => {
                Self::WalletDetails(PaymentMethodDataWalletInfo {
                    last4: None,
                    card_network: None,
                    card_type: None,
                    card_exp_month: None,
                    card_exp_year: None,
                    auth_code: None,
                    email: data.email,
                })
            }
        }
    }
}

#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct ExternalVaultTokenData {
    /// Tokenized reference for Card Number
    pub tokenized_card_number: hyperswitch_masking::Secret<String>,
}

#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CardDetailsPaymentMethod {
    pub last4_digits: Option<String>,
    pub issuer_country: Option<String>,
    pub issuer_country_code: Option<String>,
    pub expiry_month: Option<hyperswitch_masking::Secret<String>>,
    pub expiry_year: Option<hyperswitch_masking::Secret<String>>,
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,
    pub card_isin: Option<String>,
    pub card_issuer: Option<String>,
    pub card_network: Option<api_enums::CardNetwork>,
    pub card_type: Option<String>,
    #[serde(default = "saved_in_locker_default")]
    pub saved_to_locker: bool,
    pub co_badged_card_data: Option<CoBadgedCardDataToBeSaved>,
}

#[cfg(feature = "v1")]
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Eq, PartialEq, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum BankDebitDetailsPaymentMethod {
    AchBankDebit {
        masked_account_number: String,
        masked_routing_number: String,
        #[schema(value_type=Option<String>)]
        bank_account_holder_name: Option<hyperswitch_masking::Secret<String>>,
        #[schema(value_type = String, example = "ach")]
        bank_name: Option<common_enums::BankNames>,
        #[schema(value_type = String, example = "checking")]
        bank_type: Option<common_enums::BankType>,
        #[schema(value_type = String, example = "personal")]
        bank_holder_type: Option<common_enums::BankHolderType>,
    },
}

#[cfg(feature = "v2")]
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Eq, PartialEq, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum BankDebitDetailsPaymentMethod {
    AchBankDebit {
        account_number_last4_digits: String,
        routing_number_last4_digits: String,
        #[schema(value_type=Option<String>)]
        bank_account_holder_name: Option<hyperswitch_masking::Secret<String>>,
        #[schema(value_type = String, example = "ach")]
        bank_name: Option<common_enums::BankNames>,
        #[schema(value_type = String, example = "checking")]
        bank_type: Option<common_enums::BankType>,
        #[schema(value_type = String, example = "personal")]
        bank_holder_type: Option<common_enums::BankHolderType>,
    },
}

impl From<&CoBadgedCardData> for CoBadgedCardDataToBeSaved {
    fn from(co_badged_card_data: &CoBadgedCardData) -> Self {
        Self {
            co_badged_card_networks: co_badged_card_data
                .co_badged_card_networks_info
                .get_card_networks(),
            issuer_country_code: co_badged_card_data.issuer_country_code,
            is_regulated: co_badged_card_data.is_regulated,
            regulated_name: co_badged_card_data.regulated_name.clone(),
        }
    }
}

#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CoBadgedCardData {
    pub co_badged_card_networks_info: open_router::CoBadgedCardNetworks,
    pub issuer_country_code: common_enums::CountryAlpha2,
    pub is_regulated: bool,
    pub regulated_name: Option<common_enums::RegulatedName>,
}

#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CoBadgedCardDataToBeSaved {
    pub co_badged_card_networks: Vec<common_enums::CardNetwork>,
    pub issuer_country_code: common_enums::CountryAlpha2,
    pub is_regulated: bool,
    pub regulated_name: Option<common_enums::RegulatedName>,
}

impl From<CoBadgedCardDataToBeSaved> for CoBadgedCardData {
    fn from(data: CoBadgedCardDataToBeSaved) -> Self {
        Self {
            co_badged_card_networks_info: open_router::CoBadgedCardNetworks(
                data.co_badged_card_networks
                    .iter()
                    .map(|card_network| open_router::CoBadgedCardNetworksInfo {
                        network: card_network.clone(),
                        saving_percentage: None,
                    })
                    .collect(),
            ),
            issuer_country_code: data.issuer_country_code,
            is_regulated: data.is_regulated,
            regulated_name: data.regulated_name,
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct NetworkTokenDetailsPaymentMethod {
    #[schema(value_type = Option<String>, example = "4242")]
    pub last4_digits: Option<String>,
    #[schema(value_type = Option<CountryAlpha2>)]
    pub issuer_country: Option<common_enums::CountryAlpha2>,
    #[schema(value_type = Option<String>, example = "05")]
    pub network_token_expiry_month: Option<hyperswitch_masking::Secret<String>>,
    #[schema(value_type = Option<String>, example = "27")]
    pub network_token_expiry_year: Option<hyperswitch_masking::Secret<String>>,
    #[schema(value_type = Option<String>, example = "Card")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,
    #[schema(value_type = Option<String>, example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,
    #[schema(value_type = Option<String>, example = "16712672")]
    pub card_isin: Option<String>,
    #[schema(value_type = Option<String>, example = "Bank of America")]
    pub card_issuer: Option<String>,
    #[schema(value_type = Option<CardNetwork>, example = "VISA")]
    pub card_network: Option<api_enums::CardNetwork>,
    #[schema(value_type = Option<String>, example = "Credit")]
    pub card_type: Option<String>,
    #[serde(default = "saved_in_locker_default")]
    pub saved_to_locker: bool,
    #[schema(value_type = Option<String>, example = "522134KAVJ1JPZ8L77N0Z2LRYZS7J")]
    pub par: Option<hyperswitch_masking::Secret<String>>,
}

#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PaymentMethodDataBankCreds {
    pub mask: String,
    pub hash: String,
    pub account_type: Option<String>,
    pub account_name: Option<String>,
    pub payment_method_type: api_enums::PaymentMethodType,
    pub connector_details: Vec<BankAccountConnectorDetails>,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentMethodDataWalletInfo {
    /// Last 4 digits of the card number
    pub last4: Option<String>,
    /// The information of the payment method
    pub card_network: Option<String>,
    /// The type of payment method
    #[serde(rename = "type")]
    pub card_type: Option<String>,
    /// The card's expiry month
    #[schema(value_type = Option<String>,example = "10")]
    pub card_exp_month: Option<hyperswitch_masking::Secret<String>>,
    /// The card's expiry year
    #[schema(value_type = Option<String>,example = "25")]
    pub card_exp_year: Option<hyperswitch_masking::Secret<String>>,
    /// Unique authorisation code for the payment
    #[schema(value_type = Option<String>,example = "003225")]
    pub auth_code: Option<String>,
    /// Email address associated with the wallet (e.g. PayPal email)
    #[schema(value_type = Option<String>, example = "johntest@test.com")]
    pub email: Option<pii::Email>,
}

impl From<payments::additional_info::WalletAdditionalDataForCard> for PaymentMethodDataWalletInfo {
    fn from(item: payments::additional_info::WalletAdditionalDataForCard) -> Self {
        Self {
            last4: item.last4,
            card_network: item.card_network,
            card_type: item.card_type,
            card_exp_month: item.card_exp_month,
            card_exp_year: item.card_exp_year,
            auth_code: item.auth_code,
            email: item.email,
        }
    }
}

impl From<PaymentMethodDataWalletInfo> for payments::additional_info::WalletAdditionalDataForCard {
    fn from(item: PaymentMethodDataWalletInfo) -> Self {
        Self {
            last4: item.last4,
            card_network: item.card_network,
            card_type: item.card_type,
            card_exp_month: item.card_exp_month,
            card_exp_year: item.card_exp_year,
            auth_code: item.auth_code,
            email: item.email,
        }
    }
}

impl From<payments::ApplepayPaymentMethod> for PaymentMethodDataWalletInfo {
    fn from(item: payments::ApplepayPaymentMethod) -> Self {
        Self {
            last4: Some(
                item.display_name
                    .chars()
                    .rev()
                    .take(4)
                    .collect::<Vec<_>>()
                    .into_iter()
                    .rev()
                    .collect(),
            ),
            card_network: Some(item.network),
            card_type: Some(item.pm_type),
            card_exp_month: item.card_exp_month,
            card_exp_year: item.card_exp_year,
            auth_code: item.auth_code,
            email: None,
        }
    }
}

impl TryFrom<PaymentMethodDataWalletInfo> for Box<payments::ApplepayPaymentMethod> {
    type Error = error_stack::Report<errors::ValidationError>;
    fn try_from(item: PaymentMethodDataWalletInfo) -> Result<Self, Self::Error> {
        Ok(Self::new(payments::ApplepayPaymentMethod {
            display_name: item.last4.get_required_value("last4")?,
            network: item.card_network.get_required_value("card_network")?,
            pm_type: item.card_type.get_required_value("card_type")?,
            card_exp_month: item.card_exp_month,
            card_exp_year: item.card_exp_year,
            auth_code: item.auth_code,
        }))
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BankAccountTokenData {
    pub payment_method_type: api_enums::PaymentMethodType,
    pub payment_method: api_enums::PaymentMethod,
    pub connector_details: BankAccountConnectorDetails,
}

#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct BankAccountConnectorDetails {
    pub connector: String,
    pub account_id: hyperswitch_masking::Secret<String>,
    pub mca_id: id_type::MerchantConnectorAccountId,
    pub access_token: BankAccountAccessCreds,
}

#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum BankAccountAccessCreds {
    AccessToken(hyperswitch_masking::Secret<String>),
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct LockerCardResponse {
    card: Card,
    metadata: Option<pii::SecretSerdeValue>,
}

impl LockerCardResponse {
    pub fn new(card: Card, metadata: Option<pii::SecretSerdeValue>) -> Self {
        Self { card, metadata }
    }

    pub fn get_card(&self) -> Card {
        self.card.clone()
    }

    pub fn get_metadata(&self) -> Option<pii::SecretSerdeValue> {
        self.metadata.clone()
    }
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct Card {
    pub card_number: CardNumber,
    pub name_on_card: Option<hyperswitch_masking::Secret<String>>,
    pub card_exp_month: hyperswitch_masking::Secret<String>,
    pub card_exp_year: hyperswitch_masking::Secret<String>,
    pub card_brand: Option<String>,
    pub card_isin: Option<String>,
    pub nick_name: Option<String>,
}

#[cfg(feature = "v1")]
impl From<(Card, Option<common_enums::CardNetwork>)> for CardDetail {
    fn from((card, card_network): (Card, Option<common_enums::CardNetwork>)) -> Self {
        Self {
            card_number: card.card_number.clone(),
            card_exp_month: card.card_exp_month.clone(),
            card_exp_year: card.card_exp_year.clone(),
            card_holder_name: card.name_on_card.clone(),
            nick_name: card.nick_name.map(hyperswitch_masking::Secret::new),
            card_cvc: None,
            card_issuing_country: None,
            card_issuing_country_code: None,
            card_network,
            card_issuer: None,
            card_type: None,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct CardDetailFromLocker {
    pub scheme: Option<String>,
    pub issuer_country: Option<String>,
    pub issuer_country_code: Option<String>,
    pub last4_digits: Option<String>,
    #[serde(skip)]
    #[schema(value_type=Option<String>)]
    pub card_number: Option<CardNumber>,

    #[schema(value_type=Option<String>)]
    pub expiry_month: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>)]
    pub expiry_year: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>)]
    pub card_token: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>)]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>)]
    pub card_fingerprint: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>)]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<api_enums::CardNetwork>,

    pub card_isin: Option<String>,
    pub card_issuer: Option<String>,
    pub card_type: Option<String>,
    pub saved_to_locker: bool,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct CardDetailFromLocker {
    #[schema(value_type = Option<CountryAlpha2>)]
    pub issuer_country: Option<api_enums::CountryAlpha2>,
    #[schema(value_type = Option<String>, example = "4242")]
    pub last4_digits: Option<String>,
    #[serde(skip)]
    #[schema(value_type=Option<String>)]
    pub card_number: Option<CardNumber>,

    #[schema(value_type=Option<String>, example = "10")]
    pub expiry_month: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>, example = "25")]
    pub expiry_year: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>, example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>, example = "fingerprint_12345")]
    pub card_fingerprint: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type=Option<String>, example = "Card")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    #[schema(value_type = Option<CardNetwork>, example = "VISA")]
    pub card_network: Option<api_enums::CardNetwork>,

    #[schema(value_type=Option<String>, example = "4567890")]
    pub card_isin: Option<String>,
    #[schema(value_type=Option<String>, example = "Issuer Bank")]
    pub card_issuer: Option<String>,
    #[schema(value_type=Option<String>, example = "Credit")]
    pub card_type: Option<String>,
    #[schema(value_type=bool, example = true)]
    pub saved_to_locker: bool,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct NetworkTokenResponse {
    pub payment_method_data: NetworkTokenDetailsPaymentMethod,
}

fn saved_in_locker_default() -> bool {
    true
}

#[cfg(feature = "v1")]
impl From<CardDetailFromLocker> for payments::AdditionalCardInfo {
    fn from(item: CardDetailFromLocker) -> Self {
        Self {
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type,
            card_issuing_country: item.issuer_country,
            card_issuing_country_code: item.issuer_country_code,
            bank_code: None,
            last4: item.last4_digits,
            card_isin: item.card_isin,
            card_extended_bin: item
                .card_number
                .map(|card_number| card_number.get_extended_card_bin()),
            card_exp_month: item.expiry_month,
            card_exp_year: item.expiry_year,
            card_holder_name: item.card_holder_name,
            payment_checks: None,
            authentication_data: None,
            is_regulated: None,
            signature_network: None,
            auth_code: None,
        }
    }
}

#[cfg(feature = "v2")]
impl From<CardDetailFromLocker> for payments::AdditionalCardInfo {
    fn from(item: CardDetailFromLocker) -> Self {
        Self {
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type,
            card_issuing_country: item.issuer_country.map(|country| country.to_string()),
            card_issuing_country_code: None,
            bank_code: None,
            last4: item.last4_digits,
            card_isin: item.card_isin,
            card_extended_bin: item
                .card_number
                .map(|card_number| card_number.get_extended_card_bin()),
            card_exp_month: item.expiry_month,
            card_exp_year: item.expiry_year,
            card_holder_name: item.card_holder_name,
            payment_checks: None,
            authentication_data: None,
            is_regulated: None,
            signature_network: None,
            auth_code: None,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodListResponseForSession {
    /// The list of payment methods that are enabled for the business profile
    pub payment_methods_enabled: Vec<ResponsePaymentMethodTypes>,

    /// The list of saved payment methods of the customer
    pub customer_payment_methods: Vec<CustomerPaymentMethodResponseItem>,
}

#[cfg(feature = "v1")]
impl From<CardDetailsPaymentMethod> for CardDetailFromLocker {
    fn from(item: CardDetailsPaymentMethod) -> Self {
        Self {
            scheme: None,
            issuer_country: item.issuer_country,
            issuer_country_code: item.issuer_country_code,
            last4_digits: item.last4_digits,
            card_number: None,
            expiry_month: item.expiry_month,
            expiry_year: item.expiry_year,
            card_token: None,
            card_holder_name: item.card_holder_name,
            card_fingerprint: None,
            nick_name: item.nick_name,
            card_isin: item.card_isin,
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type,
            saved_to_locker: item.saved_to_locker,
        }
    }
}

#[cfg(feature = "v2")]
impl From<CardDetailsPaymentMethod> for CardDetailFromLocker {
    fn from(item: CardDetailsPaymentMethod) -> Self {
        Self {
            issuer_country: item
                .issuer_country
                .as_ref()
                .map(|c| api_enums::CountryAlpha2::from_str(c))
                .transpose()
                .ok()
                .flatten(),
            last4_digits: item.last4_digits,
            card_number: None,
            expiry_month: item.expiry_month,
            expiry_year: item.expiry_year,
            card_holder_name: item.card_holder_name,
            card_fingerprint: None,
            nick_name: item.nick_name,
            card_isin: item.card_isin,
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type,
            saved_to_locker: item.saved_to_locker,
        }
    }
}

#[cfg(feature = "v2")]
impl From<CardDetail> for CardDetailFromLocker {
    fn from(item: CardDetail) -> Self {
        Self {
            issuer_country: item.card_issuing_country,
            last4_digits: Some(item.card_number.get_last4()),
            card_number: Some(item.card_number),
            expiry_month: Some(item.card_exp_month),
            expiry_year: Some(item.card_exp_year),
            card_holder_name: item.card_holder_name,
            nick_name: item.nick_name,
            card_isin: None,
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type.map(|card| card.to_string()),
            saved_to_locker: true,
            card_fingerprint: None,
        }
    }
}

#[cfg(feature = "v1")]
impl From<CardDetail> for CardDetailFromLocker {
    fn from(item: CardDetail) -> Self {
        // scheme should be updated in case of co-badged cards
        let card_scheme = item
            .card_network
            .clone()
            .map(|card_network| card_network.to_string());
        Self {
            issuer_country: item.card_issuing_country,
            issuer_country_code: item.card_issuing_country_code,
            last4_digits: Some(item.card_number.get_last4()),
            card_number: Some(item.card_number),
            expiry_month: Some(item.card_exp_month),
            expiry_year: Some(item.card_exp_year),
            card_holder_name: item.card_holder_name,
            nick_name: item.nick_name,
            card_isin: None,
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type.map(|card| card.to_string()),
            saved_to_locker: true,
            card_fingerprint: None,
            scheme: card_scheme,
            card_token: None,
        }
    }
}

#[cfg(feature = "v2")]
impl From<CardDetail> for CardDetailsPaymentMethod {
    fn from(item: CardDetail) -> Self {
        Self {
            issuer_country: item.card_issuing_country.map(|c| c.to_string()),
            last4_digits: Some(item.card_number.get_last4()),
            expiry_month: Some(item.card_exp_month),
            expiry_year: Some(item.card_exp_year),
            card_holder_name: item.card_holder_name,
            nick_name: item.nick_name,
            card_isin: None,
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type.map(|card| card.to_string()),
            saved_to_locker: true,
            co_badged_card_data: None,
            issuer_country_code: None,
        }
    }
}

#[cfg(feature = "v1")]
impl From<(CardDetailFromLocker, Option<&CoBadgedCardData>)> for CardDetailsPaymentMethod {
    fn from(
        (item, co_badged_card_data): (CardDetailFromLocker, Option<&CoBadgedCardData>),
    ) -> Self {
        Self {
            issuer_country: item.issuer_country,
            issuer_country_code: item.issuer_country_code,
            last4_digits: item.last4_digits,
            expiry_month: item.expiry_month,
            expiry_year: item.expiry_year,
            nick_name: item.nick_name,
            card_holder_name: item.card_holder_name,
            card_isin: item.card_isin,
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type,
            saved_to_locker: item.saved_to_locker,
            co_badged_card_data: co_badged_card_data.map(CoBadgedCardDataToBeSaved::from),
        }
    }
}

#[cfg(feature = "v2")]
impl From<CardDetailFromLocker> for CardDetailsPaymentMethod {
    fn from(item: CardDetailFromLocker) -> Self {
        Self {
            issuer_country: item.issuer_country.map(|country| country.to_string()),
            last4_digits: item.last4_digits,
            expiry_month: item.expiry_month,
            expiry_year: item.expiry_year,
            nick_name: item.nick_name,
            card_holder_name: item.card_holder_name,
            card_isin: item.card_isin,
            card_issuer: item.card_issuer,
            card_network: item.card_network,
            card_type: item.card_type,
            saved_to_locker: item.saved_to_locker,
            co_badged_card_data: None,
            issuer_country_code: None,
        }
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq)]
pub struct PaymentExperienceTypes {
    /// The payment experience enabled
    #[schema(value_type = Option<PaymentExperience>, example = "redirect_to_url")]
    pub payment_experience_type: api_enums::PaymentExperience,

    /// The list of eligible connectors for a given payment experience
    #[schema(example = json!(["stripe", "adyen"]))]
    pub eligible_connectors: Vec<String>,
}

#[derive(Debug, Clone, serde::Serialize, ToSchema, PartialEq)]
pub struct CardNetworkTypes {
    /// The card network enabled
    #[schema(value_type = Option<CardNetwork>, example = "Visa")]
    pub card_network: api_enums::CardNetwork,

    /// surcharge details for this card network
    pub surcharge_details: Option<SurchargeDetailsResponse>,

    /// The list of eligible connectors for a given card network
    #[schema(example = json!(["stripe", "adyen"]))]
    pub eligible_connectors: Vec<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq)]
pub struct BankDebitTypes {
    pub eligible_connectors: Vec<String>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema, PartialEq)]
pub struct ResponsePaymentMethodTypes {
    /// The payment method type enabled
    #[schema(example = "klarna", value_type = PaymentMethodType)]
    pub payment_method_type: api_enums::PaymentMethodType,

    /// The list of payment experiences enabled, if applicable for a payment method type
    pub payment_experience: Option<Vec<PaymentExperienceTypes>>,

    /// The list of card networks enabled, if applicable for a payment method type
    pub card_networks: Option<Vec<CardNetworkTypes>>,

    #[schema(deprecated)]
    /// The list of banks enabled, if applicable for a payment method type . To be deprecated soon.
    pub bank_names: Option<Vec<BankCodeResponse>>,

    /// The Bank debit payment method information, if applicable for a payment method type.
    pub bank_debits: Option<BankDebitTypes>,

    /// The Bank transfer payment method information, if applicable for a payment method type.
    pub bank_transfers: Option<BankTransferTypes>,

    /// Required fields for the payment_method_type.
    pub required_fields: Option<HashMap<String, RequiredFieldInfo>>,

    /// surcharge details for this payment method type if exists
    pub surcharge_details: Option<SurchargeDetailsResponse>,

    /// auth service connector label for this payment method type, if exists
    pub pm_auth_connector: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema, PartialEq)]
#[serde(untagged)] // Untagged used for serialization only
pub enum PaymentMethodSubtypeSpecificData {
    #[schema(title = "card")]
    Card {
        card_networks: Vec<CardNetworkTypes>,
    },
    #[schema(title = "bank")]
    Bank {
        #[schema(value_type = BankNames)]
        bank_names: Vec<common_enums::BankNames>,
    },
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema, PartialEq)]
pub struct ResponsePaymentMethodTypes {
    /// The payment method type enabled
    #[schema(example = "pay_later", value_type = PaymentMethod)]
    pub payment_method_type: common_enums::PaymentMethod,

    /// The payment method subtype enabled
    #[schema(example = "klarna", value_type = PaymentMethodType)]
    pub payment_method_subtype: common_enums::PaymentMethodType,

    /// payment method subtype specific information
    #[serde(flatten)]
    pub extra_information: Option<PaymentMethodSubtypeSpecificData>,

    /// Required fields for the payment_method_type.
    /// This is the union of all the required fields for the payment method type enabled in all the connectors.
    pub required_fields: Vec<RequiredFieldInfo>,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub struct SurchargeDetailsResponse {
    /// surcharge value
    pub surcharge: SurchargeResponse,
    /// tax on surcharge value
    pub tax_on_surcharge: Option<SurchargePercentage>,
    /// surcharge amount for this payment
    pub display_surcharge_amount: f64,
    /// tax on surcharge amount for this payment
    pub display_tax_on_surcharge_amount: f64,
    /// sum of display_surcharge_amount and display_tax_on_surcharge_amount
    pub display_total_surcharge_amount: f64,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
pub enum SurchargeResponse {
    /// Fixed Surcharge value
    Fixed(MinorUnit),
    /// Surcharge percentage
    Rate(SurchargePercentage),
}

impl From<Surcharge> for SurchargeResponse {
    fn from(value: Surcharge) -> Self {
        match value {
            Surcharge::Fixed(amount) => Self::Fixed(amount),
            Surcharge::Rate(percentage) => Self::Rate(percentage.into()),
        }
    }
}

#[derive(Clone, Default, Debug, PartialEq, serde::Serialize, ToSchema)]
pub struct SurchargePercentage {
    percentage: f64,
}

impl From<Percentage<SURCHARGE_PERCENTAGE_PRECISION_LENGTH>> for SurchargePercentage {
    fn from(value: Percentage<SURCHARGE_PERCENTAGE_PRECISION_LENGTH>) -> Self {
        Self {
            percentage: value.get_percentage(),
        }
    }
}
/// Required fields info used while listing the payment_method_data
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq, ToSchema)]
pub struct RequiredFieldInfo {
    /// Required field for a payment_method through a payment_method_type
    pub required_field: String,

    /// Display name of the required field in the front-end
    pub display_name: String,

    /// Possible field type of required field
    #[schema(value_type = FieldType)]
    pub field_type: api_enums::FieldType,

    #[schema(value_type = Option<String>)]
    pub value: Option<hyperswitch_masking::Secret<String>>,
}

impl RequiredFieldInfo {
    pub fn for_config(&self) -> Self {
        Self {
            required_field: self.required_field.clone(),
            display_name: self.display_name.clone(),
            field_type: self.field_type.clone(),
            value: None,
        }
    }
}

#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct ResponsePaymentMethodsEnabled {
    /// The payment method enabled
    #[schema(value_type = PaymentMethod)]
    pub payment_method: api_enums::PaymentMethod,

    /// The list of payment method types enabled for a connector account
    pub payment_method_types: Vec<ResponsePaymentMethodTypes>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq)]
pub struct BankTransferTypes {
    /// The list of eligible connectors for a given payment experience
    #[schema(example = json!(["stripe", "adyen"]))]
    pub eligible_connectors: Vec<String>,
}

#[derive(Clone, Debug)]
pub struct ResponsePaymentMethodIntermediate {
    pub payment_method_type: api_enums::PaymentMethodType,
    pub payment_experience: Option<api_enums::PaymentExperience>,
    pub card_networks: Option<Vec<api_enums::CardNetwork>>,
    pub payment_method: api_enums::PaymentMethod,
    pub connector: String,
    pub merchant_connector_id: String,
}

impl ResponsePaymentMethodIntermediate {
    pub fn new(
        pm_type: RequestPaymentMethodTypes,
        connector: String,
        merchant_connector_id: String,
        pm: api_enums::PaymentMethod,
    ) -> Self {
        Self {
            payment_method_type: pm_type.payment_method_type,
            payment_experience: pm_type.payment_experience,
            card_networks: pm_type.card_networks,
            payment_method: pm,
            connector,
            merchant_connector_id,
        }
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq, Hash)]
pub struct RequestPaymentMethodTypes {
    #[schema(value_type = PaymentMethodType)]
    pub payment_method_type: api_enums::PaymentMethodType,
    #[schema(value_type = Option<PaymentExperience>)]
    pub payment_experience: Option<api_enums::PaymentExperience>,
    #[schema(value_type = Option<Vec<CardNetwork>>)]
    pub card_networks: Option<Vec<api_enums::CardNetwork>>,
    /// List of currencies accepted or has the processing capabilities of the processor
    #[schema(example = json!(
        {
            "type": "specific_accepted",
            "list": ["USD", "INR"]
        }
    ), value_type = Option<AcceptedCurrencies>)]
    pub accepted_currencies: Option<admin::AcceptedCurrencies>,

    ///  List of Countries accepted or has the processing capabilities of the processor
    #[schema(example = json!(
        {
            "type": "specific_accepted",
            "list": ["UK", "AU"]
        }
    ), value_type = Option<AcceptedCountries>)]
    pub accepted_countries: Option<admin::AcceptedCountries>,

    /// Minimum amount supported by the processor. To be represented in the lowest denomination of the target currency (For example, for USD it should be in cents)
    #[schema(example = 1)]
    pub minimum_amount: Option<MinorUnit>,

    /// Maximum amount supported by the processor. To be represented in the lowest denomination of
    /// the target currency (For example, for USD it should be in cents)
    #[schema(example = 1313)]
    pub maximum_amount: Option<MinorUnit>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = false)]
    pub recurring_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for installment payments (e.g., EMI, BNPL). Optional.
    #[schema(example = true)]
    pub installment_payment_enabled: Option<bool>,
}
impl RequestPaymentMethodTypes {
    /// Get payment_method_type
    #[cfg(feature = "v1")]
    pub fn get_payment_method_type(&self) -> Option<api_enums::PaymentMethodType> {
        Some(self.payment_method_type)
    }
}

#[cfg(feature = "v1")]
//List Payment Method
#[derive(Debug, Clone, serde::Serialize, Default, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct PaymentMethodListRequest {
    /// This is a 15 minute expiry token which shall be used from the client to authenticate and perform sessions from the SDK
    #[schema(max_length = 30, min_length = 30, example = "secret_k2uj3he2893eiu2d")]
    pub client_secret: Option<String>,

    /// The two-letter ISO currency code
    #[schema(value_type = Option<Vec<CountryAlpha2>>, example = json!(["US", "UK", "IN"]))]
    pub accepted_countries: Option<Vec<api_enums::CountryAlpha2>>,

    /// The three-letter ISO currency code
    #[schema(value_type = Option<Vec<Currency>>,example = json!(["USD", "EUR"]))]
    pub accepted_currencies: Option<Vec<api_enums::Currency>>,

    /// Filter by amount
    #[schema(example = 60)]
    pub amount: Option<MinorUnit>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for installment payments (e.g., EMI, BNPL). Optional.
    #[schema(example = true)]
    pub installment_payment_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for card netwotks
    #[schema(value_type = Option<Vec<CardNetwork>>, example = json!(["visa", "mastercard"]))]
    pub card_networks: Option<Vec<api_enums::CardNetwork>>,

    /// Indicates the limit of last used payment methods
    #[schema(example = 1)]
    pub limit: Option<i64>,
}

#[cfg(feature = "v1")]
impl<'de> serde::Deserialize<'de> for PaymentMethodListRequest {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct FieldVisitor;

        impl<'de> de::Visitor<'de> for FieldVisitor {
            type Value = PaymentMethodListRequest;

            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("Failed while deserializing as map")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
            where
                A: de::MapAccess<'de>,
            {
                let mut output = PaymentMethodListRequest::default();

                while let Some(key) = map.next_key()? {
                    match key {
                        "client_secret" => {
                            set_or_reject_duplicate(
                                &mut output.client_secret,
                                "client_secret",
                                map.next_value()?,
                            )?;
                        }
                        "accepted_countries" => match output.accepted_countries.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => {
                                output.accepted_countries = Some(vec![map.next_value()?]);
                            }
                        },
                        "accepted_currencies" => match output.accepted_currencies.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => {
                                output.accepted_currencies = Some(vec![map.next_value()?]);
                            }
                        },
                        "amount" => {
                            set_or_reject_duplicate(
                                &mut output.amount,
                                "amount",
                                map.next_value()?,
                            )?;
                        }
                        "recurring_enabled" => {
                            set_or_reject_duplicate(
                                &mut output.recurring_enabled,
                                "recurring_enabled",
                                map.next_value()?,
                            )?;
                        }
                        "installment_payment_enabled" => {
                            set_or_reject_duplicate(
                                &mut output.installment_payment_enabled,
                                "installment_payment_enabled",
                                map.next_value()?,
                            )?;
                        }
                        "card_network" => match output.card_networks.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => output.card_networks = Some(vec![map.next_value()?]),
                        },
                        "limit" => {
                            set_or_reject_duplicate(&mut output.limit, "limit", map.next_value()?)?;
                        }
                        _ => {}
                    }
                }

                Ok(output)
            }
        }

        deserializer.deserialize_identifier(FieldVisitor)
    }
}

#[cfg(feature = "v2")]
//List Payment Method
#[derive(Debug, Clone, serde::Serialize, Default, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct ListMethodsForPaymentMethodsRequest {
    /// This is a 15 minute expiry token which shall be used from the client to authenticate and perform sessions from the SDK
    #[schema(max_length = 30, min_length = 30, example = "secret_k2uj3he2893eiu2d")]
    pub client_secret: Option<String>,

    /// The two-letter ISO currency code
    #[schema(value_type = Option<Vec<CountryAlpha2>>, example = json!(["US", "UK", "IN"]))]
    pub accepted_countries: Option<Vec<api_enums::CountryAlpha2>>,

    /// Filter by amount
    #[schema(example = 60)]
    pub amount: Option<MinorUnit>,

    /// The three-letter ISO currency code
    #[schema(value_type = Option<Vec<Currency>>,example = json!(["USD", "EUR"]))]
    pub accepted_currencies: Option<Vec<api_enums::Currency>>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for card netwotks
    #[schema(value_type = Option<Vec<CardNetwork>>, example = json!(["visa", "mastercard"]))]
    pub card_networks: Option<Vec<api_enums::CardNetwork>>,

    /// Indicates the limit of last used payment methods
    #[schema(example = 1)]
    pub limit: Option<i64>,

    pub include_new: Option<bool>,
}

#[cfg(feature = "v2")]
impl<'de> serde::Deserialize<'de> for ListMethodsForPaymentMethodsRequest {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct FieldVisitor;

        impl<'de> de::Visitor<'de> for FieldVisitor {
            type Value = ListMethodsForPaymentMethodsRequest;

            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("Failed while deserializing as map")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
            where
                A: de::MapAccess<'de>,
            {
                let mut output = ListMethodsForPaymentMethodsRequest::default();

                while let Some(key) = map.next_key()? {
                    match key {
                        "client_secret" => {
                            set_or_reject_duplicate(
                                &mut output.client_secret,
                                "client_secret",
                                map.next_value()?,
                            )?;
                        }
                        "accepted_countries" => match output.accepted_countries.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => {
                                output.accepted_countries = Some(vec![map.next_value()?]);
                            }
                        },
                        "amount" => {
                            set_or_reject_duplicate(
                                &mut output.amount,
                                "amount",
                                map.next_value()?,
                            )?;
                        }
                        "accepted_currencies" => match output.accepted_currencies.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => {
                                output.accepted_currencies = Some(vec![map.next_value()?]);
                            }
                        },
                        "recurring_enabled" => {
                            set_or_reject_duplicate(
                                &mut output.recurring_enabled,
                                "recurring_enabled",
                                map.next_value()?,
                            )?;
                        }
                        "card_network" => match output.card_networks.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => output.card_networks = Some(vec![map.next_value()?]),
                        },
                        "limit" => {
                            set_or_reject_duplicate(&mut output.limit, "limit", map.next_value()?)?;
                        }
                        "include_new" => {
                            set_or_reject_duplicate(
                                &mut output.include_new,
                                "include_new",
                                map.next_value()?,
                            )?;
                        }
                        _ => {}
                    }
                }

                Ok(output)
            }
        }

        deserializer.deserialize_identifier(FieldVisitor)
    }
}

// Try to set the provided value to the data otherwise throw an error
fn set_or_reject_duplicate<T, E: de::Error>(
    data: &mut Option<T>,
    name: &'static str,
    value: T,
) -> Result<(), E> {
    match data {
        Some(_inner) => Err(de::Error::duplicate_field(name)),
        None => {
            *data = Some(value);
            Ok(())
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodListResponse {
    /// Redirect URL of the merchant
    #[schema(example = "https://www.google.com")]
    pub redirect_url: Option<String>,

    /// currency of the Payment to be done
    #[schema(example = "USD", value_type = Currency)]
    pub currency: Option<api_enums::Currency>,

    /// Information about the payment method
    pub payment_methods: Vec<ResponsePaymentMethodsEnabled>,
    /// Value indicating if the current payment is a mandate payment
    #[schema(value_type = MandateType)]
    pub mandate_payment: Option<payments::MandateType>,

    #[schema(value_type = Option<String>)]
    pub merchant_name: OptionalEncryptableName,

    /// flag to indicate if surcharge and tax breakup screen should be shown or not
    #[schema(value_type = bool)]
    pub show_surcharge_breakup_screen: bool,

    #[schema(value_type = Option<PaymentType>)]
    pub payment_type: Option<api_enums::PaymentType>,

    /// flag to indicate whether to perform external 3ds authentication
    #[schema(example = true)]
    pub request_external_three_ds_authentication: bool,

    /// flag that indicates whether to collect shipping details from wallets or from the customer
    pub collect_shipping_details_from_wallets: Option<bool>,

    /// flag that indicates whether to collect billing details from wallets or from the customer
    pub collect_billing_details_from_wallets: Option<bool>,

    /// flag that indicates whether to calculate tax on the order amount
    pub is_tax_calculation_enabled: bool,

    /// indicates the next action to be performed by the SDK
    #[schema(value_type = SdkNextAction)]
    pub sdk_next_action: payments::SdkNextAction,

    /// indicates whether this is a guest customer flow
    pub is_guest_customer: bool,

    /// Payment intent details associated with this payment method list request
    pub intent_data: Option<PaymentMethodListIntentData>,
}

/// Extra caller-supplied fields required to fully populate [`PaymentMethodListIntentData`].
///
/// Callers build this struct from their business-layer context and pass it into
/// [`PaymentIntent::into_payment_method_list_intent_data`], keeping the function pure
/// (no post-construction mutation).
#[cfg(feature = "v1")]
#[derive(Debug)]
pub struct PaymentMethodListIntentDataInput {
    /// The merchant's display name
    pub merchant_name: Option<String>,

    /// Mandate payment data derived from the payment attempt's mandate_details
    pub mandate_payment: Option<payments::MandateData>,

    /// The inferred payment type (normal / new_mandate / recurring_mandate)
    pub payment_type: Option<api_enums::PaymentType>,

    /// Capture method for the payment (e.g. "automatic", "manual")
    pub capture_method: Option<api_enums::CaptureMethod>,
}

/// Intent-only payment details returned as part of the Payment Method List response
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodListIntentData {
    /// Unique identifier for the payment
    #[schema(value_type = String)]
    pub payment_id: id_type::PaymentId,

    /// The status of the payment
    #[schema(value_type = IntentStatus)]
    pub status: api_enums::IntentStatus,

    /// The payment amount
    pub amount: MinorUnit,

    /// The currency of the payment
    #[schema(example = "USD", value_type = Option<Currency>)]
    pub currency: Option<api_enums::Currency>,

    /// Client secret for client-side payment confirmation
    #[schema(value_type = Option<String>)]
    pub client_secret: Option<hyperswitch_masking::Secret<String>>,

    /// A description for the payment
    pub description: Option<String>,

    /// The customer identifier
    #[schema(value_type = Option<String>)]
    pub customer_id: Option<id_type::CustomerId>,

    /// The URL to redirect to after payment completion
    pub return_url: Option<String>,

    /// Whether to save payment method for future use
    #[schema(value_type = Option<FutureUsage>)]
    pub setup_future_usage: Option<api_enums::FutureUsage>,

    /// The billing address for the payment
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    /// The shipping address for the payment
    #[schema(value_type = Option<Address>)]
    pub shipping: Option<payments::Address>,

    /// The email associated with the payment
    #[schema(value_type = Option<String>)]
    pub email: Option<pii::Email>,

    /// Additional metadata
    #[schema(value_type = Option<Object>)]
    pub metadata: Option<pii::SecretSerdeValue>,

    /// Order details for the payment
    #[schema(value_type = Option<Vec<Object>>)]
    pub order_details: Option<Vec<hyperswitch_masking::Secret<serde_json::Value>>>,

    /// Timestamp when the payment was created
    pub created: Option<time::PrimitiveDateTime>,

    /// Timestamp when the client secret expires
    pub expires_on: Option<time::PrimitiveDateTime>,

    /// The profile identifier
    #[schema(value_type = Option<String>)]
    pub profile_id: Option<id_type::ProfileId>,

    /// Merchant-provided reference identifier
    pub merchant_order_reference_id: Option<String>,

    /// Number of payment attempts made
    pub attempt_count: i16,

    /// Installment options available for this payment
    pub installment_options: Option<Vec<PaymentMethodListInstallmentOption>>,

    /// The capture method for the payment
    #[schema(value_type = Option<CaptureMethod>)]
    pub capture_method: Option<api_enums::CaptureMethod>,

    /// The merchant's display name
    pub merchant_name: Option<String>,

    /// Mandate payment data associated with this payment
    #[schema(value_type = Option<MandateData>)]
    pub mandate_payment: Option<payments::MandateData>,

    /// The type of payment
    #[schema(value_type = Option<PaymentType>)]
    pub payment_type: Option<api_enums::PaymentType>,

    /// Whether external 3DS authentication was requested
    pub request_external_three_ds_authentication: Option<bool>,

    /// Whether tax calculation is enabled for this payment
    pub is_tax_calculation_enabled: Option<bool>,

    /// Whether the customer is a guest (not saved)
    pub is_guest_customer: Option<bool>,
}

/// Payment experience entry in the client PM list.
/// Subtype-specific data for each payment method entry in the client list
#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema, PartialEq)]
#[serde(untagged)]
pub enum PaymentMethodSubtypeSpecificDataForClient {
    /// Card payment method — contains card network info
    Card {
        #[schema(value_type = Vec<CardNetwork>)]
        card_networks: Vec<api_enums::CardNetwork>,
    },
    /// Bank payment method (bank_redirect) — contains supported bank names
    Bank {
        #[schema(value_type = Vec<BankNames>)]
        bank_names: Vec<common_enums::BankNames>,
    },
}

/// A single flat entry in the client payment method list — one per (payment_method_type × payment_method_subtype)
#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema, PartialEq)]
pub struct ResponsePaymentMethodsEnabledForClient {
    /// The top-level payment method type (e.g. "card", "wallet")
    #[schema(value_type = PaymentMethod)]
    pub payment_method: api_enums::PaymentMethod,

    /// The payment method subtype (e.g. "credit", "apple_pay")
    #[schema(value_type = PaymentMethodType)]
    pub payment_method_type: api_enums::PaymentMethodType,

    /// Subtype-specific data (card_networks or bank list); absent (null) for wallets/pay_later/etc.
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub data: Option<PaymentMethodSubtypeSpecificDataForClient>,

    /// Payment experience options for this method (wallets, pay_later, etc.)
    #[schema(value_type = Option<Vec<PaymentExperience>>)]
    pub payment_experience: Option<Vec<api_enums::PaymentExperience>>,

    /// Whether to collect shipping details from the wallet connector (null for non-wallet)
    pub collect_shipping_details_from_wallets: Option<bool>,

    /// Whether to collect billing details from the wallet connector (null for non-wallet)
    pub collect_billing_details_from_wallets: Option<bool>,
}

/// Typed subtype-specific data for a saved customer payment method, returned in the
/// `GET /payments/{payment_id}/payment-methods/client` response.
///
/// Serializes as an externally-tagged enum, producing e.g.:
/// `{ "card": { "last4_digits": "4242", ... } }`
///
/// Only variants for subtypes that actually carry extra data are included;
/// for all other subtypes the `payment_method_data` field on
/// `CustomerPaymentMethodForClient` is `None`.
/// Wallet payment method data returned in the client-facing PM list.
#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum WalletPaymentMethodDataForClient {
    ApplePay(Box<PaymentMethodDataWalletInfo>),
    GooglePay(Box<PaymentMethodDataWalletInfo>),
    #[schema(value_type = PaypalRedirection)]
    PayPal(Box<payments::PaypalRedirection>),
}

/// Bank debit payment method data returned in the client-facing PM list.
/// Field names use `_last4_digits` to match the modular service response.
#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum BankDebitDataForClient {
    AchBankDebit {
        account_number_last4_digits: String,
        routing_number_last4_digits: String,
        #[schema(value_type = Option<String>)]
        bank_account_holder_name: Option<hyperswitch_masking::Secret<String>>,
        #[schema(value_type = String, example = "ach")]
        bank_name: Option<common_enums::BankNames>,
        #[schema(value_type = String, example = "checking")]
        bank_type: Option<common_enums::BankType>,
        #[schema(value_type = String, example = "personal")]
        bank_holder_type: Option<common_enums::BankHolderType>,
    },
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum CustomerPaymentMethodDataForClient {
    /// Masked card details from the card locker.
    Card(Box<CardDetailFromLocker>),
    /// Wallet details (Apple Pay, Google Pay, PayPal).
    Wallet(WalletPaymentMethodDataForClient),
    /// Bank debit details (ACH, …).
    BankDebit(BankDebitDataForClient),
}

/// A saved customer payment method as returned in the client-facing PM list.
///
/// This is a slimmed-down version of `CustomerPaymentMethod` intended for the
/// `GET /payments/{payment_id}/payment-methods/client` endpoint.  Fields that are
/// Only the fields needed by the SDK are included; server-side fields
/// (e.g. `surcharge_details`, `metadata`) are omitted.
#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethodForClient {
    /// Token for payment method in temporary card locker which gets refreshed often.
    /// The SDK passes this back when submitting the payment.
    #[schema(example = "7ebf443f-a050-4067-84e5-e6f6d4800aef")]
    pub payment_token: String,

    /// Top-level payment method category (card, wallet, …)
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method: api_enums::PaymentMethod,

    /// Specific subtype within the category (credit, debit, apple_pay, …)
    #[schema(value_type = Option<PaymentMethodType>, example = "credit")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,

    /// Whether this is the customer's default payment method
    #[schema(example = true)]
    pub default_payment_method_set: bool,

    /// Whether CVV collection is required before using this method
    #[schema(example = true)]
    pub requires_cvv: bool,

    /// When the payment method was saved
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2023-01-18T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,

    /// When this payment method was last used for a payment
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_used_at: Option<time::PrimitiveDateTime>,

    /// Whether this payment method supports recurring / off-session payments
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// Subtype-specific details (e.g. masked card info for card methods)
    #[schema(value_type = Option<CustomerPaymentMethodDataForClient>)]
    pub payment_method_data: Option<CustomerPaymentMethodDataForClient>,
}

/// Response for the GET /payments/{payment_id}/payment-methods/client endpoint
#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct ClientPaymentMethodsListResponse {
    /// Flat list of enabled payment methods — one entry per (payment_method_type × payment_method_subtype)
    pub payment_methods_enabled: Vec<ResponsePaymentMethodsEnabledForClient>,

    /// The customer's saved payment methods
    pub customer_payment_methods: Vec<CustomerPaymentMethodForClient>,

    /// The next action the SDK should take
    #[schema(value_type = SdkNextAction)]
    pub sdk_next_action: payments::SdkNextAction,

    /// Payment intent context data
    pub intent_data: PaymentMethodListIntentData,
}

/// Installment options for a payment method, as returned in the payment method list response
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodListInstallmentOption {
    /// The payment method these plans apply to
    #[schema(value_type = PaymentMethod)]
    pub payment_method: api_enums::PaymentMethod,
    /// Individual installment plans with computed amounts
    pub available_plans: Vec<PaymentMethodListInstallmentPlan>,
}

/// A single installment plan with pre-computed amount breakdown
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodListInstallmentPlan {
    /// Number of installments for this plan
    #[schema(value_type = u8)]
    pub number_of_installments: NonZeroU8,
    /// Billing frequency
    #[schema(value_type = BillingFrequency)]
    pub billing_frequency: BillingFrequency,
    /// Interest rate as a percentage
    #[schema(value_type = f64)]
    pub interest_rate: InstallmentInterestRate,
    /// Pre-computed amount breakdown
    pub amount_details: PaymentMethodListInstallmentAmountDetails,
}

/// Amount breakdown for a single installment plan
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodListInstallmentAmountDetails {
    /// Amount charged per installment in major units
    #[schema(value_type = f64)]
    pub amount_per_installment: FloatMajorUnit,
    /// Total amount across all installments in major units (may differ slightly from order amount due to ceiling)
    #[schema(value_type = f64)]
    pub total_amount: FloatMajorUnit,
}

impl PaymentMethodListInstallmentPlan {
    pub fn from_installment_option_data(
        data: common_types::payments::InstallmentOptionData,
        order_amount: MinorUnit,
        net_amount: MinorUnit,
        currency: api_enums::Currency,
    ) -> errors::CustomResult<Vec<Self>, errors::ParsingError> {
        data.number_of_installments
            .as_slice()
            .iter()
            .map(|&count| {
                let interest = data
                    .interest_rate
                    .calculate_emi_interest(order_amount, count)
                    .change_context(errors::ParsingError::UnknownError)
                    .attach_printable("Failed to apply installment interest rate")?;
                let total_with_interest = net_amount + interest;

                let total_decimal = Decimal::from_i64(total_with_interest.get_amount_as_i64())
                    .ok_or(errors::ParsingError::UnknownError)
                    .map_err(error_stack::Report::from)
                    .attach_printable("Failed to convert total amount to decimal")?;

                let count_decimal = Decimal::from_u8(u8::from(count))
                    .ok_or(errors::ParsingError::UnknownError)
                    .map_err(error_stack::Report::from)
                    .attach_printable("Failed to convert count to decimal")?;

                // - ceil() ensures merchant always receives at least the calculated interest
                let per_installment = MinorUnit::new(
                    (total_decimal / count_decimal)
                        .ceil()
                        .to_i64()
                        .ok_or(errors::ParsingError::UnknownError)
                        .map_err(error_stack::Report::from)
                        .attach_printable("Failed to convert per installment to i64")?,
                );
                let amount_per_installment = per_installment
                    .to_major_unit_as_f64(currency)
                    .change_context(errors::ParsingError::UnknownError)
                    .attach_printable("Failed to convert per installment amount to major unit")?;
                let total_amount = total_with_interest
                    .to_major_unit_as_f64(currency)
                    .change_context(errors::ParsingError::UnknownError)
                    .attach_printable("Failed to convert total installment amount to major unit")?;
                Ok(Self {
                    number_of_installments: count,
                    billing_frequency: data.billing_frequency.clone(),
                    interest_rate: data.interest_rate,
                    amount_details: PaymentMethodListInstallmentAmountDetails {
                        amount_per_installment,
                        total_amount,
                    },
                })
            })
            .collect()
    }
}

impl PaymentMethodListInstallmentOption {
    pub fn from_installment_option(
        opt: common_types::payments::InstallmentOption,
        order_amount: MinorUnit,
        net_amount: MinorUnit,
        currency: api_enums::Currency,
    ) -> errors::CustomResult<Self, errors::ParsingError> {
        let available_plans = opt
            .installments
            .into_iter()
            .map(|data| {
                PaymentMethodListInstallmentPlan::from_installment_option_data(
                    data,
                    order_amount,
                    net_amount,
                    currency,
                )
            })
            .collect::<errors::CustomResult<Vec<Vec<_>>, _>>()?
            .into_iter()
            .flatten()
            .collect();
        Ok(Self {
            payment_method: opt.payment_method,
            available_plans,
        })
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethodsListResponse {
    /// List of payment methods for customer
    pub customer_payment_methods: Vec<CustomerPaymentMethod>,
    /// Returns whether a customer id is not tied to a payment intent (only when the request is made against a client secret)
    pub is_guest_customer: Option<bool>,
}

// OLAP PML Response
#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethodsListResponse {
    /// List of payment methods for customer
    pub customer_payment_methods: Vec<PaymentMethodResponseItem>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct GetTokenDataRequest {
    /// Indicates the type of token to be fetched
    pub token_type: api_enums::TokenDataType,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for GetTokenDataRequest {}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct TokenDataResponse {
    /// The unique identifier of the payment method.
    #[schema(value_type = String, example = "0a_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub payment_method_id: id_type::GlobalPaymentMethodId,

    /// token type of the payment method
    #[schema(value_type = TokenDataType)]
    pub token_type: api_enums::TokenDataType,

    /// token details of the payment method
    pub token_details: TokenDetailsResponse,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for TokenDataResponse {}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
#[serde(untagged)]
pub enum TokenDetailsResponse {
    NetworkTokenDetails(NetworkTokenDetailsResponse),
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct NetworkTokenDetailsResponse {
    /// Network token generated against the Card Number
    #[schema(value_type = String)]
    pub network_token: cards::NetworkToken,

    /// Expiry month of the network token
    #[schema(value_type = String)]
    pub network_token_exp_month: hyperswitch_masking::Secret<String>,

    /// Expiry year of the network token
    #[schema(value_type = String)]
    pub network_token_exp_year: hyperswitch_masking::Secret<String>,

    /// Cryptogram generated by the Network
    #[schema(value_type = Option<String>)]
    pub cryptogram: Option<hyperswitch_masking::Secret<String>>,

    /// Issuer of the card
    pub card_issuer: Option<String>,

    /// Card network of the token
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<common_enums::CardNetwork>,

    /// Card type of the token
    pub card_type: Option<CardType>,

    /// Issuing country of the card
    #[schema(value_type = Option<CountryAlpha2>)]
    pub card_issuing_country: Option<common_enums::CountryAlpha2>,

    /// Bank code of the card
    pub bank_code: Option<String>,

    /// Name of the card holder
    #[schema(value_type = Option<String>)]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// Nick name of the card holder
    #[schema(value_type = Option<String>)]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// ECI indicator of the card
    pub eci: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct TotalPaymentMethodCountResponse {
    /// total count of payment methods under the merchant
    pub total_count: i64,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for TotalPaymentMethodCountResponse {}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodDeleteResponse {
    /// The unique identifier of the Payment method
    #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")]
    pub payment_method_id: String,

    /// Whether payment method was deleted or not
    #[schema(example = true)]
    pub deleted: bool,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodDeleteResponse {
    /// The unique identifier of the Payment method
    #[schema(value_type = String, example = "0a_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodId,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodDeleteSessionResponse {
    /// The unique identifier of the Payment method
    #[schema(value_type = String, example = "token_9wcXDRVkfEtLEsSnYKgQ")]
    pub payment_method_token: String,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for PaymentMethodDeleteSessionResponse {}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct CustomerDefaultPaymentMethodResponse {
    /// The unique identifier of the Payment method
    #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")]
    pub default_payment_method_id: Option<String>,
    /// The unique identifier of the customer.
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: id_type::CustomerId,
    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method: api_enums::PaymentMethod,
    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>,example = "credit")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct CustomerDefaultPaymentMethodResponse {
    /// The unique identifier of the Payment method
    #[schema(value_type = String, example = "card_rGK4Vi5iSW70MY7J2mIg")]
    pub default_payment_method_id: Option<id_type::GlobalPaymentMethodId>,
    /// The unique identifier of the customer.
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: id_type::GlobalCustomerId,
    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,
    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>,example = "credit")]
    pub payment_method_subtype: Option<api_enums::PaymentMethodType>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct PaymentMethodResponseItem {
    /// The unique identifier of the payment method.
    #[schema(value_type = String, example = "0a_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodId,

    /// The unique identifier of the customer.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "0a_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: id_type::GlobalCustomerId,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// This is a sub-category of payment method.
    #[schema(value_type = PaymentMethodType,example = "credit")]
    pub payment_method_subtype: api_enums::PaymentMethodType,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// PaymentMethod Data from locker
    pub payment_method_data: Option<PaymentMethodListData>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = PrimitiveDateTime, example = "2023-01-18T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created: time::PrimitiveDateTime,

    /// Whether this payment method requires CVV to be collected
    #[schema(example = true)]
    pub requires_cvv: bool,

    ///  A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = PrimitiveDateTime,example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601")]
    pub last_used_at: time::PrimitiveDateTime,

    /// Indicates if the payment method has been set to default or not
    #[schema(example = true)]
    pub is_default: bool,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    ///The network token details for the payment method
    pub network_tokenization: Option<NetworkTokenResponse>,

    /// The connector token details if available
    pub connector_tokens: Option<Vec<ConnectorTokenDetails>>,

    /// The network transaction ID provided by the card network during a Customer Initiated Transaction (CIT)
    /// when `setup_future_usage` is set to `off_session`.
    #[schema(value_type = Option<String>)]
    pub network_transaction_id: Option<hyperswitch_masking::Secret<String>>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethodResponseItem {
    /// Temporary Token for payment method in vault which gets refreshed for every payment
    #[schema(example = "7ebf443f-a050-4067-84e5-e6f6d4800aef")]
    pub payment_method_token: String,

    /// The unique identifier of the customer.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "0a_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: id_type::GlobalCustomerId,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// This is a sub-category of payment method.
    #[schema(value_type = PaymentMethodType,example = "credit")]
    pub payment_method_subtype: api_enums::PaymentMethodType,

    /// Indicates whether the payment method is eligible for recurring payments
    #[schema(example = true)]
    pub recurring_enabled: bool,

    /// PaymentMethod Data from locker
    pub payment_method_data: Option<PaymentMethodListData>,

    /// Masked bank details from PM auth services
    #[schema(example = json!({"mask": "0000"}))]
    pub bank: Option<MaskedBankDetails>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = PrimitiveDateTime, example = "2023-01-18T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created: time::PrimitiveDateTime,

    /// Whether this payment method requires CVV to be collected
    #[schema(example = true)]
    pub requires_cvv: bool,

    ///  A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = PrimitiveDateTime,example = "2024-02-24T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_used_at: time::PrimitiveDateTime,

    /// Indicates if the payment method has been set to default or not
    #[schema(example = true)]
    pub is_default: bool,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum PaymentMethodListData {
    Card(CardDetailFromLocker),
    BankDebit(BankDebitDetailsPaymentMethod),
    #[cfg(feature = "payouts")]
    #[schema(value_type = Bank)]
    Bank(payouts::Bank),
    Wallet(WalletPaymentMethodData),
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethod {
    /// Token for payment method in temporary card locker which gets refreshed often
    #[schema(example = "7ebf443f-a050-4067-84e5-e6f6d4800aef")]
    pub payment_token: String,
    /// The unique identifier of the customer.
    #[schema(example = "pm_iouuy468iyuowqs")]
    pub payment_method_id: String,

    /// The unique identifier of the customer.
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: id_type::CustomerId,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method: api_enums::PaymentMethod,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>,example = "credit_card")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,

    /// The name of the bank/ provider issuing the payment method to the end user
    #[schema(example = "Citibank")]
    pub payment_method_issuer: Option<String>,

    /// A standard code representing the issuer of payment method
    #[schema(value_type = Option<PaymentMethodIssuerCode>,example = "jp_applepay")]
    pub payment_method_issuer_code: Option<api_enums::PaymentMethodIssuerCode>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for installment payments (e.g., EMI, BNPL). Optional.
    #[schema(example = true)]
    pub installment_payment_enabled: Option<bool>,

    /// Type of payment experience enabled with the connector
    #[schema(value_type = Option<Vec<PaymentExperience>>,example = json!(["redirect_to_url"]))]
    pub payment_experience: Option<Vec<api_enums::PaymentExperience>>,

    /// Card details from card locker
    #[schema(example = json!({"last4": "1142","exp_month": "03","exp_year": "2030"}))]
    pub card: Option<CardDetailFromLocker>,

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

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = Option<PrimitiveDateTime>,example = "2023-01-18T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,

    /// Payment method details from locker
    #[cfg(feature = "payouts")]
    #[schema(value_type = Option<Bank>)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bank_transfer: Option<payouts::Bank>,

    /// Masked bank details from PM auth services
    #[schema(example = json!({"mask": "0000"}))]
    pub bank: Option<MaskedBankDetails>,

    /// Surcharge details for this saved card
    pub surcharge_details: Option<SurchargeDetailsResponse>,

    /// Whether this payment method requires CVV to be collected
    #[schema(example = true)]
    pub requires_cvv: bool,

    ///  A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = Option<PrimitiveDateTime>,example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_used_at: Option<time::PrimitiveDateTime>,
    /// Indicates if the payment method has been set to default or not
    #[schema(example = true)]
    pub default_payment_method_set: bool,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct PaymentMethodCollectLinkRequest {
    /// The unique identifier for the collect link.
    #[schema(value_type = Option<String>, example = "pm_collect_link_2bdacf398vwzq5n422S1")]
    pub pm_collect_link_id: Option<String>,

    /// The unique identifier of the customer.
    #[schema(value_type = String, example = "cus_92dnwed8s32bV9D8Snbiasd8v")]
    pub customer_id: id_type::CustomerId,

    #[serde(flatten)]
    #[schema(value_type = Option<GenericLinkUiConfig>)]
    pub ui_config: Option<link_utils::GenericLinkUiConfig>,

    /// Will be used to expire client secret after certain amount of time to be supplied in seconds
    /// (900) for 15 mins
    #[schema(value_type = Option<u32>, example = 900)]
    pub session_expiry: Option<u32>,

    /// Redirect to this URL post completion
    #[schema(value_type = Option<String>, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1/status")]
    pub return_url: Option<String>,

    /// List of payment methods shown on collect UI
    #[schema(value_type = Option<Vec<EnabledPaymentMethod>>, example = r#"[{"payment_method": "bank_transfer", "payment_method_types": ["ach", "bacs"]}]"#)]
    pub enabled_payment_methods: Option<Vec<link_utils::EnabledPaymentMethod>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct PaymentMethodCollectLinkResponse {
    /// The unique identifier for the collect link.
    #[schema(value_type = String, example = "pm_collect_link_2bdacf398vwzq5n422S1")]
    pub pm_collect_link_id: String,

    /// The unique identifier of the customer.
    #[schema(value_type = String, example = "cus_92dnwed8s32bV9D8Snbiasd8v")]
    pub customer_id: id_type::CustomerId,

    /// Time when this link will be expired in ISO8601 format
    #[schema(value_type = PrimitiveDateTime, example = "2025-01-18T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expiry: time::PrimitiveDateTime,

    /// URL to the form's link generated for collecting payment method details.
    #[schema(value_type = String, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1")]
    pub link: hyperswitch_masking::Secret<url::Url>,

    /// Redirect to this URL post completion
    #[schema(value_type = Option<String>, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1/status")]
    pub return_url: Option<String>,

    /// Collect link config used
    #[serde(flatten)]
    #[schema(value_type = GenericLinkUiConfig)]
    pub ui_config: link_utils::GenericLinkUiConfig,

    /// List of payment methods shown on collect UI
    #[schema(value_type = Option<Vec<EnabledPaymentMethod>>, example = r#"[{"payment_method": "bank_transfer", "payment_method_types": ["ach", "bacs"]}]"#)]
    pub enabled_payment_methods: Option<Vec<link_utils::EnabledPaymentMethod>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct PaymentMethodCollectLinkRenderRequest {
    /// Unique identifier for a merchant.
    #[schema(example = "merchant_1671528864", value_type = String)]
    pub merchant_id: id_type::MerchantId,

    /// The unique identifier for the collect link.
    #[schema(value_type = String, example = "pm_collect_link_2bdacf398vwzq5n422S1")]
    pub pm_collect_link_id: String,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct PaymentMethodCollectLinkDetails {
    pub publishable_key: hyperswitch_masking::Secret<String>,
    pub client_secret: hyperswitch_masking::Secret<String>,
    pub pm_collect_link_id: String,
    pub customer_id: id_type::CustomerId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub session_expiry: time::PrimitiveDateTime,
    pub return_url: Option<String>,
    #[serde(flatten)]
    pub ui_config: link_utils::GenericLinkUiConfigFormData,
    pub enabled_payment_methods: Option<Vec<link_utils::EnabledPaymentMethod>>,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct PaymentMethodCollectLinkStatusDetails {
    pub pm_collect_link_id: String,
    pub customer_id: id_type::CustomerId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub session_expiry: time::PrimitiveDateTime,
    pub return_url: Option<url::Url>,
    pub status: link_utils::PaymentMethodCollectStatus,
    #[serde(flatten)]
    pub ui_config: link_utils::GenericLinkUiConfigFormData,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct MaskedBankDetails {
    pub mask: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentMethodId {
    pub payment_method_id: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ModularPaymentMethodMigrationRecord {
    pub merchant_id: id_type::MerchantId,
    #[serde(flatten)]
    pub payment_method_id: PaymentMethodId,
    #[serde(skip_deserializing, default)]
    pub line_number: Option<i64>,
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)]
pub struct DefaultPaymentMethod {
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: id_type::CustomerId,
    pub payment_method_id: String,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)]
pub struct DefaultPaymentMethod {
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "0a_cus_01926c58bc6e77c09e809964e72af8c8")]
    pub customer_id: String,
    pub payment_method_id: String,
}

//------------------------------------------------TokenizeService------------------------------------------------
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizePayloadEncrypted {
    pub payload: String,
    pub key_id: String,
    pub version: Option<String>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizePayloadRequest {
    pub value1: String,
    pub value2: String,
    pub lookup_key: String,
    pub service_name: String,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct GetTokenizePayloadRequest {
    pub lookup_key: String,
    pub service_name: String,
    pub get_value2: bool,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct DeleteTokenizeByTokenRequest {
    pub lookup_key: String,
    pub service_name: String,
}

#[derive(Debug, serde::Serialize)] // Blocked: Yet to be implemented by `basilisk`
pub struct DeleteTokenizeByDateRequest {
    pub buffer_minutes: i32,
    pub service_name: String,
    pub max_rows: i32,
}

#[derive(Debug, serde::Deserialize)]
pub struct GetTokenizePayloadResponse {
    pub lookup_key: String,
    pub get_value2: Option<bool>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCardValue1 {
    pub card_number: String,
    pub exp_year: String,
    pub exp_month: String,
    pub name_on_card: Option<String>,
    pub nickname: Option<String>,
    pub card_last_four: Option<String>,
    pub card_token: Option<String>,
    pub card_network: Option<api_enums::CardNetwork>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListCountriesCurrenciesRequest {
    pub connector: api_enums::Connector,
    pub payment_method_type: api_enums::PaymentMethodType,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListCountriesCurrenciesResponse {
    pub currencies: HashSet<api_enums::Currency>,
    pub countries: HashSet<CountryCodeWithName>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize, Eq, Hash, PartialEq)]
pub struct CountryCodeWithName {
    pub code: api_enums::CountryAlpha2,
    pub name: api_enums::Country,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCardValue2 {
    pub card_security_code: Option<String>,
    pub card_fingerprint: Option<String>,
    pub external_id: Option<String>,
    pub customer_id: Option<id_type::CustomerId>,
    pub payment_method_id: Option<String>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedWalletValue1 {
    pub data: payments::WalletData,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedWalletValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedBankTransferValue1 {
    pub data: payments::BankTransferData,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedBankTransferValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedBankRedirectValue1 {
    pub data: payments::BankRedirectData,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedBankRedirectValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PaymentMethodRecord {
    pub customer_id: id_type::CustomerId,
    pub name: Option<hyperswitch_masking::Secret<String>>,
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,
    pub email: Option<pii::Email>,
    pub phone: Option<hyperswitch_masking::Secret<String>>,
    pub phone_country_code: Option<String>,
    pub merchant_id: Option<id_type::MerchantId>,
    pub payment_method: Option<api_enums::PaymentMethod>,
    pub payment_method_type: Option<api_enums::PaymentMethodType>,
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,
    pub payment_instrument_id: Option<hyperswitch_masking::Secret<String>>,
    pub connector_customer_id: Option<String>,
    pub connector_mandate_request_reference_id: Option<String>,
    // Card fields are optional to support non-card CSV rows (e.g., ACH bank debit).
    // For card rows these will still be populated normally from the CSV columns.
    #[serde(default)]
    pub card_number_masked: Option<hyperswitch_masking::Secret<String>>,
    #[serde(default)]
    pub card_expiry_month: Option<hyperswitch_masking::Secret<String>>,
    #[serde(default)]
    pub card_expiry_year: Option<hyperswitch_masking::Secret<String>>,
    pub card_scheme: Option<String>,
    pub original_transaction_id: Option<String>,
    pub billing_address_zip: Option<hyperswitch_masking::Secret<String>>,
    pub billing_address_state: Option<hyperswitch_masking::Secret<String>>,
    pub billing_address_first_name: Option<hyperswitch_masking::Secret<String>>,
    pub billing_address_last_name: Option<hyperswitch_masking::Secret<String>>,
    pub billing_address_city: Option<String>,
    pub billing_address_country: Option<api_enums::CountryAlpha2>,
    pub billing_address_line1: Option<hyperswitch_masking::Secret<String>>,
    pub billing_address_line2: Option<hyperswitch_masking::Secret<String>>,
    pub billing_address_line3: Option<hyperswitch_masking::Secret<String>>,
    pub raw_card_number: Option<hyperswitch_masking::Secret<String>>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub merchant_connector_ids: Option<String>,
    pub original_transaction_amount: Option<i64>,
    pub original_transaction_currency: Option<common_enums::Currency>,
    pub line_number: Option<i64>,
    pub network_token_number: Option<CardNumber>,
    pub network_token_expiry_month: Option<hyperswitch_masking::Secret<String>>,
    pub network_token_expiry_year: Option<hyperswitch_masking::Secret<String>>,
    pub network_token_requestor_ref_id: Option<String>,
    #[serde(default)]
    pub account_number: Option<hyperswitch_masking::Secret<String>>,
    #[serde(default)]
    pub routing_number: Option<hyperswitch_masking::Secret<String>>,
    #[serde(default)]
    pub bank_account_holder_name: Option<hyperswitch_masking::Secret<String>>,
    #[serde(default)]
    pub bank_type: Option<common_enums::BankType>,
    #[serde(default)]
    pub bank_holder_type: Option<common_enums::BankHolderType>,
    #[serde(default)]
    pub bank_name: Option<common_enums::BankNames>,
}

#[cfg(feature = "v1")]
impl PaymentMethodRecord {
    /// Constructs `PaymentMethodCreateData` from the CSV record fields.
    /// Returns `None` for card records (cards are handled via `MigrateCardDetail`).
    /// Extend the match arms here to support additional payment method types
    /// (e.g., SEPA, BACS, wallets) in the future.
    pub fn get_payment_method_data(&self) -> Option<PaymentMethodCreateData> {
        match (self.account_number.as_ref(), self.routing_number.as_ref()) {
            (Some(account_number), Some(routing_number))
                if !account_number.peek().is_empty() && !routing_number.peek().is_empty() =>
            {
                Some(PaymentMethodCreateData::BankDebit(BankDebitDetail::Ach {
                    account_number: account_number.clone(),
                    routing_number: routing_number.clone(),
                    bank_account_holder_name: self.bank_account_holder_name.clone(),
                    bank_type: self.bank_type,
                    bank_holder_type: self.bank_holder_type,
                }))
            }
            _ => None,
        }
    }
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct UpdatePaymentMethodRecord {
    pub payment_method_id: String,
    pub status: Option<common_enums::PaymentMethodStatus>,
    pub network_transaction_id: Option<String>,
    pub line_number: Option<i64>,
    pub payment_instrument_id: Option<hyperswitch_masking::Secret<String>>,
    pub connector_customer_id: Option<String>,
    pub merchant_connector_ids: Option<String>,
    pub card_expiry_month: Option<hyperswitch_masking::Secret<String>>,
    pub card_expiry_year: Option<hyperswitch_masking::Secret<String>>,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PaymentMethodsBatchRecord {
    pub payment_method_id: String,
    #[serde(skip_deserializing, default)]
    pub line_number: Option<i64>,
}

#[derive(Debug, serde::Serialize)]
pub struct PaymentMethodsBatchRetrieveResponse {
    pub payment_method_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_type: Option<api_enums::PaymentMethod>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_subtype: Option<api_enums::PaymentMethodType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_data: Option<PaymentMethodsData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error_message: Option<String>,
    pub line_number: Option<i64>,
}

impl common_utils::events::ApiEventMetric for PaymentMethodsBatchRecord {}

impl common_utils::events::ApiEventMetric for PaymentMethodsBatchRetrieveResponse {}

#[derive(Debug, serde::Serialize)]
pub struct PaymentMethodUpdateResponse {
    pub payment_method_id: String,
    pub status: Option<common_enums::PaymentMethodStatus>,
    pub network_transaction_id: Option<String>,
    pub connector_mandate_details: Option<pii::SecretSerdeValue>,
    pub update_status: UpdateStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub update_error: Option<String>,
    pub updated_payment_method_data: Option<bool>,
    pub line_number: Option<i64>,
}

#[derive(Debug, Default, serde::Serialize)]
pub struct PaymentMethodMigrationResponse {
    pub line_number: Option<i64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method: Option<api_enums::PaymentMethod>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,
    pub customer_id: Option<id_type::CustomerId>,
    pub migration_status: MigrationStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub migration_error: Option<String>,
    pub card_number_masked: Option<hyperswitch_masking::Secret<String>>,
    pub card_migrated: Option<bool>,
    pub payment_method_migrated: Option<bool>,
    pub network_token_migrated: Option<bool>,
    pub connector_mandate_details_migrated: Option<bool>,
    pub network_transaction_id_migrated: Option<bool>,
}

#[derive(Debug, Default, serde::Serialize)]
pub struct ModularPaymentMethodMigrationResponse {
    pub total_rows: usize,
    pub successful_count: usize,
    pub failed_count: usize,
    pub results: Vec<ModularPaymentMethodMigrationRowResult>,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ModularPaymentMethodMigrationRowResult {
    pub row_number: usize,
    pub merchant_id: Option<id_type::MerchantId>,
    pub payment_method_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub old_fingerprint_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub new_fingerprint_id: Option<String>,
    pub migration_status: MigrationStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error_message: Option<String>,
}

#[derive(Debug, Clone, Default, serde::Serialize)]
pub enum MigrationStatus {
    Success,
    #[default]
    Failed,
}

#[derive(Debug, Default, serde::Serialize)]
pub enum UpdateStatus {
    Success,
    #[default]
    Failed,
}

impl PaymentMethodRecord {
    fn create_address(&self) -> Option<payments::AddressDetails> {
        if self.billing_address_first_name.is_some()
            && self.billing_address_line1.is_some()
            && self.billing_address_zip.is_some()
            && self.billing_address_city.is_some()
            && self.billing_address_country.is_some()
        {
            Some(payments::AddressDetails {
                city: self.billing_address_city.clone(),
                country: self.billing_address_country,
                line1: self.billing_address_line1.clone(),
                line2: self.billing_address_line2.clone(),
                state: self.billing_address_state.clone(),
                line3: self.billing_address_line3.clone(),
                zip: self.billing_address_zip.clone(),
                first_name: self.billing_address_first_name.clone(),
                last_name: self.billing_address_last_name.clone(),
                origin_zip: None,
            })
        } else {
            None
        }
    }

    fn create_phone(&self) -> Option<payments::PhoneDetails> {
        if self.phone.is_some() || self.phone_country_code.is_some() {
            Some(payments::PhoneDetails {
                number: self.phone.clone(),
                country_code: self.phone_country_code.clone(),
            })
        } else {
            None
        }
    }

    fn create_billing(&self) -> Option<payments::Address> {
        let address = self.create_address();
        let phone = self.create_phone();

        if address.is_some() || phone.is_some() || self.email.is_some() {
            Some(payments::Address {
                address,
                phone,
                email: self.email.clone(),
            })
        } else {
            None
        }
    }
}

#[cfg(feature = "v1")]
type PaymentMethodMigrationResponseType = (
    Result<PaymentMethodMigrateResponse, String>,
    PaymentMethodRecord,
);

#[cfg(feature = "v1")]
type PaymentMethodUpdateResponseType = (
    Result<PaymentMethodRecordUpdateResponse, String>,
    UpdatePaymentMethodRecord,
);

#[cfg(feature = "v1")]
impl From<PaymentMethodMigrationResponseType> for PaymentMethodMigrationResponse {
    fn from((response, record): PaymentMethodMigrationResponseType) -> Self {
        match response {
            Ok(res) => Self {
                payment_method_id: Some(res.payment_method_response.payment_method_id),
                payment_method: res.payment_method_response.payment_method,
                payment_method_type: res.payment_method_response.payment_method_type,
                customer_id: res.payment_method_response.customer_id,
                migration_status: MigrationStatus::Success,
                migration_error: None,
                card_number_masked: record.card_number_masked.clone(),
                line_number: record.line_number,
                card_migrated: res.card_migrated,
                payment_method_migrated: res.payment_method_migrated,
                network_token_migrated: res.network_token_migrated,
                connector_mandate_details_migrated: res.connector_mandate_details_migrated,
                network_transaction_id_migrated: res.network_transaction_id_migrated,
            },
            Err(e) => Self {
                customer_id: Some(record.customer_id.clone()),
                migration_status: MigrationStatus::Failed,
                migration_error: Some(e),
                card_number_masked: record.card_number_masked.clone(),
                line_number: record.line_number,
                ..Self::default()
            },
        }
    }
}

#[cfg(feature = "v1")]
impl From<PaymentMethodUpdateResponseType> for PaymentMethodUpdateResponse {
    fn from((response, record): PaymentMethodUpdateResponseType) -> Self {
        match response {
            Ok(res) => Self {
                payment_method_id: res.payment_method_id,
                status: Some(res.status),
                network_transaction_id: res.network_transaction_id,
                connector_mandate_details: res.connector_mandate_details,
                updated_payment_method_data: res.updated_payment_method_data,
                update_status: UpdateStatus::Success,
                update_error: None,
                line_number: record.line_number,
            },
            Err(e) => Self {
                payment_method_id: record.payment_method_id,
                status: record.status,
                network_transaction_id: record.network_transaction_id,
                connector_mandate_details: None,
                updated_payment_method_data: None,
                update_status: UpdateStatus::Failed,
                update_error: Some(e),
                line_number: record.line_number,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl
    TryFrom<(
        &PaymentMethodRecord,
        id_type::MerchantId,
        Option<&Vec<id_type::MerchantConnectorAccountId>>,
    )> for PaymentMethodMigrate
{
    type Error = error_stack::Report<errors::ValidationError>;

    fn try_from(
        item: (
            &PaymentMethodRecord,
            id_type::MerchantId,
            Option<&Vec<id_type::MerchantConnectorAccountId>>,
        ),
    ) -> Result<Self, Self::Error> {
        let (record, merchant_id, mca_ids) = item;
        let billing = record.create_billing();
        let connector_mandate_details = if let Some(payment_instrument_id) =
            &record.payment_instrument_id
        {
            let ids = mca_ids.get_required_value("mca_ids")?;
            let mandate_map: HashMap<_, _> = ids
                .iter()
                .map(|mca_id| {
                    (
                        mca_id.clone(),
                        PaymentsMandateReferenceRecord {
                            connector_mandate_id: payment_instrument_id.peek().to_string(),
                            payment_method_type: record.payment_method_type,
                            original_payment_authorized_amount: record.original_transaction_amount,
                            original_payment_authorized_currency: record
                                .original_transaction_currency,
                            connector_customer_id: None,
                            connector_mandate_request_reference_id: record
                                .connector_mandate_request_reference_id
                                .clone(),
                        },
                    )
                })
                .collect();
            Some(PaymentsMandateReference(mandate_map))
        } else {
            None
        };

        let payment_method_data = record.get_payment_method_data();
        let is_non_card = payment_method_data.is_some();

        let card = if is_non_card {
            None
        } else {
            Some(MigrateCardDetail {
                card_number: record
                    .raw_card_number
                    .clone()
                    .unwrap_or_else(|| record.card_number_masked.clone().unwrap_or_default()),
                card_exp_month: record.card_expiry_month.clone().unwrap_or_default(),
                card_exp_year: record.card_expiry_year.clone().unwrap_or_default(),
                card_holder_name: record.card_holder_name.clone().or(record.name.clone()),
                card_network: None,
                card_type: None,
                card_issuer: None,
                card_issuing_country: None,
                card_issuing_country_code: None,
                nick_name: record.nick_name.clone(),
            })
        };

        let network_token = if is_non_card {
            None
        } else {
            Some(MigrateNetworkTokenDetail {
                network_token_data: MigrateNetworkTokenData {
                    network_token_number: record.network_token_number.clone().unwrap_or_default(),
                    network_token_exp_month: record
                        .network_token_expiry_month
                        .clone()
                        .unwrap_or_default(),
                    network_token_exp_year: record
                        .network_token_expiry_year
                        .clone()
                        .unwrap_or_default(),
                    card_holder_name: record.card_holder_name.clone().or(record.name.clone()),
                    nick_name: record.nick_name.clone(),
                    card_issuing_country: None,
                    card_issuing_country_code: None,
                    card_network: None,
                    card_issuer: None,
                    card_type: None,
                },
                network_token_requestor_ref_id: record
                    .network_token_requestor_ref_id
                    .clone()
                    .unwrap_or_default(),
            })
        };

        Ok(Self {
            merchant_id,
            customer_id: Some(record.customer_id.clone()),
            card,
            network_token,
            payment_method: record.payment_method,
            payment_method_type: record.payment_method_type,
            payment_method_issuer: None,
            billing,
            connector_mandate_details: connector_mandate_details.map(
                |payments_mandate_reference| {
                    CommonMandateReference::from(payments_mandate_reference)
                },
            ),
            metadata: None,
            payment_method_issuer_code: None,
            card_network: None,
            #[cfg(feature = "payouts")]
            bank_transfer: None,
            #[cfg(feature = "payouts")]
            bank_transfer_data: None,
            #[cfg(feature = "payouts")]
            wallet: None,
            payment_method_data,
            network_transaction_id: record.original_transaction_id.clone(),
        })
    }
}

#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct CardNetworkTokenizeRequest {
    /// Merchant ID associated with the tokenization request
    #[schema(example = "merchant_1671528864", value_type = String)]
    pub merchant_id: id_type::MerchantId,

    /// Details of the card or payment method to be tokenized
    #[serde(flatten)]
    pub data: TokenizeDataRequest,

    /// Customer details
    #[schema(value_type = CustomerDetails)]
    pub customer: payments::CustomerDetails,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

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

    /// The name of the bank/ provider issuing the payment method to the end user
    pub payment_method_issuer: Option<String>,
}

impl common_utils::events::ApiEventMetric for CardNetworkTokenizeRequest {}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum TokenizeDataRequest {
    Card(TokenizeCardRequest),
    ExistingPaymentMethod(TokenizePaymentMethodRequest),
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct TokenizeCardRequest {
    /// Card Number
    #[schema(value_type = String, example = "4111111145551142")]
    pub raw_card_number: CardNumber,

    /// Card Expiry Month
    #[schema(value_type = String, example = "10")]
    pub card_expiry_month: hyperswitch_masking::Secret<String>,

    /// Card Expiry Year
    #[schema(value_type = String, example = "25")]
    pub card_expiry_year: hyperswitch_masking::Secret<String>,

    /// The CVC number for the card
    #[schema(value_type = Option<String>,  example = "242")]
    pub card_cvc: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder Name
    #[schema(value_type = Option<String>, example = "John Doe")]
    pub card_holder_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Holder's Nick Name
    #[schema(value_type = Option<String>, example = "John Doe")]
    pub nick_name: Option<hyperswitch_masking::Secret<String>>,

    /// Card Issuing Country
    pub card_issuing_country: Option<String>,

    /// Card Issuing Country
    pub card_issuing_country_code: Option<String>,

    /// Card's Network
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<api_enums::CardNetwork>,

    /// Issuer Bank for Card
    pub card_issuer: Option<String>,

    /// Card Type
    pub card_type: Option<CardType>,
}

#[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct TokenizePaymentMethodRequest {
    /// Payment method's ID
    #[serde(skip_deserializing)]
    pub payment_method_id: String,

    /// The CVC number for the card
    #[schema(value_type = Option<String>,  example = "242")]
    pub card_cvc: Option<hyperswitch_masking::Secret<String>>,
}

#[derive(Debug, Default, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct CardNetworkTokenizeResponse {
    /// Response for payment method entry in DB
    pub payment_method_response: Option<PaymentMethodResponse>,

    /// Customer details
    #[schema(value_type = CustomerDetails)]
    pub customer: Option<payments::CustomerDetails>,

    /// Card network tokenization status
    pub card_tokenized: bool,

    /// Error code
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error_code: Option<String>,

    /// Error message
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error_message: Option<String>,

    /// Details that were sent for tokenization
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tokenization_data: Option<TokenizeDataRequest>,
}

impl common_utils::events::ApiEventMetric for CardNetworkTokenizeResponse {}

impl From<&Card> for MigrateCardDetail {
    fn from(card: &Card) -> Self {
        Self {
            card_number: hyperswitch_masking::Secret::new(card.card_number.get_card_no()),
            card_exp_month: card.card_exp_month.clone(),
            card_exp_year: card.card_exp_year.clone(),
            card_holder_name: card.name_on_card.clone(),
            nick_name: card
                .nick_name
                .as_ref()
                .map(|name| hyperswitch_masking::Secret::new(name.clone())),
            card_issuing_country: None,
            card_issuing_country_code: None,
            card_network: None,
            card_issuer: None,
            card_type: None,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentMethodSessionRequest {
    /// The customer id for which the payment methods session is to be created
    #[schema(value_type = Option<String>, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::GlobalCustomerId>,

    /// The billing address details of the customer. This will also be used for any new payment methods added during the session
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    /// The return url to which the customer should be redirected to after adding the payment method
    #[schema(value_type = Option<String>)]
    pub return_url: Option<common_utils::types::Url>,

    /// The network tokenization configuration if applicable
    #[schema(value_type = Option<NetworkTokenization>)]
    pub network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,

    /// The time (seconds ) when the session will expire
    /// If not provided, the session will expire in 15 minutes
    #[schema(example = 900, default = 900)]
    pub expires_in: Option<u32>,

    /// Contains data to be passed on to tokenization service ( if present ) to create token_id for given JSON data
    #[schema(value_type = Option<serde_json::Value>)]
    pub tokenization_data: Option<pii::SecretSerdeValue>,

    /// The storage type for the payment method
    #[schema(value_type = StorageType)]
    pub storage_type: common_enums::StorageType,

    /// Whether the card with new status should be listed in the session
    pub keep_alive: Option<bool>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentMethodsSessionUpdateRequest {
    /// The billing address details of the customer. This will also be used for any new payment methods added during the session
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    /// The network tokenization configuration if applicable
    #[schema(value_type = Option<NetworkTokenization>)]
    pub network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,

    /// Contains data to be passed on to tokenization service ( if present ) to create token_id for given JSON data
    #[schema(value_type = Option<serde_json::Value>)]
    pub tokenization_data: Option<pii::SecretSerdeValue>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentMethodSessionUpdateSavedPaymentMethod {
    /// The payment method token associated with the payment method session.
    /// If not provided, a new token will be generated.
    #[schema(value_type = Option<String>, example = "token_9wcXDRVkfEtLEsSnYKgQ")]
    pub payment_method_token: Option<String>,
    /// The update request for the payment method update
    #[serde(flatten)]
    pub payment_method_update_request: PaymentMethodUpdate,
}

#[cfg(feature = "v2")]
impl PaymentMethodSessionUpdateSavedPaymentMethod {
    pub fn fetch_card_cvc_update(&self) -> Option<hyperswitch_masking::Secret<String>> {
        match &self.payment_method_update_request.payment_method_data {
            Some(PaymentMethodUpdateData::Card(card_update)) => card_update.card_cvc.clone(),
            _ => None,
        }
    }

    pub fn is_payment_method_metadata_update(&self) -> bool {
        match &self.payment_method_update_request.payment_method_data {
            Some(PaymentMethodUpdateData::Card(card_update)) => {
                card_update.card_holder_name.is_some() || card_update.nick_name.is_some()
            }
            Some(PaymentMethodUpdateData::BankDebit(bank_debit_update)) => {
                match bank_debit_update {
                    BankDebitDetailUpdate::Ach {
                        bank_account_holder_name,
                    } => bank_account_holder_name.is_some(),
                }
            }
            None => false,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentMethodSessionDeleteSavedPaymentMethod {
    /// The payment method token associated with the payment method to be deleted
    #[schema(value_type = String, example = "token_9wcXDRVkfEtLEsSnYKgQ")]
    pub payment_method_token: String,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentMethodSessionConfirmRequest {
    /// The payment method type
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method_type: common_enums::PaymentMethod,

    /// The payment method subtype
    #[schema(value_type = Option<PaymentMethodType>, example = "credit")]
    pub payment_method_subtype: Option<common_enums::PaymentMethodType>,

    /// The payment instrument data to be used for the payment
    #[schema(value_type = PaymentMethodDataRequest)]
    pub payment_method_data: payments::PaymentMethodDataRequest,

    /// The return url to which the customer should be redirected to after adding the payment method
    #[schema(value_type = Option<String>)]
    pub return_url: Option<common_utils::types::Url>,

    /// Customer acceptance for mandate creation. Required for both single-use and multi-use tokenization flows.
    #[schema(value_type = Option<CustomerAcceptance>)]
    pub customer_acceptance: Option<common_types::payments::CustomerAcceptance>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodSessionResponse {
    #[schema(value_type = String, example = "0a_pms_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodSessionId,

    /// The customer id for which the payment methods session is to be created
    #[schema(value_type = Option<String>, example = "0a_cus_01926c58bc6e77c09e809964e72af8c8")]
    pub customer_id: Option<id_type::GlobalCustomerId>,

    /// The billing address details of the customer. This will also be used for any new payment methods added during the session
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    /// The network tokenization configuration if applicable
    #[schema(value_type = Option<NetworkTokenization>)]
    pub network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,

    /// Contains data to be passed on to tokenization service ( if present ) to create token_id for given JSON data
    #[schema(value_type = Option<serde_json::Value>)]
    pub tokenization_data: Option<pii::SecretSerdeValue>,

    /// The iso timestamp when the session will expire
    /// Trying to retrieve the session or any operations on the session after this time will result in an error
    #[schema(value_type = PrimitiveDateTime, example = "2023-01-18T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expires_at: time::PrimitiveDateTime,

    /// Client Secret
    #[schema(value_type = String, example = "cs_9wcXDRVkfEtLEsSnYKgQ")]
    pub client_secret: hyperswitch_masking::Secret<String>,

    /// The return url to which the user should be redirected to
    #[schema(value_type = Option<String>, example = "https://merchant-website.com/return")]
    pub return_url: Option<common_utils::types::Url>,

    /// The next action details for the payment method session
    #[schema(value_type = Option<NextActionData>)]
    pub next_action: Option<payments::NextActionData>,

    /// The customer authentication details for the payment method
    /// This refers to either the payment / external authentication details
    pub authentication_details: Option<AuthenticationDetails>,

    /// The payment method that was created using this payment method session
    #[schema(value_type = Option<Vec<AssociatedPaymentMethods>>)]
    pub associated_payment_methods:
        Option<Vec<common_types::payment_methods::AssociatedPaymentMethods>>,

    /// The token-id created if there is tokenization_data present
    #[schema(value_type = Option<String>, example = "0a_tok_01926c58bc6e77c09e809964e72af8c8")]
    pub associated_token_id: Option<id_type::GlobalTokenId>,

    /// The storage type for the payment method
    #[schema(value_type = StorageType)]
    pub storage_type: common_enums::StorageType,

    /// Card CVC token storage details
    #[schema(value_type = Option<CardCVCTokenStorageDetails>)]
    pub card_cvc_token_storage: Option<CardCVCTokenStorageDetails>,

    /// payment method data to be sent in session response
    #[schema(value_type = Option<PaymentMethodResponseData>)]
    pub payment_method_data: Option<PaymentMethodResponseData>,

    /// SDK authorization token for client SDK usage
    /// Contains encoded authentication details for subsequent API calls
    #[schema(value_type = Option<String>, example = "cHJvZmlsZV9pZD0uLi4=")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sdk_authorization: Option<String>,

    /// Whether the card with new status should be listed in the session
    pub keep_alive: bool,

    /// Network token details if available
    pub network_tokenization_data: Option<NetworkTokenResponse>,

    /// External vault session details if external vault is enabled for the profile
    #[schema(value_type = Option<VaultSessionDetails>)]
    pub external_vault_details: Option<payments::VaultSessionDetails>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema, Clone)]
pub struct AuthenticationDetails {
    /// The status of authentication for the payment method
    #[schema(value_type = IntentStatus)]
    pub status: common_enums::IntentStatus,

    /// Error details of the authentication
    #[schema(value_type = Option<ErrorDetails>)]
    pub error: Option<payments::ErrorDetails>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct NetworkTokenStatusCheckSuccessResponse {
    /// The status of the network token
    #[schema(value_type = TokenStatus)]
    pub status: api_enums::TokenStatus,

    /// The expiry month of the network token if active
    #[schema(value_type = Option<String>)]
    pub token_expiry_month: Option<hyperswitch_masking::Secret<String>>,

    /// The expiry year of the network token if active
    #[schema(value_type = Option<String>)]
    pub token_expiry_year: Option<hyperswitch_masking::Secret<String>>,

    /// The last four digits of the card if active
    pub card_last_four: Option<String>,

    /// The last four digits of the network token if active
    pub token_last_four: Option<String>,

    /// The expiry month of the card if active
    #[schema(value_type = Option<String>)]
    pub card_expiry_month: Option<hyperswitch_masking::Secret<String>>,

    /// The expiry year of the card if active
    #[schema(value_type = Option<String>)]
    pub card_expiry_year: Option<hyperswitch_masking::Secret<String>>,

    /// The payment method ID that was checked
    #[schema(value_type = String, example = "0a_pm_019959146f92737389eb6927ce1eb7dc")]
    pub payment_method_id: id_type::GlobalPaymentMethodId,

    /// The customer ID associated with the payment method
    #[schema(value_type = String, example = "0a_cus_0195dc62bb8e7312a44484536da76aef")]
    pub customer_id: id_type::GlobalCustomerId,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for NetworkTokenStatusCheckResponse {}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct NetworkTokenStatusCheckFailureResponse {
    /// Error message describing what went wrong
    pub error_message: String,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum NetworkTokenStatusCheckResponse {
    /// Successful network token status check response
    SuccessResponse(NetworkTokenStatusCheckSuccessResponse),
    /// Error response for network token status check
    FailureResponse(NetworkTokenStatusCheckFailureResponse),
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct NetworkTokenEligibilityRequest {
    /// The card bin to retrieve information for
    pub card_bin: String,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct GetNetworkTokenEiligibilityResponse {
    /// Indicates if the card bin is eligible for network tokenization for the particular merchant
    pub eligible_for_network_tokenization: bool,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for NetworkTokenEligibilityRequest {}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for GetNetworkTokenEiligibilityResponse {}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodGetTokenDetailsResponse {
    /// The payment method ID associated with the token
    #[schema(value_type = String, example = "0a_pm_019959146f92737389eb6927ce1eb7dc")]
    pub id: id_type::GlobalPaymentMethodId,

    /// The token associated with the payment method
    #[schema(value_type = String, example = "token_CSum555d9YxDOpGwYq6q")]
    pub payment_method_token: String,
}
