// File: crates/hyperswitch_domain_models/src/router_request_types/subscriptions.rs

use api_models::{payments::Address, subscription};
use common_utils::id_type;

use crate::connector_endpoints;

#[derive(Debug, Clone)]
pub struct SubscriptionItem {
    pub item_price_id: String,
    pub quantity: Option<u32>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionCreateRequest {
    pub customer_id: id_type::CustomerId,
    pub subscription_id: id_type::SubscriptionId,
    pub subscription_items: Vec<SubscriptionItem>,
    pub billing_address: Address,
    pub auto_collection: SubscriptionAutoCollection,
    pub connector_params: connector_endpoints::ConnectorParams,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SubscriptionAutoCollection {
    On,
    Off,
}
#[derive(Debug, Clone)]
pub struct GetSubscriptionPlansRequest {
    pub limit: Option<u32>,
    pub offset: Option<u32>,
}

impl GetSubscriptionPlansRequest {
    pub fn new(limit: Option<u32>, offset: Option<u32>) -> Self {
        Self { limit, offset }
    }
}

impl Default for GetSubscriptionPlansRequest {
    fn default() -> Self {
        Self {
            limit: Some(10),
            offset: Some(0),
        }
    }
}

#[derive(Debug, Clone)]
pub struct GetSubscriptionPlanPricesRequest {
    pub plan_price_id: String,
}

#[derive(Debug, Clone)]
pub struct SubscriptionPauseRequest {
    pub subscription_id: id_type::SubscriptionId,
    pub pause_option: Option<subscription::PauseOption>,
    pub pause_date: Option<time::PrimitiveDateTime>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionResumeRequest {
    pub subscription_id: id_type::SubscriptionId,
    pub resume_option: Option<subscription::ResumeOption>,
    pub resume_date: Option<time::PrimitiveDateTime>,
    pub charges_handling: Option<subscription::ChargesHandling>,
    pub unpaid_invoices_handling: Option<subscription::UnpaidInvoicesHandling>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionCancelRequest {
    pub subscription_id: id_type::SubscriptionId,
    pub cancel_option: Option<subscription::CancelOption>,
    pub cancel_date: Option<time::PrimitiveDateTime>,
    pub unbilled_charges_option: Option<subscription::UnbilledChargesOption>,
    pub credit_option_for_current_term_charges: Option<subscription::CreditOption>,
    pub account_receivables_handling: Option<subscription::AccountReceivablesHandling>,
    pub refundable_credits_handling: Option<subscription::RefundableCreditsHandling>,
    pub cancel_reason_code: Option<String>,
}

#[derive(Debug, Clone)]
pub struct GetSubscriptionEstimateRequest {
    pub price_id: String,
}


// File: crates/hyperswitch_domain_models/src/router_request_types/fraud_check.rs

use common_utils::{
    events::{ApiEventMetric, ApiEventsType},
    pii::Email,
};
use diesel_models::types::OrderDetailsWithAmount;
use masking::Secret;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use crate::router_request_types;
#[derive(Debug, Clone)]
pub struct FraudCheckSaleData {
    pub amount: i64,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub currency: Option<common_enums::Currency>,
    pub email: Option<Email>,
}

#[derive(Debug, Clone)]
pub struct FraudCheckCheckoutData {
    pub amount: i64,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub currency: Option<common_enums::Currency>,
    pub browser_info: Option<router_request_types::BrowserInformation>,
    pub payment_method_data: Option<api_models::payments::AdditionalPaymentData>,
    pub email: Option<Email>,
    pub gateway: Option<String>,
}

#[derive(Debug, Clone)]
pub struct FraudCheckTransactionData {
    pub amount: i64,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub currency: Option<common_enums::Currency>,
    pub payment_method: Option<common_enums::PaymentMethod>,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
    pub connector_transaction_id: Option<String>,
    //The name of the payment gateway or financial institution that processed the transaction.
    pub connector: Option<String>,
}

#[derive(Debug, Clone)]
pub struct FraudCheckRecordReturnData {
    pub amount: i64,
    pub currency: Option<common_enums::Currency>,
    pub refund_method: RefundMethod,
    pub refund_transaction_id: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum RefundMethod {
    StoreCredit,
    OriginalPaymentInstrument,
    NewPaymentInstrument,
}

#[derive(Debug, Clone)]
pub struct FraudCheckFulfillmentData {
    pub amount: i64,
    pub order_details: Option<Vec<Secret<serde_json::Value>>>,
    pub fulfillment_req: FrmFulfillmentRequest,
}

#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "snake_case")]
pub struct FrmFulfillmentRequest {
    ///unique payment_id for the transaction
    #[schema(max_length = 255, example = "pay_qiYfHcDou1ycIaxVXKHF")]
    pub payment_id: common_utils::id_type::PaymentId,
    ///unique order_id for the order_details in the transaction
    #[schema(max_length = 255, example = "pay_qiYfHcDou1ycIaxVXKHF")]
    pub order_id: String,
    ///denotes the status of the fulfillment... can be one of PARTIAL, COMPLETE, REPLACEMENT, CANCELED
    #[schema(value_type = Option<FulfillmentStatus>, example = "COMPLETE")]
    pub fulfillment_status: Option<FulfillmentStatus>,
    ///contains details of the fulfillment
    #[schema(value_type = Vec<Fulfillments>)]
    pub fulfillments: Vec<Fulfillments>,
    //name of the tracking Company
    #[schema(max_length = 255, example = "fedex")]
    pub tracking_company: Option<String>,
    //tracking ID of the product
    #[schema(example = r#"["track_8327446667", "track_8327446668"]"#)]
    pub tracking_numbers: Option<Vec<String>>,
    //tracking_url for tracking the product
    pub tracking_urls: Option<Vec<String>>,
    // The name of the Shipper.
    pub carrier: Option<String>,
    // Fulfillment method for the shipment.
    pub fulfillment_method: Option<String>,
    // Statuses to indicate shipment state.
    pub shipment_status: Option<String>,
    // The date and time items are ready to be shipped.
    pub shipped_at: Option<String>,
}

#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "snake_case")]
pub struct Fulfillments {
    ///shipment_id of the shipped items
    #[schema(max_length = 255, example = "ship_101")]
    pub shipment_id: String,
    ///products sent in the shipment
    #[schema(value_type = Option<Vec<Product>>)]
    pub products: Option<Vec<Product>>,
    ///destination address of the shipment
    #[schema(value_type = Destination)]
    pub destination: Destination,
}

#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
#[serde(untagged)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "snake_case")]
pub enum FulfillmentStatus {
    PARTIAL,
    COMPLETE,
    REPLACEMENT,
    CANCELED,
}

#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "snake_case")]
pub struct Product {
    pub item_name: String,
    pub item_quantity: i64,
    pub item_id: String,
}

#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "snake_case")]
pub struct Destination {
    pub full_name: Secret<String>,
    pub organization: Option<String>,
    pub email: Option<Email>,
    pub address: Address,
}

#[derive(Debug, Serialize, Eq, PartialEq, Deserialize, Clone)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "snake_case")]
pub struct Address {
    pub street_address: Secret<String>,
    pub unit: Option<Secret<String>>,
    pub postal_code: Secret<String>,
    pub city: String,
    pub province_code: Secret<String>,
    pub country_code: common_enums::CountryAlpha2,
}

impl ApiEventMetric for FrmFulfillmentRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::FraudCheck)
    }
}


// File: crates/hyperswitch_domain_models/src/router_request_types/unified_authentication_service.rs

use api_models::payments::DeviceChannel;
use common_enums::MerchantCategoryCode;
use common_types::payments::MerchantCountryCode;
use common_utils::types::MinorUnit;
use masking::Secret;

use crate::address::Address;

#[derive(Clone, Debug)]
pub struct UasPreAuthenticationRequestData {
    pub service_details: Option<CtpServiceDetails>,
    pub transaction_details: Option<TransactionDetails>,
    pub payment_details: Option<PaymentDetails>,
    pub authentication_info: Option<AuthenticationInfo>,
    pub merchant_details: Option<MerchantDetails>,
    pub billing_address: Option<Address>,
    pub acquirer_bin: Option<String>,
    pub acquirer_merchant_id: Option<String>,
}

#[derive(Debug, Clone)]
pub struct MerchantDetails {
    pub merchant_id: Option<String>,
    pub merchant_name: Option<String>,
    pub merchant_category_code: Option<MerchantCategoryCode>,
    pub merchant_country_code: Option<MerchantCountryCode>,
    pub endpoint_prefix: Option<String>,
    pub three_ds_requestor_url: Option<String>,
    pub three_ds_requestor_id: Option<String>,
    pub three_ds_requestor_name: Option<String>,
    pub notification_url: Option<url::Url>,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize)]
pub struct AuthenticationInfo {
    pub authentication_type: Option<String>,
    pub authentication_reasons: Option<Vec<String>>,
    pub consent_received: bool,
    pub is_authenticated: bool,
    pub locale: Option<String>,
    pub supported_card_brands: Option<String>,
    pub encrypted_payload: Option<Secret<String>>,
}
#[derive(Clone, Debug)]
pub struct UasAuthenticationRequestData {
    pub browser_details: Option<super::BrowserInformation>,
    pub transaction_details: TransactionDetails,
    pub pre_authentication_data: super::authentication::PreAuthenticationData,
    pub return_url: Option<String>,
    pub sdk_information: Option<api_models::payments::SdkInformation>,
    pub email: Option<common_utils::pii::Email>,
    pub threeds_method_comp_ind: api_models::payments::ThreeDsCompletionIndicator,
    pub webhook_url: String,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct CtpServiceDetails {
    pub service_session_ids: Option<ServiceSessionIds>,
    pub payment_details: Option<PaymentDetails>,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct PaymentDetails {
    pub pan: cards::CardNumber,
    pub digital_card_id: Option<String>,
    pub payment_data_type: Option<common_enums::PaymentMethodType>,
    pub encrypted_src_card_details: Option<String>,
    pub card_expiry_month: Secret<String>,
    pub card_expiry_year: Secret<String>,
    pub cardholder_name: Option<Secret<String>>,
    pub card_token_number: Option<Secret<String>>,
    pub account_type: Option<common_enums::PaymentMethodType>,
    pub card_cvc: Option<Secret<String>>,
}

#[derive(Clone, serde::Deserialize, Debug, serde::Serialize)]
pub struct ServiceSessionIds {
    pub correlation_id: Option<String>,
    pub merchant_transaction_id: Option<String>,
    pub x_src_flow_id: Option<String>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
pub struct TransactionDetails {
    pub amount: Option<MinorUnit>,
    pub currency: Option<common_enums::Currency>,
    pub device_channel: Option<DeviceChannel>,
    pub message_category: Option<super::authentication::MessageCategory>,
    pub force_3ds_challenge: Option<bool>,
    pub psd2_sca_exemption_type: Option<common_enums::ScaExemptionType>,
}

#[derive(Clone, Debug)]
pub struct UasPostAuthenticationRequestData {
    pub threeds_server_transaction_id: Option<String>,
}

#[derive(Debug, Clone)]
pub enum UasAuthenticationResponseData {
    PreAuthentication {
        authentication_details: PreAuthenticationDetails,
    },
    Authentication {
        authentication_details: AuthenticationDetails,
    },
    PostAuthentication {
        authentication_details: PostAuthenticationDetails,
    },
    Confirmation {},
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct PreAuthenticationDetails {
    pub threeds_server_transaction_id: Option<String>,
    pub maximum_supported_3ds_version: Option<common_utils::types::SemanticVersion>,
    pub connector_authentication_id: Option<String>,
    pub three_ds_method_data: Option<String>,
    pub three_ds_method_url: Option<String>,
    pub message_version: Option<common_utils::types::SemanticVersion>,
    pub connector_metadata: Option<serde_json::Value>,
    pub directory_server_id: Option<String>,
}

#[derive(Debug, Clone)]
pub struct AuthenticationDetails {
    pub authn_flow_type: super::authentication::AuthNFlowType,
    pub authentication_value: Option<Secret<String>>,
    pub trans_status: common_enums::TransactionStatus,
    pub connector_metadata: Option<serde_json::Value>,
    pub ds_trans_id: Option<String>,
    pub eci: Option<String>,
    pub challenge_code: Option<String>,
    pub challenge_cancel: Option<String>,
    pub challenge_code_reason: Option<String>,
    pub message_extension: Option<common_utils::pii::SecretSerdeValue>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct PostAuthenticationDetails {
    pub eci: Option<String>,
    pub token_details: Option<TokenDetails>,
    pub dynamic_data_details: Option<DynamicData>,
    pub trans_status: Option<common_enums::TransactionStatus>,
    pub challenge_cancel: Option<String>,
    pub challenge_code_reason: Option<String>,
    pub raw_card_details: Option<RawCardDetails>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct RawCardDetails {
    pub pan: cards::CardNumber,
    pub expiration_month: Secret<String>,
    pub expiration_year: Secret<String>,
    pub card_security_code: Option<Secret<String>>,
    pub payment_account_reference: Option<String>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct TokenDetails {
    pub payment_token: cards::CardNumber,
    pub payment_account_reference: String,
    pub token_expiration_month: Secret<String>,
    pub token_expiration_year: Secret<String>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct DynamicData {
    pub dynamic_data_value: Option<Secret<String>>,
    pub dynamic_data_type: Option<String>,
    pub ds_trans_id: Option<String>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct UasConfirmationRequestData {
    pub x_src_flow_id: Option<String>,
    pub transaction_amount: MinorUnit,
    pub transaction_currency: common_enums::Currency,
    // Type of event associated with the checkout. Valid values are: - 01 - Authorise - 02 - Capture - 03 - Refund - 04 - Cancel - 05 - Fraud - 06 - Chargeback - 07 - Other
    pub checkout_event_type: Option<String>,
    pub checkout_event_status: Option<String>,
    pub confirmation_status: Option<String>,
    pub confirmation_reason: Option<String>,
    pub confirmation_timestamp: Option<String>,
    // Authorisation code associated with an approved transaction.
    pub network_authorization_code: Option<String>,
    // The unique authorisation related tracing value assigned by a Payment Network and provided in an authorisation response. Required only when checkoutEventType=01. If checkoutEventType=01 and the value of networkTransactionIdentifier is unknown, please pass UNAVLB
    pub network_transaction_identifier: Option<String>,
    pub correlation_id: Option<String>,
    pub merchant_transaction_id: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ThreeDsMetaData {
    pub merchant_category_code: Option<MerchantCategoryCode>,
    pub merchant_country_code: Option<MerchantCountryCode>,
    pub merchant_name: Option<String>,
    pub endpoint_prefix: Option<String>,
    pub three_ds_requestor_name: Option<String>,
    pub three_ds_requestor_id: Option<String>,
    pub merchant_configuration_id: Option<String>,
}

#[cfg(feature = "v1")]
impl From<PostAuthenticationDetails>
    for Option<api_models::authentication::AuthenticationPaymentMethodDataResponse>
{
    fn from(item: PostAuthenticationDetails) -> Self {
        match (item.raw_card_details, item.token_details) {
            (Some(card_data), _) => Some(
                api_models::authentication::AuthenticationPaymentMethodDataResponse::CardData {
                    card_expiry_year: Some(card_data.expiration_year),
                    card_expiry_month: Some(card_data.expiration_month),
                },
            ),
            (None, Some(network_token_data)) => {
                Some(
                    api_models::authentication::AuthenticationPaymentMethodDataResponse::NetworkTokenData {
                        network_token_expiry_year: Some(network_token_data.token_expiration_year),
                        network_token_expiry_month: Some(network_token_data.token_expiration_month),
                    },
                )
            }
            (None, None) => None,
        }
    }
}


// File: crates/hyperswitch_domain_models/src/router_request_types/revenue_recovery.rs

use common_enums::enums;

use crate::connector_endpoints;

#[derive(Debug, Clone)]
pub struct BillingConnectorPaymentsSyncRequest {
    /// unique id for making billing connector psync call
    pub billing_connector_psync_id: String,
    /// connector params of the connector
    pub connector_params: connector_endpoints::ConnectorParams,
}

#[derive(Debug, Clone)]
pub struct InvoiceRecordBackRequest {
    pub merchant_reference_id: common_utils::id_type::PaymentReferenceId,
    pub amount: common_utils::types::MinorUnit,
    pub currency: enums::Currency,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub attempt_status: common_enums::AttemptStatus,
    pub connector_transaction_id: Option<common_utils::types::ConnectorTransactionId>,
    pub connector_params: connector_endpoints::ConnectorParams,
}

#[derive(Debug, Clone)]
pub struct BillingConnectorInvoiceSyncRequest {
    /// Invoice id
    pub billing_connector_invoice_id: String,
    /// connector params of the connector
    pub connector_params: connector_endpoints::ConnectorParams,
}


// File: crates/hyperswitch_domain_models/src/router_request_types/authentication.rs

use common_utils::{ext_traits::OptionExt, pii::Email};
use error_stack::{Report, ResultExt};
use serde::{Deserialize, Serialize};

use crate::{
    address,
    errors::api_error_response::ApiErrorResponse,
    payment_method_data::{Card, PaymentMethodData},
    router_request_types::BrowserInformation,
};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ChallengeParams {
    pub acs_url: Option<url::Url>,
    pub challenge_request: Option<String>,
    pub challenge_request_key: Option<String>,
    pub acs_reference_number: Option<String>,
    pub acs_trans_id: Option<String>,
    pub three_dsserver_trans_id: Option<String>,
    pub acs_signed_content: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthNFlowType {
    Challenge(Box<ChallengeParams>),
    Frictionless,
}

impl AuthNFlowType {
    pub fn get_acs_url(&self) -> Option<String> {
        if let Self::Challenge(challenge_params) = self {
            challenge_params.acs_url.as_ref().map(ToString::to_string)
        } else {
            None
        }
    }
    pub fn get_challenge_request(&self) -> Option<String> {
        if let Self::Challenge(challenge_params) = self {
            challenge_params.challenge_request.clone()
        } else {
            None
        }
    }
    pub fn get_challenge_request_key(&self) -> Option<String> {
        if let Self::Challenge(challenge_params) = self {
            challenge_params.challenge_request_key.clone()
        } else {
            None
        }
    }
    pub fn get_acs_reference_number(&self) -> Option<String> {
        if let Self::Challenge(challenge_params) = self {
            challenge_params.acs_reference_number.clone()
        } else {
            None
        }
    }
    pub fn get_acs_trans_id(&self) -> Option<String> {
        if let Self::Challenge(challenge_params) = self {
            challenge_params.acs_trans_id.clone()
        } else {
            None
        }
    }
    pub fn get_acs_signed_content(&self) -> Option<String> {
        if let Self::Challenge(challenge_params) = self {
            challenge_params.acs_signed_content.clone()
        } else {
            None
        }
    }
    pub fn get_decoupled_authentication_type(&self) -> common_enums::DecoupledAuthenticationType {
        match self {
            Self::Challenge(_) => common_enums::DecoupledAuthenticationType::Challenge,
            Self::Frictionless => common_enums::DecoupledAuthenticationType::Frictionless,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct MessageExtensionAttribute {
    pub id: String,
    pub name: String,
    pub criticality_indicator: bool,
    pub data: serde_json::Value,
}

#[derive(Clone, Default, Debug)]
pub struct PreAuthNRequestData {
    // card data
    pub card: Card,
}

#[derive(Clone, Debug)]
pub struct ConnectorAuthenticationRequestData {
    pub payment_method_data: PaymentMethodData,
    pub billing_address: address::Address,
    pub shipping_address: Option<address::Address>,
    pub browser_details: Option<BrowserInformation>,
    pub amount: Option<i64>,
    pub currency: Option<common_enums::Currency>,
    pub message_category: MessageCategory,
    pub device_channel: api_models::payments::DeviceChannel,
    pub pre_authentication_data: PreAuthenticationData,
    pub return_url: Option<String>,
    pub sdk_information: Option<api_models::payments::SdkInformation>,
    pub email: Option<Email>,
    pub threeds_method_comp_ind: api_models::payments::ThreeDsCompletionIndicator,
    pub three_ds_requestor_url: String,
    pub webhook_url: String,
    pub force_3ds_challenge: bool,
}

#[derive(Clone, serde::Deserialize, Debug, serde::Serialize, PartialEq, Eq)]
pub enum MessageCategory {
    Payment,
    NonPayment,
}

#[derive(Clone, Debug)]
pub struct ConnectorPostAuthenticationRequestData {
    pub threeds_server_transaction_id: String,
}

#[derive(Clone, Debug)]
pub struct PreAuthenticationData {
    pub threeds_server_transaction_id: String,
    pub message_version: common_utils::types::SemanticVersion,
    pub acquirer_bin: Option<String>,
    pub acquirer_merchant_id: Option<String>,
    pub acquirer_country_code: Option<String>,
    pub connector_metadata: Option<serde_json::Value>,
}

impl TryFrom<&diesel_models::authentication::Authentication> for PreAuthenticationData {
    type Error = Report<ApiErrorResponse>;

    fn try_from(
        authentication: &diesel_models::authentication::Authentication,
    ) -> Result<Self, Self::Error> {
        let error_message = ApiErrorResponse::UnprocessableEntity { message: "Pre Authentication must be completed successfully before Authentication can be performed".to_string() };
        let threeds_server_transaction_id = authentication
            .threeds_server_transaction_id
            .clone()
            .get_required_value("threeds_server_transaction_id")
            .change_context(error_message.clone())?;
        let message_version = authentication
            .message_version
            .clone()
            .get_required_value("message_version")
            .change_context(error_message)?;
        Ok(Self {
            threeds_server_transaction_id,
            message_version,
            acquirer_bin: authentication.acquirer_bin.clone(),
            acquirer_merchant_id: authentication.acquirer_merchant_id.clone(),
            connector_metadata: authentication.connector_metadata.clone(),
            acquirer_country_code: authentication.acquirer_country_code.clone(),
        })
    }
}

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct ThreeDsMethodData {
    pub three_ds_method_data_submission: bool,
    pub three_ds_method_data: String,
    pub three_ds_method_url: Option<String>,
}
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct AcquirerDetails {
    pub acquirer_bin: String,
    pub acquirer_merchant_id: String,
    pub acquirer_country_code: Option<String>,
}

#[derive(Clone, Debug, Deserialize)]
pub struct ExternalThreeDSConnectorMetadata {
    pub pull_mechanism_for_external_3ds_enabled: Option<bool>,
}

#[derive(Clone, Debug)]
pub struct AuthenticationStore {
    pub cavv: Option<masking::Secret<String>>,
    pub authentication: diesel_models::authentication::Authentication,
}


// File: crates/hyperswitch_domain_models/src/router_response_types/subscriptions.rs

use common_enums::Currency;
use common_utils::{id_type, types::MinorUnit};
use time::PrimitiveDateTime;
#[derive(Debug, Clone)]
pub struct SubscriptionCreateResponse {
    pub subscription_id: id_type::SubscriptionId,
    pub status: SubscriptionStatus,
    pub customer_id: id_type::CustomerId,
    pub currency_code: Currency,
    pub total_amount: MinorUnit,
    pub next_billing_at: Option<PrimitiveDateTime>,
    pub created_at: Option<PrimitiveDateTime>,
    pub invoice_details: Option<SubscriptionInvoiceData>,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct SubscriptionInvoiceData {
    pub id: id_type::InvoiceId,
    pub total: MinorUnit,
    pub currency_code: Currency,
    pub status: Option<common_enums::connector_enums::InvoiceStatus>,
    pub billing_address: Option<api_models::payments::Address>,
}

#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum SubscriptionStatus {
    Pending,
    Trial,
    Active,
    Paused,
    Unpaid,
    Onetime,
    Cancelled,
    Failed,
    Created,
}

impl From<SubscriptionStatus> for common_enums::SubscriptionStatus {
    fn from(status: SubscriptionStatus) -> Self {
        match status {
            SubscriptionStatus::Pending => Self::Pending,
            SubscriptionStatus::Trial => Self::Trial,
            SubscriptionStatus::Active => Self::Active,
            SubscriptionStatus::Paused => Self::Paused,
            SubscriptionStatus::Unpaid => Self::Unpaid,
            SubscriptionStatus::Onetime => Self::Onetime,
            SubscriptionStatus::Cancelled => Self::Cancelled,
            SubscriptionStatus::Failed => Self::Failed,
            SubscriptionStatus::Created => Self::Created,
        }
    }
}

#[derive(Debug, Clone)]
pub struct GetSubscriptionPlansResponse {
    pub list: Vec<SubscriptionPlans>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionPlans {
    pub subscription_provider_plan_id: String,
    pub name: String,
    pub description: Option<String>,
}

#[derive(Debug, Clone)]
pub struct GetSubscriptionPlanPricesResponse {
    pub list: Vec<SubscriptionPlanPrices>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionPauseResponse {
    pub subscription_id: id_type::SubscriptionId,
    pub status: SubscriptionStatus,
    pub paused_at: Option<PrimitiveDateTime>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionResumeResponse {
    pub subscription_id: id_type::SubscriptionId,
    pub status: SubscriptionStatus,
    pub next_billing_at: Option<PrimitiveDateTime>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionCancelResponse {
    pub subscription_id: id_type::SubscriptionId,
    pub status: SubscriptionStatus,
    pub cancelled_at: Option<PrimitiveDateTime>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionPlanPrices {
    pub price_id: String,
    pub plan_id: Option<String>,
    pub amount: MinorUnit,
    pub currency: Currency,
    pub interval: PeriodUnit,
    pub interval_count: i64,
    pub trial_period: Option<i64>,
    pub trial_period_unit: Option<PeriodUnit>,
}

impl From<SubscriptionPlanPrices> for api_models::subscription::SubscriptionPlanPrices {
    fn from(item: SubscriptionPlanPrices) -> Self {
        Self {
            price_id: item.price_id,
            plan_id: item.plan_id,
            amount: item.amount,
            currency: item.currency,
            interval: item.interval.into(),
            interval_count: item.interval_count,
            trial_period: item.trial_period,
            trial_period_unit: item.trial_period_unit.map(Into::into),
        }
    }
}

#[derive(Debug, Clone)]
pub enum PeriodUnit {
    Day,
    Week,
    Month,
    Year,
}

impl From<PeriodUnit> for api_models::subscription::PeriodUnit {
    fn from(unit: PeriodUnit) -> Self {
        match unit {
            PeriodUnit::Day => Self::Day,
            PeriodUnit::Week => Self::Week,
            PeriodUnit::Month => Self::Month,
            PeriodUnit::Year => Self::Year,
        }
    }
}

#[derive(Debug, Clone)]
pub struct GetSubscriptionEstimateResponse {
    pub sub_total: MinorUnit,
    pub total: MinorUnit,
    pub credits_applied: Option<MinorUnit>,
    pub amount_paid: Option<MinorUnit>,
    pub amount_due: Option<MinorUnit>,
    pub currency: Currency,
    pub next_billing_at: Option<PrimitiveDateTime>,
    pub line_items: Vec<SubscriptionLineItem>,
    pub customer_id: Option<id_type::CustomerId>,
}

impl From<GetSubscriptionEstimateResponse>
    for api_models::subscription::EstimateSubscriptionResponse
{
    fn from(value: GetSubscriptionEstimateResponse) -> Self {
        Self {
            amount: value.total,
            currency: value.currency,
            plan_id: None,
            item_price_id: None,
            coupon_code: None,
            customer_id: value.customer_id,
            line_items: value
                .line_items
                .into_iter()
                .map(api_models::subscription::SubscriptionLineItem::from)
                .collect(),
        }
    }
}
#[derive(Debug, Clone)]
pub struct SubscriptionLineItem {
    pub item_id: String,
    pub item_type: String,
    pub description: String,
    pub amount: MinorUnit,
    pub currency: Currency,
    pub unit_amount: Option<MinorUnit>,
    pub quantity: i64,
    pub pricing_model: Option<String>,
}

impl From<SubscriptionLineItem> for api_models::subscription::SubscriptionLineItem {
    fn from(value: SubscriptionLineItem) -> Self {
        Self {
            item_id: value.item_id,
            description: value.description,
            item_type: value.item_type,
            amount: value.amount,
            currency: value.currency,
            quantity: value.quantity,
        }
    }
}


// File: crates/hyperswitch_domain_models/src/router_response_types/fraud_check.rs

use serde::Serialize;

use crate::router_response_types::ResponseId;

#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub enum FraudCheckResponseData {
    TransactionResponse {
        resource_id: ResponseId,
        status: diesel_models::enums::FraudCheckStatus,
        connector_metadata: Option<serde_json::Value>,
        reason: Option<serde_json::Value>,
        score: Option<i32>,
    },
    FulfillmentResponse {
        order_id: String,
        shipment_ids: Vec<String>,
    },
    RecordReturnResponse {
        resource_id: ResponseId,
        connector_metadata: Option<serde_json::Value>,
        return_id: Option<String>,
    },
}

impl common_utils::events::ApiEventMetric for FraudCheckResponseData {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::FraudCheck)
    }
}


// File: crates/hyperswitch_domain_models/src/router_response_types/disputes.rs

#[derive(Default, Clone, Debug)]
pub struct AcceptDisputeResponse {
    pub dispute_status: api_models::enums::DisputeStatus,
    pub connector_status: Option<String>,
}

#[derive(Default, Clone, Debug)]
pub struct SubmitEvidenceResponse {
    pub dispute_status: api_models::enums::DisputeStatus,
    pub connector_status: Option<String>,
}

#[derive(Default, Debug, Clone)]
pub struct DefendDisputeResponse {
    pub dispute_status: api_models::enums::DisputeStatus,
    pub connector_status: Option<String>,
}

pub struct FileInfo {
    pub file_data: Option<Vec<u8>>,
    pub provider_file_id: Option<String>,
    pub file_type: Option<String>,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub struct DisputeSyncResponse {
    pub object_reference_id: api_models::webhooks::ObjectReferenceId,
    pub amount: common_utils::types::StringMinorUnit,
    pub currency: common_enums::enums::Currency,
    pub dispute_stage: common_enums::enums::DisputeStage,
    pub dispute_status: api_models::enums::DisputeStatus,
    pub connector_status: String,
    pub connector_dispute_id: String,
    pub connector_reason: Option<String>,
    pub connector_reason_code: Option<String>,
    pub challenge_required_by: Option<time::PrimitiveDateTime>,
    pub created_at: Option<time::PrimitiveDateTime>,
    pub updated_at: Option<time::PrimitiveDateTime>,
}

pub type FetchDisputesResponse = Vec<DisputeSyncResponse>;


// File: crates/hyperswitch_domain_models/src/router_response_types/revenue_recovery.rs

use common_utils::types::MinorUnit;
use time::PrimitiveDateTime;
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct BillingConnectorPaymentsSyncResponse {
    /// transaction amount against invoice, accepted in minor unit.
    pub amount: MinorUnit,
    /// currency of the transaction
    pub currency: common_enums::enums::Currency,
    /// merchant reference id at billing connector. ex: invoice_id
    pub merchant_reference_id: common_utils::id_type::PaymentReferenceId,
    /// transaction id reference at payment connector
    pub connector_transaction_id: Option<common_utils::types::ConnectorTransactionId>,
    /// error code sent by billing connector.
    pub error_code: Option<String>,
    /// error message sent by billing connector.
    pub error_message: Option<String>,
    /// mandate token at payment processor end.
    pub processor_payment_method_token: String,
    /// customer id at payment connector for which mandate is attached.
    pub connector_customer_id: String,
    /// Payment gateway identifier id at billing processor.
    pub connector_account_reference_id: String,
    /// timestamp at which transaction has been created at billing connector
    pub transaction_created_at: Option<PrimitiveDateTime>,
    /// transaction status at billing connector equivalent to payment attempt status.
    pub status: common_enums::enums::AttemptStatus,
    /// payment method of payment attempt.
    pub payment_method_type: common_enums::enums::PaymentMethod,
    /// payment method sub type of the payment attempt.
    pub payment_method_sub_type: common_enums::enums::PaymentMethodType,
    /// stripe specific id used to validate duplicate attempts.
    pub charge_id: Option<String>,
    /// card information
    pub card_info: api_models::payments::AdditionalCardInfo,
}

#[derive(Debug, Clone)]
pub struct InvoiceRecordBackResponse {
    pub merchant_reference_id: common_utils::id_type::PaymentReferenceId,
}

#[derive(Debug, Clone)]
pub struct BillingConnectorInvoiceSyncResponse {
    /// transaction amount against invoice, accepted in minor unit.
    pub amount: MinorUnit,
    /// currency of the transaction
    pub currency: common_enums::enums::Currency,
    /// merchant reference id at billing connector. ex: invoice_id
    pub merchant_reference_id: common_utils::id_type::PaymentReferenceId,
    /// No of attempts made against an invoice
    pub retry_count: Option<u16>,
    ///  Billing Address of the customer for Invoice
    pub billing_address: Option<api_models::payments::Address>,
    /// creation time of the invoice
    pub created_at: Option<PrimitiveDateTime>,
    /// Ending time of Invoice
    pub ends_at: Option<PrimitiveDateTime>,
}


// File: crates/hyperswitch_domain_models/src/router_flow_types/dispute.rs

#[derive(Debug, Clone)]
pub struct Accept;
#[derive(Debug, Clone)]
pub struct Evidence;

#[derive(Debug, Clone)]
pub struct Defend;

#[derive(Debug, Clone)]
pub struct Fetch;

#[derive(Debug, Clone)]
pub struct Dsync;


// File: crates/hyperswitch_domain_models/src/router_flow_types/refunds.rs

#[derive(Debug, Clone)]
pub struct Execute;
#[derive(Debug, Clone)]
pub struct RSync;


// File: crates/hyperswitch_domain_models/src/router_flow_types/subscriptions.rs

use common_enums::connector_enums::InvoiceStatus;
#[derive(Debug, Clone)]
pub struct SubscriptionCreate;
#[derive(Debug, Clone)]
pub struct SubscriptionPause;
#[derive(Debug, Clone)]
pub struct SubscriptionResume;
#[derive(Debug, Clone)]
pub struct SubscriptionCancel;
#[derive(Debug, Clone)]
pub struct GetSubscriptionPlans;

#[derive(Debug, Clone)]
pub struct GetSubscriptionPlanPrices;

#[derive(Debug, Clone)]
pub struct GetSubscriptionEstimate;

/// Generic structure for subscription MIT (Merchant Initiated Transaction) payment data
#[derive(Debug, Clone)]
pub struct SubscriptionMitPaymentData {
    pub invoice_id: common_utils::id_type::InvoiceId,
    pub amount_due: common_utils::types::MinorUnit,
    pub currency_code: common_enums::enums::Currency,
    pub status: Option<InvoiceStatus>,
    pub customer_id: common_utils::id_type::CustomerId,
    pub subscription_id: common_utils::id_type::SubscriptionId,
    pub first_invoice: bool,
}


// File: crates/hyperswitch_domain_models/src/router_flow_types/payouts.rs

#[derive(Debug, Clone)]
pub struct PoCancel;

#[derive(Debug, Clone)]
pub struct PoCreate;

#[derive(Debug, Clone)]
pub struct PoEligibility;

#[derive(Debug, Clone)]
pub struct PoFulfill;

#[derive(Debug, Clone)]
pub struct PoQuote;

#[derive(Debug, Clone)]
pub struct PoRecipient;

#[derive(Debug, Clone)]
pub struct PoRecipientAccount;

#[derive(Debug, Clone)]
pub struct PoSync;


// File: crates/hyperswitch_domain_models/src/router_flow_types/fraud_check.rs

#[derive(Debug, Clone)]
pub struct Sale;

#[derive(Debug, Clone)]
pub struct Checkout;

#[derive(Debug, Clone)]
pub struct Transaction;

#[derive(Debug, Clone)]
pub struct Fulfillment;

#[derive(Debug, Clone)]
pub struct RecordReturn;


// File: crates/hyperswitch_domain_models/src/router_flow_types/access_token_auth.rs

#[derive(Clone, Debug)]
pub struct AccessTokenAuthentication;

#[derive(Clone, Debug)]
pub struct AccessTokenAuth;


// File: crates/hyperswitch_domain_models/src/router_flow_types/payments.rs

// Core related api layer.
#[derive(Debug, Clone)]
pub struct Authorize;

#[derive(Debug, Clone)]
pub struct AuthorizeSessionToken;

#[derive(Debug, Clone)]
pub struct CompleteAuthorize;

#[derive(Debug, Clone)]
pub struct Approve;

// Used in gift cards balance check
#[derive(Debug, Clone)]
pub struct Balance;

#[derive(Debug, Clone)]
pub struct InitPayment;

#[derive(Debug, Clone)]
pub struct Capture;

#[derive(Debug, Clone)]
pub struct PSync;
#[derive(Debug, Clone)]
pub struct Void;

#[derive(Debug, Clone)]
pub struct PostCaptureVoid;

#[derive(Debug, Clone)]
pub struct Reject;

#[derive(Debug, Clone)]
pub struct Session;

#[derive(Debug, Clone)]
pub struct PaymentMethodToken;

#[derive(Debug, Clone)]
pub struct CreateConnectorCustomer;

#[derive(Debug, Clone)]
pub struct SetupMandate;

#[derive(Debug, Clone)]
pub struct PreProcessing;

#[derive(Debug, Clone)]
pub struct IncrementalAuthorization;

#[derive(Debug, Clone)]
pub struct ExtendAuthorization;

#[derive(Debug, Clone)]
pub struct PostProcessing;

#[derive(Debug, Clone)]
pub struct CalculateTax;

#[derive(Debug, Clone)]
pub struct SdkSessionUpdate;

#[derive(Debug, Clone)]
pub struct PaymentCreateIntent;

#[derive(Debug, Clone)]
pub struct PaymentGetIntent;

#[derive(Debug, Clone)]
pub struct PaymentUpdateIntent;

#[derive(Debug, Clone)]
pub struct PostSessionTokens;

#[derive(Debug, Clone)]
pub struct RecordAttempt;

#[derive(Debug, Clone)]
pub struct UpdateMetadata;

#[derive(Debug, Clone)]
pub struct CreateOrder;

#[derive(Debug, Clone)]
pub struct PaymentGetListAttempts;

#[derive(Debug, Clone)]
pub struct ExternalVaultProxy;

#[derive(Debug, Clone)]
pub struct GiftCardBalanceCheck;


// File: crates/hyperswitch_domain_models/src/router_flow_types/unified_authentication_service.rs

#[derive(Debug, Clone)]
pub struct PreAuthenticate;

#[derive(Debug, Clone)]
pub struct PostAuthenticate;

#[derive(Debug, Clone)]
pub struct AuthenticationConfirmation;

#[derive(Debug, Clone)]
pub struct Authenticate;


// File: crates/hyperswitch_domain_models/src/router_flow_types/files.rs

#[derive(Debug, Clone)]
pub struct Retrieve;

#[derive(Debug, Clone)]
pub struct Upload;


// File: crates/hyperswitch_domain_models/src/router_flow_types/mandate_revoke.rs

#[derive(Clone, Debug)]
pub struct MandateRevoke;


// File: crates/hyperswitch_domain_models/src/router_flow_types/webhooks.rs

use serde::Serialize;

#[derive(Clone, Debug)]
pub struct VerifyWebhookSource;

#[derive(Debug, Clone, Serialize)]
pub struct ConnectorMandateDetails {
    pub connector_mandate_id: masking::Secret<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct ConnectorNetworkTxnId(masking::Secret<String>);

impl ConnectorNetworkTxnId {
    pub fn new(txn_id: masking::Secret<String>) -> Self {
        Self(txn_id)
    }
    pub fn get_id(&self) -> &masking::Secret<String> {
        &self.0
    }
}


// File: crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs

#[derive(Debug, Clone)]
pub struct BillingConnectorPaymentsSync;
#[derive(Debug, Clone)]
pub struct InvoiceRecordBack;

#[derive(Debug, Clone)]
pub struct BillingConnectorInvoiceSync;


// File: crates/hyperswitch_domain_models/src/router_flow_types/authentication.rs

#[derive(Debug, Clone)]
pub struct PreAuthentication;

#[derive(Debug, Clone)]
pub struct PreAuthenticationVersionCall;

#[derive(Debug, Clone)]
pub struct Authentication;

#[derive(Debug, Clone)]
pub struct PostAuthentication;


// File: crates/hyperswitch_domain_models/src/router_flow_types/vault.rs

#[derive(Debug, Clone)]
pub struct ExternalVaultInsertFlow;

#[derive(Debug, Clone)]
pub struct ExternalVaultRetrieveFlow;

#[derive(Debug, Clone)]
pub struct ExternalVaultDeleteFlow;

#[derive(Debug, Clone)]
pub struct ExternalVaultCreateFlow;


// File: crates/hyperswitch_domain_models/src/router_data_v2/flow_common_types.rs

use common_utils::{pii, types::MinorUnit};

use crate::{
    payment_address::PaymentAddress,
    payment_method_data::ApplePayFlow,
    router_data::{
        AccessToken, ConnectorResponseData, PaymentMethodBalance, PaymentMethodToken,
        RecurringMandatePaymentData,
    },
};

#[derive(Debug, Clone)]
pub struct PaymentFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub connector_customer: Option<String>,
    pub connector: String,
    pub payment_id: String,
    pub attempt_id: String,
    pub status: common_enums::AttemptStatus,
    pub payment_method: common_enums::PaymentMethod,
    pub description: Option<String>,
    pub address: PaymentAddress,
    pub auth_type: common_enums::AuthenticationType,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub amount_captured: Option<i64>,
    // minor amount for amount framework
    pub minor_amount_captured: Option<MinorUnit>,
    pub access_token: Option<AccessToken>,
    pub session_token: Option<String>,
    pub reference_id: Option<String>,
    pub payment_method_token: Option<PaymentMethodToken>,
    pub recurring_mandate_payment_data: Option<RecurringMandatePaymentData>,
    pub preprocessing_id: Option<String>,
    /// This is the balance amount for gift cards or voucher
    pub payment_method_balance: Option<PaymentMethodBalance>,

    ///for switching between two different versions of the same connector
    pub connector_api_version: Option<String>,
    /// Contains a reference ID that should be sent in the connector request
    pub connector_request_reference_id: String,

    pub test_mode: Option<bool>,
    pub connector_http_status_code: Option<u16>,
    pub external_latency: Option<u128>,
    /// Contains apple pay flow type simplified or manual
    pub apple_pay_flow: Option<ApplePayFlow>,

    /// This field is used to store various data regarding the response from connector
    pub connector_response: Option<ConnectorResponseData>,
    pub payment_method_status: Option<common_enums::PaymentMethodStatus>,
}

#[derive(Debug, Clone)]
pub struct RefundFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub payment_id: String,
    pub attempt_id: String,
    pub status: common_enums::AttemptStatus,
    pub payment_method: common_enums::PaymentMethod,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub amount_captured: Option<i64>,
    // minor amount for amount framework
    pub minor_amount_captured: Option<MinorUnit>,
    /// Contains a reference ID that should be sent in the connector request
    pub connector_request_reference_id: String,
    pub refund_id: String,
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone)]
pub struct PayoutFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub connector_customer: Option<String>,
    pub address: PaymentAddress,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub connector_wallets_details: Option<pii::SecretSerdeValue>,
    /// Contains a reference ID that should be sent in the connector request
    pub connector_request_reference_id: String,
    pub payout_method_data: Option<api_models::payouts::PayoutMethodData>,
    pub quote_id: Option<String>,
}

#[cfg(feature = "frm")]
#[derive(Debug, Clone)]
pub struct FrmFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: String,
    pub attempt_id: String,
    pub payment_method: common_enums::enums::PaymentMethod,
    pub connector_request_reference_id: String,
    pub auth_type: common_enums::enums::AuthenticationType,
    pub connector_wallets_details: Option<pii::SecretSerdeValue>,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub amount_captured: Option<i64>,
    // minor amount for amount framework
    pub minor_amount_captured: Option<MinorUnit>,
}

#[derive(Debug, Clone)]
pub struct ExternalAuthenticationFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub address: PaymentAddress,
}

#[derive(Debug, Clone)]
pub struct DisputesFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: String,
    pub attempt_id: String,
    pub payment_method: common_enums::enums::PaymentMethod,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub amount_captured: Option<i64>,
    // minor amount for amount framework
    pub minor_amount_captured: Option<MinorUnit>,
    /// Contains a reference ID that should be sent in the connector request
    pub connector_request_reference_id: String,
    pub dispute_id: String,
}

#[derive(Debug, Clone)]
pub struct MandateRevokeFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: common_utils::id_type::CustomerId,
    pub payment_id: Option<String>,
}

#[derive(Debug, Clone)]
pub struct WebhookSourceVerifyData {
    pub merchant_id: common_utils::id_type::MerchantId,
}

#[derive(Debug, Clone)]
pub struct AuthenticationTokenFlowData {}

#[derive(Debug, Clone)]
pub struct AccessTokenFlowData {}

#[derive(Debug, Clone)]
pub struct FilesFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: String,
    pub attempt_id: String,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub connector_request_reference_id: String,
}

#[derive(Debug, Clone)]
pub struct InvoiceRecordBackData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionCustomerData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionCreateData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct GetSubscriptionPlansData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct GetSubscriptionPlanPricesData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct GetSubscriptionEstimateData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionPauseData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionResumeData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct SubscriptionCancelData {
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Clone)]
pub struct UasFlowData {
    pub authenticate_by: String,
    pub source_authentication_id: common_utils::id_type::AuthenticationId,
}

#[derive(Debug, Clone)]
pub struct BillingConnectorPaymentsSyncFlowData;

#[derive(Debug, Clone)]
pub struct BillingConnectorInvoiceSyncFlowData;

#[derive(Debug, Clone)]
pub struct VaultConnectorFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
}

#[derive(Debug, Clone)]
pub struct GiftCardBalanceCheckFlowData;

#[derive(Debug, Clone)]
pub struct ExternalVaultProxyFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub connector_customer: Option<String>,
    pub payment_id: String,
    pub attempt_id: String,
    pub status: common_enums::AttemptStatus,
    pub payment_method: common_enums::PaymentMethod,
    pub description: Option<String>,
    pub address: PaymentAddress,
    pub auth_type: common_enums::AuthenticationType,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub amount_captured: Option<i64>,
    // minor amount for amount framework
    pub minor_amount_captured: Option<MinorUnit>,
    pub access_token: Option<AccessToken>,
    pub session_token: Option<String>,
    pub reference_id: Option<String>,
    pub payment_method_token: Option<PaymentMethodToken>,
    pub recurring_mandate_payment_data: Option<RecurringMandatePaymentData>,
    pub preprocessing_id: Option<String>,
    /// This is the balance amount for gift cards or voucher
    pub payment_method_balance: Option<PaymentMethodBalance>,

    ///for switching between two different versions of the same connector
    pub connector_api_version: Option<String>,
    /// Contains a reference ID that should be sent in the connector request
    pub connector_request_reference_id: String,

    pub test_mode: Option<bool>,
    pub connector_http_status_code: Option<u16>,
    pub external_latency: Option<u128>,
    /// Contains apple pay flow type simplified or manual
    pub apple_pay_flow: Option<ApplePayFlow>,

    /// This field is used to store various data regarding the response from connector
    pub connector_response: Option<ConnectorResponseData>,
    pub payment_method_status: Option<common_enums::PaymentMethodStatus>,
}


// File: crates/hyperswitch_domain_models/src/errors/api_error_response.rs

use api_models::errors::types::Extra;
use common_utils::errors::ErrorSwitch;
use http::StatusCode;

use crate::router_data;

#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorType {
    InvalidRequestError,
    ObjectNotFound,
    RouterError,
    ProcessingError,
    BadGateway,
    ServerNotAvailable,
    DuplicateRequest,
    ValidationError,
    ConnectorError,
    LockTimeout,
}

// CE	Connector Error	Errors originating from connector's end
// HE	Hyperswitch Error	Errors originating from Hyperswitch's end
// IR	Invalid Request Error	Error caused due to invalid fields and values in API request
// WE	Webhook Error	Errors related to Webhooks
#[derive(Debug, Clone, router_derive::ApiError)]
#[error(error_type_enum = ErrorType)]
pub enum ApiErrorResponse {
    #[error(error_type = ErrorType::ConnectorError, code = "CE_00", message = "{code}: {message}", ignore = "status_code")]
    ExternalConnectorError {
        code: String,
        message: String,
        connector: String,
        status_code: u16,
        reason: Option<String>,
    },
    #[error(error_type = ErrorType::ProcessingError, code = "CE_01", message = "Payment failed during authorization with connector. Retry payment")]
    PaymentAuthorizationFailed { data: Option<serde_json::Value> },
    #[error(error_type = ErrorType::ProcessingError, code = "CE_02", message = "Payment failed during authentication with connector. Retry payment")]
    PaymentAuthenticationFailed { data: Option<serde_json::Value> },
    #[error(error_type = ErrorType::ProcessingError, code = "CE_03", message = "Capture attempt failed while processing with connector")]
    PaymentCaptureFailed { data: Option<serde_json::Value> },
    #[error(error_type = ErrorType::ProcessingError, code = "CE_04", message = "The card data is invalid")]
    InvalidCardData { data: Option<serde_json::Value> },
    #[error(error_type = ErrorType::ProcessingError, code = "CE_05", message = "The card has expired")]
    CardExpired { data: Option<serde_json::Value> },
    #[error(error_type = ErrorType::ProcessingError, code = "CE_06", message = "Refund failed while processing with connector. Retry refund")]
    RefundFailed { data: Option<serde_json::Value> },
    #[error(error_type = ErrorType::ProcessingError, code = "CE_07", message = "Verification failed while processing with connector. Retry operation")]
    VerificationFailed { data: Option<serde_json::Value> },
    #[error(error_type = ErrorType::ProcessingError, code = "CE_08", message = "Dispute operation failed while processing with connector. Retry operation")]
    DisputeFailed { data: Option<serde_json::Value> },

    #[error(error_type = ErrorType::LockTimeout, code = "HE_00", message = "Resource is busy. Please try again later.")]
    ResourceBusy,
    #[error(error_type = ErrorType::ServerNotAvailable, code = "HE_00", message = "Something went wrong")]
    InternalServerError,
    #[error(error_type = ErrorType::ServerNotAvailable, code= "HE_00", message = "{component} health check is failing with error: {message}")]
    HealthCheckError {
        component: &'static str,
        message: String,
    },
    #[error(error_type = ErrorType::ValidationError, code = "HE_00", message = "Failed to convert currency to minor unit")]
    CurrencyConversionFailed,
    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "Duplicate refund request. Refund already attempted with the refund ID")]
    DuplicateRefundRequest,
    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "Duplicate mandate request. Mandate already attempted with the Mandate ID")]
    DuplicateMandate,
    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The merchant account with the specified details already exists in our records")]
    DuplicateMerchantAccount,
    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The merchant connector account with the specified profile_id '{profile_id}' and connector_label '{connector_label}' already exists in our records")]
    DuplicateMerchantConnectorAccount {
        profile_id: String,
        connector_label: String,
    },
    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The payment method with the specified details already exists in our records")]
    DuplicatePaymentMethod,
    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The payment with the specified payment_id already exists in our records")]
    DuplicatePayment {
        payment_id: common_utils::id_type::PaymentId,
    },
    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The payout with the specified payout_id '{payout_id:?}' already exists in our records")]
    DuplicatePayout {
        payout_id: common_utils::id_type::PayoutId,
    },
    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The config with the specified key already exists in our records")]
    DuplicateConfig,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Refund does not exist in our records")]
    RefundNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payment Link does not exist in our records")]
    PaymentLinkNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Customer does not exist in our records")]
    CustomerNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Config key does not exist in our records.")]
    ConfigNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payment does not exist in our records")]
    PaymentNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payment method does not exist in our records")]
    PaymentMethodNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Merchant account does not exist in our records")]
    MerchantAccountNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Merchant connector account does not exist in our records")]
    MerchantConnectorAccountNotFound { id: String },
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Business profile with the given id  '{id}' does not exist in our records")]
    ProfileNotFound { id: String },
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Profile acquirer with id '{profile_acquirer_id}' not found for profile '{profile_id}'.")]
    ProfileAcquirerNotFound {
        profile_acquirer_id: String,
        profile_id: String,
    },
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Poll with the given id  '{id}' does not exist in our records")]
    PollNotFound { id: String },
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Resource ID does not exist in our records")]
    ResourceIdNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Mandate does not exist in our records")]
    MandateNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Authentication does not exist in our records")]
    AuthenticationNotFound { id: String },
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Failed to update mandate")]
    MandateUpdateFailed,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "API Key does not exist in our records")]
    ApiKeyNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payout does not exist in our records")]
    PayoutNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Event does not exist in our records")]
    EventNotFound,
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "Invalid mandate id passed from connector")]
    MandateSerializationFailed,
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "Unable to parse the mandate identifier passed from connector")]
    MandateDeserializationFailed,
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "Return URL is not configured and not passed in payments request")]
    ReturnUrlUnavailable,
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "This refund is not possible through Hyperswitch. Please raise the refund through {connector} dashboard")]
    RefundNotPossible { connector: String },
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "Mandate Validation Failed" )]
    MandateValidationFailed { reason: String },
    #[error(error_type= ErrorType::ValidationError, code = "HE_03", message = "The payment has not succeeded yet. Please pass a successful payment to initiate refund")]
    PaymentNotSucceeded,
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "The specified merchant connector account is disabled")]
    MerchantConnectorAccountDisabled,
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "{code}: {message}")]
    PaymentBlockedError {
        code: u16,
        message: String,
        status: String,
        reason: String,
    },
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "File validation failed")]
    FileValidationFailed { reason: String },
    #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "Dispute status validation failed")]
    DisputeStatusValidationFailed { reason: String },
    #[error(error_type= ErrorType::ObjectNotFound, code = "HE_04", message = "Successful payment not found for the given payment id")]
    SuccessfulPaymentNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "The connector provided in the request is incorrect or not available")]
    IncorrectConnectorNameGiven,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "Address does not exist in our records")]
    AddressNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "Dispute does not exist in our records")]
    DisputeNotFound { dispute_id: String },
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "File does not exist in our records")]
    FileNotFound,
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "File not available")]
    FileNotAvailable,
    #[error(error_type = ErrorType::ProcessingError, code = "HE_05", message = "Missing tenant id")]
    MissingTenantId,
    #[error(error_type = ErrorType::ProcessingError, code = "HE_05", message = "Invalid tenant id: {tenant_id}")]
    InvalidTenant { tenant_id: String },
    #[error(error_type = ErrorType::ValidationError, code = "HE_06", message = "Failed to convert amount to {amount_type} type")]
    AmountConversionFailed { amount_type: &'static str },
    #[error(error_type = ErrorType::ServerNotAvailable, code = "IR_00", message = "{message:?}")]
    NotImplemented { message: NotImplementedMessage },
    #[error(
        error_type = ErrorType::InvalidRequestError, code = "IR_01",
        message = "API key not provided or invalid API key used"
    )]
    Unauthorized,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_02", message = "Unrecognized request URL")]
    InvalidRequestUrl,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "The HTTP method is not applicable for this API")]
    InvalidHttpMethod,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "Missing required param: {field_name}")]
    MissingRequiredField { field_name: &'static str },
    #[error(
        error_type = ErrorType::InvalidRequestError, code = "IR_05",
        message = "{field_name} contains invalid data. Expected format is {expected_format}"
    )]
    InvalidDataFormat {
        field_name: String,
        expected_format: String,
    },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_06", message = "{message}")]
    InvalidRequestData { message: String },
    /// Typically used when a field has invalid value, or deserialization of the value contained in a field fails.
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "Invalid value provided: {field_name}")]
    InvalidDataValue { field_name: &'static str },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Client secret was not provided")]
    ClientSecretNotGiven,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Client secret has expired")]
    ClientSecretExpired,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_09", message = "The client_secret provided does not match the client_secret associated with the Payment")]
    ClientSecretInvalid,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_10", message = "Customer has active mandate/subsciption")]
    MandateActive,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_11", message = "Customer has already been redacted")]
    CustomerRedacted,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_12", message = "Reached maximum refund attempts")]
    MaximumRefundCount,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_13", message = "The refund amount exceeds the amount captured")]
    RefundAmountExceedsPaymentAmount,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_14", message = "This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}")]
    PaymentUnexpectedState {
        current_flow: String,
        field_name: String,
        current_value: String,
        states: String,
    },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_15", message = "Invalid Ephemeral Key for the customer")]
    InvalidEphemeralKey,
    /// Typically used when information involving multiple fields or previously provided information doesn't satisfy a condition.
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_16", message = "{message}")]
    PreconditionFailed { message: String },
    #[error(
        error_type = ErrorType::InvalidRequestError, code = "IR_17",
        message = "Access forbidden, invalid JWT token was used"
    )]
    InvalidJwtToken,
    #[error(
        error_type = ErrorType::InvalidRequestError, code = "IR_18",
        message = "{message}",
    )]
    GenericUnauthorized { message: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_19", message = "{message}")]
    NotSupported { message: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_20", message = "{flow} flow not supported by the {connector} connector")]
    FlowNotSupported { flow: String, connector: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_21", message = "Missing required params")]
    MissingRequiredFields { field_names: Vec<&'static str> },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_22", message = "Access forbidden. Not authorized to access this resource {resource}")]
    AccessForbidden { resource: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_23", message = "{message}")]
    FileProviderNotSupported { message: String },
    #[error(
        error_type = ErrorType::ProcessingError, code = "IR_24",
        message = "Invalid {wallet_name} wallet token"
    )]
    InvalidWalletToken { wallet_name: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_25", message = "Cannot delete the default payment method")]
    PaymentMethodDeleteFailed,
    #[error(
        error_type = ErrorType::InvalidRequestError, code = "IR_26",
        message = "Invalid Cookie"
    )]
    InvalidCookie,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_27", message = "Extended card info does not exist")]
    ExtendedCardInfoNotFound,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_28", message = "{message}")]
    CurrencyNotSupported { message: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_29", message = "{message}")]
    UnprocessableEntity { message: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_30", message = "Merchant connector account is configured with invalid {config}")]
    InvalidConnectorConfiguration { config: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_31", message = "Card with the provided iin does not exist")]
    InvalidCardIin,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_32", message = "The provided card IIN length is invalid, please provide an iin with 6 or 8 digits")]
    InvalidCardIinLength,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_33", message = "File not found / valid in the request")]
    MissingFile,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_34", message = "Dispute id not found in the request")]
    MissingDisputeId,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_35", message = "File purpose not found in the request or is invalid")]
    MissingFilePurpose,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_36", message = "File content type not found / valid")]
    MissingFileContentType,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_37", message = "{message}")]
    GenericNotFoundError { message: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_38", message = "{message}")]
    GenericDuplicateError { message: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_39", message = "required payment method is not configured or configured incorrectly for all configured connectors")]
    IncorrectPaymentMethodConfiguration,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_40", message = "{message}")]
    LinkConfigurationError { message: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_41", message = "Payout validation failed")]
    PayoutFailed { data: Option<serde_json::Value> },
    #[error(
        error_type = ErrorType::InvalidRequestError, code = "IR_42",
        message = "Cookies are not found in the request"
    )]
    CookieNotFound,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_43", message = "API does not support platform account operation")]
    PlatformAccountAuthNotSupported,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_44", message = "Invalid platform account operation")]
    InvalidPlatformOperation,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_45", message = "External vault failed during processing with connector")]
    ExternalVaultFailed,
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_46", message = "Field {fields} doesn't match with the ones used during mandate creation")]
    MandatePaymentDataMismatch { fields: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_47", message = "Connector '{connector}' rejected field '{field_name}': length {received_length} exceeds maximum of {max_length}")]
    MaxFieldLengthViolated {
        connector: String,
        field_name: String,
        max_length: usize,
        received_length: usize,
    },
    #[error(error_type = ErrorType::InvalidRequestError, code = "IR_48", message = "The Status token for connector costumer id {resource} is locked by different PaymentIntent ID")]
    InvalidPaymentIdProvided { resource: String },
    #[error(error_type = ErrorType::InvalidRequestError, code = "WE_01", message = "Failed to authenticate the webhook")]
    WebhookAuthenticationFailed,
    #[error(error_type = ErrorType::InvalidRequestError, code = "WE_02", message = "Bad request received in webhook")]
    WebhookBadRequest,
    #[error(error_type = ErrorType::RouterError, code = "WE_03", message = "There was some issue processing the webhook")]
    WebhookProcessingFailure,
    #[error(error_type = ErrorType::ObjectNotFound, code = "WE_04", message = "Webhook resource not found")]
    WebhookResourceNotFound,
    #[error(error_type = ErrorType::InvalidRequestError, code = "WE_05", message = "Unable to process the webhook body")]
    WebhookUnprocessableEntity,
    #[error(error_type = ErrorType::InvalidRequestError, code = "WE_06", message = "Merchant Secret set my merchant for webhook source verification is invalid")]
    WebhookInvalidMerchantSecret,
    #[error(error_type = ErrorType::ServerNotAvailable, code = "IE", message = "{reason} as data mismatched for {field_names}")]
    IntegrityCheckFailed {
        reason: String,
        field_names: String,
        connector_transaction_id: Option<String>,
    },
    #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Tokenization record not found for the given token_id {id}")]
    TokenizationRecordNotFound { id: String },
    #[error(error_type = ErrorType::ConnectorError, code = "CE_00", message = "Subscription operation: {operation} failed with connector")]
    SubscriptionError { operation: String },
}

#[derive(Clone)]
pub enum NotImplementedMessage {
    Reason(String),
    Default,
}

impl std::fmt::Debug for NotImplementedMessage {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Reason(message) => write!(fmt, "{message} is not implemented"),
            Self::Default => {
                write!(
                    fmt,
                    "This API is under development and will be made available soon."
                )
            }
        }
    }
}

impl ::core::fmt::Display for ApiErrorResponse {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            r#"{{"error":{}}}"#,
            serde_json::to_string(self).unwrap_or_else(|_| "API error response".to_string())
        )
    }
}

impl ErrorSwitch<api_models::errors::types::ApiErrorResponse> for ApiErrorResponse {
    fn switch(&self) -> api_models::errors::types::ApiErrorResponse {
        use api_models::errors::types::{ApiError, ApiErrorResponse as AER};

        match self {
            Self::ExternalConnectorError {
                code,
                message,
                connector,
                reason,
                status_code,
            } => AER::ConnectorError(ApiError::new("CE", 0, format!("{code}: {message}"), Some(Extra {connector: Some(connector.clone()), reason: reason.to_owned(), ..Default::default()})), StatusCode::from_u16(*status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)),
            Self::PaymentAuthorizationFailed { data } => {
                AER::BadRequest(ApiError::new("CE", 1, "Payment failed during authorization with connector. Retry payment", Some(Extra { data: data.clone(), ..Default::default()})))
            }
            Self::PaymentAuthenticationFailed { data } => {
                AER::BadRequest(ApiError::new("CE", 2, "Payment failed during authentication with connector. Retry payment", Some(Extra { data: data.clone(), ..Default::default()})))
            }
            Self::PaymentCaptureFailed { data } => {
                AER::BadRequest(ApiError::new("CE", 3, "Capture attempt failed while processing with connector", Some(Extra { data: data.clone(), ..Default::default()})))
            }
            Self::InvalidCardData { data } => AER::BadRequest(ApiError::new("CE", 4, "The card data is invalid", Some(Extra { data: data.clone(), ..Default::default()}))),
            Self::CardExpired { data } => AER::BadRequest(ApiError::new("CE", 5, "The card has expired", Some(Extra { data: data.clone(), ..Default::default()}))),
            Self::RefundFailed { data } => AER::BadRequest(ApiError::new("CE", 6, "Refund failed while processing with connector. Retry refund", Some(Extra { data: data.clone(), ..Default::default()}))),
            Self::VerificationFailed { data } => {
                AER::BadRequest(ApiError::new("CE", 7, "Verification failed while processing with connector. Retry operation", Some(Extra { data: data.clone(), ..Default::default()})))
            },
            Self::DisputeFailed { data } => {
                AER::BadRequest(ApiError::new("CE", 8, "Dispute operation failed while processing with connector. Retry operation", Some(Extra { data: data.clone(), ..Default::default()})))
            }

            Self::ResourceBusy => {
                AER::Unprocessable(ApiError::new("HE", 0, "There was an issue processing the webhook body", None))
            }
            Self::CurrencyConversionFailed => {
                AER::Unprocessable(ApiError::new("HE", 0, "Failed to convert currency to minor unit", None))
            }
            Self::InternalServerError => {
                AER::InternalServerError(ApiError::new("HE", 0, "Something went wrong", None))
            },
            Self::HealthCheckError { message,component } => {
                AER::InternalServerError(ApiError::new("HE",0,format!("{component} health check failed with error: {message}"),None))
            },
            Self::DuplicateRefundRequest => AER::BadRequest(ApiError::new("HE", 1, "Duplicate refund request. Refund already attempted with the refund ID", None)),
            Self::DuplicateMandate => AER::BadRequest(ApiError::new("HE", 1, "Duplicate mandate request. Mandate already attempted with the Mandate ID", None)),
            Self::DuplicateMerchantAccount => AER::BadRequest(ApiError::new("HE", 1, "The merchant account with the specified details already exists in our records", None)),
            Self::DuplicateMerchantConnectorAccount { profile_id, connector_label: connector_name } => {
                AER::BadRequest(ApiError::new("HE", 1, format!("The merchant connector account with the specified profile_id '{profile_id}' and connector_label '{connector_name}' already exists in our records"), None))
            }
            Self::DuplicatePaymentMethod => AER::BadRequest(ApiError::new("HE", 1, "The payment method with the specified details already exists in our records", None)),
            Self::DuplicatePayment { payment_id } => {
                AER::BadRequest(ApiError::new("HE", 1, "The payment with the specified payment_id already exists in our records", Some(Extra {reason: Some(format!("{payment_id:?} already exists")), ..Default::default()})))
            }
            Self::DuplicatePayout { payout_id } => {
                AER::BadRequest(ApiError::new("HE", 1, format!("The payout with the specified payout_id '{payout_id:?}' already exists in our records"), None))
            }
            Self::DuplicateConfig => {
                AER::BadRequest(ApiError::new("HE", 1, "The config with the specified key already exists in our records", None))
            }
            Self::RefundNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Refund does not exist in our records.", None))
            }
            Self::PaymentLinkNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Payment Link does not exist in our records", None))
            }
            Self::CustomerNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Customer does not exist in our records", None))
            }
            Self::ConfigNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Config key does not exist in our records.", None))
            },
            Self::PaymentNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Payment does not exist in our records", None))
            }
            Self::PaymentMethodNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Payment method does not exist in our records", None))
            }
            Self::MerchantAccountNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Merchant account does not exist in our records", None))
            }
            Self::MerchantConnectorAccountNotFound {id } => {
                AER::NotFound(ApiError::new("HE", 2, "Merchant connector account does not exist in our records", Some(Extra {reason: Some(format!("{id} does not exist")), ..Default::default()})))
            }
            Self::ProfileNotFound { id } => {
                AER::NotFound(ApiError::new("HE", 2, format!("Business profile with the given id {id} does not exist"), None))
            }
            Self::ProfileAcquirerNotFound { profile_acquirer_id, profile_id } => {
                AER::NotFound(ApiError::new("HE", 2, format!("Profile acquirer with id '{profile_acquirer_id}' not found for profile '{profile_id}'."), None))
            }
            Self::PollNotFound { .. } => {
                AER::NotFound(ApiError::new("HE", 2, "Poll does not exist in our records", None))
            },
            Self::ResourceIdNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Resource ID does not exist in our records", None))
            }
            Self::MandateNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Mandate does not exist in our records", None))
            }
            Self::AuthenticationNotFound { .. } => {
                AER::NotFound(ApiError::new("HE", 2, "Authentication does not exist in our records", None))
            },
            Self::MandateUpdateFailed => {
                AER::InternalServerError(ApiError::new("HE", 2, "Mandate update failed", None))
            },
            Self::ApiKeyNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "API Key does not exist in our records", None))
            }
            Self::PayoutNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Payout does not exist in our records", None))
            }
            Self::EventNotFound => {
                AER::NotFound(ApiError::new("HE", 2, "Event does not exist in our records", None))
            }
            Self::MandateSerializationFailed | Self::MandateDeserializationFailed => {
                AER::InternalServerError(ApiError::new("HE", 3, "Something went wrong", None))
            },
            Self::ReturnUrlUnavailable => AER::NotFound(ApiError::new("HE", 3, "Return URL is not configured and not passed in payments request", None)),
            Self::RefundNotPossible { connector } => {
                AER::BadRequest(ApiError::new("HE", 3, format!("This refund is not possible through Hyperswitch. Please raise the refund through {connector} dashboard"), None))
            }
            Self::MandateValidationFailed { reason } => {
                AER::BadRequest(ApiError::new("HE", 3, "Mandate Validation Failed", Some(Extra { reason: Some(reason.to_owned()), ..Default::default() })))
            }
            Self::PaymentNotSucceeded => AER::BadRequest(ApiError::new("HE", 3, "The payment has not succeeded yet. Please pass a successful payment to initiate refund", None)),
            Self::MerchantConnectorAccountDisabled => {
                AER::BadRequest(ApiError::new("HE", 3, "The selected merchant connector account is disabled", None))
            }
            Self::PaymentBlockedError {
                message,
                reason,
                ..
            } => AER::DomainError(ApiError::new("HE", 3, message, Some(Extra { reason: Some(reason.clone()), ..Default::default() }))),
            Self::FileValidationFailed { reason } => {
                AER::BadRequest(ApiError::new("HE", 3, format!("File validation failed {reason}"), None))
            }
            Self::DisputeStatusValidationFailed { .. } => {
                AER::BadRequest(ApiError::new("HE", 3, "Dispute status validation failed", None))
            }
            Self::SuccessfulPaymentNotFound => {
                AER::NotFound(ApiError::new("HE", 4, "Successful payment not found for the given payment id", None))
            }
            Self::IncorrectConnectorNameGiven => {
                AER::NotFound(ApiError::new("HE", 4, "The connector provided in the request is incorrect or not available", None))
            }
            Self::AddressNotFound => {
                AER::NotFound(ApiError::new("HE", 4, "Address does not exist in our records", None))
            },
            Self::DisputeNotFound { .. } => {
                AER::NotFound(ApiError::new("HE", 4, "Dispute does not exist in our records", None))
            },
            Self::FileNotFound => {
                AER::NotFound(ApiError::new("HE", 4, "File does not exist in our records", None))
            }
            Self::FileNotAvailable => {
                AER::NotFound(ApiError::new("HE", 4, "File not available", None))
            }
            Self::MissingTenantId => {
                AER::InternalServerError(ApiError::new("HE", 5, "Missing Tenant ID in the request".to_string(), None))
            }
            Self::InvalidTenant { tenant_id }  => {
                AER::InternalServerError(ApiError::new("HE", 5, format!("Invalid Tenant {tenant_id}"), None))
            }
            Self::AmountConversionFailed { amount_type }  => {
                AER::InternalServerError(ApiError::new("HE", 6, format!("Failed to convert amount to {amount_type} type"), None))
            }

            Self::NotImplemented { message } => {
                AER::NotImplemented(ApiError::new("IR", 0, format!("{message:?}"), None))
            }
            Self::Unauthorized => AER::Unauthorized(ApiError::new(
                "IR",
                1,
                "API key not provided or invalid API key used", None
            )),
            Self::InvalidRequestUrl => {
                AER::NotFound(ApiError::new("IR", 2, "Unrecognized request URL", None))
            }
            Self::InvalidHttpMethod => AER::MethodNotAllowed(ApiError::new(
                "IR",
                3,
                "The HTTP method is not applicable for this API", None
            )),
            Self::MissingRequiredField { field_name } => AER::BadRequest(
                ApiError::new("IR", 4, format!("Missing required param: {field_name}"), None),
            ),
            Self::InvalidDataFormat {
                field_name,
                expected_format,
            } => AER::Unprocessable(ApiError::new(
                "IR",
                5,
                format!(
                    "{field_name} contains invalid data. Expected format is {expected_format}"
                ), None
            )),
            Self::InvalidRequestData { message } => {
                AER::Unprocessable(ApiError::new("IR", 6, message.to_string(), None))
            }
            Self::InvalidDataValue { field_name } => AER::BadRequest(ApiError::new(
                "IR",
                7,
                format!("Invalid value provided: {field_name}"), None
            )),
            Self::ClientSecretNotGiven => AER::BadRequest(ApiError::new(
                "IR",
                8,
                "client_secret was not provided", None
            )),
            Self::ClientSecretExpired => AER::BadRequest(ApiError::new(
                "IR",
                8,
                "The provided client_secret has expired", None
            )),
            Self::ClientSecretInvalid => {
                AER::BadRequest(ApiError::new("IR", 9, "The client_secret provided does not match the client_secret associated with the Payment", None))
            }
            Self::MandateActive => {
                AER::BadRequest(ApiError::new("IR", 10, "Customer has active mandate/subsciption", None))
            }
            Self::CustomerRedacted => {
                AER::BadRequest(ApiError::new("IR", 11, "Customer has already been redacted", None))
            }
            Self::MaximumRefundCount => AER::BadRequest(ApiError::new("IR", 12, "Reached maximum refund attempts", None)),
            Self::RefundAmountExceedsPaymentAmount => {
                AER::BadRequest(ApiError::new("IR", 13, "The refund amount exceeds the amount captured", None))
            }
            Self::PaymentUnexpectedState {
                current_flow,
                field_name,
                current_value,
                states,
            } => AER::BadRequest(ApiError::new("IR", 14, format!("This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}"), None)),
            Self::InvalidEphemeralKey => AER::Unauthorized(ApiError::new("IR", 15, "Invalid Ephemeral Key for the customer", None)),
            Self::PreconditionFailed { message } => {
                AER::BadRequest(ApiError::new("IR", 16, message.to_string(), None))
            }
            Self::InvalidJwtToken => AER::Unauthorized(ApiError::new("IR", 17, "Access forbidden, invalid JWT token was used", None)),
            Self::GenericUnauthorized { message } => {
                AER::Unauthorized(ApiError::new("IR", 18, message.to_string(), None))
            },
            Self::NotSupported { message } => {
                AER::BadRequest(ApiError::new("IR", 19, "Payment method type not supported", Some(Extra {reason: Some(message.to_owned()), ..Default::default()})))
            },
            Self::FlowNotSupported { flow, connector } => {
                AER::BadRequest(ApiError::new("IR", 20, format!("{flow} flow not supported"), Some(Extra {connector: Some(connector.to_owned()), ..Default::default()}))) //FIXME: error message
            }
            Self::MissingRequiredFields { field_names } => AER::BadRequest(
                ApiError::new("IR", 21, "Missing required params".to_string(), Some(Extra {data: Some(serde_json::json!(field_names)), ..Default::default() })),
            ),
            Self::AccessForbidden {resource} => {
                AER::ForbiddenCommonResource(ApiError::new("IR", 22, format!("Access forbidden. Not authorized to access this resource {resource}"), None))
            },
            Self::FileProviderNotSupported { message } => {
                AER::BadRequest(ApiError::new("IR", 23, message.to_string(), None))
            },
            Self::InvalidWalletToken { wallet_name} => AER::Unprocessable(ApiError::new(
                "IR",
                24,
                format!("Invalid {wallet_name} wallet token"), None
            )),
            Self::PaymentMethodDeleteFailed => {
                AER::BadRequest(ApiError::new("IR", 25, "Cannot delete the default payment method", None))
            }
            Self::InvalidCookie => {
                AER::BadRequest(ApiError::new("IR", 26, "Invalid Cookie", None))
            }
            Self::ExtendedCardInfoNotFound => {
                AER::NotFound(ApiError::new("IR", 27, "Extended card info does not exist", None))
            }
            Self::CurrencyNotSupported { message } => {
                AER::BadRequest(ApiError::new("IR", 28, message, None))
            }
            Self::UnprocessableEntity {message} => AER::Unprocessable(ApiError::new("IR", 29, message.to_string(), None)),
            Self::InvalidConnectorConfiguration {config} => {
                AER::BadRequest(ApiError::new("IR", 30, format!("Merchant connector account is configured with invalid {config}"), None))
            }
            Self::InvalidCardIin => AER::BadRequest(ApiError::new("IR", 31, "The provided card IIN does not exist", None)),
            Self::InvalidCardIinLength  => AER::BadRequest(ApiError::new("IR", 32, "The provided card IIN length is invalid, please provide an IIN with 6 digits", None)),
            Self::MissingFile => {
                AER::BadRequest(ApiError::new("IR", 33, "File not found in the request", None))
            }
            Self::MissingDisputeId => {
                AER::BadRequest(ApiError::new("IR", 34, "Dispute id not found in the request", None))
            }
            Self::MissingFilePurpose => {
                AER::BadRequest(ApiError::new("IR", 35, "File purpose not found in the request or is invalid", None))
            }
            Self::MissingFileContentType => {
                AER::BadRequest(ApiError::new("IR", 36, "File content type not found", None))
            }
            Self::GenericNotFoundError { message } => {
                AER::NotFound(ApiError::new("IR", 37, message, None))
            },
            Self::GenericDuplicateError { message } => {
                AER::BadRequest(ApiError::new("IR", 38, message, None))
            }
            Self::IncorrectPaymentMethodConfiguration => {
                AER::BadRequest(ApiError::new("IR", 39, "No eligible connector was found for the current payment method configuration", None))
            }
            Self::LinkConfigurationError { message } => {
                AER::BadRequest(ApiError::new("IR", 40, message, None))
            },
            Self::PayoutFailed { data } => {
                AER::BadRequest(ApiError::new("IR", 41, "Payout failed while processing with connector.", Some(Extra { data: data.clone(), ..Default::default()})))
            },
            Self::CookieNotFound => {
                AER::Unauthorized(ApiError::new("IR", 42, "Cookies are not found in the request", None))
            },
            Self::ExternalVaultFailed => {
                AER::BadRequest(ApiError::new("IR", 45, "External Vault failed while processing with connector.", None))
            },
            Self::MandatePaymentDataMismatch { fields} => {
                AER::BadRequest(ApiError::new("IR", 46, format!("Field {fields} doesn't match with the ones used during mandate creation"), Some(Extra {fields: Some(fields.to_owned()), ..Default::default()}))) //FIXME: error message
            }
            Self::MaxFieldLengthViolated { connector, field_name,  max_length, received_length} => {
                AER::BadRequest(ApiError::new("IR", 47, format!("Connector '{connector}' rejected field '{field_name}': length {received_length} exceeds maximum of {max_length}"), Some(Extra {connector: Some(connector.to_string()), ..Default::default()})))
            }
            Self::InvalidPaymentIdProvided {resource} => {
                AER::NotFound(ApiError::new("IR", 48, format!("The Status token for connector costumer id {resource} is locked by different PaymentIntent ID"), None))
            },
            Self::WebhookAuthenticationFailed => {
                AER::Unauthorized(ApiError::new("WE", 1, "Webhook authentication failed", None))
            }
            Self::WebhookBadRequest => {
                AER::BadRequest(ApiError::new("WE", 2, "Bad request body received", None))
            }
            Self::WebhookProcessingFailure => {
                AER::InternalServerError(ApiError::new("WE", 3, "There was an issue processing the webhook", None))
            },
            Self::WebhookResourceNotFound => {
                AER::NotFound(ApiError::new("WE", 4, "Webhook resource was not found", None))
            }
            Self::WebhookUnprocessableEntity => {
                AER::Unprocessable(ApiError::new("WE", 5, "There was an issue processing the webhook body", None))
            },
            Self::WebhookInvalidMerchantSecret => {
                AER::BadRequest(ApiError::new("WE", 6, "Merchant Secret set for webhook source verification is invalid", None))
            }
            Self::IntegrityCheckFailed {
                reason,
                field_names,
                connector_transaction_id
            } => AER::InternalServerError(ApiError::new(
                "IE",
                0,
                format!("{reason} as data mismatched for {field_names}"),
                Some(Extra {
                    connector_transaction_id: connector_transaction_id.to_owned(),
                    ..Default::default()
                })
            )),
            Self::PlatformAccountAuthNotSupported => {
                AER::BadRequest(ApiError::new("IR", 43, "API does not support platform operation", None))
            }
            Self::InvalidPlatformOperation => {
                AER::Unauthorized(ApiError::new("IR", 44, "Invalid platform account operation", None))
            }
            Self::TokenizationRecordNotFound{ id } => {
                AER::NotFound(ApiError::new("HE", 2, format!("Tokenization record not found for the given token_id '{id}' "), None))
            }
            Self::SubscriptionError { operation } => {
                AER::BadRequest(ApiError::new("CE", 9, format!("Subscription operation: {operation} failed with connector"), None))
            }
        }
    }
}

impl actix_web::ResponseError for ApiErrorResponse {
    fn status_code(&self) -> StatusCode {
        ErrorSwitch::<api_models::errors::types::ApiErrorResponse>::switch(self).status_code()
    }

    fn error_response(&self) -> actix_web::HttpResponse {
        ErrorSwitch::<api_models::errors::types::ApiErrorResponse>::switch(self).error_response()
    }
}

impl From<ApiErrorResponse> for router_data::ErrorResponse {
    fn from(error: ApiErrorResponse) -> Self {
        Self {
            code: error.error_code(),
            message: error.error_message(),
            reason: None,
            status_code: match error {
                ApiErrorResponse::ExternalConnectorError { status_code, .. } => status_code,
                _ => 500,
            },
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        }
    }
}