// File: crates/router/src/core/webhooks/webhook_events.rs
// Module: router
// Public functions: 3

use std::collections::HashSet;

use common_utils::{self, errors::CustomResult, fp_utils};
use error_stack::ResultExt;
use masking::PeekInterface;
use router_env::{instrument, tracing};

use crate::{
    core::errors::{self, RouterResponse, StorageErrorExt},
    routes::SessionState,
    services::ApplicationResponse,
    types::{api, domain, storage, transformers::ForeignTryFrom},
    utils::{OptionExt, StringExt},
};

const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT: i64 = 100;
const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS: i64 = 90;

#[derive(Debug)]
enum MerchantAccountOrProfile {
    MerchantAccount(Box<domain::MerchantAccount>),
    Profile(Box<domain::Profile>),
}

#[instrument(skip(state))]
pub async fn list_initial_delivery_attempts(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    api_constraints: api::webhook_events::EventListConstraints,
) -> RouterResponse<api::webhook_events::TotalEventsResponse> {
    let profile_id = api_constraints.profile_id.clone();
    let constraints = api::webhook_events::EventListConstraintsInternal::foreign_try_from(
        api_constraints.clone(),
    )?;

    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let (account, key_store) =
        get_account_and_key_store(state.clone(), merchant_id.clone(), profile_id.clone()).await?;

    let now = common_utils::date_time::now();
    let events_list_begin_time =
        (now.date() - time::Duration::days(INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS)).midnight();

    let (events, total_count) = match constraints {
        api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => {
            let events = match account {
                MerchantAccountOrProfile::MerchantAccount(merchant_account) => {
                    store
                        .list_initial_events_by_merchant_id_primary_object_id(
                            key_manager_state,
                            merchant_account.get_id(),
                            &object_id,
                            &key_store,
                        )
                        .await
                }
                MerchantAccountOrProfile::Profile(business_profile) => {
                    store
                        .list_initial_events_by_profile_id_primary_object_id(
                            key_manager_state,
                            business_profile.get_id(),
                            &object_id,
                            &key_store,
                        )
                        .await
                }
            }
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to list events with specified constraints")?;

            let total_count = i64::try_from(events.len())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error while converting from usize to i64")?;
            (events, total_count)
        }
        api_models::webhook_events::EventListConstraintsInternal::GenericFilter {
            created_after,
            created_before,
            limit,
            offset,
            event_classes,
            event_types,
            is_delivered,
        } => {
            let limit = match limit {
                Some(limit) if  limit <= INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT => Ok(Some(limit)),
                Some(limit) if limit > INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT => Err(
                    errors::ApiErrorResponse::InvalidRequestData{
                        message: format!("`limit` must be a number less than {INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT}")
                    }
                ),
                _  => Ok(Some(INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT)),
            }?;
            let offset = match offset {
                Some(offset) if offset > 0 => Some(offset),
                _ => None,
            };

            let event_classes = event_classes.unwrap_or(HashSet::new());
            let mut event_types = event_types.unwrap_or(HashSet::new());
            if !event_classes.is_empty() {
                event_types = finalize_event_types(event_classes, event_types).await?;
            }

            fp_utils::when(
                !created_after
                    .zip(created_before)
                    .map(|(created_after, created_before)| created_after <= created_before)
                    .unwrap_or(true),
                || {
                    Err(errors::ApiErrorResponse::InvalidRequestData { message: "The `created_after` timestamp must be an earlier timestamp compared to the `created_before` timestamp".to_string() })
                },
            )?;

            let created_after = match created_after {
                Some(created_after) => {
                    if created_after < events_list_begin_time {
                        Err(errors::ApiErrorResponse::InvalidRequestData { message: format!("`created_after` must be a timestamp within the past {INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS} days.") })
                    } else {
                        Ok(created_after)
                    }
                }
                None => Ok(events_list_begin_time),
            }?;

            let created_before = match created_before {
                Some(created_before) => {
                    if created_before < events_list_begin_time {
                        Err(errors::ApiErrorResponse::InvalidRequestData { message: format!("`created_before` must be a timestamp within the past {INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS} days.") })
                    } else {
                        Ok(created_before)
                    }
                }
                None => Ok(now),
            }?;

            let events = match account {
                MerchantAccountOrProfile::MerchantAccount(merchant_account) => {
                    store
                        .list_initial_events_by_merchant_id_constraints(
                            key_manager_state,
                            merchant_account.get_id(),
                            created_after,
                            created_before,
                            limit,
                            offset,
                            event_types.clone(),
                            is_delivered,
                            &key_store,
                        )
                        .await
                }
                MerchantAccountOrProfile::Profile(business_profile) => {
                    store
                        .list_initial_events_by_profile_id_constraints(
                            key_manager_state,
                            business_profile.get_id(),
                            created_after,
                            created_before,
                            limit,
                            offset,
                            event_types.clone(),
                            is_delivered,
                            &key_store,
                        )
                        .await
                }
            }
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to list events with specified constraints")?;

            let total_count = store
                .count_initial_events_by_constraints(
                    &merchant_id,
                    profile_id,
                    created_after,
                    created_before,
                    event_types,
                    is_delivered,
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to get total events count")?;

            (events, total_count)
        }
    };

    let events = events
        .into_iter()
        .map(api::webhook_events::EventListItemResponse::try_from)
        .collect::<Result<Vec<_>, _>>()?;

    Ok(ApplicationResponse::Json(
        api::webhook_events::TotalEventsResponse::new(total_count, events),
    ))
}

#[instrument(skip(state))]
pub async fn list_delivery_attempts(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    initial_attempt_id: String,
) -> RouterResponse<Vec<api::webhook_events::EventRetrieveResponse>> {
    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let key_store = store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let events = store
        .list_events_by_merchant_id_initial_attempt_id(
            key_manager_state,
            &merchant_id,
            &initial_attempt_id,
            &key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to list delivery attempts for initial event")?;

    if events.is_empty() {
        Err(error_stack::report!(
            errors::ApiErrorResponse::EventNotFound
        ))
        .attach_printable("No delivery attempts found with the specified `initial_attempt_id`")
    } else {
        Ok(ApplicationResponse::Json(
            events
                .into_iter()
                .map(api::webhook_events::EventRetrieveResponse::try_from)
                .collect::<Result<Vec<_>, _>>()?,
        ))
    }
}

#[instrument(skip(state))]
#[cfg(feature = "v1")]
pub async fn retry_delivery_attempt(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    event_id: String,
) -> RouterResponse<api::webhook_events::EventRetrieveResponse> {
    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let key_store = store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let event_to_retry = store
        .find_event_by_merchant_id_event_id(
            key_manager_state,
            &key_store.merchant_id,
            &event_id,
            &key_store,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?;

    let business_profile_id = event_to_retry
        .business_profile_id
        .get_required_value("business_profile_id")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to read business profile ID from event to retry")?;
    let business_profile = store
        .find_business_profile_by_profile_id(key_manager_state, &key_store, &business_profile_id)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to find business profile")?;

    let delivery_attempt = storage::enums::WebhookDeliveryAttempt::ManualRetry;
    let new_event_id = super::utils::generate_event_id();
    let idempotent_event_id = super::utils::get_idempotent_event_id(
        &event_to_retry.primary_object_id,
        event_to_retry.event_type,
        delivery_attempt,
    )
    .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
    .attach_printable("Failed to generate idempotent event ID")?;

    let now = common_utils::date_time::now();
    let new_event = domain::Event {
        event_id: new_event_id.clone(),
        event_type: event_to_retry.event_type,
        event_class: event_to_retry.event_class,
        is_webhook_notified: false,
        primary_object_id: event_to_retry.primary_object_id,
        primary_object_type: event_to_retry.primary_object_type,
        created_at: now,
        merchant_id: Some(business_profile.merchant_id.clone()),
        business_profile_id: Some(business_profile.get_id().to_owned()),
        primary_object_created_at: event_to_retry.primary_object_created_at,
        idempotent_event_id: Some(idempotent_event_id),
        initial_attempt_id: event_to_retry.initial_attempt_id,
        request: event_to_retry.request,
        response: None,
        delivery_attempt: Some(delivery_attempt),
        metadata: event_to_retry.metadata,
        is_overall_delivery_successful: Some(false),
    };

    let event = store
        .insert_event(key_manager_state, new_event, &key_store)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to insert event")?;

    // We only allow retrying deliveries for events with `request` populated.
    let request_content = event
        .request
        .as_ref()
        .get_required_value("request")
        .change_context(errors::ApiErrorResponse::InternalServerError)?
        .peek()
        .parse_struct("OutgoingWebhookRequestContent")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to parse webhook event request information")?;

    Box::pin(super::outgoing::trigger_webhook_and_raise_event(
        state.clone(),
        business_profile,
        &key_store,
        event,
        request_content,
        delivery_attempt,
        None,
        None,
    ))
    .await;

    let updated_event = store
        .find_event_by_merchant_id_event_id(
            key_manager_state,
            &key_store.merchant_id,
            &new_event_id,
            &key_store,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?;

    Ok(ApplicationResponse::Json(
        api::webhook_events::EventRetrieveResponse::try_from(updated_event)?,
    ))
}

async fn get_account_and_key_store(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    profile_id: Option<common_utils::id_type::ProfileId>,
) -> errors::RouterResult<(MerchantAccountOrProfile, domain::MerchantKeyStore)> {
    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let merchant_key_store = store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    match profile_id {
        // If profile ID is specified, return business profile, since a business profile is more
        // specific than a merchant account.
        Some(profile_id) => {
            let business_profile = store
                .find_business_profile_by_merchant_id_profile_id(
                    key_manager_state,
                    &merchant_key_store,
                    &merchant_id,
                    &profile_id,
                )
                .await
                .attach_printable_lazy(|| {
                    format!(
                        "Failed to find business profile by merchant_id `{merchant_id:?}` and profile_id `{profile_id:?}`. \
                        The merchant_id associated with the business profile `{profile_id:?}` may be \
                        different than the merchant_id specified (`{merchant_id:?}`)."
                    )
                })
                .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
                    id: profile_id.get_string_repr().to_owned(),
                })?;

            Ok((
                MerchantAccountOrProfile::Profile(Box::new(business_profile)),
                merchant_key_store,
            ))
        }

        None => {
            let merchant_account = store
                .find_merchant_account_by_merchant_id(
                    key_manager_state,
                    &merchant_id,
                    &merchant_key_store,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

            Ok((
                MerchantAccountOrProfile::MerchantAccount(Box::new(merchant_account)),
                merchant_key_store,
            ))
        }
    }
}

async fn finalize_event_types(
    event_classes: HashSet<common_enums::EventClass>,
    mut event_types: HashSet<common_enums::EventType>,
) -> CustomResult<HashSet<common_enums::EventType>, errors::ApiErrorResponse> {
    // Examples:
    // 1. event_classes = ["payments", "refunds"], event_types = ["payment_succeeded"]
    // 2. event_classes = ["refunds"], event_types = ["payment_succeeded"]

    // Create possible_event_types based on event_classes
    // Example 1: possible_event_types = ["payment_*", "refund_*"]
    // Example 2: possible_event_types = ["refund_*"]
    let possible_event_types = event_classes
        .clone()
        .into_iter()
        .flat_map(common_enums::EventClass::event_types)
        .collect::<HashSet<_>>();

    if event_types.is_empty() {
        return Ok(possible_event_types);
    }

    // Extend event_types if disjoint with event_classes
    // Example 1: event_types = ["payment_succeeded", "refund_*"], is_disjoint is used to extend "refund_*" and ignore "payment_*".
    // Example 2: event_types = ["payment_succeeded", "refund_*"], is_disjoint is only used to extend "refund_*".
    event_classes.into_iter().for_each(|class| {
        let valid_event_types = class.event_types();
        if event_types.is_disjoint(&valid_event_types) {
            event_types.extend(valid_event_types);
        }
    });

    // Validate event_types is a subset of possible_event_types
    // Example 1: event_types is a subset of possible_event_types (valid)
    // Example 2: event_types is not a subset of possible_event_types (error due to "payment_succeeded")
    if !event_types.is_subset(&possible_event_types) {
        return Err(error_stack::report!(
            errors::ApiErrorResponse::InvalidRequestData {
                message: "`event_types` must be a subset of `event_classes`".to_string(),
            }
        ));
    }

    Ok(event_types.clone())
}


// Struct: ApplepayPaymentMethod
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct ApplepayPaymentMethod


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

impl api::PaymentSession for for Redsys


// File: crates/test_utils/src/lib.rs
// Module: test_utils

#![allow(clippy::print_stdout, clippy::print_stderr)]

pub mod connector_auth;
pub mod newman_runner;


// Struct: PlaidRecipientCreateResponse
// File: crates/pm_auth/src/connector/plaid/transformers.rs
// Module: pm_auth
// Implementations: 0

pub struct PlaidRecipientCreateResponse


// Struct: OrderInformation
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct OrderInformation


// Struct: FiuuCardWithNTI
// File: crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FiuuCardWithNTI


// Implementation: impl ConnectorRedirectResponse for for Braintree
// File: crates/hyperswitch_connectors/src/connectors/braintree.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl ConnectorRedirectResponse for for Braintree


// Function: operation_derive
// File: crates/router_derive/src/lib.rs
// Module: router_derive

pub fn operation_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream


// Implementation: impl UploadFile for for Adyen
// File: crates/hyperswitch_connectors/src/connectors/adyen.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl UploadFile for for Adyen


// File: crates/router/tests/connectors/xendit.rs
// Module: router

use masking::Secret;
use router::{
    types::{self, api, storage::enums,
}};

use crate::utils::{self, ConnectorActions};
use test_utils::connector_auth;

#[derive(Clone, Copy)]
struct XenditTest;
impl ConnectorActions for XenditTest {}
impl utils::Connector for XenditTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Xendit;
        api::ConnectorData {
            connector: Box::new(Xendit::new()),
            connector_name: types::Connector::Xendit,
            get_token: types::api::GetToken::Connector,
            merchant_connector_id: None,
        }
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .xendit
                .expect("Missing connector authentication configuration").into(),
        )
    }

    fn get_name(&self) -> String {
        "xendit".to_string()
    }
}

static CONNECTOR: XenditTest = XenditTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(payment_method_details(), None, None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(payment_method_details(), None, None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR.make_payment(payment_method_details(), get_default_payment_info()).await.unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR.make_payment(payment_method_details(), get_default_payment_info()).await.unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: types::api::PaymentMethodData::Card(api::Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: api::PaymentMethodData::Card(api::Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: api::PaymentMethodData::Card(api::Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR.make_payment(payment_method_details(), get_default_payment_info()).await.unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


// Struct: MifinityErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/mifinity/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MifinityErrorResponse


        | "responsetoissuerarbitration" => Ok(common_enums::enums::DisputeStage::PreArbitration),

        "retrievalrequest" => Ok(common_enums::enums::DisputeStage::PreDispute),
        _ => Err(errors::ConnectorError::NotSupported {
            message: format!("Dispute stage {dispute_cycle}",),
            connector: "worldpayvantiv",
        }
        .into()),
    }
}

pub fn get_dispute_status(
    dispute_cycle: String,
    dispute_activities: Vec<Activity>,
) -> Result<common_enums::DisputeStatus, error_stack::Report<errors::ConnectorError>> {
    if let Some(activity) = get_last_non_auxiliary_activity_type(dispute_activities) {
        match activity.as_ref() {
            "Merchant Accept"
            | "Issuer Accepted Pre-Arbitration"
            | "Vantiv Accept"
            | "Sent Credit" => Ok(common_enums::DisputeStatus::DisputeAccepted),

            "Merchant Represent"
            | "Respond to Dispute"
            | "Respond to PreArb"
            | "Request Arbitration"
            | "Request Pre-Arbitration"
            | "Create Arbitration"
            | "Record Arbitration"
            | "Create Pre-Arbitration"
            | "File Arbitration"
            | "File Pre-Arbitration"
            | "File Visa Pre-Arbitration"
            | "Send Representment"
            | "Send Response"
            | "Arbitration"
            | "Arbitration (Mastercard)"
            | "Arbitration Chargeback"
            | "Issuer Declined Pre-Arbitration"
            | "Issuer Arbitration"
            | "Request Response to Pre-Arbitration"
            | "Vantiv Represent"
            | "Vantiv Respond"
            | "Auto Represent"
            | "Arbitration Ruling" => Ok(common_enums::DisputeStatus::DisputeChallenged),

            "Arbitration Lost" | "Unsuccessful Arbitration" | "Unsuccessful Pre-Arbitration" => {
                Ok(common_enums::DisputeStatus::DisputeLost)
            }

            "Arbitration Won"
            | "Arbitration Split"
            | "Successful Arbitration"
            | "Successful Pre-Arbitration" => Ok(common_enums::DisputeStatus::DisputeWon),

            "Chargeback Reversal" => Ok(common_enums::DisputeStatus::DisputeCancelled),

            "Receive Network Transaction" => Ok(common_enums::DisputeStatus::DisputeOpened),

            "Unaccept" | "Unrepresent" => Ok(common_enums::DisputeStatus::DisputeOpened),

            unexpected_activity => Err(errors::ConnectorError::UnexpectedResponseError(
                bytes::Bytes::from(format!("Dispute Activity: {unexpected_activity})")),
            )
            .into()),
        }
    } else {
        match connector_utils::normalize_string(dispute_cycle.clone())
            .change_context(errors::ConnectorError::RequestEncodingFailed)?
            .as_str()
        {
            "arbitration"
            | "arbitrationmastercard"
            | "arbitrationsplit"
            | "representment"
            | "issuerarbitration"
            | "prearbitration"
            | "responsetoissuerarbitration"
            | "arbitrationchargeback" => Ok(api_models::enums::DisputeStatus::DisputeChallenged),
            "chargebackreversal" | "issueracceptedprearbitration" | "arbitrationwon" => {
                Ok(api_models::enums::DisputeStatus::DisputeWon)
            }
            "arbitrationlost" | "issuerdeclinedprearbitration" => {
                Ok(api_models::enums::DisputeStatus::DisputeLost)
            }
            "firstchargeback" | "retrievalrequest" | "rapiddisputeresolution" => {
                Ok(api_models::enums::DisputeStatus::DisputeOpened)
            }
            dispute_cycle => Err(errors::ConnectorError::UnexpectedResponseError(
                bytes::Bytes::from(format!("Dispute Stage: {dispute_cycle}")),
            )
            .into()),
        }
    }
}

fn convert_string_to_primitive_date(
    item: Option<String>,
) -> Result<Option<time::PrimitiveDateTime>, error_stack::Report<errors::ConnectorError>> {
    item.map(|day| {
        let full_datetime_str = format!("{day}T00:00:00");
        let format =
            time::macros::format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
        time::PrimitiveDateTime::parse(&full_datetime_str, &format)
    })
    .transpose()
    .change_context(errors::ConnectorError::ParsingFailed)
}

impl TryFrom<ChargebackCase> for DisputeSyncResponse {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: ChargebackCase) -> Result<Self, Self::Error> {
        let amount = connector_utils::convert_amount(
            &StringMinorUnitForConnector,
            item.chargeback_amount,
            item.chargeback_currency_type,
        )?;
        Ok(Self {
            object_reference_id: api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::ConnectorTransactionId(item.vantiv_cnp_txn_id),
            ),
            amount,
            currency: item.chargeback_currency_type,
            dispute_stage: get_dispute_stage(item.cycle.clone())?,
            dispute_status: get_dispute_status(item.cycle.clone(), item.activity)?,
            connector_status: item.cycle.clone(),
            connector_dispute_id: item.case_id.clone(),
            connector_reason: item.reason_code_description.clone(),
            connector_reason_code: item.reason_code.clone(),
            challenge_required_by: convert_string_to_primitive_date(item.reply_by_day.clone())?,
            created_at: convert_string_to_primitive_date(item.date_received_by_vantiv_cnp.clone())?,
            updated_at: None,
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ActivityType {
    MerchantAcceptsLiability,
    MerchantRepresent,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename = "chargebackUpdateRequest", rename_all = "camelCase")]
pub struct ChargebackUpdateRequest {
    #[serde(rename = "@xmlns")]
    xmlns: String,
    activity_type: ActivityType,
}

impl From<&SubmitEvidenceRouterData> for ChargebackUpdateRequest {
    fn from(_item: &SubmitEvidenceRouterData) -> Self {
        Self {
            xmlns: worldpayvantiv_constants::XML_CHARGEBACK.to_string(),
            activity_type: ActivityType::MerchantRepresent,
        }
    }
}

impl From<&AcceptDisputeRouterData> for ChargebackUpdateRequest {
    fn from(_item: &AcceptDisputeRouterData) -> Self {
        Self {
            xmlns: worldpayvantiv_constants::XML_CHARGEBACK.to_string(),
            activity_type: ActivityType::MerchantAcceptsLiability,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename = "chargebackDocumentUploadResponse", rename_all = "camelCase")]
pub struct ChargebackDocumentUploadResponse {
    #[serde(rename = "@xmlns")]
    pub xmlns: String,
    pub merchant_id: String,
    pub case_id: String,
    pub document_id: Option<String>,
    pub response_code: WorldpayvantivFileUploadResponseCode,
    pub response_message: String,
}

#[derive(Debug, strum::Display, Serialize, Deserialize, PartialEq, Clone, Copy)]
pub enum WorldpayvantivFileUploadResponseCode {
    #[serde(rename = "000")]
    #[strum(serialize = "000")]
    Success,

    #[serde(rename = "001")]
    #[strum(serialize = "001")]
    InvalidMerchant,

    #[serde(rename = "002")]
    #[strum(serialize = "002")]
    FutureUse002,

    #[serde(rename = "003")]
    #[strum(serialize = "003")]
    CaseNotFound,

    #[serde(rename = "004")]
    #[strum(serialize = "004")]
    CaseNotInMerchantQueue,

    #[serde(rename = "005")]
    #[strum(serialize = "005")]
    DocumentAlreadyExists,

    #[serde(rename = "006")]
    #[strum(serialize = "006")]
    InternalError,

    #[serde(rename = "007")]
    #[strum(serialize = "007")]
    FutureUse007,

    #[serde(rename = "008")]
    #[strum(serialize = "008")]
    MaxDocumentLimitReached,

    #[serde(rename = "009")]
    #[strum(serialize = "009")]
    DocumentNotFound,

    #[serde(rename = "010")]
    #[strum(serialize = "010")]
    CaseNotInValidCycle,

    #[serde(rename = "011")]
    #[strum(serialize = "011")]
    ServerBusy,

    #[serde(rename = "012")]
    #[strum(serialize = "012")]
    FileSizeExceedsLimit,

    #[serde(rename = "013")]
    #[strum(serialize = "013")]
    InvalidFileContent,

    #[serde(rename = "014")]
    #[strum(serialize = "014")]
    UnableToConvert,

    #[serde(rename = "015")]
    #[strum(serialize = "015")]
    InvalidImageSize,

    #[serde(rename = "016")]
    #[strum(serialize = "016")]
    MaxDocumentPageCountReached,
}

fn is_file_uploaded(vantiv_file_upload_status: WorldpayvantivFileUploadResponseCode) -> bool {
    match vantiv_file_upload_status {
        WorldpayvantivFileUploadResponseCode::Success
        | WorldpayvantivFileUploadResponseCode::FutureUse002
        | WorldpayvantivFileUploadResponseCode::FutureUse007 => true,
        WorldpayvantivFileUploadResponseCode::InvalidMerchant
        | WorldpayvantivFileUploadResponseCode::CaseNotFound
        | WorldpayvantivFileUploadResponseCode::CaseNotInMerchantQueue
        | WorldpayvantivFileUploadResponseCode::DocumentAlreadyExists
        | WorldpayvantivFileUploadResponseCode::InternalError
        | WorldpayvantivFileUploadResponseCode::MaxDocumentLimitReached
        | WorldpayvantivFileUploadResponseCode::DocumentNotFound
        | WorldpayvantivFileUploadResponseCode::CaseNotInValidCycle
        | WorldpayvantivFileUploadResponseCode::ServerBusy
        | WorldpayvantivFileUploadResponseCode::FileSizeExceedsLimit
        | WorldpayvantivFileUploadResponseCode::InvalidFileContent
        | WorldpayvantivFileUploadResponseCode::UnableToConvert
        | WorldpayvantivFileUploadResponseCode::InvalidImageSize
        | WorldpayvantivFileUploadResponseCode::MaxDocumentPageCountReached => false,
    }
}

impl
    TryFrom<
        ResponseRouterData<
            Upload,
            ChargebackDocumentUploadResponse,
            UploadFileRequestData,
            UploadFileResponse,
        >,
    > for RouterData<Upload, UploadFileRequestData, UploadFileResponse>
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ResponseRouterData<
            Upload,
            ChargebackDocumentUploadResponse,
            UploadFileRequestData,
            UploadFileResponse,
        >,
    ) -> Result<Self, Self::Error> {
        let response = if is_file_uploaded(item.response.response_code) {
            let provider_file_id = item
                .response
                .document_id
                .ok_or(errors::ConnectorError::MissingConnectorTransactionID)?;

            Ok(UploadFileResponse { provider_file_id })
        } else {
            Err(ErrorResponse {
                code: item.response.response_code.to_string(),
                message: item.response.response_message.clone(),
                reason: Some(item.response.response_message.clone()),
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        };

        Ok(Self {
            response,
            ..item.data
        })
    }
}

impl
    TryFrom<
        ResponseRouterData<
            Retrieve,
            ChargebackDocumentUploadResponse,
            RetrieveFileRequestData,
            RetrieveFileResponse,
        >,
    > for RouterData<Retrieve, RetrieveFileRequestData, RetrieveFileResponse>
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ResponseRouterData<
            Retrieve,
            ChargebackDocumentUploadResponse,
            RetrieveFileRequestData,
            RetrieveFileResponse,
        >,
    ) -> Result<Self, Self::Error> {
        Ok(RetrieveFileRouterData {
            response: Err(ErrorResponse {
                code: item.response.response_code.to_string(),
                message: item.response.response_message.clone(),
                reason: Some(item.response.response_message.clone()),
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
            ..item.data.clone()
        })
    }
}

fn get_last_non_auxiliary_activity_type(activities: Vec<Activity>) -> Option<String> {
    let auxiliary_activities: std::collections::HashSet<&'static str> = [
        "Add Note",
        "Attach Document",
        "Attempted Attach Document",
        "Delete Document",
        "Update Document",
        "Move To Error Queue",
        "Assign to Vantiv",
        "Assign To Merchant",
        "Merchant Auto Assign",
        "Issuer Recalled",
        "Network Decision",
        "Request Declined",
        "Sent Gift",
        "Successful PayPal",
    ]
    .iter()
    .copied()
    .collect();

    let mut last_non_auxiliary_activity = None;

    for activity in activities {
        let auxiliary_activity = activity
            .activity_type
            .as_deref()
            .map(|activity_type| auxiliary_activities.contains(activity_type))
            .unwrap_or(false);

        if !auxiliary_activity {
            last_non_auxiliary_activity = activity.activity_type.clone()
        }
    }
    last_non_auxiliary_activity
}


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

impl api::RefundSync for for Affirm


// Implementation: impl ConnectorSpecifications for for Braintree
// File: crates/hyperswitch_connectors/src/connectors/braintree.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl ConnectorSpecifications for for Braintree


// Struct: WebCheckoutDetails
// File: crates/hyperswitch_connectors/src/connectors/amazonpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct WebCheckoutDetails


// Struct: StaxPaymentsResponse
// File: crates/hyperswitch_connectors/src/connectors/stax/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct StaxPaymentsResponse


// Struct: PaymentConfirm
// File: crates/router/src/core/payments/operations/payment_confirm.rs
// Module: router
// Implementations: 0

pub struct PaymentConfirm


// Struct: SmtpServer
// File: crates/external_services/src/email/smtp.rs
// Module: external_services
// Implementations: 2
// Traits: EmailClient

pub struct SmtpServer


// Implementation: impl PaymentMethodIntentConfirm
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Methods: 1 total (1 public)

impl PaymentMethodIntentConfirm


// Struct: PaymentStatus
// File: crates/router/src/core/payments/operations/payment_status.rs
// Module: router
// Implementations: 0

pub struct PaymentStatus


// Struct: Apm
// File: crates/hyperswitch_connectors/src/connectors/globalpay/requests.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Apm


// Implementation: impl Barclaycard
// File: crates/hyperswitch_connectors/src/connectors/barclaycard.rs
// Module: hyperswitch_connectors
// Methods: 2 total (2 public)

impl Barclaycard


// Function: search_index_to_opensearch_index
// File: crates/analytics/src/opensearch.rs
// Module: analytics

pub fn search_index_to_opensearch_index(&self, index: SearchIndex) -> String


// Function: get_primary_details_as_value
// File: crates/api_models/src/admin.rs
// Module: api_models

pub fn get_primary_details_as_value(
        &self,
    ) -> CustomResult<Option<serde_json::Value>, errors::ParsingError>


// Struct: RefundErrorMessageDistributionRow
// File: crates/analytics/src/refunds/accumulator.rs
// Module: analytics
// Implementations: 0

pub struct RefundErrorMessageDistributionRow


// Function: from_bytes
// File: crates/redis_interface/src/types.rs
// Module: redis_interface

pub fn from_bytes(val: Vec<u8>) -> Self


// Function: new
// File: crates/external_services/src/hubspot_proxy.rs
// Module: external_services

pub fn new(
        country: String,
        hubspot_form_id: String,
        firstname: Secret<String>,
        email: Secret<String>,
        company_name: String,
        website: String,
    ) -> Self


// Struct: VgsTokenResponseItem
// File: crates/hyperswitch_connectors/src/connectors/vgs/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct VgsTokenResponseItem


// Struct: ValidateResult
// File: crates/router/src/core/payments/operations.rs
// Module: router
// Implementations: 0

pub struct ValidateResult


// Struct: Server
// File: crates/scheduler/src/configs/settings.rs
// Module: scheduler
// Implementations: 0

pub struct Server


// Implementation: impl MandateNew
// File: crates/diesel_models/src/query/mandate.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl MandateNew


// Implementation: impl ProfileNew
// File: crates/diesel_models/src/query/business_profile.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl ProfileNew


// Implementation: impl webhooks::IncomingWebhook for for Cashtocode
// File: crates/hyperswitch_connectors/src/connectors/cashtocode.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl webhooks::IncomingWebhook for for Cashtocode


// File: crates/common_utils/src/id_type/global_id/refunds.rs
// Module: common_utils
// Public functions: 2
// Public structs: 1

use error_stack::ResultExt;

use crate::errors;

/// A global id that can be used to identify a refund
#[derive(
    Debug,
    Clone,
    Hash,
    PartialEq,
    Eq,
    serde::Serialize,
    serde::Deserialize,
    diesel::expression::AsExpression,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
pub struct GlobalRefundId(super::GlobalId);

// Database related implementations so that this field can be used directly in the database tables
crate::impl_queryable_id_type!(GlobalRefundId);

impl GlobalRefundId {
    /// Get string representation of the id
    pub fn get_string_repr(&self) -> &str {
        self.0.get_string_repr()
    }

    /// Generate a new GlobalRefundId from a cell id
    pub fn generate(cell_id: &crate::id_type::CellId) -> Self {
        let global_id = super::GlobalId::generate(cell_id, super::GlobalEntity::Refund);
        Self(global_id)
    }
}

// TODO: refactor the macro to include this id use case as well
impl TryFrom<std::borrow::Cow<'static, str>> for GlobalRefundId {
    type Error = error_stack::Report<errors::ValidationError>;
    fn try_from(value: std::borrow::Cow<'static, str>) -> Result<Self, Self::Error> {
        let merchant_ref_id = super::GlobalId::from_string(value).change_context(
            errors::ValidationError::IncorrectValueProvided {
                field_name: "refund_id",
            },
        )?;
        Ok(Self(merchant_ref_id))
    }
}

// TODO: refactor the macro to include this id use case as well
impl<DB> diesel::serialize::ToSql<diesel::sql_types::Text, DB> for GlobalRefundId
where
    DB: diesel::backend::Backend,
    super::GlobalId: diesel::serialize::ToSql<diesel::sql_types::Text, DB>,
{
    fn to_sql<'b>(
        &'b self,
        out: &mut diesel::serialize::Output<'b, '_, DB>,
    ) -> diesel::serialize::Result {
        self.0.to_sql(out)
    }
}

impl<DB> diesel::deserialize::FromSql<diesel::sql_types::Text, DB> for GlobalRefundId
where
    DB: diesel::backend::Backend,
    super::GlobalId: diesel::deserialize::FromSql<diesel::sql_types::Text, DB>,
{
    fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
        super::GlobalId::from_sql(value).map(Self)
    }
}


// Implementation: impl AuthenticationConnectorAccountMap
// File: crates/common_types/src/payments.rs
// Module: common_types
// Methods: 2 total (1 public)

impl AuthenticationConnectorAccountMap


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/juspaythreedsserver.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// File: crates/analytics/src/health_check.rs
// Module: analytics

use common_utils::errors::CustomResult;

use crate::types::QueryExecutionError;

#[async_trait::async_trait]
pub trait HealthCheck {
    async fn deep_health_check(&self) -> CustomResult<(), QueryExecutionError>;
}


// Implementation: impl Responder
// File: crates/router/src/routes/routing.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Implementation: impl MessagingInterface for for EventLogger
// File: crates/router/src/events/event_logger.rs
// Module: router
// Methods: 1 total (0 public)

impl MessagingInterface for for EventLogger


// Implementation: impl ConnectorRedirectResponse for for Paypal
// File: crates/hyperswitch_connectors/src/connectors/paypal.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl ConnectorRedirectResponse for for Paypal


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

pub struct OpennodePaymentsResponseData


// Function: find_by_merchant_id_authentication_id
// File: crates/diesel_models/src/query/authentication.rs
// Module: diesel_models

pub fn find_by_merchant_id_authentication_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        authentication_id: &common_utils::id_type::AuthenticationId,
    ) -> StorageResult<Self>


// Implementation: impl api::Payment for for Gigadat
// File: crates/hyperswitch_connectors/src/connectors/gigadat.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Payment for for Gigadat


// Struct: ThreedsecureioPostAuthenticationResponse
// File: crates/hyperswitch_connectors/src/connectors/threedsecureio/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreedsecureioPostAuthenticationResponse


// Struct: ExternalToken
// File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ExternalToken


// Implementation: impl ConnectorPreAuthenticationVersionCall for for Gpayments
// File: crates/hyperswitch_connectors/src/connectors/gpayments.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorPreAuthenticationVersionCall for for Gpayments


// Function: get_error_code
// File: crates/hyperswitch_connectors/src/connectors/tokenio/transformers.rs
// Module: hyperswitch_connectors

pub fn get_error_code(&self) -> String


// Struct: ScoreDataEventResponse
// File: crates/router/src/core/payments/routing/utils.rs
// Module: router
// Implementations: 0

pub struct ScoreDataEventResponse


// Implementation: impl api::Refund for for Facilitapay
// File: crates/hyperswitch_connectors/src/connectors/facilitapay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Refund for for Facilitapay


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

impl api::MandateSetup for for Gocardless


// Function: get_current_date
// File: crates/router/src/types/storage/revenue_recovery_redis_operation.rs
// Module: router

// Documentation: Get current date in `yyyy-mm-dd` format.

pub fn get_current_date() -> String


// Struct: TransactionAuthChargeResponseBody
// File: crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TransactionAuthChargeResponseBody


// Function: get_idempotent_event_id
// File: crates/router/src/core/webhooks/utils.rs
// Module: router

pub fn get_idempotent_event_id(
    primary_object_id: &str,
    event_type: types::storage::enums::EventType,
    delivery_attempt: types::storage::enums::WebhookDeliveryAttempt,
) -> Result<String, Report<errors::WebhooksFlowError>>


// Function: apple_pay_certificates_migration
// File: crates/router/src/core/apple_pay_certificates_migration.rs
// Module: router

pub fn apple_pay_certificates_migration(
    state: SessionState,
    req: &apple_pay_certificates_migration::ApplePayCertificatesMigrationRequest,
) -> CustomResult<
    services::ApplicationResponse<
        apple_pay_certificates_migration::ApplePayCertificatesMigrationResponse,
    >,
    errors::ApiErrorResponse,
>


// File: crates/storage_impl/src/mock_db/payment_attempt.rs
// Module: storage_impl

use common_utils::errors::CustomResult;
#[cfg(feature = "v2")]
use common_utils::{id_type, types::keymanager::KeyManagerState};
use diesel_models::enums as storage_enums;
#[cfg(feature = "v2")]
use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore;
#[cfg(feature = "v1")]
use hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptNew;
use hyperswitch_domain_models::payments::payment_attempt::{
    PaymentAttempt, PaymentAttemptInterface, PaymentAttemptUpdate,
};

use super::MockDb;
use crate::errors::StorageError;
#[cfg(feature = "v1")]
use crate::DataModelExt;

#[async_trait::async_trait]
impl PaymentAttemptInterface for MockDb {
    type Error = StorageError;
    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id(
        &self,
        _payment_id: &common_utils::id_type::PaymentId,
        _merchant_id: &common_utils::id_type::MerchantId,
        _attempt_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_filters_for_payments(
        &self,
        _pi: &[hyperswitch_domain_models::payments::PaymentIntent],
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<
        hyperswitch_domain_models::payments::payment_attempt::PaymentListFilters,
        StorageError,
    > {
        Err(StorageError::MockDbError)?
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_total_count_of_filtered_payment_attempts(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _active_attempt_ids: &[String],
        _connector: Option<Vec<api_models::enums::Connector>>,
        _payment_method: Option<Vec<common_enums::PaymentMethod>>,
        _payment_method_type: Option<Vec<common_enums::PaymentMethodType>>,
        _authentication_type: Option<Vec<common_enums::AuthenticationType>>,
        _merchanat_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        _card_network: Option<Vec<storage_enums::CardNetwork>>,
        _card_discovery: Option<Vec<storage_enums::CardDiscovery>>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<i64, StorageError> {
        Err(StorageError::MockDbError)?
    }

    #[cfg(all(feature = "v2", feature = "olap"))]
    async fn get_total_count_of_filtered_payment_attempts(
        &self,
        _merchant_id: &id_type::MerchantId,
        _active_attempt_ids: &[String],
        _connector: Option<Vec<api_models::enums::Connector>>,
        _payment_method_type: Option<Vec<common_enums::PaymentMethod>>,
        _payment_method_subtype: Option<Vec<common_enums::PaymentMethodType>>,
        _authentication_type: Option<Vec<common_enums::AuthenticationType>>,
        _merchant_connector_id: Option<Vec<id_type::MerchantConnectorAccountId>>,
        _card_network: Option<Vec<storage_enums::CardNetwork>>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<i64, StorageError> {
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_attempt_id_merchant_id(
        &self,
        _attempt_id: &str,
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v2")]
    async fn find_payment_attempt_by_id(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        _attempt_id: &id_type::GlobalAttemptId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v2")]
    async fn find_payment_attempts_by_payment_intent_id(
        &self,
        _key_manager_state: &KeyManagerState,
        _id: &id_type::GlobalPaymentId,
        _merchant_key_store: &MerchantKeyStore,
        _storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<PaymentAttempt>, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_preprocessing_id_merchant_id(
        &self,
        _preprocessing_id: &str,
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_merchant_id_connector_txn_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_txn_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v2")]
    async fn find_payment_attempt_by_profile_id_connector_transaction_id(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        _profile_id: &id_type::ProfileId,
        _connector_transaction_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_attempts_by_merchant_id_payment_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _payment_id: &common_utils::id_type::PaymentId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<Vec<PaymentAttempt>, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    #[allow(clippy::panic)]
    async fn insert_payment_attempt(
        &self,
        payment_attempt: PaymentAttemptNew,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let mut payment_attempts = self.payment_attempts.lock().await;
        let time = common_utils::date_time::now();
        let payment_attempt = PaymentAttempt {
            payment_id: payment_attempt.payment_id,
            merchant_id: payment_attempt.merchant_id,
            attempt_id: payment_attempt.attempt_id,
            status: payment_attempt.status,
            net_amount: payment_attempt.net_amount,
            currency: payment_attempt.currency,
            save_to_locker: payment_attempt.save_to_locker,
            connector: payment_attempt.connector,
            error_message: payment_attempt.error_message,
            offer_amount: payment_attempt.offer_amount,
            payment_method_id: payment_attempt.payment_method_id,
            payment_method: payment_attempt.payment_method,
            connector_transaction_id: None,
            capture_method: payment_attempt.capture_method,
            capture_on: payment_attempt.capture_on,
            confirm: payment_attempt.confirm,
            authentication_type: payment_attempt.authentication_type,
            created_at: payment_attempt.created_at.unwrap_or(time),
            modified_at: payment_attempt.modified_at.unwrap_or(time),
            last_synced: payment_attempt.last_synced,
            cancellation_reason: payment_attempt.cancellation_reason,
            amount_to_capture: payment_attempt.amount_to_capture,
            mandate_id: None,
            browser_info: None,
            payment_token: None,
            error_code: payment_attempt.error_code,
            connector_metadata: None,
            charge_id: None,
            payment_experience: payment_attempt.payment_experience,
            payment_method_type: payment_attempt.payment_method_type,
            payment_method_data: payment_attempt.payment_method_data,
            business_sub_label: payment_attempt.business_sub_label,
            straight_through_algorithm: payment_attempt.straight_through_algorithm,
            mandate_details: payment_attempt.mandate_details,
            preprocessing_step_id: payment_attempt.preprocessing_step_id,
            error_reason: payment_attempt.error_reason,
            multiple_capture_count: payment_attempt.multiple_capture_count,
            connector_response_reference_id: None,
            amount_capturable: payment_attempt.amount_capturable,
            updated_by: storage_scheme.to_string(),
            authentication_data: payment_attempt.authentication_data,
            encoded_data: payment_attempt.encoded_data,
            merchant_connector_id: payment_attempt.merchant_connector_id,
            unified_code: payment_attempt.unified_code,
            unified_message: payment_attempt.unified_message,
            external_three_ds_authentication_attempted: payment_attempt
                .external_three_ds_authentication_attempted,
            authentication_connector: payment_attempt.authentication_connector,
            authentication_id: payment_attempt.authentication_id,
            mandate_data: payment_attempt.mandate_data,
            payment_method_billing_address_id: payment_attempt.payment_method_billing_address_id,
            fingerprint_id: payment_attempt.fingerprint_id,
            client_source: payment_attempt.client_source,
            client_version: payment_attempt.client_version,
            customer_acceptance: payment_attempt.customer_acceptance,
            organization_id: payment_attempt.organization_id,
            profile_id: payment_attempt.profile_id,
            connector_mandate_detail: payment_attempt.connector_mandate_detail,
            request_extended_authorization: payment_attempt.request_extended_authorization,
            extended_authorization_applied: payment_attempt.extended_authorization_applied,
            capture_before: payment_attempt.capture_before,
            card_discovery: payment_attempt.card_discovery,
            charges: None,
            issuer_error_code: None,
            issuer_error_message: None,
            processor_merchant_id: payment_attempt.processor_merchant_id,
            created_by: payment_attempt.created_by,
            setup_future_usage_applied: payment_attempt.setup_future_usage_applied,
            routing_approach: payment_attempt.routing_approach,
            connector_request_reference_id: payment_attempt.connector_request_reference_id,
            debit_routing_savings: None,
            network_transaction_id: payment_attempt.network_transaction_id,
            is_overcapture_enabled: None,
            network_details: payment_attempt.network_details,
            is_stored_credential: payment_attempt.is_stored_credential,
        };
        payment_attempts.push(payment_attempt.clone());
        Ok(payment_attempt)
    }

    #[cfg(feature = "v2")]
    #[allow(clippy::panic)]
    async fn insert_payment_attempt(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        _payment_attempt: PaymentAttempt,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    // safety: only used for testing
    #[allow(clippy::unwrap_used)]
    async fn update_payment_attempt_with_attempt_id(
        &self,
        this: PaymentAttempt,
        payment_attempt: PaymentAttemptUpdate,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let mut payment_attempts = self.payment_attempts.lock().await;

        let item = payment_attempts
            .iter_mut()
            .find(|item| item.attempt_id == this.attempt_id)
            .unwrap();

        *item = PaymentAttempt::from_storage_model(
            payment_attempt
                .to_storage_model()
                .apply_changeset(this.to_storage_model()),
        );

        Ok(item.clone())
    }

    #[cfg(feature = "v2")]
    async fn update_payment_attempt(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        _this: PaymentAttempt,
        _payment_attempt: PaymentAttemptUpdate,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
        &self,
        _connector_transaction_id: &common_utils::types::ConnectorTransactionId,
        _payment_id: &common_utils::id_type::PaymentId,
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    // safety: only used for testing
    #[allow(clippy::unwrap_used)]
    async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id(
        &self,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let payment_attempts = self.payment_attempts.lock().await;

        Ok(payment_attempts
            .iter()
            .find(|payment_attempt| {
                payment_attempt.payment_id == *payment_id
                    && payment_attempt.merchant_id.eq(merchant_id)
            })
            .cloned()
            .unwrap())
    }

    #[cfg(feature = "v1")]
    #[allow(clippy::unwrap_used)]
    async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id(
        &self,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let payment_attempts = self.payment_attempts.lock().await;

        Ok(payment_attempts
            .iter()
            .find(|payment_attempt| {
                payment_attempt.payment_id == *payment_id
                    && payment_attempt.merchant_id.eq(merchant_id)
                    && (payment_attempt.status == storage_enums::AttemptStatus::PartialCharged
                        || payment_attempt.status == storage_enums::AttemptStatus::Charged)
            })
            .cloned()
            .unwrap())
    }

    #[cfg(feature = "v2")]
    #[allow(clippy::unwrap_used)]
    async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        payment_id: &id_type::GlobalPaymentId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let payment_attempts = self.payment_attempts.lock().await;

        Ok(payment_attempts
            .iter()
            .find(|payment_attempt| {
                payment_attempt.payment_id == *payment_id
                    && (payment_attempt.status == storage_enums::AttemptStatus::PartialCharged
                        || payment_attempt.status == storage_enums::AttemptStatus::Charged)
            })
            .cloned()
            .unwrap())
    }
}


// Struct: PartnerReferralRestApiIntegration
// File: crates/router/src/types/api/connector_onboarding/paypal.rs
// Module: router
// Implementations: 0

pub struct PartnerReferralRestApiIntegration


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

pub struct PaymentMethodType


// Function: get_webhook_response
// File: crates/hyperswitch_connectors/src/connectors/getnet/transformers.rs
// Module: hyperswitch_connectors

pub fn get_webhook_response(
    body: &[u8],
) -> CustomResult<GetnetWebhookNotificationResponse, errors::ConnectorError>


// Struct: Profile
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Profile


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

pub fn apply_changeset(self, source: Customer) -> Customer


// File: crates/analytics/src/payment_intents/core.rs
// Module: analytics
// Public functions: 3

#![allow(dead_code)]
use std::collections::{HashMap, HashSet};

use api_models::analytics::{
    payment_intents::{
        MetricsBucketResponse, PaymentIntentDimensions, PaymentIntentMetrics,
        PaymentIntentMetricsBucketIdentifier,
    },
    GetPaymentIntentFiltersRequest, GetPaymentIntentMetricRequest, PaymentIntentFilterValue,
    PaymentIntentFiltersResponse, PaymentIntentsAnalyticsMetadata, PaymentIntentsMetricsResponse,
};
use bigdecimal::ToPrimitive;
use common_enums::Currency;
use common_utils::{errors::CustomResult, types::TimeRange};
use currency_conversion::{conversion::convert, types::ExchangeRates};
use error_stack::ResultExt;
use router_env::{
    instrument, logger,
    tracing::{self, Instrument},
};

use super::{
    filters::{get_payment_intent_filter_for_dimension, PaymentIntentFilterRow},
    metrics::PaymentIntentMetricRow,
    sankey::{get_sankey_data, SankeyRow},
    PaymentIntentMetricsAccumulator,
};
use crate::{
    enums::AuthInfo,
    errors::{AnalyticsError, AnalyticsResult},
    metrics,
    payment_intents::PaymentIntentMetricAccumulator,
    AnalyticsProvider,
};

#[derive(Debug)]
pub enum TaskType {
    MetricTask(
        PaymentIntentMetrics,
        CustomResult<
            HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>,
            AnalyticsError,
        >,
    ),
}

#[instrument(skip_all)]
pub async fn get_sankey(
    pool: &AnalyticsProvider,
    auth: &AuthInfo,
    req: TimeRange,
) -> AnalyticsResult<Vec<SankeyRow>> {
    match pool {
        AnalyticsProvider::Sqlx(_) => Err(AnalyticsError::NotImplemented(
            "Sankey not implemented for sqlx",
        ))?,
        AnalyticsProvider::Clickhouse(ckh_pool)
        | AnalyticsProvider::CombinedCkh(_, ckh_pool)
        | AnalyticsProvider::CombinedSqlx(_, ckh_pool) => {
            let sankey_rows = get_sankey_data(ckh_pool, auth, &req)
                .await
                .change_context(AnalyticsError::UnknownError)?;
            Ok(sankey_rows)
        }
    }
}

#[instrument(skip_all)]
pub async fn get_metrics(
    pool: &AnalyticsProvider,
    ex_rates: &Option<ExchangeRates>,
    auth: &AuthInfo,
    req: GetPaymentIntentMetricRequest,
) -> AnalyticsResult<PaymentIntentsMetricsResponse<MetricsBucketResponse>> {
    let mut metrics_accumulator: HashMap<
        PaymentIntentMetricsBucketIdentifier,
        PaymentIntentMetricsAccumulator,
    > = HashMap::new();

    let mut set = tokio::task::JoinSet::new();
    for metric_type in req.metrics.iter().cloned() {
        let req = req.clone();
        let pool = pool.clone();
        let task_span = tracing::debug_span!(
            "analytics_payment_intents_metrics_query",
            payment_metric = metric_type.as_ref()
        );

        // TODO: lifetime issues with joinset,
        // can be optimized away if joinset lifetime requirements are relaxed
        let auth_scoped = auth.to_owned();
        set.spawn(
            async move {
                let data = pool
                    .get_payment_intent_metrics(
                        &metric_type,
                        &req.group_by_names.clone(),
                        &auth_scoped,
                        &req.filters,
                        req.time_series.map(|t| t.granularity),
                        &req.time_range,
                    )
                    .await
                    .change_context(AnalyticsError::UnknownError);
                TaskType::MetricTask(metric_type, data)
            }
            .instrument(task_span),
        );
    }

    while let Some(task_type) = set
        .join_next()
        .await
        .transpose()
        .change_context(AnalyticsError::UnknownError)?
    {
        match task_type {
            TaskType::MetricTask(metric, data) => {
                let data = data?;
                let attributes = router_env::metric_attributes!(
                    ("metric_type", metric.to_string()),
                    ("source", pool.to_string()),
                );

                let value = u64::try_from(data.len());
                if let Ok(val) = value {
                    metrics::BUCKETS_FETCHED.record(val, attributes);
                    logger::debug!("Attributes: {:?}, Buckets fetched: {}", attributes, val);
                }

                for (id, value) in data {
                    logger::debug!(bucket_id=?id, bucket_value=?value, "Bucket row for metric {metric}");
                    let metrics_builder = metrics_accumulator.entry(id).or_default();
                    match metric {
                        PaymentIntentMetrics::SuccessfulSmartRetries
                        | PaymentIntentMetrics::SessionizedSuccessfulSmartRetries => {
                            metrics_builder
                                .successful_smart_retries
                                .add_metrics_bucket(&value)
                        }
                        PaymentIntentMetrics::TotalSmartRetries
                        | PaymentIntentMetrics::SessionizedTotalSmartRetries => metrics_builder
                            .total_smart_retries
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::SmartRetriedAmount
                        | PaymentIntentMetrics::SessionizedSmartRetriedAmount => metrics_builder
                            .smart_retried_amount
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::PaymentIntentCount
                        | PaymentIntentMetrics::SessionizedPaymentIntentCount => metrics_builder
                            .payment_intent_count
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::PaymentsSuccessRate
                        | PaymentIntentMetrics::SessionizedPaymentsSuccessRate => metrics_builder
                            .payments_success_rate
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::SessionizedPaymentProcessedAmount
                        | PaymentIntentMetrics::PaymentProcessedAmount => metrics_builder
                            .payment_processed_amount
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::SessionizedPaymentsDistribution => metrics_builder
                            .payments_distribution
                            .add_metrics_bucket(&value),
                    }
                }

                logger::debug!(
                    "Analytics Accumulated Results: metric: {}, results: {:#?}",
                    metric,
                    metrics_accumulator
                );
            }
        }
    }

    let mut success = 0;
    let mut success_without_smart_retries = 0;
    let mut total_smart_retried_amount = 0;
    let mut total_smart_retried_amount_in_usd = 0;
    let mut total_smart_retried_amount_without_smart_retries = 0;
    let mut total_smart_retried_amount_without_smart_retries_in_usd = 0;
    let mut total = 0;
    let mut total_payment_processed_amount = 0;
    let mut total_payment_processed_amount_in_usd = 0;
    let mut total_payment_processed_count = 0;
    let mut total_payment_processed_amount_without_smart_retries = 0;
    let mut total_payment_processed_amount_without_smart_retries_in_usd = 0;
    let mut total_payment_processed_count_without_smart_retries = 0;
    let query_data: Vec<MetricsBucketResponse> = metrics_accumulator
        .into_iter()
        .map(|(id, val)| {
            let mut collected_values = val.collect();
            if let Some(success_count) = collected_values.successful_payments {
                success += success_count;
            }
            if let Some(success_count) = collected_values.successful_payments_without_smart_retries
            {
                success_without_smart_retries += success_count;
            }
            if let Some(total_count) = collected_values.total_payments {
                total += total_count;
            }
            if let Some(retried_amount) = collected_values.smart_retried_amount {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(retried_amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.smart_retried_amount_in_usd = amount_in_usd;
                total_smart_retried_amount += retried_amount;
                total_smart_retried_amount_in_usd += amount_in_usd.unwrap_or(0);
            }
            if let Some(retried_amount) =
                collected_values.smart_retried_amount_without_smart_retries
            {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(retried_amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.smart_retried_amount_without_smart_retries_in_usd = amount_in_usd;
                total_smart_retried_amount_without_smart_retries += retried_amount;
                total_smart_retried_amount_without_smart_retries_in_usd +=
                    amount_in_usd.unwrap_or(0);
            }
            if let Some(amount) = collected_values.payment_processed_amount {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.payment_processed_amount_in_usd = amount_in_usd;
                total_payment_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
                total_payment_processed_amount += amount;
            }
            if let Some(count) = collected_values.payment_processed_count {
                total_payment_processed_count += count;
            }
            if let Some(amount) = collected_values.payment_processed_amount_without_smart_retries {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.payment_processed_amount_without_smart_retries_in_usd =
                    amount_in_usd;
                total_payment_processed_amount_without_smart_retries_in_usd +=
                    amount_in_usd.unwrap_or(0);
                total_payment_processed_amount_without_smart_retries += amount;
            }
            if let Some(count) = collected_values.payment_processed_count_without_smart_retries {
                total_payment_processed_count_without_smart_retries += count;
            }
            MetricsBucketResponse {
                values: collected_values,
                dimensions: id,
            }
        })
        .collect();
    let total_success_rate = match (success, total) {
        (s, t) if t > 0 => Some(f64::from(s) * 100.0 / f64::from(t)),
        _ => None,
    };
    let total_success_rate_without_smart_retries = match (success_without_smart_retries, total) {
        (s, t) if t > 0 => Some(f64::from(s) * 100.0 / f64::from(t)),
        _ => None,
    };
    Ok(PaymentIntentsMetricsResponse {
        query_data,
        meta_data: [PaymentIntentsAnalyticsMetadata {
            total_success_rate,
            total_success_rate_without_smart_retries,
            total_smart_retried_amount: Some(total_smart_retried_amount),
            total_smart_retried_amount_without_smart_retries: Some(
                total_smart_retried_amount_without_smart_retries,
            ),
            total_payment_processed_amount: Some(total_payment_processed_amount),
            total_payment_processed_amount_without_smart_retries: Some(
                total_payment_processed_amount_without_smart_retries,
            ),
            total_smart_retried_amount_in_usd: if ex_rates.is_some() {
                Some(total_smart_retried_amount_in_usd)
            } else {
                None
            },
            total_smart_retried_amount_without_smart_retries_in_usd: if ex_rates.is_some() {
                Some(total_smart_retried_amount_without_smart_retries_in_usd)
            } else {
                None
            },
            total_payment_processed_amount_in_usd: if ex_rates.is_some() {
                Some(total_payment_processed_amount_in_usd)
            } else {
                None
            },
            total_payment_processed_amount_without_smart_retries_in_usd: if ex_rates.is_some() {
                Some(total_payment_processed_amount_without_smart_retries_in_usd)
            } else {
                None
            },
            total_payment_processed_count: Some(total_payment_processed_count),
            total_payment_processed_count_without_smart_retries: Some(
                total_payment_processed_count_without_smart_retries,
            ),
        }],
    })
}

pub async fn get_filters(
    pool: &AnalyticsProvider,
    req: GetPaymentIntentFiltersRequest,
    merchant_id: &common_utils::id_type::MerchantId,
) -> AnalyticsResult<PaymentIntentFiltersResponse> {
    let mut res = PaymentIntentFiltersResponse::default();

    for dim in req.group_by_names {
        let values = match pool {
                        AnalyticsProvider::Sqlx(pool) => {
                get_payment_intent_filter_for_dimension(dim, merchant_id, &req.time_range, pool)
                    .await
            }
                        AnalyticsProvider::Clickhouse(pool) => {
                get_payment_intent_filter_for_dimension(dim, merchant_id, &req.time_range, pool)
                    .await
            }
                    AnalyticsProvider::CombinedCkh(sqlx_poll, ckh_pool) => {
                let ckh_result = get_payment_intent_filter_for_dimension(
                    dim,
                    merchant_id,
                    &req.time_range,
                    ckh_pool,
                )
                .await;
                let sqlx_result = get_payment_intent_filter_for_dimension(
                    dim,
                    merchant_id,
                    &req.time_range,
                    sqlx_poll,
                )
                .await;
                match (&sqlx_result, &ckh_result) {
                    (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                        router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres payment intents analytics filters")
                    },
                    _ => {}
                };
                ckh_result
            }
                    AnalyticsProvider::CombinedSqlx(sqlx_poll, ckh_pool) => {
                let ckh_result = get_payment_intent_filter_for_dimension(
                    dim,
                    merchant_id,
                    &req.time_range,
                    ckh_pool,
                )
                .await;
                let sqlx_result = get_payment_intent_filter_for_dimension(
                    dim,
                    merchant_id,
                    &req.time_range,
                    sqlx_poll,
                )
                .await;
                match (&sqlx_result, &ckh_result) {
                    (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                        router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres payment intents analytics filters")
                    },
                    _ => {}
                };
                sqlx_result
            }
        }
        .change_context(AnalyticsError::UnknownError)?
        .into_iter()
        .filter_map(|fil: PaymentIntentFilterRow| match dim {
            PaymentIntentDimensions::PaymentIntentStatus => fil.status.map(|i| i.as_ref().to_string()),
            PaymentIntentDimensions::Currency => fil.currency.map(|i| i.as_ref().to_string()),
            PaymentIntentDimensions::ProfileId => fil.profile_id,
            PaymentIntentDimensions::Connector => fil.connector,
            PaymentIntentDimensions::AuthType => fil.authentication_type.map(|i| i.as_ref().to_string()),
            PaymentIntentDimensions::PaymentMethod => fil.payment_method,
            PaymentIntentDimensions::PaymentMethodType => fil.payment_method_type,
            PaymentIntentDimensions::CardNetwork => fil.card_network,
            PaymentIntentDimensions::MerchantId => fil.merchant_id,
            PaymentIntentDimensions::CardLast4 => fil.card_last_4,
            PaymentIntentDimensions::CardIssuer => fil.card_issuer,
            PaymentIntentDimensions::ErrorReason => fil.error_reason,
        })
        .collect::<Vec<String>>();
        res.query_data.push(PaymentIntentFilterValue {
            dimension: dim,
            values,
        })
    }
    Ok(res)
}


// Function: collect
// File: crates/analytics/src/disputes/accumulators.rs
// Module: analytics

pub fn collect(self) -> DisputeMetricsBucketValue


// File: crates/api_models/src/conditional_configs.rs
// Module: api_models
// Public structs: 4

use common_utils::events;
use euclid::frontend::ast::Program;

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DecisionManagerRecord {
    pub name: String,
    pub program: Program<common_types::payments::ConditionalConfigs>,
    pub created_at: i64,
    pub modified_at: i64,
}
impl events::ApiEventMetric for DecisionManagerRecord {
    fn get_api_event_type(&self) -> Option<events::ApiEventsType> {
        Some(events::ApiEventsType::Routing)
    }
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ConditionalConfigReq {
    pub name: Option<String>,
    pub algorithm: Option<Program<common_types::payments::ConditionalConfigs>>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DecisionManagerRequest {
    pub name: Option<String>,
    pub program: Option<Program<common_types::payments::ConditionalConfigs>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum DecisionManager {
    DecisionManagerv0(ConditionalConfigReq),
    DecisionManagerv1(DecisionManagerRequest),
}

impl events::ApiEventMetric for DecisionManager {
    fn get_api_event_type(&self) -> Option<events::ApiEventsType> {
        Some(events::ApiEventsType::Routing)
    }
}

pub type DecisionManagerResponse = DecisionManagerRecord;

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DecisionManagerRequest {
    pub name: String,
    pub program: Program<common_types::payments::ConditionalConfigs>,
}

#[cfg(feature = "v2")]
impl events::ApiEventMetric for DecisionManagerRequest {
    fn get_api_event_type(&self) -> Option<events::ApiEventsType> {
        Some(events::ApiEventsType::Routing)
    }
}


// Implementation: impl IncomingWebhook for for Worldpay
// File: crates/hyperswitch_connectors/src/connectors/worldpay.rs
// Module: hyperswitch_connectors
// Methods: 9 total (0 public)

impl IncomingWebhook for for Worldpay


// Function: is_refund_event
// File: crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
// Module: hyperswitch_connectors

pub fn is_refund_event(event_code: &CheckoutWebhookEventType) -> bool


// Implementation: impl ConnectorSpecifications for for Placetopay
// File: crates/hyperswitch_connectors/src/connectors/placetopay.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl ConnectorSpecifications for for Placetopay


// Struct: PaymentIntentFilters
// File: crates/api_models/src/analytics/payment_intents.rs
// Module: api_models
// Implementations: 0

pub struct PaymentIntentFilters


// Function: get_id
// File: crates/diesel_models/src/business_profile.rs
// Module: diesel_models

pub fn get_id(&self) -> &common_utils::id_type::ProfileId


// Function: list_user_authentication_methods
// File: crates/router/src/routes/user.rs
// Module: router

pub fn list_user_authentication_methods(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<user_api::GetUserAuthenticationMethodsRequest>,
) -> HttpResponse