// File: crates/router/src/compatibility/stripe/payment_intents/types.rs

use std::str::FromStr;

use api_models::payments;
use common_types::payments as common_payments_types;
use common_utils::{
    crypto::Encryptable,
    date_time,
    ext_traits::StringExt,
    id_type,
    pii::{IpAddress, SecretSerdeValue, UpiVpaMaskingStrategy},
    types::MinorUnit,
};
use error_stack::ResultExt;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{
    compatibility::stripe::refunds::types as stripe_refunds,
    connector::utils::AddressData,
    consts,
    core::errors,
    pii::{Email, PeekInterface},
    types::{
        api::{admin, enums as api_enums},
        transformers::{ForeignFrom, ForeignTryFrom},
    },
};

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone, Debug)]
pub struct StripeBillingDetails {
    pub address: Option<AddressDetails>,
    pub email: Option<Email>,
    pub name: Option<String>,
    pub phone: Option<masking::Secret<String>>,
}

impl From<StripeBillingDetails> for payments::Address {
    fn from(details: StripeBillingDetails) -> Self {
        Self {
            phone: Some(payments::PhoneDetails {
                number: details.phone,
                country_code: details.address.as_ref().and_then(|address| {
                    address.country.as_ref().map(|country| country.to_string())
                }),
            }),
            email: details.email,
            address: details.address.map(|address| payments::AddressDetails {
                city: address.city,
                country: address.country,
                line1: address.line1,
                line2: address.line2,
                zip: address.postal_code,
                state: address.state,
                first_name: None,
                line3: None,
                last_name: None,
                origin_zip: None,
            }),
        }
    }
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone, Debug)]
pub struct StripeCard {
    pub number: cards::CardNumber,
    pub exp_month: masking::Secret<String>,
    pub exp_year: masking::Secret<String>,
    pub cvc: masking::Secret<String>,
    pub holder_name: Option<masking::Secret<String>>,
}

// ApplePay wallet param is not available in stripe Docs
#[derive(Serialize, PartialEq, Eq, Deserialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripeWallet {
    ApplePay(payments::ApplePayWalletData),
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone, Debug)]
pub struct StripeUpi {
    pub vpa_id: masking::Secret<String, UpiVpaMaskingStrategy>,
}

#[derive(Debug, Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodType {
    #[default]
    Card,
    Wallet,
    Upi,
    BankRedirect,
    RealTimePayment,
}

impl From<StripePaymentMethodType> for api_enums::PaymentMethod {
    fn from(item: StripePaymentMethodType) -> Self {
        match item {
            StripePaymentMethodType::Card => Self::Card,
            StripePaymentMethodType::Wallet => Self::Wallet,
            StripePaymentMethodType::Upi => Self::Upi,
            StripePaymentMethodType::BankRedirect => Self::BankRedirect,
            StripePaymentMethodType::RealTimePayment => Self::RealTimePayment,
        }
    }
}

#[derive(Default, PartialEq, Eq, Deserialize, Clone, Debug)]
pub struct StripePaymentMethodData {
    #[serde(rename = "type")]
    pub stype: StripePaymentMethodType,
    pub billing_details: Option<StripeBillingDetails>,
    #[serde(flatten)]
    pub payment_method_details: Option<StripePaymentMethodDetails>, // enum
    pub metadata: Option<SecretSerdeValue>,
}

#[derive(PartialEq, Eq, Deserialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodDetails {
    Card(StripeCard),
    Wallet(StripeWallet),
    Upi(StripeUpi),
}

impl From<StripeCard> for payments::Card {
    fn from(card: StripeCard) -> Self {
        Self {
            card_number: card.number,
            card_exp_month: card.exp_month,
            card_exp_year: card.exp_year,
            card_holder_name: card.holder_name,
            card_cvc: card.cvc,
            card_issuer: None,
            card_network: None,
            bank_code: None,
            card_issuing_country: None,
            card_type: None,
            nick_name: None,
        }
    }
}

impl From<StripeWallet> for payments::WalletData {
    fn from(wallet: StripeWallet) -> Self {
        match wallet {
            StripeWallet::ApplePay(data) => Self::ApplePay(data),
        }
    }
}

impl From<StripeUpi> for payments::UpiData {
    fn from(upi_data: StripeUpi) -> Self {
        Self::UpiCollect(payments::UpiCollectData {
            vpa_id: Some(upi_data.vpa_id),
        })
    }
}

impl From<StripePaymentMethodDetails> for payments::PaymentMethodData {
    fn from(item: StripePaymentMethodDetails) -> Self {
        match item {
            StripePaymentMethodDetails::Card(card) => Self::Card(payments::Card::from(card)),
            StripePaymentMethodDetails::Wallet(wallet) => {
                Self::Wallet(payments::WalletData::from(wallet))
            }
            StripePaymentMethodDetails::Upi(upi) => Self::Upi(payments::UpiData::from(upi)),
        }
    }
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone, Debug)]
pub struct Shipping {
    pub address: AddressDetails,
    pub name: Option<masking::Secret<String>>,
    pub carrier: Option<String>,
    pub phone: Option<masking::Secret<String>>,
    pub tracking_number: Option<masking::Secret<String>>,
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone, Debug)]
pub struct AddressDetails {
    pub city: Option<String>,
    pub country: Option<api_enums::CountryAlpha2>,
    pub line1: Option<masking::Secret<String>>,
    pub line2: Option<masking::Secret<String>>,
    pub postal_code: Option<masking::Secret<String>>,
    pub state: Option<masking::Secret<String>>,
}

impl From<Shipping> for payments::Address {
    fn from(details: Shipping) -> Self {
        Self {
            phone: Some(payments::PhoneDetails {
                number: details.phone,
                country_code: details.address.country.map(|country| country.to_string()),
            }),
            email: None,
            address: Some(payments::AddressDetails {
                city: details.address.city,
                country: details.address.country,
                line1: details.address.line1,
                line2: details.address.line2,
                zip: details.address.postal_code,
                state: details.address.state,
                first_name: details.name,
                line3: None,
                last_name: None,
                origin_zip: None,
            }),
        }
    }
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone, Debug)]
pub struct MandateData {
    pub customer_acceptance: CustomerAcceptance,
    pub mandate_type: Option<StripeMandateType>,
    pub amount: Option<i64>,
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub start_date: Option<PrimitiveDateTime>,
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub end_date: Option<PrimitiveDateTime>,
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone, Debug)]
pub struct CustomerAcceptance {
    #[serde(rename = "type")]
    pub acceptance_type: Option<AcceptanceType>,
    pub accepted_at: Option<PrimitiveDateTime>,
    pub online: Option<OnlineMandate>,
}

#[derive(Default, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
pub enum AcceptanceType {
    Online,
    #[default]
    Offline,
}

#[derive(Default, Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct OnlineMandate {
    pub ip_address: masking::Secret<String, IpAddress>,
    pub user_agent: String,
}

#[derive(Deserialize, Clone, Debug)]
pub struct StripePaymentIntentRequest {
    pub id: Option<id_type::PaymentId>,
    pub amount: Option<i64>, // amount in cents, hence passed as integer
    pub connector: Option<Vec<api_enums::RoutableConnectors>>,
    pub currency: Option<String>,
    #[serde(rename = "amount_to_capture")]
    pub amount_capturable: Option<i64>,
    pub confirm: Option<bool>,
    pub capture_method: Option<api_enums::CaptureMethod>,
    pub customer: Option<id_type::CustomerId>,
    pub description: Option<String>,
    pub payment_method_data: Option<StripePaymentMethodData>,
    pub receipt_email: Option<Email>,
    pub return_url: Option<url::Url>,
    pub setup_future_usage: Option<api_enums::FutureUsage>,
    pub shipping: Option<Shipping>,
    pub statement_descriptor: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub metadata: Option<serde_json::Value>,
    pub client_secret: Option<masking::Secret<String>>,
    pub payment_method_options: Option<StripePaymentMethodOptions>,
    pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
    pub mandate: Option<String>,
    pub off_session: Option<bool>,
    pub payment_method_types: Option<api_enums::PaymentMethodType>,
    pub receipt_ipaddress: Option<String>,
    pub user_agent: Option<String>,
    pub mandate_data: Option<MandateData>,
    pub automatic_payment_methods: Option<SecretSerdeValue>, // not used
    pub payment_method: Option<String>,                      // not used
    pub confirmation_method: Option<String>,                 // not used
    pub error_on_requires_action: Option<String>,            // not used
    pub radar_options: Option<SecretSerdeValue>,             // not used
    pub connector_metadata: Option<payments::ConnectorMetadata>,
}

impl TryFrom<StripePaymentIntentRequest> for payments::PaymentsRequest {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn try_from(item: StripePaymentIntentRequest) -> errors::RouterResult<Self> {
        let routable_connector: Option<api_enums::RoutableConnectors> =
            item.connector.and_then(|v| v.into_iter().next());

        let routing = routable_connector
            .map(|connector| {
                api_models::routing::StaticRoutingAlgorithm::Single(Box::new(
                    api_models::routing::RoutableConnectorChoice {
                        choice_kind: api_models::routing::RoutableChoiceKind::FullStruct,
                        connector,
                        merchant_connector_id: None,
                    },
                ))
            })
            .map(|r| {
                serde_json::to_value(r)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("converting to routing failed")
            })
            .transpose()?;

        let ip_address = item
            .receipt_ipaddress
            .map(|ip| std::net::IpAddr::from_str(ip.as_str()))
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                field_name: "receipt_ipaddress".to_string(),
                expected_format: "127.0.0.1".to_string(),
            })?;

        let amount = item.amount.map(|amount| MinorUnit::new(amount).into());

        let payment_method_data = item.payment_method_data.as_ref().map(|pmd| {
            let billing = pmd.billing_details.clone().map(payments::Address::from);
            let payment_method_data = match pmd.payment_method_details.as_ref() {
                Some(spmd) => Some(payments::PaymentMethodData::from(spmd.to_owned())),
                None => get_pmd_based_on_payment_method_type(
                    item.payment_method_types,
                    billing.clone().map(From::from),
                ),
            };

            payments::PaymentMethodDataRequest {
                payment_method_data,
                billing,
            }
        });

        let request = Ok(Self {
            payment_id: item.id.map(payments::PaymentIdType::PaymentIntentId),
            amount,
            currency: item
                .currency
                .as_ref()
                .map(|c| c.to_uppercase().parse_enum("currency"))
                .transpose()
                .change_context(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "currency",
                })?,
            capture_method: item.capture_method,
            amount_to_capture: item.amount_capturable.map(MinorUnit::new),
            confirm: item.confirm,
            customer_id: item.customer,
            email: item.receipt_email,
            phone: item.shipping.as_ref().and_then(|s| s.phone.clone()),
            description: item.description,
            return_url: item.return_url,
            payment_method_data,
            payment_method: item
                .payment_method_data
                .as_ref()
                .map(|pmd| api_enums::PaymentMethod::from(pmd.stype.to_owned())),
            shipping: item
                .shipping
                .as_ref()
                .map(|s| payments::Address::from(s.to_owned())),
            billing: item
                .payment_method_data
                .and_then(|pmd| pmd.billing_details.map(payments::Address::from)),
            statement_descriptor_name: item.statement_descriptor,
            statement_descriptor_suffix: item.statement_descriptor_suffix,
            metadata: item.metadata,
            client_secret: item.client_secret.map(|s| s.peek().clone()),
            authentication_type: match item.payment_method_options {
                Some(pmo) => {
                    let StripePaymentMethodOptions::Card {
                        request_three_d_secure,
                    }: StripePaymentMethodOptions = pmo;
                    Some(api_enums::AuthenticationType::foreign_from(
                        request_three_d_secure,
                    ))
                }
                None => None,
            },
            mandate_data: ForeignTryFrom::foreign_try_from((
                item.mandate_data,
                item.currency.to_owned(),
            ))?,
            merchant_connector_details: item.merchant_connector_details,
            setup_future_usage: item.setup_future_usage,
            mandate_id: item.mandate,
            off_session: item.off_session,
            payment_method_type: item.payment_method_types,
            routing,
            browser_info: Some(
                serde_json::to_value(crate::types::BrowserInformation {
                    ip_address,
                    user_agent: item.user_agent,
                    ..Default::default()
                })
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("convert to browser info failed")?,
            ),
            connector_metadata: item.connector_metadata,
            ..Self::default()
        });
        request
    }
}

#[derive(Clone, Default, Eq, PartialEq, Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentStatus {
    Succeeded,
    Canceled,
    #[default]
    Processing,
    RequiresAction,
    RequiresPaymentMethod,
    RequiresConfirmation,
    RequiresCapture,
}

impl From<api_enums::IntentStatus> for StripePaymentStatus {
    fn from(item: api_enums::IntentStatus) -> Self {
        match item {
            api_enums::IntentStatus::Succeeded | api_enums::IntentStatus::PartiallyCaptured => {
                Self::Succeeded
            }
            api_enums::IntentStatus::Failed | api_enums::IntentStatus::Expired => Self::Canceled,
            api_enums::IntentStatus::Processing => Self::Processing,
            api_enums::IntentStatus::RequiresCustomerAction
            | api_enums::IntentStatus::RequiresMerchantAction
            | api_enums::IntentStatus::Conflicted => Self::RequiresAction,
            api_enums::IntentStatus::RequiresPaymentMethod => Self::RequiresPaymentMethod,
            api_enums::IntentStatus::RequiresConfirmation => Self::RequiresConfirmation,
            api_enums::IntentStatus::RequiresCapture
            | api_enums::IntentStatus::PartiallyCapturedAndCapturable
            | api_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture => {
                Self::RequiresCapture
            }
            api_enums::IntentStatus::Cancelled | api_enums::IntentStatus::CancelledPostCapture => {
                Self::Canceled
            }
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Copy, Clone, strum::Display)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CancellationReason {
    Duplicate,
    Fraudulent,
    RequestedByCustomer,
    Abandoned,
}

#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
pub struct StripePaymentCancelRequest {
    cancellation_reason: Option<CancellationReason>,
}

impl From<StripePaymentCancelRequest> for payments::PaymentsCancelRequest {
    fn from(item: StripePaymentCancelRequest) -> Self {
        Self {
            cancellation_reason: item.cancellation_reason.map(|c| c.to_string()),
            ..Self::default()
        }
    }
}

#[derive(Default, Eq, PartialEq, Serialize, Debug)]
pub struct StripePaymentIntentResponse {
    pub id: id_type::PaymentId,
    pub object: &'static str,
    pub amount: i64,
    pub amount_received: Option<i64>,
    pub amount_capturable: i64,
    pub currency: String,
    pub status: StripePaymentStatus,
    pub client_secret: Option<masking::Secret<String>>,
    pub created: Option<i64>,
    pub customer: Option<id_type::CustomerId>,
    pub refunds: Option<Vec<stripe_refunds::StripeRefundResponse>>,
    pub mandate: Option<String>,
    pub metadata: Option<serde_json::Value>,
    pub charges: Charges,
    pub connector: Option<String>,
    pub description: Option<String>,
    pub mandate_data: Option<payments::MandateData>,
    pub setup_future_usage: Option<api_models::enums::FutureUsage>,
    pub off_session: Option<bool>,
    pub authentication_type: Option<api_models::enums::AuthenticationType>,
    pub next_action: Option<StripeNextAction>,
    pub cancellation_reason: Option<String>,
    pub payment_method: Option<api_models::enums::PaymentMethod>,
    pub payment_method_data: Option<payments::PaymentMethodDataResponse>,
    pub shipping: Option<payments::Address>,
    pub billing: Option<payments::Address>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub capture_on: Option<PrimitiveDateTime>,
    pub payment_token: Option<String>,
    pub email: Option<Email>,
    pub phone: Option<masking::Secret<String>>,
    pub statement_descriptor_suffix: Option<String>,
    pub statement_descriptor_name: Option<String>,
    pub capture_method: Option<api_models::enums::CaptureMethod>,
    pub name: Option<masking::Secret<String>>,
    pub last_payment_error: Option<LastPaymentError>,
    pub connector_transaction_id: Option<String>,
}

#[derive(Default, Eq, PartialEq, Serialize, Debug)]
pub struct LastPaymentError {
    charge: Option<String>,
    code: Option<String>,
    decline_code: Option<String>,
    message: String,
    param: Option<String>,
    payment_method: StripePaymentMethod,
    #[serde(rename = "type")]
    error_type: String,
}

impl From<payments::PaymentsResponse> for StripePaymentIntentResponse {
    fn from(resp: payments::PaymentsResponse) -> Self {
        Self {
            object: "payment_intent",
            id: resp.payment_id,
            status: StripePaymentStatus::from(resp.status),
            amount: resp.amount.get_amount_as_i64(),
            amount_capturable: resp.amount_capturable.get_amount_as_i64(),
            amount_received: resp.amount_received.map(|amt| amt.get_amount_as_i64()),
            connector: resp.connector,
            client_secret: resp.client_secret,
            created: resp.created.map(|t| t.assume_utc().unix_timestamp()),
            currency: resp.currency.to_lowercase(),
            customer: resp.customer_id,
            description: resp.description,
            refunds: resp
                .refunds
                .map(|a| a.into_iter().map(Into::into).collect()),
            mandate: resp.mandate_id,
            mandate_data: resp.mandate_data,
            setup_future_usage: resp.setup_future_usage,
            off_session: resp.off_session,
            capture_on: resp.capture_on,
            capture_method: resp.capture_method,
            payment_method: resp.payment_method,
            payment_method_data: resp
                .payment_method_data
                .and_then(|pmd| pmd.payment_method_data),
            payment_token: resp.payment_token,
            shipping: resp.shipping,
            billing: resp.billing,
            email: resp.email.map(|inner| inner.into()),
            name: resp.name.map(Encryptable::into_inner),
            phone: resp.phone.map(Encryptable::into_inner),
            authentication_type: resp.authentication_type,
            statement_descriptor_name: resp.statement_descriptor_name,
            statement_descriptor_suffix: resp.statement_descriptor_suffix,
            next_action: into_stripe_next_action(resp.next_action, resp.return_url),
            cancellation_reason: resp.cancellation_reason,
            metadata: resp.metadata,
            charges: Charges::new(),
            last_payment_error: resp.error_code.map(|code| LastPaymentError {
                charge: None,
                code: Some(code.to_owned()),
                decline_code: None,
                message: resp
                    .error_message
                    .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
                param: None,
                payment_method: StripePaymentMethod {
                    payment_method_id: "place_holder_id".to_string(),
                    object: "payment_method",
                    card: None,
                    created: u64::try_from(date_time::now().assume_utc().unix_timestamp())
                        .unwrap_or_default(),
                    method_type: "card".to_string(),
                    livemode: false,
                },
                error_type: code,
            }),
            connector_transaction_id: resp.connector_transaction_id,
        }
    }
}

#[derive(Default, Eq, PartialEq, Serialize, Debug)]
pub struct StripePaymentMethod {
    #[serde(rename = "id")]
    payment_method_id: String,
    object: &'static str,
    card: Option<StripeCard>,
    created: u64,
    #[serde(rename = "type")]
    method_type: String,
    livemode: bool,
}

#[derive(Default, Eq, PartialEq, Serialize, Debug)]
pub struct Charges {
    object: &'static str,
    data: Vec<String>,
    has_more: bool,
    total_count: i32,
    url: String,
}

impl Charges {
    pub fn new() -> Self {
        Self {
            object: "list",
            data: vec![],
            has_more: false,
            total_count: 0,
            url: "http://placeholder".to_string(),
        }
    }
}

#[derive(Clone, Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct StripePaymentListConstraints {
    pub customer: Option<id_type::CustomerId>,
    pub starting_after: Option<id_type::PaymentId>,
    pub ending_before: Option<id_type::PaymentId>,
    #[serde(default = "default_limit")]
    pub limit: u32,
    pub created: Option<i64>,
    #[serde(rename = "created[lt]")]
    pub created_lt: Option<i64>,
    #[serde(rename = "created[gt]")]
    pub created_gt: Option<i64>,
    #[serde(rename = "created[lte]")]
    pub created_lte: Option<i64>,
    #[serde(rename = "created[gte]")]
    pub created_gte: Option<i64>,
}

fn default_limit() -> u32 {
    10
}

impl TryFrom<StripePaymentListConstraints> for payments::PaymentListConstraints {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn try_from(item: StripePaymentListConstraints) -> Result<Self, Self::Error> {
        Ok(Self {
            customer_id: item.customer,
            starting_after: item.starting_after,
            ending_before: item.ending_before,
            limit: item.limit,
            created: from_timestamp_to_datetime(item.created)?,
            created_lt: from_timestamp_to_datetime(item.created_lt)?,
            created_gt: from_timestamp_to_datetime(item.created_gt)?,
            created_lte: from_timestamp_to_datetime(item.created_lte)?,
            created_gte: from_timestamp_to_datetime(item.created_gte)?,
        })
    }
}

#[inline]
fn from_timestamp_to_datetime(
    time: Option<i64>,
) -> Result<Option<PrimitiveDateTime>, errors::ApiErrorResponse> {
    if let Some(time) = time {
        let time = time::OffsetDateTime::from_unix_timestamp(time).map_err(|_| {
            errors::ApiErrorResponse::InvalidRequestData {
                message: "Error while converting timestamp".to_string(),
            }
        })?;

        Ok(Some(PrimitiveDateTime::new(time.date(), time.time())))
    } else {
        Ok(None)
    }
}

#[derive(Default, Eq, PartialEq, Serialize)]
pub struct StripePaymentIntentListResponse {
    pub object: String,
    pub url: String,
    pub has_more: bool,
    pub data: Vec<StripePaymentIntentResponse>,
}

impl From<payments::PaymentListResponse> for StripePaymentIntentListResponse {
    fn from(it: payments::PaymentListResponse) -> Self {
        Self {
            object: "list".to_string(),
            url: "/v1/payment_intents".to_string(),
            has_more: false,
            data: it.data.into_iter().map(Into::into).collect(),
        }
    }
}

#[derive(PartialEq, Eq, Deserialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodOptions {
    Card {
        request_three_d_secure: Option<Request3DS>,
    },
}

#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripeMandateType {
    SingleUse,
    MultiUse,
}

#[derive(PartialEq, Eq, Clone, Default, Deserialize, Serialize, Debug)]
pub struct MandateOption {
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub accepted_at: Option<PrimitiveDateTime>,
    pub user_agent: Option<String>,
    pub ip_address: Option<masking::Secret<String, IpAddress>>,
    pub mandate_type: Option<StripeMandateType>,
    pub amount: Option<i64>,
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub start_date: Option<PrimitiveDateTime>,
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub end_date: Option<PrimitiveDateTime>,
}

impl ForeignTryFrom<(Option<MandateData>, Option<String>)> for Option<payments::MandateData> {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(
        (mandate_data, currency): (Option<MandateData>, Option<String>),
    ) -> errors::RouterResult<Self> {
        let currency = currency
            .ok_or(
                errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "currency",
                }
                .into(),
            )
            .and_then(|c| {
                c.to_uppercase().parse_enum("currency").change_context(
                    errors::ApiErrorResponse::InvalidDataValue {
                        field_name: "currency",
                    },
                )
            })?;
        let mandate_data = mandate_data.map(|mandate| payments::MandateData {
            mandate_type: match mandate.mandate_type {
                Some(item) => match item {
                    StripeMandateType::SingleUse => Some(payments::MandateType::SingleUse(
                        payments::MandateAmountData {
                            amount: MinorUnit::new(mandate.amount.unwrap_or_default()),
                            currency,
                            start_date: mandate.start_date,
                            end_date: mandate.end_date,
                            metadata: None,
                        },
                    )),
                    StripeMandateType::MultiUse => Some(payments::MandateType::MultiUse(Some(
                        payments::MandateAmountData {
                            amount: MinorUnit::new(mandate.amount.unwrap_or_default()),
                            currency,
                            start_date: mandate.start_date,
                            end_date: mandate.end_date,
                            metadata: None,
                        },
                    ))),
                },
                None => Some(payments::MandateType::MultiUse(Some(
                    payments::MandateAmountData {
                        amount: MinorUnit::new(mandate.amount.unwrap_or_default()),
                        currency,
                        start_date: mandate.start_date,
                        end_date: mandate.end_date,
                        metadata: None,
                    },
                ))),
            },
            customer_acceptance: Some(common_payments_types::CustomerAcceptance {
                acceptance_type: common_payments_types::AcceptanceType::Online,
                accepted_at: mandate.customer_acceptance.accepted_at,
                online: mandate.customer_acceptance.online.map(|online| {
                    common_payments_types::OnlineMandate {
                        ip_address: Some(online.ip_address),
                        user_agent: online.user_agent,
                    }
                }),
            }),
            update_mandate_id: None,
        });
        Ok(mandate_data)
    }
}

#[derive(Default, Eq, PartialEq, Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Request3DS {
    #[default]
    Automatic,
    Any,
}

impl ForeignFrom<Option<Request3DS>> for api_models::enums::AuthenticationType {
    fn foreign_from(item: Option<Request3DS>) -> Self {
        match item.unwrap_or_default() {
            Request3DS::Automatic => Self::NoThreeDs,
            Request3DS::Any => Self::ThreeDs,
        }
    }
}

#[derive(Default, Eq, PartialEq, Serialize, Debug)]
pub struct RedirectUrl {
    pub return_url: Option<String>,
    pub url: Option<String>,
}

#[derive(Eq, PartialEq, serde::Serialize, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum StripeNextAction {
    RedirectToUrl {
        redirect_to_url: RedirectUrl,
    },
    DisplayBankTransferInformation {
        bank_transfer_steps_and_charges_details: payments::BankTransferNextStepsData,
    },
    ThirdPartySdkSessionToken {
        session_token: Option<payments::SessionToken>,
    },
    QrCodeInformation {
        image_data_url: Option<url::Url>,
        display_to_timestamp: Option<i64>,
        qr_code_url: Option<url::Url>,
        border_color: Option<String>,
        display_text: Option<String>,
    },
    FetchQrCodeInformation {
        qr_code_fetch_url: url::Url,
    },
    DisplayVoucherInformation {
        voucher_details: payments::VoucherNextStepData,
    },
    WaitScreenInformation {
        display_from_timestamp: i128,
        display_to_timestamp: Option<i128>,
        poll_config: Option<payments::PollConfig>,
    },
    InvokeSdkClient {
        next_action_data: payments::SdkNextActionData,
    },
    CollectOtp {
        consent_data_required: payments::MobilePaymentConsent,
    },
    InvokeHiddenIframe {
        iframe_data: payments::IframeData,
    },
    InvokeUpiIntentSdk {
        sdk_uri: url::Url,
    },
    InvokeUpiQrFlow {
        sdk_uri: url::Url,
    },
}

pub(crate) fn into_stripe_next_action(
    next_action: Option<payments::NextActionData>,
    return_url: Option<String>,
) -> Option<StripeNextAction> {
    next_action.map(|next_action_data| match next_action_data {
        payments::NextActionData::RedirectToUrl { redirect_to_url } => {
            StripeNextAction::RedirectToUrl {
                redirect_to_url: RedirectUrl {
                    return_url,
                    url: Some(redirect_to_url),
                },
            }
        }
        payments::NextActionData::RedirectInsidePopup { popup_url, .. } => {
            StripeNextAction::RedirectToUrl {
                redirect_to_url: RedirectUrl {
                    return_url,
                    url: Some(popup_url),
                },
            }
        }
        payments::NextActionData::DisplayBankTransferInformation {
            bank_transfer_steps_and_charges_details,
        } => StripeNextAction::DisplayBankTransferInformation {
            bank_transfer_steps_and_charges_details,
        },
        payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
            StripeNextAction::ThirdPartySdkSessionToken { session_token }
        }

        payments::NextActionData::QrCodeInformation {
            image_data_url,
            display_to_timestamp,
            qr_code_url,
            border_color,
            display_text,
        } => StripeNextAction::QrCodeInformation {
            image_data_url,
            display_to_timestamp,
            qr_code_url,
            border_color,
            display_text,
        },
        payments::NextActionData::FetchQrCodeInformation { qr_code_fetch_url } => {
            StripeNextAction::FetchQrCodeInformation { qr_code_fetch_url }
        }
        payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
            StripeNextAction::DisplayVoucherInformation { voucher_details }
        }
        payments::NextActionData::WaitScreenInformation {
            display_from_timestamp,
            display_to_timestamp,
            poll_config: _,
        } => StripeNextAction::WaitScreenInformation {
            display_from_timestamp,
            display_to_timestamp,
            poll_config: None,
        },
        payments::NextActionData::ThreeDsInvoke { .. } => StripeNextAction::RedirectToUrl {
            redirect_to_url: RedirectUrl {
                return_url: None,
                url: None,
            },
        },
        payments::NextActionData::InvokeSdkClient { next_action_data } => {
            StripeNextAction::InvokeSdkClient { next_action_data }
        }
        payments::NextActionData::CollectOtp {
            consent_data_required,
        } => StripeNextAction::CollectOtp {
            consent_data_required,
        },
        payments::NextActionData::InvokeHiddenIframe { iframe_data } => {
            StripeNextAction::InvokeHiddenIframe { iframe_data }
        }
        payments::NextActionData::InvokeUpiIntentSdk { sdk_uri, .. } => {
            StripeNextAction::InvokeUpiIntentSdk { sdk_uri }
        }
        payments::NextActionData::InvokeUpiQrFlow { qr_code_url, .. } => {
            StripeNextAction::InvokeUpiQrFlow {
                sdk_uri: qr_code_url,
            }
        }
    })
}

#[derive(Deserialize, Clone)]
pub struct StripePaymentRetrieveBody {
    pub client_secret: Option<String>,
}

//To handle payment types that have empty payment method data
fn get_pmd_based_on_payment_method_type(
    payment_method_type: Option<api_enums::PaymentMethodType>,
    billing_details: Option<hyperswitch_domain_models::address::Address>,
) -> Option<payments::PaymentMethodData> {
    match payment_method_type {
        Some(api_enums::PaymentMethodType::UpiIntent) => Some(payments::PaymentMethodData::Upi(
            payments::UpiData::UpiIntent(payments::UpiIntentData {}),
        )),
        Some(api_enums::PaymentMethodType::Fps) => {
            Some(payments::PaymentMethodData::RealTimePayment(Box::new(
                payments::RealTimePaymentData::Fps {},
            )))
        }
        Some(api_enums::PaymentMethodType::DuitNow) => {
            Some(payments::PaymentMethodData::RealTimePayment(Box::new(
                payments::RealTimePaymentData::DuitNow {},
            )))
        }
        Some(api_enums::PaymentMethodType::PromptPay) => {
            Some(payments::PaymentMethodData::RealTimePayment(Box::new(
                payments::RealTimePaymentData::PromptPay {},
            )))
        }
        Some(api_enums::PaymentMethodType::VietQr) => {
            Some(payments::PaymentMethodData::RealTimePayment(Box::new(
                payments::RealTimePaymentData::VietQr {},
            )))
        }
        Some(api_enums::PaymentMethodType::Ideal) => Some(
            payments::PaymentMethodData::BankRedirect(payments::BankRedirectData::Ideal {
                billing_details: billing_details.as_ref().map(|billing_data| {
                    payments::BankRedirectBilling {
                        billing_name: billing_data.get_optional_full_name(),
                        email: billing_data.email.clone(),
                    }
                }),
                bank_name: None,
                country: billing_details
                    .as_ref()
                    .and_then(|billing_data| billing_data.get_optional_country()),
            }),
        ),
        Some(api_enums::PaymentMethodType::LocalBankRedirect) => {
            Some(payments::PaymentMethodData::BankRedirect(
                payments::BankRedirectData::LocalBankRedirect {},
            ))
        }
        _ => None,
    }
}


// File: crates/router/src/compatibility/stripe/setup_intents/types.rs

use std::str::FromStr;

use api_models::payments;
use common_utils::{date_time, ext_traits::StringExt, id_type, pii as secret};
use error_stack::ResultExt;
use router_env::logger;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::{
    compatibility::stripe::{
        payment_intents::types as payment_intent, refunds::types as stripe_refunds,
    },
    consts,
    core::errors,
    pii::{self, PeekInterface},
    types::{
        api::{self as api_types, admin, enums as api_enums},
        transformers::{ForeignFrom, ForeignTryFrom},
    },
    utils::OptionExt,
};

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
pub struct StripeBillingDetails {
    pub address: Option<payments::AddressDetails>,
    pub email: Option<pii::Email>,
    pub name: Option<String>,
    pub phone: Option<pii::Secret<String>>,
}

impl From<StripeBillingDetails> for payments::Address {
    fn from(details: StripeBillingDetails) -> Self {
        Self {
            address: details.address,
            phone: Some(payments::PhoneDetails {
                number: details.phone,
                country_code: None,
            }),
            email: details.email,
        }
    }
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
pub struct StripeCard {
    pub number: cards::CardNumber,
    pub exp_month: pii::Secret<String>,
    pub exp_year: pii::Secret<String>,
    pub cvc: pii::Secret<String>,
}

// ApplePay wallet param is not available in stripe Docs
#[derive(Serialize, PartialEq, Eq, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripeWallet {
    ApplePay(payments::ApplePayWalletData),
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodType {
    #[default]
    Card,
    Wallet,
}

impl From<StripePaymentMethodType> for api_enums::PaymentMethod {
    fn from(item: StripePaymentMethodType) -> Self {
        match item {
            StripePaymentMethodType::Card => Self::Card,
            StripePaymentMethodType::Wallet => Self::Wallet,
        }
    }
}
#[derive(Default, PartialEq, Eq, Deserialize, Clone)]
pub struct StripePaymentMethodData {
    #[serde(rename = "type")]
    pub stype: StripePaymentMethodType,
    pub billing_details: Option<StripeBillingDetails>,
    #[serde(flatten)]
    pub payment_method_details: Option<StripePaymentMethodDetails>, // enum
    pub metadata: Option<Value>,
}

#[derive(PartialEq, Eq, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodDetails {
    Card(StripeCard),
    Wallet(StripeWallet),
}

impl From<StripeCard> for payments::Card {
    fn from(card: StripeCard) -> Self {
        Self {
            card_number: card.number,
            card_exp_month: card.exp_month,
            card_exp_year: card.exp_year,
            card_holder_name: Some(masking::Secret::new("stripe_cust".to_owned())),
            card_cvc: card.cvc,
            card_issuer: None,
            card_network: None,
            bank_code: None,
            card_issuing_country: None,
            card_type: None,
            nick_name: None,
        }
    }
}

impl From<StripeWallet> for payments::WalletData {
    fn from(wallet: StripeWallet) -> Self {
        match wallet {
            StripeWallet::ApplePay(data) => Self::ApplePay(data),
        }
    }
}

impl From<StripePaymentMethodDetails> for payments::PaymentMethodData {
    fn from(item: StripePaymentMethodDetails) -> Self {
        match item {
            StripePaymentMethodDetails::Card(card) => Self::Card(payments::Card::from(card)),
            StripePaymentMethodDetails::Wallet(wallet) => {
                Self::Wallet(payments::WalletData::from(wallet))
            }
        }
    }
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
pub struct Shipping {
    pub address: Option<payments::AddressDetails>,
    pub name: Option<String>,
    pub carrier: Option<String>,
    pub phone: Option<pii::Secret<String>>,
    pub tracking_number: Option<pii::Secret<String>>,
}

impl From<Shipping> for payments::Address {
    fn from(details: Shipping) -> Self {
        Self {
            address: details.address,
            phone: Some(payments::PhoneDetails {
                number: details.phone,
                country_code: None,
            }),
            email: None,
        }
    }
}

#[derive(Default, Deserialize, Clone)]
pub struct StripeSetupIntentRequest {
    pub confirm: Option<bool>,
    pub customer: Option<id_type::CustomerId>,
    pub connector: Option<Vec<api_enums::RoutableConnectors>>,
    pub description: Option<String>,
    pub currency: Option<String>,
    pub payment_method_data: Option<StripePaymentMethodData>,
    pub receipt_email: Option<pii::Email>,
    pub return_url: Option<url::Url>,
    pub setup_future_usage: Option<api_enums::FutureUsage>,
    pub shipping: Option<Shipping>,
    pub billing_details: Option<StripeBillingDetails>,
    pub statement_descriptor: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub metadata: Option<secret::SecretSerdeValue>,
    pub client_secret: Option<pii::Secret<String>>,
    pub payment_method_options: Option<payment_intent::StripePaymentMethodOptions>,
    pub payment_method: Option<String>,
    pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
    pub receipt_ipaddress: Option<String>,
    pub user_agent: Option<String>,
    pub mandate_data: Option<payment_intent::MandateData>,
    pub connector_metadata: Option<payments::ConnectorMetadata>,
}

impl TryFrom<StripeSetupIntentRequest> for payments::PaymentsRequest {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn try_from(item: StripeSetupIntentRequest) -> errors::RouterResult<Self> {
        let routable_connector: Option<api_enums::RoutableConnectors> =
            item.connector.and_then(|v| v.into_iter().next());

        let routing = routable_connector
            .map(|connector| {
                api_models::routing::StaticRoutingAlgorithm::Single(Box::new(
                    api_models::routing::RoutableConnectorChoice {
                        choice_kind: api_models::routing::RoutableChoiceKind::FullStruct,
                        connector,
                        merchant_connector_id: None,
                    },
                ))
            })
            .map(|r| {
                serde_json::to_value(r)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("converting to routing failed")
            })
            .transpose()?;
        let ip_address = item
            .receipt_ipaddress
            .map(|ip| std::net::IpAddr::from_str(ip.as_str()))
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                field_name: "receipt_ipaddress".to_string(),
                expected_format: "127.0.0.1".to_string(),
            })?;
        let metadata_object = item
            .metadata
            .clone()
            .parse_value("metadata")
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "metadata mapping failed",
            })?;
        let request = Ok(Self {
            amount: Some(api_types::Amount::Zero),
            capture_method: None,
            amount_to_capture: None,
            confirm: item.confirm,
            customer_id: item.customer,
            currency: item
                .currency
                .as_ref()
                .map(|c| c.to_uppercase().parse_enum("currency"))
                .transpose()
                .change_context(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "currency",
                })?,
            email: item.receipt_email,
            name: item
                .billing_details
                .as_ref()
                .and_then(|b| b.name.as_ref().map(|x| masking::Secret::new(x.to_owned()))),
            phone: item.shipping.as_ref().and_then(|s| s.phone.clone()),
            description: item.description,
            return_url: item.return_url,
            payment_method_data: item.payment_method_data.as_ref().and_then(|pmd| {
                pmd.payment_method_details
                    .as_ref()
                    .map(|spmd| payments::PaymentMethodDataRequest {
                        payment_method_data: Some(payments::PaymentMethodData::from(
                            spmd.to_owned(),
                        )),
                        billing: pmd.billing_details.clone().map(payments::Address::from),
                    })
            }),
            payment_method: item
                .payment_method_data
                .as_ref()
                .map(|pmd| api_enums::PaymentMethod::from(pmd.stype.to_owned())),
            shipping: item
                .shipping
                .as_ref()
                .map(|s| payments::Address::from(s.to_owned())),
            billing: item
                .billing_details
                .as_ref()
                .map(|b| payments::Address::from(b.to_owned())),
            statement_descriptor_name: item.statement_descriptor,
            statement_descriptor_suffix: item.statement_descriptor_suffix,
            metadata: metadata_object,
            client_secret: item.client_secret.map(|s| s.peek().clone()),
            setup_future_usage: item.setup_future_usage,
            merchant_connector_details: item.merchant_connector_details,
            routing,
            authentication_type: match item.payment_method_options {
                Some(pmo) => {
                    let payment_intent::StripePaymentMethodOptions::Card {
                        request_three_d_secure,
                    }: payment_intent::StripePaymentMethodOptions = pmo;
                    Some(api_enums::AuthenticationType::foreign_from(
                        request_three_d_secure,
                    ))
                }
                None => None,
            },
            mandate_data: ForeignTryFrom::foreign_try_from((
                item.mandate_data,
                item.currency.to_owned(),
            ))?,
            browser_info: Some(
                serde_json::to_value(crate::types::BrowserInformation {
                    ip_address,
                    user_agent: item.user_agent,
                    ..Default::default()
                })
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("convert to browser info failed")?,
            ),
            connector_metadata: item.connector_metadata,
            ..Default::default()
        });
        request
    }
}

#[derive(Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StripeSetupStatus {
    Succeeded,
    Canceled,
    #[default]
    Processing,
    RequiresAction,
    RequiresPaymentMethod,
    RequiresConfirmation,
}

impl From<api_enums::IntentStatus> for StripeSetupStatus {
    fn from(item: api_enums::IntentStatus) -> Self {
        match item {
            api_enums::IntentStatus::Succeeded | api_enums::IntentStatus::PartiallyCaptured => {
                Self::Succeeded
            }
            api_enums::IntentStatus::Failed | api_enums::IntentStatus::Expired => Self::Canceled,
            api_enums::IntentStatus::Processing
            | api_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture => Self::Processing,
            api_enums::IntentStatus::RequiresCustomerAction => Self::RequiresAction,
            api_enums::IntentStatus::RequiresMerchantAction
            | api_enums::IntentStatus::Conflicted => Self::RequiresAction,
            api_enums::IntentStatus::RequiresPaymentMethod => Self::RequiresPaymentMethod,
            api_enums::IntentStatus::RequiresConfirmation => Self::RequiresConfirmation,
            api_enums::IntentStatus::RequiresCapture
            | api_enums::IntentStatus::PartiallyCapturedAndCapturable => {
                logger::error!("Invalid status change");
                Self::Canceled
            }
            api_enums::IntentStatus::Cancelled | api_enums::IntentStatus::CancelledPostCapture => {
                Self::Canceled
            }
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Copy, Clone, strum::Display)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CancellationReason {
    Duplicate,
    Fraudulent,
    RequestedByCustomer,
    Abandoned,
}

#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
pub struct StripePaymentCancelRequest {
    cancellation_reason: Option<CancellationReason>,
}

impl From<StripePaymentCancelRequest> for payments::PaymentsCancelRequest {
    fn from(item: StripePaymentCancelRequest) -> Self {
        Self {
            cancellation_reason: item.cancellation_reason.map(|c| c.to_string()),
            ..Self::default()
        }
    }
}
#[derive(Default, Eq, PartialEq, Serialize)]
pub struct RedirectUrl {
    pub return_url: Option<String>,
    pub url: Option<String>,
}

#[derive(Eq, PartialEq, serde::Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum StripeNextAction {
    RedirectToUrl {
        redirect_to_url: RedirectUrl,
    },
    DisplayBankTransferInformation {
        bank_transfer_steps_and_charges_details: payments::BankTransferNextStepsData,
    },
    ThirdPartySdkSessionToken {
        session_token: Option<payments::SessionToken>,
    },
    QrCodeInformation {
        image_data_url: Option<url::Url>,
        display_to_timestamp: Option<i64>,
        qr_code_url: Option<url::Url>,
        border_color: Option<String>,
        display_text: Option<String>,
    },
    FetchQrCodeInformation {
        qr_code_fetch_url: url::Url,
    },
    DisplayVoucherInformation {
        voucher_details: payments::VoucherNextStepData,
    },
    WaitScreenInformation {
        display_from_timestamp: i128,
        display_to_timestamp: Option<i128>,
        poll_config: Option<payments::PollConfig>,
    },
    InvokeSdkClient {
        next_action_data: payments::SdkNextActionData,
    },
    CollectOtp {
        consent_data_required: payments::MobilePaymentConsent,
    },
    InvokeHiddenIframe {
        iframe_data: payments::IframeData,
    },
    InvokeUpiIntentSdk {
        sdk_uri: url::Url,
    },
    InvokeUpiQrFlow {
        sdk_uri: url::Url,
    },
}

pub(crate) fn into_stripe_next_action(
    next_action: Option<payments::NextActionData>,
    return_url: Option<String>,
) -> Option<StripeNextAction> {
    next_action.map(|next_action_data| match next_action_data {
        payments::NextActionData::RedirectToUrl { redirect_to_url } => {
            StripeNextAction::RedirectToUrl {
                redirect_to_url: RedirectUrl {
                    return_url,
                    url: Some(redirect_to_url),
                },
            }
        }
        payments::NextActionData::RedirectInsidePopup { popup_url, .. } => {
            StripeNextAction::RedirectToUrl {
                redirect_to_url: RedirectUrl {
                    return_url,
                    url: Some(popup_url),
                },
            }
        }
        payments::NextActionData::DisplayBankTransferInformation {
            bank_transfer_steps_and_charges_details,
        } => StripeNextAction::DisplayBankTransferInformation {
            bank_transfer_steps_and_charges_details,
        },
        payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
            StripeNextAction::ThirdPartySdkSessionToken { session_token }
        }

        payments::NextActionData::QrCodeInformation {
            image_data_url,
            display_to_timestamp,
            qr_code_url,
            display_text,
            border_color,
        } => StripeNextAction::QrCodeInformation {
            image_data_url,
            display_to_timestamp,
            qr_code_url,
            display_text,
            border_color,
        },
        payments::NextActionData::FetchQrCodeInformation { qr_code_fetch_url } => {
            StripeNextAction::FetchQrCodeInformation { qr_code_fetch_url }
        }
        payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
            StripeNextAction::DisplayVoucherInformation { voucher_details }
        }
        payments::NextActionData::WaitScreenInformation {
            display_from_timestamp,
            display_to_timestamp,
            poll_config: _,
        } => StripeNextAction::WaitScreenInformation {
            display_from_timestamp,
            display_to_timestamp,
            poll_config: None,
        },
        payments::NextActionData::ThreeDsInvoke { .. } => StripeNextAction::RedirectToUrl {
            redirect_to_url: RedirectUrl {
                return_url: None,
                url: None,
            },
        },
        payments::NextActionData::InvokeSdkClient { next_action_data } => {
            StripeNextAction::InvokeSdkClient { next_action_data }
        }
        payments::NextActionData::CollectOtp {
            consent_data_required,
        } => StripeNextAction::CollectOtp {
            consent_data_required,
        },
        payments::NextActionData::InvokeHiddenIframe { iframe_data } => {
            StripeNextAction::InvokeHiddenIframe { iframe_data }
        }
        payments::NextActionData::InvokeUpiIntentSdk { sdk_uri, .. } => {
            StripeNextAction::InvokeUpiIntentSdk { sdk_uri }
        }
        payments::NextActionData::InvokeUpiQrFlow { qr_code_url, .. } => {
            StripeNextAction::InvokeUpiQrFlow {
                sdk_uri: qr_code_url,
            }
        }
    })
}

#[derive(Default, Eq, PartialEq, Serialize)]
pub struct StripeSetupIntentResponse {
    pub id: id_type::PaymentId,
    pub object: String,
    pub status: StripeSetupStatus,
    pub client_secret: Option<masking::Secret<String>>,
    pub metadata: Option<Value>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,
    pub customer: Option<id_type::CustomerId>,
    pub refunds: Option<Vec<stripe_refunds::StripeRefundResponse>>,
    pub mandate_id: Option<String>,
    pub next_action: Option<StripeNextAction>,
    pub last_payment_error: Option<LastPaymentError>,
    pub charges: payment_intent::Charges,
    pub connector_transaction_id: Option<String>,
}

#[derive(Default, Eq, PartialEq, Serialize)]
pub struct LastPaymentError {
    charge: Option<String>,
    code: Option<String>,
    decline_code: Option<String>,
    message: String,
    param: Option<String>,
    payment_method: StripePaymentMethod,
    #[serde(rename = "type")]
    error_type: String,
}

#[derive(Default, Eq, PartialEq, Serialize)]
pub struct StripePaymentMethod {
    #[serde(rename = "id")]
    payment_method_id: String,
    object: &'static str,
    card: Option<StripeCard>,
    created: u64,
    #[serde(rename = "type")]
    method_type: String,
    livemode: bool,
}

impl From<payments::PaymentsResponse> for StripeSetupIntentResponse {
    fn from(resp: payments::PaymentsResponse) -> Self {
        Self {
            object: "setup_intent".to_owned(),
            status: StripeSetupStatus::from(resp.status),
            client_secret: resp.client_secret,
            charges: payment_intent::Charges::new(),
            created: resp.created,
            customer: resp.customer_id,
            metadata: resp.metadata,
            id: resp.payment_id,
            refunds: resp
                .refunds
                .map(|a| a.into_iter().map(Into::into).collect()),
            mandate_id: resp.mandate_id,
            next_action: into_stripe_next_action(resp.next_action, resp.return_url),
            last_payment_error: resp.error_code.map(|code| -> LastPaymentError {
                LastPaymentError {
                    charge: None,
                    code: Some(code.to_owned()),
                    decline_code: None,
                    message: resp
                        .error_message
                        .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
                    param: None,
                    payment_method: StripePaymentMethod {
                        payment_method_id: "place_holder_id".to_string(),
                        object: "payment_method",
                        card: None,
                        created: u64::try_from(date_time::now().assume_utc().unix_timestamp())
                            .unwrap_or_default(),
                        method_type: "card".to_string(),
                        livemode: false,
                    },
                    error_type: code,
                }
            }),
            connector_transaction_id: resp.connector_transaction_id,
        }
    }
}
#[derive(Clone, Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct StripePaymentListConstraints {
    pub customer: Option<id_type::CustomerId>,
    pub starting_after: Option<id_type::PaymentId>,
    pub ending_before: Option<id_type::PaymentId>,
    #[serde(default = "default_limit")]
    pub limit: u32,
    pub created: Option<i64>,
    #[serde(rename = "created[lt]")]
    pub created_lt: Option<i64>,
    #[serde(rename = "created[gt]")]
    pub created_gt: Option<i64>,
    #[serde(rename = "created[lte]")]
    pub created_lte: Option<i64>,
    #[serde(rename = "created[gte]")]
    pub created_gte: Option<i64>,
}

fn default_limit() -> u32 {
    10
}

impl TryFrom<StripePaymentListConstraints> for payments::PaymentListConstraints {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn try_from(item: StripePaymentListConstraints) -> Result<Self, Self::Error> {
        Ok(Self {
            customer_id: item.customer,
            starting_after: item.starting_after,
            ending_before: item.ending_before,
            limit: item.limit,
            created: from_timestamp_to_datetime(item.created)?,
            created_lt: from_timestamp_to_datetime(item.created_lt)?,
            created_gt: from_timestamp_to_datetime(item.created_gt)?,
            created_lte: from_timestamp_to_datetime(item.created_lte)?,
            created_gte: from_timestamp_to_datetime(item.created_gte)?,
        })
    }
}

#[inline]
fn from_timestamp_to_datetime(
    time: Option<i64>,
) -> Result<Option<time::PrimitiveDateTime>, errors::ApiErrorResponse> {
    if let Some(time) = time {
        let time = time::OffsetDateTime::from_unix_timestamp(time).map_err(|err| {
            logger::error!("Error: from_unix_timestamp: {}", err);
            errors::ApiErrorResponse::InvalidRequestData {
                message: "Error while converting timestamp".to_string(),
            }
        })?;

        Ok(Some(time::PrimitiveDateTime::new(time.date(), time.time())))
    } else {
        Ok(None)
    }
}


// File: crates/router/src/compatibility/stripe/refunds/types.rs

use std::{convert::From, default::Default};

use common_utils::pii;
use serde::{Deserialize, Serialize};

use crate::types::api::{admin, refunds};

#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct StripeCreateRefundRequest {
    pub refund_id: Option<String>,
    pub amount: Option<i64>,
    pub payment_intent: common_utils::id_type::PaymentId,
    pub reason: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
}

#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct StripeUpdateRefundRequest {
    #[serde(skip)]
    pub refund_id: String,
    pub metadata: Option<pii::SecretSerdeValue>,
}

#[derive(Clone, Serialize, PartialEq, Eq, Debug)]
pub struct StripeRefundResponse {
    pub id: String,
    pub amount: i64,
    pub currency: String,
    pub payment_intent: common_utils::id_type::PaymentId,
    pub status: StripeRefundStatus,
    pub created: Option<i64>,
    pub metadata: pii::SecretSerdeValue,
}

#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripeRefundStatus {
    Succeeded,
    Failed,
    Pending,
    RequiresAction,
}

impl From<StripeCreateRefundRequest> for refunds::RefundRequest {
    fn from(req: StripeCreateRefundRequest) -> Self {
        Self {
            refund_id: req.refund_id,
            amount: req.amount.map(common_utils::types::MinorUnit::new),
            payment_id: req.payment_intent,
            reason: req.reason,
            refund_type: Some(refunds::RefundType::Instant),
            metadata: req.metadata,
            merchant_connector_details: req.merchant_connector_details,
            ..Default::default()
        }
    }
}

impl From<StripeUpdateRefundRequest> for refunds::RefundUpdateRequest {
    fn from(req: StripeUpdateRefundRequest) -> Self {
        Self {
            refund_id: req.refund_id,
            metadata: req.metadata,
            reason: None,
        }
    }
}

impl From<refunds::RefundStatus> for StripeRefundStatus {
    fn from(status: refunds::RefundStatus) -> Self {
        match status {
            refunds::RefundStatus::Succeeded => Self::Succeeded,
            refunds::RefundStatus::Failed => Self::Failed,
            refunds::RefundStatus::Pending => Self::Pending,
            refunds::RefundStatus::Review => Self::RequiresAction,
        }
    }
}

impl From<refunds::RefundResponse> for StripeRefundResponse {
    fn from(res: refunds::RefundResponse) -> Self {
        Self {
            id: res.refund_id,
            amount: res.amount.get_amount_as_i64(),
            currency: res.currency.to_ascii_lowercase(),
            payment_intent: res.payment_id,
            status: res.status.into(),
            created: res.created_at.map(|t| t.assume_utc().unix_timestamp()),
            metadata: res
                .metadata
                .unwrap_or_else(|| masking::Secret::new(serde_json::json!({}))),
        }
    }
}


// File: crates/router/src/types/payment_methods.rs

use std::fmt::Debug;

use api_models::enums as api_enums;
#[cfg(feature = "v1")]
use cards::CardNumber;
#[cfg(feature = "v2")]
use cards::{CardNumber, NetworkToken};
#[cfg(feature = "v2")]
use common_types::primitive_wrappers;
#[cfg(feature = "v2")]
use common_utils::generate_id;
use common_utils::id_type;
#[cfg(feature = "v2")]
use hyperswitch_domain_models::payment_method_data::NetworkTokenDetails;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::types::api;
#[cfg(feature = "v2")]
use crate::{
    consts,
    types::{domain, storage},
};

#[cfg(feature = "v2")]
pub trait VaultingInterface {
    fn get_vaulting_request_url() -> &'static str;

    fn get_vaulting_flow_name() -> &'static str;
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultFingerprintRequest {
    pub data: String,
    pub key: String,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultFingerprintResponse {
    pub fingerprint_id: String,
}

#[cfg(any(feature = "v2", feature = "tokenization_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AddVaultRequest<D> {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
    pub data: D,
    pub ttl: i64,
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AddVaultResponse {
    #[cfg(feature = "v2")]
    pub entity_id: Option<id_type::GlobalCustomerId>,
    #[cfg(feature = "v1")]
    pub entity_id: Option<id_type::CustomerId>,
    #[cfg(feature = "v2")]
    pub vault_id: domain::VaultId,
    #[cfg(feature = "v1")]
    pub vault_id: hyperswitch_domain_models::router_response_types::VaultIdType,
    pub fingerprint_id: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AddVault;

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct GetVaultFingerprint;

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultRetrieve;

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultDelete;

#[cfg(feature = "v2")]
impl VaultingInterface for AddVault {
    fn get_vaulting_request_url() -> &'static str {
        consts::ADD_VAULT_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_ADD_FLOW_TYPE
    }
}

#[cfg(feature = "v2")]
impl VaultingInterface for GetVaultFingerprint {
    fn get_vaulting_request_url() -> &'static str {
        consts::VAULT_FINGERPRINT_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_GET_FINGERPRINT_FLOW_TYPE
    }
}

#[cfg(feature = "v2")]
impl VaultingInterface for VaultRetrieve {
    fn get_vaulting_request_url() -> &'static str {
        consts::VAULT_RETRIEVE_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_RETRIEVE_FLOW_TYPE
    }
}

#[cfg(feature = "v2")]
impl VaultingInterface for VaultDelete {
    fn get_vaulting_request_url() -> &'static str {
        consts::VAULT_DELETE_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_DELETE_FLOW_TYPE
    }
}

#[cfg(feature = "v2")]
pub struct SavedPMLPaymentsInfo {
    pub payment_intent: storage::PaymentIntent,
    pub profile: domain::Profile,
    pub collect_cvv_during_payment: Option<primitive_wrappers::ShouldCollectCvvDuringPayment>,
    pub off_session_payment_flag: bool,
    pub is_connector_agnostic_mit_enabled: bool,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultRetrieveRequest {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultRetrieveResponse {
    pub data: domain::PaymentMethodVaultingData,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultDeleteRequest {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultDeleteResponse {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardData {
    pub card_number: CardNumber,
    pub exp_month: Secret<String>,
    pub exp_year: Secret<String>,
    pub card_security_code: Option<Secret<String>>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardData {
    pub card_number: CardNumber,
    pub exp_month: Secret<String>,
    pub exp_year: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub card_security_code: Option<Secret<String>>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderData {
    pub consent_id: String,
    pub customer_id: id_type::CustomerId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderData {
    pub consent_id: String,
    pub customer_id: id_type::GlobalCustomerId,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiPayload {
    pub service: String,
    pub card_data: Secret<String>, //encrypted card data
    pub order_data: OrderData,
    pub should_send_token: bool,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct CardNetworkTokenResponse {
    pub payload: Secret<String>, //encrypted payload
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardNetworkTokenResponsePayload {
    pub card_brand: api_enums::CardNetwork,
    pub card_fingerprint: Option<Secret<String>>,
    pub card_reference: String,
    pub correlation_id: String,
    pub customer_id: String,
    pub par: String,
    pub token: CardNumber,
    pub token_expiry_month: Secret<String>,
    pub token_expiry_year: Secret<String>,
    pub token_isin: String,
    pub token_last_four: String,
    pub token_status: String,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GenerateNetworkTokenResponsePayload {
    pub card_brand: api_enums::CardNetwork,
    pub card_fingerprint: Option<Secret<String>>,
    pub card_reference: String,
    pub correlation_id: String,
    pub customer_id: String,
    pub par: String,
    pub token: NetworkToken,
    pub token_expiry_month: Secret<String>,
    pub token_expiry_year: Secret<String>,
    pub token_isin: String,
    pub token_last_four: String,
    pub token_status: String,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize)]
pub struct GetCardToken {
    pub card_reference: String,
    pub customer_id: id_type::CustomerId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize)]
pub struct GetCardToken {
    pub card_reference: String,
    pub customer_id: id_type::GlobalCustomerId,
}

#[cfg(feature = "v1")]
#[derive(Debug, Deserialize)]
pub struct AuthenticationDetails {
    pub cryptogram: Secret<String>,
    pub token: CardNumber, //network token
}

#[cfg(feature = "v2")]
#[derive(Debug, Deserialize)]
pub struct AuthenticationDetails {
    pub cryptogram: Secret<String>,
    pub token: NetworkToken, //network token
}

#[derive(Debug, Serialize, Deserialize)]
pub struct TokenDetails {
    pub exp_month: Secret<String>,
    pub exp_year: Secret<String>,
}

#[derive(Debug, Deserialize)]
pub struct TokenResponse {
    pub authentication_details: AuthenticationDetails,
    pub network: api_enums::CardNetwork,
    pub token_details: TokenDetails,
    pub eci: Option<String>,
    pub card_type: Option<String>,
    pub issuer: Option<String>,
    pub nickname: Option<Secret<String>>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteCardToken {
    pub card_reference: String, //network token requestor ref id
    pub customer_id: id_type::CustomerId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteCardToken {
    pub card_reference: String, //network token requestor ref id
    pub customer_id: id_type::GlobalCustomerId,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum DeleteNetworkTokenStatus {
    Success,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct NetworkTokenErrorInfo {
    pub code: String,
    pub developer_message: String,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct NetworkTokenErrorResponse {
    pub error_message: Option<String>,
    pub error_info: NetworkTokenErrorInfo,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct DeleteNetworkTokenResponse {
    pub status: DeleteNetworkTokenStatus,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize, Deserialize)]
pub struct CheckTokenStatus {
    pub card_reference: String,
    pub customer_id: id_type::CustomerId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckTokenStatus {
    pub card_reference: String,
    pub customer_id: id_type::GlobalCustomerId,
}

#[derive(Debug, Deserialize, Clone)]
#[serde(rename_all = "UPPERCASE")]
pub enum TokenStatus {
    Active,
    Inactive,
    Suspended,
    Expired,
    Deleted,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckTokenStatusResponsePayload {
    pub token_status: TokenStatus,
    pub token_expiry_month: Option<Secret<String>>,
    pub token_expiry_year: Option<Secret<String>>,
    pub card_last_four: Option<String>,
    pub card_expiry_month: Option<Secret<String>>,
    pub card_expiry_year: Option<Secret<String>>,
    pub token_last_four: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct CheckTokenStatusResponse {
    pub payload: CheckTokenStatusResponsePayload,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NetworkTokenRequestorData {
    pub card_reference: String,
    pub customer_id: String,
    pub expiry_year: Secret<String>,
    pub expiry_month: Secret<String>,
}

impl NetworkTokenRequestorData {
    pub fn is_update_required(
        &self,
        data_stored_in_vault: api::payment_methods::CardDetailFromLocker,
    ) -> bool {
        //if the expiry year and month in the vault are not the same as the ones in the requestor data,
        //then we need to update the vault data with the updated expiry year and month.
        !((data_stored_in_vault.expiry_year.unwrap_or_default() == self.expiry_year)
            && (data_stored_in_vault.expiry_month.unwrap_or_default() == self.expiry_month))
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NetworkTokenMetaDataUpdateBody {
    pub token: NetworkTokenRequestorData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PanMetadataUpdateBody {
    pub card: NetworkTokenRequestorData,
}


// File: crates/router/src/types/fraud_check.rs

pub use hyperswitch_domain_models::{
    router_request_types::fraud_check::{
        FraudCheckCheckoutData, FraudCheckFulfillmentData, FraudCheckRecordReturnData,
        FraudCheckSaleData, FraudCheckTransactionData, RefundMethod,
    },
    router_response_types::fraud_check::FraudCheckResponseData,
};

use crate::{
    services,
    types::{api, ErrorResponse, RouterData},
};

pub type FrmSaleRouterData = RouterData<api::Sale, FraudCheckSaleData, FraudCheckResponseData>;

pub type FrmSaleType =
    dyn services::ConnectorIntegration<api::Sale, FraudCheckSaleData, FraudCheckResponseData>;

#[derive(Debug, Clone)]
pub struct FrmRouterData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub connector: String,
    // TODO: change this to PaymentId type
    pub payment_id: String,
    pub attempt_id: String,
    pub request: FrmRequest,
    pub response: FrmResponse,
}
#[derive(Debug, Clone)]
pub enum FrmRequest {
    Sale(FraudCheckSaleData),
    Checkout(Box<FraudCheckCheckoutData>),
    Transaction(FraudCheckTransactionData),
    Fulfillment(FraudCheckFulfillmentData),
    RecordReturn(FraudCheckRecordReturnData),
}
#[derive(Debug, Clone)]
pub enum FrmResponse {
    Sale(Result<FraudCheckResponseData, ErrorResponse>),
    Checkout(Result<FraudCheckResponseData, ErrorResponse>),
    Transaction(Result<FraudCheckResponseData, ErrorResponse>),
    Fulfillment(Result<FraudCheckResponseData, ErrorResponse>),
    RecordReturn(Result<FraudCheckResponseData, ErrorResponse>),
}

pub type FrmCheckoutRouterData =
    RouterData<api::Checkout, FraudCheckCheckoutData, FraudCheckResponseData>;

pub type FrmCheckoutType = dyn services::ConnectorIntegration<
    api::Checkout,
    FraudCheckCheckoutData,
    FraudCheckResponseData,
>;

pub type FrmTransactionRouterData =
    RouterData<api::Transaction, FraudCheckTransactionData, FraudCheckResponseData>;

pub type FrmTransactionType = dyn services::ConnectorIntegration<
    api::Transaction,
    FraudCheckTransactionData,
    FraudCheckResponseData,
>;

pub type FrmFulfillmentRouterData =
    RouterData<api::Fulfillment, FraudCheckFulfillmentData, FraudCheckResponseData>;

pub type FrmFulfillmentType = dyn services::ConnectorIntegration<
    api::Fulfillment,
    FraudCheckFulfillmentData,
    FraudCheckResponseData,
>;
pub type FrmRecordReturnRouterData =
    RouterData<api::RecordReturn, FraudCheckRecordReturnData, FraudCheckResponseData>;

pub type FrmRecordReturnType = dyn services::ConnectorIntegration<
    api::RecordReturn,
    FraudCheckRecordReturnData,
    FraudCheckResponseData,
>;


// File: crates/router/src/types/connector_transformers.rs

use api_models::enums as api_enums;

use super::ForeignTryFrom;

impl ForeignTryFrom<api_enums::Connector> for common_enums::RoutableConnectors {
    type Error = error_stack::Report<common_utils::errors::ValidationError>;

    fn foreign_try_from(from: api_enums::Connector) -> Result<Self, Self::Error> {
        Ok(match from {
            api_enums::Connector::Aci => Self::Aci,
            api_enums::Connector::Adyen => Self::Adyen,
            api_enums::Connector::Affirm => Self::Affirm,
            api_enums::Connector::Adyenplatform => Self::Adyenplatform,
            api_enums::Connector::Airwallex => Self::Airwallex,
            api_enums::Connector::Amazonpay => Self::Amazonpay,
            api_enums::Connector::Archipel => Self::Archipel,
            api_enums::Connector::Authipay => Self::Authipay,
            api_enums::Connector::Authorizedotnet => Self::Authorizedotnet,
            api_enums::Connector::Bambora => Self::Bambora,
            api_enums::Connector::Bamboraapac => Self::Bamboraapac,
            api_enums::Connector::Bankofamerica => Self::Bankofamerica,
            api_enums::Connector::Barclaycard => Self::Barclaycard,
            api_enums::Connector::Billwerk => Self::Billwerk,
            api_enums::Connector::Bitpay => Self::Bitpay,
            api_enums::Connector::Bluesnap => Self::Bluesnap,
            api_enums::Connector::Blackhawknetwork => Self::Blackhawknetwork,
            api_enums::Connector::Calida => Self::Calida,
            api_enums::Connector::Boku => Self::Boku,
            api_enums::Connector::Braintree => Self::Braintree,
            api_enums::Connector::Breadpay => Self::Breadpay,
            api_enums::Connector::Cardinal => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "cardinal is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Cashtocode => Self::Cashtocode,
            api_enums::Connector::Celero => Self::Celero,
            api_enums::Connector::Chargebee => Self::Chargebee,
            api_enums::Connector::Checkbook => Self::Checkbook,
            api_enums::Connector::Checkout => Self::Checkout,
            api_enums::Connector::Coinbase => Self::Coinbase,
            api_enums::Connector::Coingate => Self::Coingate,
            api_enums::Connector::Cryptopay => Self::Cryptopay,
            api_enums::Connector::Custombilling => Self::Custombilling,
            api_enums::Connector::CtpVisa => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "ctp visa is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::CtpMastercard => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "ctp mastercard is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Cybersource => Self::Cybersource,
            api_enums::Connector::Datatrans => Self::Datatrans,
            api_enums::Connector::Deutschebank => Self::Deutschebank,
            api_enums::Connector::Digitalvirgo => Self::Digitalvirgo,
            api_enums::Connector::Dlocal => Self::Dlocal,
            api_enums::Connector::Dwolla => Self::Dwolla,
            api_enums::Connector::Ebanx => Self::Ebanx,
            api_enums::Connector::Elavon => Self::Elavon,
            api_enums::Connector::Facilitapay => Self::Facilitapay,
            api_enums::Connector::Finix => Self::Finix,
            api_enums::Connector::Fiserv => Self::Fiserv,
            api_enums::Connector::Fiservemea => Self::Fiservemea,
            api_enums::Connector::Fiuu => Self::Fiuu,
            api_enums::Connector::Flexiti => Self::Flexiti,
            api_enums::Connector::Forte => Self::Forte,
            api_enums::Connector::Getnet => Self::Getnet,
            api_enums::Connector::Gigadat => Self::Gigadat,
            api_enums::Connector::Globalpay => Self::Globalpay,
            api_enums::Connector::Globepay => Self::Globepay,
            api_enums::Connector::Gocardless => Self::Gocardless,
            api_enums::Connector::Gpayments => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "gpayments is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Hipay => Self::Hipay,
            api_enums::Connector::Helcim => Self::Helcim,
            api_enums::Connector::HyperswitchVault => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "Hyperswitch Vault is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Iatapay => Self::Iatapay,
            api_enums::Connector::Inespay => Self::Inespay,
            api_enums::Connector::Itaubank => Self::Itaubank,
            api_enums::Connector::Jpmorgan => Self::Jpmorgan,
            api_enums::Connector::Juspaythreedsserver => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "juspaythreedsserver is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Klarna => Self::Klarna,
            api_enums::Connector::Loonio => Self::Loonio,
            api_enums::Connector::Mifinity => Self::Mifinity,
            api_enums::Connector::Mollie => Self::Mollie,
            api_enums::Connector::Moneris => Self::Moneris,
            api_enums::Connector::Multisafepay => Self::Multisafepay,
            api_enums::Connector::Netcetera => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "netcetera is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Nexinets => Self::Nexinets,
            api_enums::Connector::Nexixpay => Self::Nexixpay,
            api_enums::Connector::Nmi => Self::Nmi,
            api_enums::Connector::Nomupay => Self::Nomupay,
            api_enums::Connector::Noon => Self::Noon,
            api_enums::Connector::Nordea => Self::Nordea,
            api_enums::Connector::Novalnet => Self::Novalnet,
            api_enums::Connector::Nuvei => Self::Nuvei,
            api_enums::Connector::Opennode => Self::Opennode,
            api_enums::Connector::Paybox => Self::Paybox,
            api_enums::Connector::Payjustnow => Self::Payjustnow,
            api_enums::Connector::Payload => Self::Payload,
            api_enums::Connector::Payme => Self::Payme,
            api_enums::Connector::Payone => Self::Payone,
            api_enums::Connector::Paypal => Self::Paypal,
            api_enums::Connector::Paysafe => Self::Paysafe,
            api_enums::Connector::Paystack => Self::Paystack,
            api_enums::Connector::Payu => Self::Payu,
            api_enums::Connector::Peachpayments => Self::Peachpayments,
            api_models::enums::Connector::Placetopay => Self::Placetopay,
            api_enums::Connector::Plaid => Self::Plaid,
            api_enums::Connector::Powertranz => Self::Powertranz,
            api_enums::Connector::Prophetpay => Self::Prophetpay,
            api_enums::Connector::Rapyd => Self::Rapyd,
            api_enums::Connector::Razorpay => Self::Razorpay,
            api_enums::Connector::Recurly => Self::Recurly,
            api_enums::Connector::Redsys => Self::Redsys,
            api_enums::Connector::Santander => Self::Santander,
            api_enums::Connector::Shift4 => Self::Shift4,
            api_enums::Connector::Zift => Self::Zift,
            api_enums::Connector::Silverflow => Self::Silverflow,
            api_enums::Connector::Signifyd => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "signifyd is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Riskified => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "riskified is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Square => Self::Square,
            api_enums::Connector::Stax => Self::Stax,
            api_enums::Connector::Stripe => Self::Stripe,
            api_enums::Connector::Stripebilling => Self::Stripebilling,
            // api_enums::Connector::Thunes => Self::Thunes,
            api_enums::Connector::Tesouro => Self::Tesouro,
            api_enums::Connector::Tokenex => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "Tokenex is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Tokenio => Self::Tokenio,
            api_enums::Connector::Trustpay => Self::Trustpay,
            api_enums::Connector::Trustpayments => Self::Trustpayments,
            api_enums::Connector::Tsys => Self::Tsys,
            // api_enums::Connector::UnifiedAuthenticationService => {
            //     Self::UnifiedAuthenticationService
            // }
            api_enums::Connector::Vgs => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "Vgs is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Volt => Self::Volt,
            api_enums::Connector::Wellsfargo => Self::Wellsfargo,
            // api_enums::Connector::Wellsfargopayout => Self::Wellsfargopayout,
            api_enums::Connector::Wise => Self::Wise,
            api_enums::Connector::Worldline => Self::Worldline,
            api_enums::Connector::Worldpay => Self::Worldpay,
            api_enums::Connector::Worldpayvantiv => Self::Worldpayvantiv,
            api_enums::Connector::Worldpayxml => Self::Worldpayxml,
            api_enums::Connector::Xendit => Self::Xendit,
            api_enums::Connector::Zen => Self::Zen,
            api_enums::Connector::Zsl => Self::Zsl,
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyBillingConnector => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "stripe_billing_test is not a routable connector".to_string(),
                })?
            }
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyConnector1 => Self::DummyConnector1,
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyConnector2 => Self::DummyConnector2,
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyConnector3 => Self::DummyConnector3,
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyConnector4 => Self::DummyConnector4,
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyConnector5 => Self::DummyConnector5,
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyConnector6 => Self::DummyConnector6,
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyConnector7 => Self::DummyConnector7,
            api_enums::Connector::Threedsecureio => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "threedsecureio is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Taxjar => {
                Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "Taxjar is not a routable connector".to_string(),
                })?
            }
            api_enums::Connector::Phonepe => Self::Phonepe,
            api_enums::Connector::Paytm => Self::Paytm,
        })
    }
}