// File: crates/router/src/core/payments/transformers.rs
// Module: router
// Public functions: 25
// Public structs: 1

use std::{fmt::Debug, marker::PhantomData, str::FromStr};

#[cfg(feature = "v2")]
use api_models::enums as api_enums;
use api_models::payments::{
    Address, ConnectorMandateReferenceId, CustomerDetails, CustomerDetailsResponse, FrmMessage,
    MandateIds, NetworkDetails, RequestSurchargeDetails,
};
use common_enums::{Currency, RequestIncrementalAuthorization};
use common_utils::{
    consts::X_HS_LATENCY,
    fp_utils, pii,
    types::{
        self as common_utils_type, AmountConvertor, MinorUnit, StringMajorUnit,
        StringMajorUnitForConnector,
    },
};
use diesel_models::{
    ephemeral_key,
    payment_attempt::{
        ConnectorMandateReferenceId as DieselConnectorMandateReferenceId,
        NetworkDetails as DieselNetworkDetails,
    },
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{payments::payment_intent::CustomerData, router_request_types};
#[cfg(feature = "v2")]
use hyperswitch_domain_models::{
    router_data_v2::{flow_common_types, RouterDataV2},
    ApiModelToDieselModelConvertor,
};
#[cfg(feature = "v2")]
use hyperswitch_interfaces::api::ConnectorSpecifications;
#[cfg(feature = "v2")]
use hyperswitch_interfaces::connector_integration_interface::RouterDataConversion;
use masking::{ExposeInterface, Maskable, Secret};
#[cfg(feature = "v2")]
use masking::{ExposeOptionInterface, PeekInterface};
use router_env::{instrument, tracing};

use super::{flows::Feature, types::AuthenticationData, OperationSessionGetters, PaymentData};
use crate::{
    configs::settings::ConnectorRequestReferenceIdConfig,
    core::{
        errors::{self, RouterResponse, RouterResult},
        payments::{self, helpers},
        utils as core_utils,
    },
    headers::{X_CONNECTOR_HTTP_STATUS_CODE, X_PAYMENT_CONFIRM_SOURCE},
    routes::{metrics, SessionState},
    services::{self, RedirectForm},
    types::{
        self,
        api::{self, ConnectorTransactionId},
        domain, payment_methods as pm_types,
        storage::{self, enums},
        transformers::{ForeignFrom, ForeignInto, ForeignTryFrom},
        MultipleCaptureRequestData,
    },
    utils::{OptionExt, ValueExt},
};

#[cfg(feature = "v2")]
pub async fn construct_router_data_to_update_calculated_tax<'a, F, T>(
    state: &'a SessionState,
    payment_data: PaymentData<F>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &helpers::MerchantConnectorAccountType,
) -> RouterResult<types::RouterData<F, T, types::PaymentsResponseData>>
where
    T: TryFrom<PaymentAdditionalData<'a, F>>,
    types::RouterData<F, T, types::PaymentsResponseData>: Feature<F, T>,
    F: Clone,
    error_stack::Report<errors::ApiErrorResponse>:
        From<<T as TryFrom<PaymentAdditionalData<'a, F>>>::Error>,
{
    todo!()
}

#[cfg(feature = "v1")]
pub async fn construct_router_data_to_update_calculated_tax<'a, F, T>(
    state: &'a SessionState,
    payment_data: PaymentData<F>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &helpers::MerchantConnectorAccountType,
) -> RouterResult<types::RouterData<F, T, types::PaymentsResponseData>>
where
    T: TryFrom<PaymentAdditionalData<'a, F>>,
    types::RouterData<F, T, types::PaymentsResponseData>: Feature<F, T>,
    F: Clone,
    error_stack::Report<errors::ApiErrorResponse>:
        From<<T as TryFrom<PaymentAdditionalData<'a, F>>>::Error>,
{
    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    let test_mode = merchant_connector_account.is_test_mode_on();

    let auth_type: types::ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .parse_value("ConnectorAuthType")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let additional_data = PaymentAdditionalData {
        router_base_url: state.base_url.clone(),
        connector_name: connector_id.to_string(),
        payment_data: payment_data.clone(),
        state,
        customer_data: customer,
    };

    let connector_mandate_request_reference_id = payment_data
        .payment_attempt
        .connector_mandate_detail
        .as_ref()
        .and_then(|detail| detail.get_connector_mandate_request_reference_id());

    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        customer_id: None,
        connector: connector_id.to_owned(),
        payment_id: payment_data
            .payment_attempt
            .payment_id
            .get_string_repr()
            .to_owned(),
        tenant_id: state.tenant.tenant_id.clone(),
        attempt_id: payment_data.payment_attempt.get_id().to_owned(),
        status: payment_data.payment_attempt.status,
        payment_method: diesel_models::enums::PaymentMethod::default(),
        connector_auth_type: auth_type,
        description: None,
        address: payment_data.address.clone(),
        auth_type: payment_data
            .payment_attempt
            .authentication_type
            .unwrap_or_default(),
        connector_meta_data: None,
        connector_wallets_details: None,
        request: T::try_from(additional_data)?,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_status: None,
        payment_method_token: None,
        connector_customer: None,
        recurring_mandate_payment_data: None,
        connector_request_reference_id: core_utils::get_connector_request_reference_id(
            &state.conf,
            merchant_context.get_merchant_account().get_id(),
            &payment_data.payment_intent,
            &payment_data.payment_attempt,
            connector_id,
        )?,
        preprocessing_id: None,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        test_mode,
        payment_method_balance: None,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload: None,
        connector_mandate_request_reference_id,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: payment_data.payment_intent.is_payment_id_from_merchant,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };
    Ok(router_data)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_external_vault_proxy_router_data_v2<'a>(
    state: &'a SessionState,
    merchant_account: &domain::MerchantAccount,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    payment_data: &hyperswitch_domain_models::payments::PaymentConfirmData<api::ExternalVaultProxy>,
    request: types::ExternalVaultProxyPaymentsData,
    connector_request_reference_id: String,
    connector_customer_id: Option<String>,
    customer_id: Option<common_utils::id_type::CustomerId>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<
    RouterDataV2<
        api::ExternalVaultProxy,
        hyperswitch_domain_models::router_data_v2::ExternalVaultProxyFlowData,
        types::ExternalVaultProxyPaymentsData,
        types::PaymentsResponseData,
    >,
> {
    use hyperswitch_domain_models::router_data_v2::{ExternalVaultProxyFlowData, RouterDataV2};

    let auth_type = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let external_vault_proxy_flow_data = ExternalVaultProxyFlowData {
        merchant_id: merchant_account.get_id().clone(),
        customer_id,
        connector_customer: connector_customer_id,
        payment_id: payment_data
            .payment_attempt
            .payment_id
            .get_string_repr()
            .to_owned(),
        attempt_id: payment_data
            .payment_attempt
            .get_id()
            .get_string_repr()
            .to_owned(),
        status: payment_data.payment_attempt.status,
        payment_method: payment_data.payment_attempt.payment_method_type,
        description: payment_data
            .payment_intent
            .description
            .as_ref()
            .map(|description| description.get_string_repr())
            .map(ToOwned::to_owned),
        address: payment_data.payment_address.clone(),
        auth_type: payment_data.payment_attempt.authentication_type,
        connector_meta_data: merchant_connector_account.get_metadata(),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_token: None,
        recurring_mandate_payment_data: None,
        preprocessing_id: payment_data.payment_attempt.preprocessing_step_id.clone(),
        payment_method_balance: None,
        connector_api_version: None,
        connector_request_reference_id,
        test_mode: Some(true),
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        connector_response: None,
        payment_method_status: None,
    };

    let router_data_v2 = RouterDataV2 {
        flow: PhantomData,
        tenant_id: state.tenant.tenant_id.clone(),
        resource_common_data: external_vault_proxy_flow_data,
        connector_auth_type: auth_type,
        request,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
    };

    Ok(router_data_v2)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_payment_router_data_for_authorize<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentConfirmData<api::Authorize>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::PaymentsAuthorizeRouterData> {
    use masking::ExposeOptionInterface;

    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    let auth_type = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    // TODO: Take Globalid and convert to connector reference id
    let customer_id = customer
        .to_owned()
        .map(|customer| common_utils::id_type::CustomerId::try_from(customer.id.clone()))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Invalid global customer generated, not able to convert to reference id",
        )?;

    let connector_customer_id =
        payment_data.get_connector_customer_id(customer.as_ref(), merchant_connector_account);

    let payment_method = payment_data.payment_attempt.payment_method_type;

    let router_base_url = &state.base_url;
    let attempt = &payment_data.payment_attempt;

    let complete_authorize_url = Some(helpers::create_complete_authorize_url(
        router_base_url,
        attempt,
        connector_id,
        None,
    ));

    let webhook_url = match merchant_connector_account {
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(
            merchant_connector_account,
        ) => Some(helpers::create_webhook_url(
            router_base_url,
            &attempt.merchant_id,
            merchant_connector_account.get_id().get_string_repr(),
        )),
        // TODO: Implement for connectors that require a webhook URL to be included in the request payload.
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None,
    };

    let router_return_url = payment_data
        .payment_intent
        .create_finish_redirection_url(
            router_base_url,
            merchant_context
                .get_merchant_account()
                .publishable_key
                .as_ref(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to construct finish redirection url")?
        .to_string();

    let connector_request_reference_id = payment_data
        .payment_attempt
        .connector_request_reference_id
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("connector_request_reference_id not found in payment_attempt")?;

    let email = customer
        .as_ref()
        .and_then(|customer| customer.email.clone())
        .map(pii::Email::from);

    let browser_info = payment_data
        .payment_attempt
        .browser_info
        .clone()
        .map(types::BrowserInformation::from);
    let additional_payment_method_data: Option<api_models::payments::AdditionalPaymentData> =
            payment_data.payment_attempt
                .payment_method_data
                .as_ref().map(|data| data.clone().parse_value("AdditionalPaymentData"))
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to parse AdditionalPaymentData from payment_data.payment_attempt.payment_method_data")?;

    // TODO: few fields are repeated in both routerdata and request
    let request = types::PaymentsAuthorizeData {
        payment_method_data: payment_data
            .payment_method_data
            .get_required_value("payment_method_data")?,
        setup_future_usage: Some(payment_data.payment_intent.setup_future_usage),
        mandate_id: payment_data.mandate_data.clone(),
        off_session: None,
        setup_mandate_details: None,
        confirm: true,
        statement_descriptor_suffix: None,
        statement_descriptor: None,
        capture_method: Some(payment_data.payment_intent.capture_method),
        amount: payment_data
            .payment_attempt
            .amount_details
            .get_net_amount()
            .get_amount_as_i64(),
        minor_amount: payment_data.payment_attempt.amount_details.get_net_amount(),
        order_tax_amount: None,
        currency: payment_data.payment_intent.amount_details.currency,
        browser_info,
        email,
        customer_name: None,
        payment_experience: None,
        order_details: None,
        order_category: None,
        session_token: None,
        enrolled_for_3ds: true,
        related_transaction_id: None,
        payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype),
        router_return_url: Some(router_return_url),
        webhook_url,
        complete_authorize_url,
        customer_id: customer_id.clone(),
        surcharge_details: None,
        request_extended_authorization: None,
        request_incremental_authorization: matches!(
            payment_data
                .payment_intent
                .request_incremental_authorization,
            RequestIncrementalAuthorization::True
        ),
        metadata: payment_data.payment_intent.metadata.expose_option(),
        authentication_data: None,
        customer_acceptance: None,
        split_payments: None,
        merchant_order_reference_id: payment_data
            .payment_intent
            .merchant_reference_id
            .map(|reference_id| reference_id.get_string_repr().to_owned()),
        integrity_object: None,
        shipping_cost: payment_data.payment_intent.amount_details.shipping_cost,
        additional_payment_method_data,
        merchant_account_id: None,
        merchant_config_currency: None,
        connector_testing_data: None,
        order_id: None,
        locale: None,
        payment_channel: None,
        enable_partial_authorization: payment_data.payment_intent.enable_partial_authorization,
        enable_overcapture: None,
        is_stored_credential: None,
    };
    let connector_mandate_request_reference_id = payment_data
        .payment_attempt
        .connector_token_details
        .as_ref()
        .and_then(|detail| detail.get_connector_token_request_reference_id());

    // TODO: evaluate the fields in router data, if they are required or not
    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        tenant_id: state.tenant.tenant_id.clone(),
        // TODO: evaluate why we need customer id at the connector level. We already have connector customer id.
        customer_id,
        connector: connector_id.to_owned(),
        // TODO: evaluate why we need payment id at the connector level. We already have connector reference id
        payment_id: payment_data
            .payment_attempt
            .payment_id
            .get_string_repr()
            .to_owned(),
        // TODO: evaluate why we need attempt id at the connector level. We already have connector reference id
        attempt_id: payment_data
            .payment_attempt
            .get_id()
            .get_string_repr()
            .to_owned(),
        status: payment_data.payment_attempt.status,
        payment_method,
        connector_auth_type: auth_type,
        description: payment_data
            .payment_intent
            .description
            .as_ref()
            .map(|description| description.get_string_repr())
            .map(ToOwned::to_owned),
        // TODO: Create unified address
        address: payment_data.payment_address.clone(),
        auth_type: payment_data.payment_attempt.authentication_type,
        connector_meta_data: merchant_connector_account.get_metadata(),
        connector_wallets_details: None,
        request,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        amount_captured: payment_data
            .payment_intent
            .amount_captured
            .map(|amt| amt.get_amount_as_i64()),
        minor_amount_captured: payment_data.payment_intent.amount_captured,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_status: None,
        payment_method_token: None,
        connector_customer: connector_customer_id,
        recurring_mandate_payment_data: None,
        // TODO: This has to be generated as the reference id based on the connector configuration
        // Some connectros might not accept accept the global id. This has to be done when generating the reference id
        connector_request_reference_id,
        preprocessing_id: payment_data.payment_attempt.preprocessing_step_id,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        // TODO: take this based on the env
        test_mode: Some(true),
        payment_method_balance: None,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload,
        connector_mandate_request_reference_id,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: payment_data.payment_intent.is_payment_id_from_merchant,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };

    Ok(router_data)
}
#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_external_vault_proxy_payment_router_data<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentConfirmData<api::ExternalVaultProxy>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::ExternalVaultProxyPaymentsRouterData> {
    use masking::ExposeOptionInterface;

    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    let auth_type = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    // TODO: Take Globalid and convert to connector reference id
    let customer_id = customer
        .to_owned()
        .map(|customer| common_utils::id_type::CustomerId::try_from(customer.id.clone()))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Invalid global customer generated, not able to convert to reference id",
        )?;

    let connector_customer_id =
        payment_data.get_connector_customer_id(customer.as_ref(), merchant_connector_account);

    let payment_method = payment_data.payment_attempt.payment_method_type;

    let router_base_url = &state.base_url;
    let attempt = &payment_data.payment_attempt;

    let complete_authorize_url = Some(helpers::create_complete_authorize_url(
        router_base_url,
        attempt,
        connector_id,
        None,
    ));

    let webhook_url = match merchant_connector_account {
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(
            merchant_connector_account,
        ) => Some(helpers::create_webhook_url(
            router_base_url,
            &attempt.merchant_id,
            merchant_connector_account.get_id().get_string_repr(),
        )),
        // TODO: Implement for connectors that require a webhook URL to be included in the request payload.
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None,
    };

    let router_return_url = payment_data
        .payment_intent
        .create_finish_redirection_url(
            router_base_url,
            merchant_context
                .get_merchant_account()
                .publishable_key
                .as_ref(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to construct finish redirection url")?
        .to_string();

    let connector_request_reference_id = payment_data
        .payment_attempt
        .connector_request_reference_id
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("connector_request_reference_id not found in payment_attempt")?;

    let email = customer
        .as_ref()
        .and_then(|customer| customer.email.clone())
        .map(pii::Email::from);

    let browser_info = payment_data
        .payment_attempt
        .browser_info
        .clone()
        .map(types::BrowserInformation::from);
    // TODO: few fields are repeated in both routerdata and request
    let request = types::ExternalVaultProxyPaymentsData {
        payment_method_data: payment_data
            .external_vault_pmd
            .clone()
            .get_required_value("external vault proxy payment_method_data")?,
        setup_future_usage: Some(payment_data.payment_intent.setup_future_usage),
        mandate_id: payment_data.mandate_data.clone(),
        off_session: None,
        setup_mandate_details: None,
        confirm: true,
        statement_descriptor_suffix: None,
        statement_descriptor: None,
        capture_method: Some(payment_data.payment_intent.capture_method),
        amount: payment_data
            .payment_attempt
            .amount_details
            .get_net_amount()
            .get_amount_as_i64(),
        minor_amount: payment_data.payment_attempt.amount_details.get_net_amount(),
        order_tax_amount: None,
        currency: payment_data.payment_intent.amount_details.currency,
        browser_info,
        email,
        customer_name: None,
        payment_experience: None,
        order_details: None,
        order_category: None,
        session_token: None,
        enrolled_for_3ds: true,
        related_transaction_id: None,
        payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype),
        router_return_url: Some(router_return_url),
        webhook_url,
        complete_authorize_url,
        customer_id: customer_id.clone(),
        surcharge_details: None,
        request_extended_authorization: None,
        request_incremental_authorization: matches!(
            payment_data
                .payment_intent
                .request_incremental_authorization,
            RequestIncrementalAuthorization::True
        ),
        metadata: payment_data.payment_intent.metadata.clone().expose_option(),
        authentication_data: None,
        customer_acceptance: None,
        split_payments: None,
        merchant_order_reference_id: payment_data.payment_intent.merchant_reference_id.clone(),
        integrity_object: None,
        shipping_cost: payment_data.payment_intent.amount_details.shipping_cost,
        additional_payment_method_data: None,
        merchant_account_id: None,
        merchant_config_currency: None,
        connector_testing_data: None,
        order_id: None,
    };
    let connector_mandate_request_reference_id = payment_data
        .payment_attempt
        .connector_token_details
        .as_ref()
        .and_then(|detail| detail.get_connector_token_request_reference_id());

    // Construct RouterDataV2 for external vault proxy
    let router_data_v2 = construct_external_vault_proxy_router_data_v2(
        state,
        merchant_context.get_merchant_account(),
        merchant_connector_account,
        &payment_data,
        request,
        connector_request_reference_id.clone(),
        connector_customer_id.clone(),
        customer_id.clone(),
        header_payload.clone(),
    )
    .await?;

    // Convert RouterDataV2 to old RouterData (v1) using the existing RouterDataConversion trait
    let router_data =
        flow_common_types::ExternalVaultProxyFlowData::to_old_router_data(router_data_v2)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable(
                "Cannot construct router data for making the unified connector service call",
            )?;

    Ok(router_data)
}
#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_payment_router_data_for_capture<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentCaptureData<api::Capture>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::PaymentsCaptureRouterData> {
    use masking::ExposeOptionInterface;

    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    let auth_type = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let customer_id = customer
        .to_owned()
        .map(|customer| common_utils::id_type::CustomerId::try_from(customer.id.clone()))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Invalid global customer generated, not able to convert to reference id",
        )?;

    let payment_method = payment_data.payment_attempt.payment_method_type;

    let connector_mandate_request_reference_id = payment_data
        .payment_attempt
        .connector_token_details
        .as_ref()
        .and_then(|detail| detail.get_connector_token_request_reference_id());

    let connector = api::ConnectorData::get_connector_by_name(
        &state.conf.connectors,
        connector_id,
        api::GetToken::Connector,
        payment_data.payment_attempt.merchant_connector_id.clone(),
    )?;

    let connector_request_reference_id = payment_data
        .payment_attempt
        .connector_request_reference_id
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("connector_request_reference_id not found in payment_attempt")?;

    let amount_to_capture = payment_data
        .payment_attempt
        .amount_details
        .get_amount_to_capture()
        .unwrap_or(payment_data.payment_attempt.amount_details.get_net_amount());

    let amount = payment_data.payment_attempt.amount_details.get_net_amount();
    let request = types::PaymentsCaptureData {
        capture_method: Some(payment_data.payment_intent.capture_method),
        amount_to_capture: amount_to_capture.get_amount_as_i64(), // This should be removed once we start moving to connector module
        minor_amount_to_capture: amount_to_capture,
        currency: payment_data.payment_intent.amount_details.currency,
        connector_transaction_id: connector
            .connector
            .connector_transaction_id(&payment_data.payment_attempt)?
            .ok_or(errors::ApiErrorResponse::ResourceIdNotFound)?,
        payment_amount: amount.get_amount_as_i64(), // This should be removed once we start moving to connector module
        minor_payment_amount: amount,
        connector_meta: payment_data
            .payment_attempt
            .connector_metadata
            .clone()
            .expose_option(),
        // TODO: add multiple capture data
        multiple_capture_data: None,
        // TODO: why do we need browser info during capture?
        browser_info: None,
        metadata: payment_data.payment_intent.metadata.expose_option(),
        integrity_object: None,
        split_payments: None,
        webhook_url: None,
    };

    // TODO: evaluate the fields in router data, if they are required or not
    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        // TODO: evaluate why we need customer id at the connector level. We already have connector customer id.
        customer_id,
        connector: connector_id.to_owned(),
        tenant_id: state.tenant.tenant_id.clone(),
        // TODO: evaluate why we need payment id at the connector level. We already have connector reference id
        payment_id: payment_data
            .payment_attempt
            .payment_id
            .get_string_repr()
            .to_owned(),
        // TODO: evaluate why we need attempt id at the connector level. We already have connector reference id
        attempt_id: payment_data
            .payment_attempt
            .get_id()
            .get_string_repr()
            .to_owned(),
        status: payment_data.payment_attempt.status,
        payment_method,
        connector_auth_type: auth_type,
        description: payment_data
            .payment_intent
            .description
            .as_ref()
            .map(|description| description.get_string_repr())
            .map(ToOwned::to_owned),
        // TODO: Create unified address
        address: hyperswitch_domain_models::payment_address::PaymentAddress::default(),
        auth_type: payment_data.payment_attempt.authentication_type,
        connector_meta_data: None,
        connector_wallets_details: None,
        request,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_status: None,
        payment_method_token: None,
        connector_customer: None,
        recurring_mandate_payment_data: None,
        // TODO: This has to be generated as the reference id based on the connector configuration
        // Some connectros might not accept accept the global id. This has to be done when generating the reference id
        connector_request_reference_id,
        preprocessing_id: payment_data.payment_attempt.preprocessing_step_id,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        // TODO: take this based on the env
        test_mode: Some(true),
        payment_method_balance: None,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload,
        connector_mandate_request_reference_id,
        psd2_sca_exemption_type: None,
        authentication_id: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };

    Ok(router_data)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_router_data_for_psync<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentStatusData<api::PSync>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::PaymentsSyncRouterData> {
    use masking::ExposeOptionInterface;

    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    // TODO: Take Globalid / CustomerReferenceId and convert to connector reference id
    let customer_id = None;

    let payment_intent = payment_data.payment_intent;

    let auth_type: types::ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let attempt = &payment_data.payment_attempt;

    let connector_request_reference_id = payment_data
        .payment_attempt
        .connector_request_reference_id
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("connector_request_reference_id not found in payment_attempt")?;

    let request = types::PaymentsSyncData {
        amount: attempt.amount_details.get_net_amount(),
        integrity_object: None,
        mandate_id: None,
        connector_transaction_id: match attempt.get_connector_payment_id() {
            Some(connector_txn_id) => {
                types::ResponseId::ConnectorTransactionId(connector_txn_id.to_owned())
            }
            None => types::ResponseId::NoResponseId,
        },
        encoded_data: attempt.encoded_data.clone().expose_option(),
        capture_method: Some(payment_intent.capture_method),
        connector_meta: attempt.connector_metadata.clone().expose_option(),
        sync_type: types::SyncRequestType::SinglePaymentSync,
        payment_method_type: Some(attempt.payment_method_subtype),
        currency: payment_intent.amount_details.currency,
        // TODO: Get the charges object from feature metadata
        split_payments: None,
        payment_experience: None,
        connector_reference_id: attempt.connector_response_reference_id.clone(),
        setup_future_usage: Some(payment_intent.setup_future_usage),
    };

    // TODO: evaluate the fields in router data, if they are required or not
    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        // TODO: evaluate why we need customer id at the connector level. We already have connector customer id.
        customer_id,
        tenant_id: state.tenant.tenant_id.clone(),
        connector: connector_id.to_owned(),
        // TODO: evaluate why we need payment id at the connector level. We already have connector reference id
        payment_id: payment_intent.id.get_string_repr().to_owned(),
        // TODO: evaluate why we need attempt id at the connector level. We already have connector reference id
        attempt_id: attempt.get_id().get_string_repr().to_owned(),
        status: attempt.status,
        payment_method: attempt.payment_method_type,
        connector_auth_type: auth_type,
        description: payment_intent
            .description
            .as_ref()
            .map(|description| description.get_string_repr())
            .map(ToOwned::to_owned),
        // TODO: Create unified address
        address: hyperswitch_domain_models::payment_address::PaymentAddress::default(),
        auth_type: attempt.authentication_type,
        connector_meta_data: None,
        connector_wallets_details: None,
        request,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_status: None,
        payment_method_token: None,
        connector_customer: None,
        recurring_mandate_payment_data: None,
        // TODO: This has to be generated as the reference id based on the connector configuration
        // Some connectros might not accept accept the global id. This has to be done when generating the reference id
        connector_request_reference_id,
        preprocessing_id: attempt.preprocessing_step_id.clone(),
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        // TODO: take this based on the env
        test_mode: Some(true),
        payment_method_balance: None,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),


// Struct: RecoveryCodes
// File: crates/router/src/types/domain/user.rs
// Module: router
// Implementations: 1

pub struct RecoveryCodes


// File: crates/router/tests/cache.rs
// Module: router

#![allow(clippy::unwrap_used, clippy::print_stdout)]
use std::sync::Arc;

use router::{configs::settings::Settings, routes, services};
use storage_impl::redis::cache::{self, CacheKey};

mod utils;

#[actix_web::test]
async fn invalidate_existing_cache_success() {
    // Arrange
    Box::pin(utils::setup()).await;
    let (tx, _) = tokio::sync::oneshot::channel();
    let app_state = Box::pin(routes::AppState::new(
        Settings::default(),
        tx,
        Box::new(services::MockApiClient),
    ))
    .await;
    let state = Arc::new(app_state)
        .get_session_state(
            &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(),
            None,
            || {},
        )
        .unwrap();
    let cache_key = "cacheKey".to_string();
    let cache_key_value = "val".to_string();
    let _ = state
        .store
        .get_redis_conn()
        .unwrap()
        .set_key(&cache_key.clone().into(), cache_key_value.clone())
        .await;

    let api_key = ("api-key", "test_admin");
    let client = awc::Client::default();

    cache::CONFIG_CACHE
        .push(
            CacheKey {
                key: cache_key.clone(),
                prefix: String::default(),
            },
            cache_key_value.clone(),
        )
        .await;

    cache::ACCOUNTS_CACHE
        .push(
            CacheKey {
                key: cache_key.clone(),
                prefix: String::default(),
            },
            cache_key_value.clone(),
        )
        .await;

    // Act
    let mut response = client
        .post(format!(
            "http://127.0.0.1:8080/cache/invalidate/{cache_key}"
        ))
        .insert_header(api_key)
        .send()
        .await
        .unwrap();

    // Assert
    let response_body = response.body().await;
    println!("invalidate Cache: {response:?} : {response_body:?}");
    assert_eq!(response.status(), awc::http::StatusCode::OK);
    assert!(cache::CONFIG_CACHE
        .get_val::<String>(CacheKey {
            key: cache_key.clone(),
            prefix: String::default()
        })
        .await
        .is_none());
    assert!(cache::ACCOUNTS_CACHE
        .get_val::<String>(CacheKey {
            key: cache_key,
            prefix: String::default()
        })
        .await
        .is_none());
}

#[actix_web::test]
async fn invalidate_non_existing_cache_success() {
    // Arrange
    Box::pin(utils::setup()).await;
    let cache_key = "cacheKey".to_string();
    let api_key = ("api-key", "test_admin");
    let client = awc::Client::default();

    // Act
    let mut response = client
        .post(format!(
            "http://127.0.0.1:8080/cache/invalidate/{cache_key}"
        ))
        .insert_header(api_key)
        .send()
        .await
        .unwrap();

    // Assert
    let response_body = response.body().await;
    println!("invalidate Cache: {response:?} : {response_body:?}");
    assert_eq!(response.status(), awc::http::StatusCode::NOT_FOUND);
}


// File: crates/router/src/core/poll.rs
// Module: router
// Public functions: 1

use api_models::poll::PollResponse;
use common_utils::ext_traits::StringExt;
use error_stack::ResultExt;
use router_env::{instrument, tracing};

use super::errors;
use crate::{
    core::errors::RouterResponse, services::ApplicationResponse, types::domain, SessionState,
};

#[instrument(skip_all)]
pub async fn retrieve_poll_status(
    state: SessionState,
    req: crate::types::api::PollId,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<PollResponse> {
    let redis_conn = state
        .store
        .get_redis_conn()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get redis connection")?;
    let request_poll_id = req.poll_id;
    // prepend 'poll_{merchant_id}_' to restrict access to only fetching Poll IDs, as this is a freely passed string in the request
    let poll_id = super::utils::get_poll_id(
        merchant_context.get_merchant_account().get_id(),
        request_poll_id.clone(),
    );
    let redis_value = redis_conn
        .get_key::<Option<String>>(&poll_id.as_str().into())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Error while fetching the value for {} from redis",
                poll_id.clone()
            )
        })?
        .ok_or(errors::ApiErrorResponse::PollNotFound {
            id: request_poll_id.clone(),
        })?;
    let status = redis_value
        .parse_enum("PollStatus")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error while parsing PollStatus")?;
    let poll_response = PollResponse {
        poll_id: request_poll_id,
        status,
    };
    Ok(ApplicationResponse::Json(poll_response))
}


// File: crates/analytics/src/payment_intents/metrics/smart_retried_amount.rs
// Module: analytics

use std::collections::HashSet;

use api_models::{
    analytics::{
        payment_intents::{
            PaymentIntentDimensions, PaymentIntentFilters, PaymentIntentMetricsBucketIdentifier,
        },
        Granularity, TimeRange,
    },
    enums::IntentStatus,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::PaymentIntentMetricRow;
use crate::{
    enums::AuthInfo,
    query::{
        Aggregate, FilterTypes, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql,
        Window,
    },
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};

#[derive(Default)]
pub(super) struct SmartRetriedAmount;

#[async_trait::async_trait]
impl<T> super::PaymentIntentMetric<T> for SmartRetriedAmount
where
    T: AnalyticsDataSource + super::PaymentIntentMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentIntentDimensions],
        auth: &AuthInfo,
        filters: &PaymentIntentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>>
    {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::PaymentIntent);

        for dim in dimensions.iter() {
            query_builder.add_select_column(dim).switch()?;
        }
        query_builder
            .add_select_column(Aggregate::Sum {
                field: "amount",
                alias: Some("total"),
            })
            .switch()?;

        query_builder.add_select_column("currency").switch()?;
        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        filters.set_filter_clause(&mut query_builder).switch()?;

        auth.set_filter_clause(&mut query_builder).switch()?;

        query_builder
            .add_custom_filter_clause("attempt_count", "1", FilterTypes::Gt)
            .switch()?;
        query_builder
            .add_custom_filter_clause("status", IntentStatus::Succeeded, FilterTypes::Equal)
            .switch()?;
        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        query_builder
            .add_group_by_clause("currency")
            .attach_printable("Error grouping by currency")
            .switch()?;
        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<PaymentIntentMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    PaymentIntentMetricsBucketIdentifier::new(
                        i.status.as_ref().map(|i| i.0),
                        i.currency.as_ref().map(|i| i.0),
                        i.profile_id.clone(),
                        i.connector.clone(),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.payment_method.clone(),
                        i.payment_method_type.clone(),
                        i.card_network.clone(),
                        i.merchant_id.clone(),
                        i.card_last_4.clone(),
                        i.card_issuer.clone(),
                        i.error_reason.clone(),
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


// File: crates/router/src/utils/user/sample_data.rs
// Module: router
// Public functions: 1

use api_models::{
    enums::Connector::{DummyConnector4, DummyConnector7},
    user::sample_data::SampleDataRequest,
};
use common_utils::{
    id_type,
    types::{AmountConvertor, ConnectorTransactionId, MinorUnit, StringMinorUnitForConnector},
};
#[cfg(feature = "v1")]
use diesel_models::user::sample_data::PaymentAttemptBatchNew;
use diesel_models::{enums as storage_enums, DisputeNew, RefundNew};
use error_stack::ResultExt;
use hyperswitch_domain_models::payments::PaymentIntent;
use rand::{prelude::SliceRandom, thread_rng, Rng};
use time::OffsetDateTime;

use crate::{
    consts,
    core::errors::sample_data::{SampleDataError, SampleDataResult},
    types::domain,
    SessionState,
};

#[cfg(feature = "v1")]
#[allow(clippy::type_complexity)]
pub async fn generate_sample_data(
    state: &SessionState,
    req: SampleDataRequest,
    merchant_id: &id_type::MerchantId,
    org_id: &id_type::OrganizationId,
) -> SampleDataResult<
    Vec<(
        PaymentIntent,
        PaymentAttemptBatchNew,
        Option<RefundNew>,
        Option<DisputeNew>,
    )>,
> {
    let sample_data_size: usize = req.record.unwrap_or(100);
    let key_manager_state = &state.into();
    if !(10..=100).contains(&sample_data_size) {
        return Err(SampleDataError::InvalidRange.into());
    }

    let key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .change_context(SampleDataError::InternalServerError)?;

    let merchant_from_db = state
        .store
        .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store)
        .await
        .change_context::<SampleDataError>(SampleDataError::DataDoesNotExist)?;

    let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
        merchant_from_db.clone(),
        key_store,
    )));
    #[cfg(feature = "v1")]
    let (profile_id_result, business_country_default, business_label_default) = {
        let merchant_parsed_details: Vec<api_models::admin::PrimaryBusinessDetails> =
            serde_json::from_value(merchant_from_db.primary_business_details.clone())
                .change_context(SampleDataError::InternalServerError)
                .attach_printable("Error while parsing primary business details")?;

        let business_country_default = merchant_parsed_details.first().map(|x| x.country);

        let business_label_default = merchant_parsed_details.first().map(|x| x.business.clone());

        let profile_id = crate::core::utils::get_profile_id_from_business_details(
            key_manager_state,
            business_country_default,
            business_label_default.as_ref(),
            &merchant_context,
            req.profile_id.as_ref(),
            &*state.store,
            false,
        )
        .await;
        (profile_id, business_country_default, business_label_default)
    };

    #[cfg(feature = "v2")]
    let (profile_id_result, business_country_default, business_label_default) = {
        let profile_id = req
            .profile_id.clone()
            .ok_or(hyperswitch_domain_models::errors::api_error_response::ApiErrorResponse::MissingRequiredField {
                field_name: "profile_id",
            });

        (profile_id, None, None)
    };

    let profile_id = match profile_id_result {
        Ok(id) => id.clone(),
        Err(error) => {
            router_env::logger::error!(
                "Profile ID not found in business details. Attempting to fetch from the database {error:?}"
            );

            state
                .store
                .list_profile_by_merchant_id(
                    key_manager_state,
                    merchant_context.get_merchant_key_store(),
                    merchant_id,
                )
                .await
                .change_context(SampleDataError::InternalServerError)
                .attach_printable("Failed to get business profile")?
                .first()
                .ok_or(SampleDataError::InternalServerError)?
                .get_id()
                .to_owned()
        }
    };

    // 10 percent payments should be failed
    #[allow(clippy::as_conversions)]
    let failure_attempts = usize::try_from((sample_data_size as f32 / 10.0).round() as i64)
        .change_context(SampleDataError::InvalidParameters)?;

    let failure_after_attempts = sample_data_size / failure_attempts;

    // 20 percent refunds for payments
    #[allow(clippy::as_conversions)]
    let number_of_refunds = usize::try_from((sample_data_size as f32 / 5.0).round() as i64)
        .change_context(SampleDataError::InvalidParameters)?;

    let mut refunds_count = 0;

    // 2 disputes if generated data size is between 50 and 100, 1 dispute if it is less than 50.
    let number_of_disputes: usize = if sample_data_size >= 50 { 2 } else { 1 };

    let mut disputes_count = 0;

    let mut random_array: Vec<usize> = (1..=sample_data_size).collect();

    // Shuffle the array
    let mut rng = thread_rng();
    random_array.shuffle(&mut rng);

    let mut res: Vec<(
        PaymentIntent,
        PaymentAttemptBatchNew,
        Option<RefundNew>,
        Option<DisputeNew>,
    )> = Vec::new();
    let start_time = req
        .start_time
        .unwrap_or(common_utils::date_time::now() - time::Duration::days(7))
        .assume_utc()
        .unix_timestamp();
    let end_time = req
        .end_time
        .unwrap_or_else(common_utils::date_time::now)
        .assume_utc()
        .unix_timestamp();

    let current_time = common_utils::date_time::now().assume_utc().unix_timestamp();

    let min_amount = req.min_amount.unwrap_or(100);
    let max_amount = req.max_amount.unwrap_or(min_amount + 100);

    if min_amount > max_amount
        || start_time > end_time
        || start_time > current_time
        || end_time > current_time
    {
        return Err(SampleDataError::InvalidParameters.into());
    };

    let currency_vec = req.currency.unwrap_or(vec![common_enums::Currency::USD]);
    let currency_vec_len = currency_vec.len();

    let connector_vec = req
        .connector
        .unwrap_or(vec![DummyConnector4, DummyConnector7]);
    let connector_vec_len = connector_vec.len();

    let auth_type = req.auth_type.unwrap_or(vec![
        common_enums::AuthenticationType::ThreeDs,
        common_enums::AuthenticationType::NoThreeDs,
    ]);
    let auth_type_len = auth_type.len();

    if currency_vec_len == 0 || connector_vec_len == 0 || auth_type_len == 0 {
        return Err(SampleDataError::InvalidParameters.into());
    }

    // This has to be an internal server error because, this function failing means that the intended functionality is not working as expected
    let dashboard_customer_id =
        id_type::CustomerId::try_from(std::borrow::Cow::from("hs-dashboard-user"))
            .change_context(SampleDataError::InternalServerError)?;

    for num in 1..=sample_data_size {
        let payment_id = id_type::PaymentId::generate_test_payment_id_for_sample_data();
        let attempt_id = payment_id.get_attempt_id(1);
        let client_secret = payment_id.generate_client_secret();
        let amount = thread_rng().gen_range(min_amount..=max_amount);

        let created_at @ modified_at @ last_synced =
            OffsetDateTime::from_unix_timestamp(thread_rng().gen_range(start_time..=end_time))
                .map(common_utils::date_time::convert_to_pdt)
                .unwrap_or(
                    req.start_time.unwrap_or_else(|| {
                        common_utils::date_time::now() - time::Duration::days(7)
                    }),
                );
        let session_expiry =
            created_at.saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY));

        // After some set of payments sample data will have a failed attempt
        let is_failed_payment =
            (random_array.get(num - 1).unwrap_or(&0) % failure_after_attempts) == 0;

        let payment_intent = PaymentIntent {
            payment_id: payment_id.clone(),
            merchant_id: merchant_id.clone(),
            status: match is_failed_payment {
                true => common_enums::IntentStatus::Failed,
                _ => common_enums::IntentStatus::Succeeded,
            },
            amount: MinorUnit::new(amount * 100),
            currency: Some(
                *currency_vec
                    .get((num - 1) % currency_vec_len)
                    .unwrap_or(&common_enums::Currency::USD),
            ),
            description: Some("This is a sample payment".to_string()),
            created_at,
            modified_at,
            last_synced: Some(last_synced),
            client_secret: Some(client_secret),
            business_country: business_country_default,
            business_label: business_label_default.clone(),
            active_attempt: hyperswitch_domain_models::RemoteStorageObject::ForeignID(
                attempt_id.clone(),
            ),
            attempt_count: 1,
            customer_id: Some(dashboard_customer_id.clone()),
            amount_captured: Some(MinorUnit::new(amount * 100)),
            profile_id: Some(profile_id.clone()),
            return_url: Default::default(),
            metadata: Default::default(),
            connector_id: Default::default(),
            shipping_address_id: Default::default(),
            billing_address_id: Default::default(),
            statement_descriptor_name: Default::default(),
            statement_descriptor_suffix: Default::default(),
            setup_future_usage: Default::default(),
            off_session: Default::default(),
            order_details: Default::default(),
            allowed_payment_method_types: Default::default(),
            connector_metadata: Default::default(),
            feature_metadata: Default::default(),
            merchant_decision: Default::default(),
            payment_link_id: Default::default(),
            payment_confirm_source: Default::default(),
            updated_by: merchant_from_db.storage_scheme.to_string(),
            surcharge_applicable: Default::default(),
            request_incremental_authorization: Default::default(),
            incremental_authorization_allowed: Default::default(),
            authorization_count: Default::default(),
            fingerprint_id: None,
            session_expiry: Some(session_expiry),
            request_external_three_ds_authentication: None,
            split_payments: None,
            frm_metadata: Default::default(),
            customer_details: None,
            billing_details: None,
            merchant_order_reference_id: Default::default(),
            shipping_details: None,
            is_payment_processor_token_flow: None,
            organization_id: org_id.clone(),
            shipping_cost: None,
            tax_details: None,
            skip_external_tax_calculation: None,
            request_extended_authorization: None,
            psd2_sca_exemption_type: None,
            processor_merchant_id: merchant_id.clone(),
            created_by: None,
            force_3ds_challenge: None,
            force_3ds_challenge_trigger: None,
            is_iframe_redirection_enabled: None,
            is_payment_id_from_merchant: None,
            payment_channel: None,
            order_date: None,
            discount_amount: None,
            duty_amount: None,
            tax_status: None,
            shipping_amount_tax: None,
            enable_partial_authorization: None,
            enable_overcapture: None,
        };
        let (connector_transaction_id, processor_transaction_data) =
            ConnectorTransactionId::form_id_and_data(attempt_id.clone());
        let payment_attempt = PaymentAttemptBatchNew {
            attempt_id: attempt_id.clone(),
            payment_id: payment_id.clone(),
            connector_transaction_id: Some(connector_transaction_id),
            merchant_id: merchant_id.clone(),
            status: match is_failed_payment {
                true => common_enums::AttemptStatus::Failure,
                _ => common_enums::AttemptStatus::Charged,
            },
            amount: MinorUnit::new(amount * 100),
            currency: payment_intent.currency,
            connector: Some(
                (*connector_vec
                    .get((num - 1) % connector_vec_len)
                    .unwrap_or(&DummyConnector4))
                .to_string(),
            ),
            payment_method: Some(common_enums::PaymentMethod::Card),
            payment_method_type: Some(get_payment_method_type(thread_rng().gen_range(1..=2))),
            authentication_type: Some(
                *auth_type
                    .get((num - 1) % auth_type_len)
                    .unwrap_or(&common_enums::AuthenticationType::NoThreeDs),
            ),
            error_message: match is_failed_payment {
                true => Some("This is a test payment which has a failed status".to_string()),
                _ => None,
            },
            error_code: match is_failed_payment {
                true => Some("HS001".to_string()),
                _ => None,
            },
            confirm: true,
            created_at,
            modified_at,
            last_synced: Some(last_synced),
            amount_to_capture: Some(MinorUnit::new(amount * 100)),
            connector_response_reference_id: Some(attempt_id.clone()),
            updated_by: merchant_from_db.storage_scheme.to_string(),
            save_to_locker: None,
            offer_amount: None,
            surcharge_amount: None,
            tax_amount: None,
            payment_method_id: None,
            capture_method: None,
            capture_on: None,
            cancellation_reason: None,
            mandate_id: None,
            browser_info: None,
            payment_token: None,
            connector_metadata: None,
            payment_experience: None,
            payment_method_data: None,
            business_sub_label: None,
            straight_through_algorithm: None,
            preprocessing_step_id: None,
            mandate_details: None,
            error_reason: None,
            multiple_capture_count: None,
            amount_capturable: MinorUnit::new(i64::default()),
            merchant_connector_id: None,
            authentication_data: None,
            encoded_data: None,
            unified_code: None,
            unified_message: None,
            net_amount: None,
            external_three_ds_authentication_attempted: None,
            authentication_connector: None,
            authentication_id: None,
            mandate_data: None,
            payment_method_billing_address_id: None,
            fingerprint_id: None,
            charge_id: None,
            client_source: None,
            client_version: None,
            customer_acceptance: None,
            profile_id: profile_id.clone(),
            organization_id: org_id.clone(),
            shipping_cost: None,
            order_tax_amount: None,
            processor_transaction_data,
            connector_mandate_detail: None,
            request_extended_authorization: None,
            extended_authorization_applied: None,
            capture_before: None,
            card_discovery: None,
            processor_merchant_id: Some(merchant_id.clone()),
            created_by: None,
            setup_future_usage_applied: None,
            routing_approach: None,
            connector_request_reference_id: None,
            network_transaction_id: None,
            network_details: None,
            is_stored_credential: None,
        };

        let refund = if refunds_count < number_of_refunds && !is_failed_payment {
            refunds_count += 1;
            let (connector_transaction_id, processor_transaction_data) =
                ConnectorTransactionId::form_id_and_data(attempt_id.clone());
            Some(RefundNew {
                refund_id: common_utils::generate_id_with_default_len("test"),
                internal_reference_id: common_utils::generate_id_with_default_len("test"),
                external_reference_id: None,
                payment_id: payment_id.clone(),
                attempt_id: attempt_id.clone(),
                merchant_id: merchant_id.clone(),
                connector_transaction_id,
                connector_refund_id: None,
                description: Some("This is a sample refund".to_string()),
                created_at,
                modified_at,
                refund_reason: Some("Sample Refund".to_string()),
                connector: payment_attempt
                    .connector
                    .clone()
                    .unwrap_or(DummyConnector4.to_string()),
                currency: *currency_vec
                    .get((num - 1) % currency_vec_len)
                    .unwrap_or(&common_enums::Currency::USD),
                total_amount: MinorUnit::new(amount * 100),
                refund_amount: MinorUnit::new(amount * 100),
                refund_status: common_enums::RefundStatus::Success,
                sent_to_gateway: true,
                refund_type: diesel_models::enums::RefundType::InstantRefund,
                metadata: None,
                refund_arn: None,
                profile_id: payment_intent.profile_id.clone(),
                updated_by: merchant_from_db.storage_scheme.to_string(),
                merchant_connector_id: payment_attempt.merchant_connector_id.clone(),
                charges: None,
                split_refunds: None,
                organization_id: org_id.clone(),
                processor_refund_data: None,
                processor_transaction_data,
            })
        } else {
            None
        };

        let dispute =
            if disputes_count < number_of_disputes && !is_failed_payment && refund.is_none() {
                disputes_count += 1;
                let currency = payment_intent
                    .currency
                    .unwrap_or(common_enums::Currency::USD);
                Some(DisputeNew {
                    dispute_id: common_utils::generate_id_with_default_len("test"),
                    amount: StringMinorUnitForConnector::convert(
                        &StringMinorUnitForConnector,
                        MinorUnit::new(amount * 100),
                        currency,
                    )
                    .change_context(SampleDataError::InternalServerError)?,
                    currency: currency.to_string(),
                    dispute_stage: storage_enums::DisputeStage::Dispute,
                    dispute_status: storage_enums::DisputeStatus::DisputeOpened,
                    payment_id: payment_id.clone(),
                    attempt_id: attempt_id.clone(),
                    merchant_id: merchant_id.clone(),
                    connector_status: "Sample connector status".into(),
                    connector_dispute_id: common_utils::generate_id_with_default_len("test"),
                    connector_reason: Some("Sample Dispute".into()),
                    connector_reason_code: Some("123".into()),
                    challenge_required_by: None,
                    connector_created_at: None,
                    connector_updated_at: None,
                    connector: payment_attempt
                        .connector
                        .clone()
                        .unwrap_or(DummyConnector4.to_string()),
                    evidence: None,
                    profile_id: payment_intent.profile_id.clone(),
                    merchant_connector_id: payment_attempt.merchant_connector_id.clone(),
                    dispute_amount: MinorUnit::new(amount * 100),
                    organization_id: org_id.clone(),
                    dispute_currency: Some(payment_intent.currency.unwrap_or_default()),
                })
            } else {
                None
            };

        res.push((payment_intent, payment_attempt, refund, dispute));
    }
    Ok(res)
}

fn get_payment_method_type(num: u8) -> common_enums::PaymentMethodType {
    let rem: u8 = (num) % 2;
    match rem {
        0 => common_enums::PaymentMethodType::Debit,
        _ => common_enums::PaymentMethodType::Credit,
    }
}


// Implementation: impl api::MandateSetup for for Redsys
// File: crates/hyperswitch_connectors/src/connectors/redsys.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::MandateSetup for for Redsys


// Struct: Address
// File: crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Address


// Function: authentication_eligibility_core
// File: crates/router/src/core/unified_authentication_service.rs
// Module: router

pub fn authentication_eligibility_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: AuthenticationEligibilityRequest,
    authentication_id: common_utils::id_type::AuthenticationId,
) -> RouterResponse<AuthenticationEligibilityResponse>


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

impl api::PaymentToken for for Worldpayxml


// Function: retrieve_dispute
// File: crates/router/src/core/disputes.rs
// Module: router

pub fn retrieve_dispute(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: Option<common_utils::id_type::ProfileId>,
    req: dispute_models::DisputeRetrieveRequest,
) -> RouterResponse<api_models::disputes::DisputeResponse>


// Struct: OrderAddress
// File: crates/hyperswitch_connectors/src/connectors/riskified/transformers/api.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct OrderAddress


// Function: retrieve_routing_algorithm_from_algorithm_id
// File: crates/router/src/core/routing.rs
// Module: router

pub fn retrieve_routing_algorithm_from_algorithm_id(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    authentication_profile_id: Option<common_utils::id_type::ProfileId>,
    algorithm_id: common_utils::id_type::RoutingId,
) -> RouterResponse<routing_types::MerchantRoutingAlgorithm>


// Function: record_operation_time
// File: crates/analytics/src/metrics/request.rs
// Module: analytics

pub fn record_operation_time<F, R, T>(
    future: F,
    metric: &router_env::opentelemetry::metrics::Histogram<f64>,
    metric_name: &T,
    source: &crate::AnalyticsProvider,
) -> R
where
    F: futures::Future<Output = R>,
    T: ToString,


// Implementation: impl RequiredFromNullable
// File: crates/diesel_models/src/lib.rs
// Module: diesel_models
// Methods: 1 total (1 public)

impl RequiredFromNullable


// Implementation: impl api::ConnectorAccessToken for for Worldpayvantiv
// File: crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Worldpayvantiv


// Function: new
// File: crates/router/src/events/outgoing_webhook_logs.rs
// Module: router

pub fn new(
        tenant_id: common_utils::id_type::TenantId,
        merchant_id: common_utils::id_type::MerchantId,
        event_id: String,
        event_type: OutgoingWebhookEventType,
        content: Option<OutgoingWebhookEventContent>,
        error: Option<Value>,
        initial_attempt_id: Option<String>,
        status_code: Option<u16>,
        delivery_attempt: Option<WebhookDeliveryAttempt>,
    ) -> Self


// Function: payments_update
// File: crates/openapi/src/routes/payments.rs
// Module: openapi

pub fn payments_update()


// Implementation: impl api::PaymentAuthorize for for Bluecode
// File: crates/hyperswitch_connectors/src/connectors/bluecode.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Bluecode


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

pub fn new() -> &'static Self


// Function: get_org_auth_event_sankey
// File: crates/router/src/analytics.rs
// Module: router

pub fn get_org_auth_event_sankey(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<TimeRange>,
    ) -> impl Responder


// Struct: BluesnapMetadata
// File: crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BluesnapMetadata


// Struct: AddressUpdateInternal
// File: crates/diesel_models/src/address.rs
// Module: diesel_models
// Implementations: 1

pub struct AddressUpdateInternal


// Function: payouts_list
// File: crates/openapi/src/routes/payouts.rs
// Module: openapi

pub fn payouts_list()


// Struct: DeutschebankThreeDSInitializeRequestAmountTotal
// File: crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DeutschebankThreeDSInitializeRequestAmountTotal


// Function: schedule_next_dispute_list_task
// File: crates/router/src/workflows/dispute_list.rs
// Module: router

pub fn schedule_next_dispute_list_task(
    db: &dyn StorageInterface,
    tracking_data: &api::DisputeListPTData,
    dispute_polling_interval: i32,
) -> Result<(), errors::ProcessTrackerError>


// Function: get_pm_list_context
// File: crates/router/src/core/payment_methods/cards.rs
// Module: router

pub fn get_pm_list_context(
    state: &routes::SessionState,
    payment_method: &enums::PaymentMethod,
    #[cfg(feature = "payouts")


// Implementation: impl api::PaymentAuthorize for for Xendit
// File: crates/hyperswitch_connectors/src/connectors/xendit.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Xendit


// Function: generate_token_data_response
// File: crates/router/src/core/payment_methods.rs
// Module: router

pub fn generate_token_data_response(
    state: &SessionState,
    request: payment_methods::GetTokenDataRequest,
    profile: domain::Profile,
    payment_method: &domain::PaymentMethod,
) -> RouterResult<api::TokenDataResponse>


// Struct: OpennodeRefundRequest
// File: crates/hyperswitch_connectors/src/connectors/opennode/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct OpennodeRefundRequest


// Struct: ErrorDetails
// File: crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ErrorDetails


// Function: apply_changeset
// File: crates/diesel_models/src/payment_methods_session.rs
// Module: diesel_models

pub fn apply_changeset(self, update_session: PaymentMethodsSessionUpdateInternal) -> Self


// Function: comparison
// File: crates/euclid/src/frontend/ast/parser.rs
// Module: euclid

pub fn comparison(input: &str) -> ParseResult<&str, ast::Comparison>


// Function: customers_retrieve
// File: crates/router/src/routes/customers.rs
// Module: router

pub fn customers_retrieve(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<id_type::GlobalCustomerId>,
) -> HttpResponse


// File: crates/router/src/core/payments/operations/payment_post_session_tokens.rs
// Module: router
// Public structs: 1

use std::marker::PhantomData;

use api_models::enums::FrmSuggestion;
use async_trait::async_trait;
use common_utils::types::keymanager::KeyManagerState;
use error_stack::ResultExt;
use masking::PeekInterface;
use router_derive::PaymentOperation;
use router_env::{instrument, tracing};

use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
use crate::{
    core::{
        errors::{self, RouterResult, StorageErrorExt},
        payments::{self, helpers, operations, PaymentData},
    },
    routes::{app::ReqState, SessionState},
    services,
    types::{
        api::{self, PaymentIdTypeExt},
        domain,
        storage::{self, enums as storage_enums},
    },
    utils::OptionExt,
};

#[derive(Debug, Clone, Copy, PaymentOperation)]
#[operation(operations = "all", flow = "post_session_tokens")]
pub struct PaymentPostSessionTokens;

type PaymentPostSessionTokensOperation<'b, F> =
    BoxedOperation<'b, F, api::PaymentsPostSessionTokensRequest, PaymentData<F>>;

#[async_trait]
impl<F: Send + Clone + Sync> GetTracker<F, PaymentData<F>, api::PaymentsPostSessionTokensRequest>
    for PaymentPostSessionTokens
{
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &api::PaymentIdType,
        request: &api::PaymentsPostSessionTokensRequest,
        merchant_context: &domain::MerchantContext,
        _auth_flow: services::AuthFlow,
        _header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<
        operations::GetTrackerResponse<
            'a,
            F,
            api::PaymentsPostSessionTokensRequest,
            PaymentData<F>,
        >,
    > {
        let payment_id = payment_id
            .get_payment_intent_id()
            .change_context(errors::ApiErrorResponse::PaymentNotFound)?;

        let db = &*state.store;
        let key_manager_state: &KeyManagerState = &state.into();
        let merchant_id = merchant_context.get_merchant_account().get_id();
        let storage_scheme = merchant_context.get_merchant_account().storage_scheme;
        let payment_intent = db
            .find_payment_intent_by_payment_id_merchant_id(
                &state.into(),
                &payment_id,
                merchant_id,
                merchant_context.get_merchant_key_store(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

        // TODO (#7195): Add platform merchant account validation once publishable key auth is solved
        helpers::authenticate_client_secret(Some(request.client_secret.peek()), &payment_intent)?;

        let mut payment_attempt = db
            .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_intent.payment_id,
                merchant_id,
                payment_intent.active_attempt.get_id().as_str(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
        let currency = payment_intent.currency.get_required_value("currency")?;
        let amount = payment_attempt.get_total_amount().into();
        let profile_id = payment_intent
            .profile_id
            .as_ref()
            .get_required_value("profile_id")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("'profile_id' not set in payment intent")?;

        let business_profile = db
            .find_business_profile_by_profile_id(
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                profile_id,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
                id: profile_id.get_string_repr().to_owned(),
            })?;
        payment_attempt.payment_method = Some(request.payment_method);
        payment_attempt.payment_method_type = Some(request.payment_method_type);
        let shipping_address = helpers::get_address_by_id(
            state,
            payment_intent.shipping_address_id.clone(),
            merchant_context.get_merchant_key_store(),
            &payment_intent.payment_id,
            merchant_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

        let billing_address = helpers::get_address_by_id(
            state,
            payment_intent.billing_address_id.clone(),
            merchant_context.get_merchant_key_store(),
            &payment_intent.payment_id,
            merchant_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

        let payment_data = PaymentData {
            flow: PhantomData,
            payment_intent,
            payment_attempt,
            currency,
            amount,
            email: None,
            mandate_id: None,
            mandate_connector: None,
            customer_acceptance: None,
            token: None,
            token_data: None,
            setup_mandate: None,
            address: payments::PaymentAddress::new(
                shipping_address.as_ref().map(From::from),
                billing_address.as_ref().map(From::from),
                None,
                business_profile.use_billing_as_payment_method_billing,
            ),
            confirm: None,
            payment_method_data: None,
            payment_method_token: None,
            payment_method_info: None,
            force_sync: None,
            all_keys_required: None,
            refunds: vec![],
            disputes: vec![],
            attempts: None,
            sessions_token: vec![],
            card_cvc: None,
            creds_identifier: None,
            pm_token: None,
            connector_customer_id: None,
            recurring_mandate_payment_data: None,
            ephemeral_key: None,
            multiple_capture_data: None,
            redirect_response: None,
            surcharge_details: None,
            frm_message: None,
            payment_link_data: None,
            incremental_authorization_details: None,
            authorizations: vec![],
            authentication: None,
            recurring_details: None,
            poll_config: None,
            tax_data: None,
            session_id: None,
            service_details: None,
            card_testing_guard_data: None,
            vault_operation: None,
            threeds_method_comp_ind: None,
            whole_connector_response: None,
            is_manual_retry_enabled: None,
        };
        let get_trackers_response = operations::GetTrackerResponse {
            operation: Box::new(self),
            customer_details: None,
            payment_data,
            business_profile,
            mandate_type: None,
        };

        Ok(get_trackers_response)
    }
}

#[async_trait]
impl<F: Clone + Send + Sync> Domain<F, api::PaymentsPostSessionTokensRequest, PaymentData<F>>
    for PaymentPostSessionTokens
{
    #[instrument(skip_all)]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut PaymentData<F>,
        _request: Option<payments::CustomerDetails>,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> errors::CustomResult<
        (
            PaymentPostSessionTokensOperation<'a, F>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        Ok((Box::new(self), None))
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut PaymentData<F>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        PaymentPostSessionTokensOperation<'a, F>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        _request: &api::PaymentsPostSessionTokensRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> errors::CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut PaymentData<F>,
    ) -> errors::CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[async_trait]
impl<F: Clone + Sync> UpdateTracker<F, PaymentData<F>, api::PaymentsPostSessionTokensRequest>
    for PaymentPostSessionTokens
{
    #[instrument(skip_all)]
    async fn update_trackers<'b>(
        &'b self,
        _state: &'b SessionState,
        _req_state: ReqState,
        payment_data: PaymentData<F>,
        _customer: Option<domain::Customer>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
        _updated_customer: Option<storage::CustomerUpdate>,
        _key_store: &domain::MerchantKeyStore,
        _frm_suggestion: Option<FrmSuggestion>,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(PaymentPostSessionTokensOperation<'b, F>, PaymentData<F>)>
    where
        F: 'b + Send,
    {
        Ok((Box::new(self), payment_data))
    }
}

impl<F: Send + Clone + Sync>
    ValidateRequest<F, api::PaymentsPostSessionTokensRequest, PaymentData<F>>
    for PaymentPostSessionTokens
{
    #[instrument(skip_all)]
    fn validate_request<'a, 'b>(
        &'b self,
        request: &api::PaymentsPostSessionTokensRequest,
        merchant_context: &'a domain::MerchantContext,
    ) -> RouterResult<(
        PaymentPostSessionTokensOperation<'b, F>,
        operations::ValidateResult,
    )> {
        //payment id is already generated and should be sent in the request
        let given_payment_id = request.payment_id.clone();

        Ok((
            Box::new(self),
            operations::ValidateResult {
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                payment_id: api::PaymentIdType::PaymentIntentId(given_payment_id),
                storage_scheme: merchant_context.get_merchant_account().storage_scheme,
                requeue: false,
            },
        ))
    }
}


// Implementation: impl api::ConnectorAccessToken for for Nuvei
// File: crates/hyperswitch_connectors/src/connectors/nuvei.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Nuvei


// Struct: FrictionlessSuccessCount
// File: crates/api_models/src/analytics/auth_events.rs
// Module: api_models
// Implementations: 0

pub struct FrictionlessSuccessCount


// Struct: ChargebeeInvoiceContent
// File: crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ChargebeeInvoiceContent


// Implementation: impl ConnectorErrorTypeMapping for for Bluesnap
// File: crates/hyperswitch_connectors/src/connectors/bluesnap.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl ConnectorErrorTypeMapping for for Bluesnap