pub use analytics::*;

pub mod routes {
    use std::{
        collections::{HashMap, HashSet},
        sync::Arc,
    };

    use actix_web::{web, Responder, Scope};
    use analytics::{
        api_event::api_events_core,
        connector_events::{connector_events_core, ConnectorEventSource},
        enums::AuthInfo,
        errors::AnalyticsError,
        lambda_utils::invoke_lambda,
        opensearch::OpenSearchError,
        outgoing_webhook_event::outgoing_webhook_events_core,
        routing_events::routing_events_core,
        sdk_events::sdk_events_core,
        AnalyticsFlow,
    };
    use api_models::analytics::{
        api_event::QueryType,
        search::{
            GetGlobalSearchRequest, GetSearchRequest, GetSearchRequestWithIndex, SearchFilters,
            SearchIndex,
        },
        AnalyticsRequest, GenerateReportRequest, GetActivePaymentsMetricRequest,
        GetApiEventFiltersRequest, GetApiEventMetricRequest, GetAuthEventFilterRequest,
        GetAuthEventMetricRequest, GetDisputeMetricRequest, GetFrmFilterRequest,
        GetFrmMetricRequest, GetPaymentFiltersRequest, GetPaymentIntentFiltersRequest,
        GetPaymentIntentMetricRequest, GetPaymentMetricRequest, GetRefundFilterRequest,
        GetRefundMetricRequest, GetSdkEventFiltersRequest, GetSdkEventMetricRequest, ReportRequest,
    };
    use common_enums::EntityType;
    use common_utils::{pii::Email, types::TimeRange};
    use error_stack::{report, ResultExt};
    use futures::{stream::FuturesUnordered, StreamExt};
    use hyperswitch_masking::ExposeInterface;
    use router_env::logger;

    use crate::{
        analytics_validator::{request_validator, validate_report_request},
        consts::opensearch::SEARCH_INDEXES,
        core::{api_locking, errors::user::UserErrors, verification::utils},
        db::user_role::ListUserRolesByUserIdPayload,
        routes::{metrics, AppState},
        services::{
            api,
            authentication::{self as auth, AuthenticationData, UserFromToken},
            authorization::{permissions::Permission, roles::RoleInfo},
            ApplicationResponse,
        },
        types::{domain::UserEmail, storage::UserRole},
        utils::get_payment_response_hash_key,
    };

    pub struct Analytics;

    impl Analytics {
        #[cfg(feature = "v2")]
        pub fn server(state: AppState) -> Scope {
            web::scope("/v2/analytics")
                .app_data(web::Data::new(state))
                .service(
                    web::scope("/profile")
                        .service(
                            web::resource("report/payments")
                                .route(web::post().to(generate_profile_payment_report)),
                        )
                        .service(
                            web::resource("payment_intents/aggregate")
                                .route(web::get().to(get_profile_payment_intent_aggregate)),
                        ),
                )
                .service(
                    web::scope("/merchant")
                        .service(
                            web::resource("report/payments")
                                .route(web::post().to(generate_merchant_payment_report)),
                        )
                        .service(
                            web::resource("payment_intents/aggregate")
                                .route(web::get().to(get_merchant_payment_intent_aggregate)),
                        ),
                )
                .service(
                    web::scope("/org").service(
                        web::resource("report/payments")
                            .route(web::post().to(generate_org_payment_report)),
                    ),
                )
        }
        #[cfg(feature = "v1")]
        pub fn server(state: AppState) -> Scope {
            web::scope("/analytics")
                .app_data(web::Data::new(state))
                .service(
                    web::scope("/v1")
                        .service(
                            web::resource("metrics/payments")
                                .route(web::post().to(get_merchant_payment_metrics)),
                        )
                        .service(
                            web::resource("metrics/routing")
                                .route(web::post().to(get_merchant_payment_metrics)),
                        )
                        .service(
                            web::resource("metrics/refunds")
                                .route(web::post().to(get_merchant_refund_metrics)),
                        )
                        .service(
                            web::resource("filters/payments")
                                .route(web::post().to(get_merchant_payment_filters)),
                        )
                        .service(
                            web::resource("filters/routing")
                                .route(web::post().to(get_merchant_payment_filters)),
                        )
                        .service(
                            web::resource("filters/frm").route(web::post().to(get_frm_filters)),
                        )
                        .service(
                            web::resource("filters/refunds")
                                .route(web::post().to(get_merchant_refund_filters)),
                        )
                        .service(web::resource("{domain}/info").route(web::get().to(get_info)))
                        .service(
                            web::resource("report/dispute")
                                .route(web::post().to(generate_merchant_dispute_report)),
                        )
                        .service(
                            web::resource("report/refunds")
                                .route(web::post().to(generate_merchant_refund_report)),
                        )
                        .service(
                            web::resource("report/payments")
                                .route(web::post().to(generate_merchant_payment_report)),
                        )
                        .service(
                            web::resource("report/payouts")
                                .route(web::post().to(generate_merchant_payout_report)),
                        )
                        .service(
                            web::resource("report/authentications")
                                .route(web::post().to(generate_merchant_authentication_report)),
                        )
                        .service(
                            web::resource("metrics/sdk_events")
                                .route(web::post().to(get_sdk_event_metrics)),
                        )
                        .service(
                            web::resource("metrics/active_payments")
                                .route(web::post().to(get_active_payments_metrics)),
                        )
                        .service(
                            web::resource("filters/sdk_events")
                                .route(web::post().to(get_sdk_event_filters)),
                        )
                        .service(
                            web::resource("metrics/auth_events")
                                .route(web::post().to(get_merchant_auth_event_metrics)),
                        )
                        .service(
                            web::resource("filters/auth_events")
                                .route(web::post().to(get_merchant_auth_events_filters)),
                        )
                        .service(
                            web::resource("metrics/frm").route(web::post().to(get_frm_metrics)),
                        )
                        .service(
                            web::resource("api_event_logs")
                                .route(web::get().to(get_profile_api_events)),
                        )
                        .service(
                            web::resource("sdk_event_logs")
                                .route(web::post().to(get_profile_sdk_events)),
                        )
                        .service(
                            web::resource("connector_event_logs")
                                .route(web::get().to(get_profile_connector_events)),
                        )
                        .service(
                            web::resource("prism_connector_event_logs")
                                .route(web::get().to(get_profile_prism_connector_events)),
                        )
                        .service(
                            web::resource("routing_event_logs")
                                .route(web::get().to(get_profile_routing_events)),
                        )
                        .service(
                            web::resource("outgoing_webhook_event_logs")
                                .route(web::get().to(get_profile_outgoing_webhook_events)),
                        )
                        .service(
                            web::resource("metrics/api_events")
                                .route(web::post().to(get_merchant_api_events_metrics)),
                        )
                        .service(
                            web::resource("filters/api_events")
                                .route(web::post().to(get_merchant_api_event_filters)),
                        )
                        .service(
                            web::resource("search")
                                .route(web::post().to(get_global_search_results)),
                        )
                        .service(
                            web::resource("search/{domain}")
                                .route(web::post().to(get_search_results)),
                        )
                        .service(
                            web::resource("payments/list")
                                .route(web::post().to(get_payment_list_from_opensearch)),
                        )
                        .service(
                            web::resource("metrics/disputes")
                                .route(web::post().to(get_merchant_dispute_metrics)),
                        )
                        .service(
                            web::resource("filters/disputes")
                                .route(web::post().to(get_merchant_dispute_filters)),
                        )
                        .service(
                            web::resource("metrics/sankey")
                                .route(web::post().to(get_merchant_sankey)),
                        )
                        .service(
                            web::resource("payment_intents/aggregate")
                                .route(web::get().to(get_merchant_payment_intent_aggregate)),
                        )
                        .service(
                            web::resource("metrics/auth_events/sankey")
                                .route(web::post().to(get_merchant_auth_event_sankey)),
                        )
                        .service(
                            web::scope("/merchant")
                                .service(
                                    web::resource("metrics/payments")
                                        .route(web::post().to(get_merchant_payment_metrics)),
                                )
                                .service(
                                    web::resource("metrics/routing")
                                        .route(web::post().to(get_merchant_payment_metrics)),
                                )
                                .service(
                                    web::resource("metrics/refunds")
                                        .route(web::post().to(get_merchant_refund_metrics)),
                                )
                                .service(
                                    web::resource("metrics/auth_events")
                                        .route(web::post().to(get_merchant_auth_event_metrics)),
                                )
                                .service(
                                    web::resource("filters/payments")
                                        .route(web::post().to(get_merchant_payment_filters)),
                                )
                                .service(
                                    web::resource("filters/routing")
                                        .route(web::post().to(get_merchant_payment_filters)),
                                )
                                .service(
                                    web::resource("filters/refunds")
                                        .route(web::post().to(get_merchant_refund_filters)),
                                )
                                .service(
                                    web::resource("filters/auth_events")
                                        .route(web::post().to(get_merchant_auth_events_filters)),
                                )
                                .service(
                                    web::resource("{domain}/info").route(web::get().to(get_info)),
                                )
                                .service(
                                    web::resource("report/dispute")
                                        .route(web::post().to(generate_merchant_dispute_report)),
                                )
                                .service(
                                    web::resource("report/refunds")
                                        .route(web::post().to(generate_merchant_refund_report)),
                                )
                                .service(
                                    web::resource("report/relay")
                                        .route(web::post().to(generate_merchant_relay_report)),
                                )
                                .service(
                                    web::resource("report/payments")
                                        .route(web::post().to(generate_merchant_payment_report)),
                                )
                                .service(
                                    web::resource("report/payouts")
                                        .route(web::post().to(generate_merchant_payout_report)),
                                )
                                .service(
                                    web::resource("report/authentications").route(
                                        web::post().to(generate_merchant_authentication_report),
                                    ),
                                )
                                .service(
                                    web::resource("metrics/api_events")
                                        .route(web::post().to(get_merchant_api_events_metrics)),
                                )
                                .service(
                                    web::resource("filters/api_events")
                                        .route(web::post().to(get_merchant_api_event_filters)),
                                )
                                .service(
                                    web::resource("metrics/disputes")
                                        .route(web::post().to(get_merchant_dispute_metrics)),
                                )
                                .service(
                                    web::resource("filters/disputes")
                                        .route(web::post().to(get_merchant_dispute_filters)),
                                )
                                .service(
                                    web::resource("metrics/sankey")
                                        .route(web::post().to(get_merchant_sankey)),
                                )
                                .service(
                                    web::resource("metrics/auth_events/sankey")
                                        .route(web::post().to(get_merchant_auth_event_sankey)),
                                ),
                        )
                        .service(
                            web::scope("/org")
                                .service(
                                    web::resource("{domain}/info").route(web::get().to(get_info)),
                                )
                                .service(
                                    web::resource("metrics/payments")
                                        .route(web::post().to(get_org_payment_metrics)),
                                )
                                .service(
                                    web::resource("filters/payments")
                                        .route(web::post().to(get_org_payment_filters)),
                                )
                                .service(
                                    web::resource("metrics/routing")
                                        .route(web::post().to(get_org_payment_metrics)),
                                )
                                .service(
                                    web::resource("filters/routing")
                                        .route(web::post().to(get_org_payment_filters)),
                                )
                                .service(
                                    web::resource("metrics/refunds")
                                        .route(web::post().to(get_org_refund_metrics)),
                                )
                                .service(
                                    web::resource("filters/refunds")
                                        .route(web::post().to(get_org_refund_filters)),
                                )
                                .service(
                                    web::resource("metrics/disputes")
                                        .route(web::post().to(get_org_dispute_metrics)),
                                )
                                .service(
                                    web::resource("metrics/auth_events")
                                        .route(web::post().to(get_org_auth_event_metrics)),
                                )
                                .service(
                                    web::resource("filters/disputes")
                                        .route(web::post().to(get_org_dispute_filters)),
                                )
                                .service(
                                    web::resource("filters/auth_events")
                                        .route(web::post().to(get_org_auth_events_filters)),
                                )
                                .service(
                                    web::resource("report/dispute")
                                        .route(web::post().to(generate_org_dispute_report)),
                                )
                                .service(
                                    web::resource("report/refunds")
                                        .route(web::post().to(generate_org_refund_report)),
                                )
                                .service(
                                    web::resource("report/payments")
                                        .route(web::post().to(generate_org_payment_report)),
                                )
                                .service(
                                    web::resource("report/payouts")
                                        .route(web::post().to(generate_org_payout_report)),
                                )
                                .service(
                                    web::resource("report/authentications")
                                        .route(web::post().to(generate_org_authentication_report)),
                                )
                                .service(
                                    web::resource("metrics/sankey")
                                        .route(web::post().to(get_org_sankey)),
                                )
                                .service(
                                    web::resource("metrics/auth_events/sankey")
                                        .route(web::post().to(get_org_auth_event_sankey)),
                                ),
                        )
                        .service(
                            web::scope("/profile")
                                .service(
                                    web::resource("{domain}/info").route(web::get().to(get_info)),
                                )
                                .service(
                                    web::resource("metrics/payments")
                                        .route(web::post().to(get_profile_payment_metrics)),
                                )
                                .service(
                                    web::resource("filters/payments")
                                        .route(web::post().to(get_profile_payment_filters)),
                                )
                                .service(
                                    web::resource("metrics/routing")
                                        .route(web::post().to(get_profile_payment_metrics)),
                                )
                                .service(
                                    web::resource("filters/routing")
                                        .route(web::post().to(get_profile_payment_filters)),
                                )
                                .service(
                                    web::resource("metrics/refunds")
                                        .route(web::post().to(get_profile_refund_metrics)),
                                )
                                .service(
                                    web::resource("filters/refunds")
                                        .route(web::post().to(get_profile_refund_filters)),
                                )
                                .service(
                                    web::resource("metrics/disputes")
                                        .route(web::post().to(get_profile_dispute_metrics)),
                                )
                                .service(
                                    web::resource("metrics/auth_events")
                                        .route(web::post().to(get_profile_auth_event_metrics)),
                                )
                                .service(
                                    web::resource("filters/disputes")
                                        .route(web::post().to(get_profile_dispute_filters)),
                                )
                                .service(
                                    web::resource("filters/auth_events")
                                        .route(web::post().to(get_profile_auth_events_filters)),
                                )
                                .service(
                                    web::resource("connector_event_logs")
                                        .route(web::get().to(get_profile_connector_events)),
                                )
                                .service(
                                    web::resource("prism_connector_event_logs")
                                        .route(web::get().to(get_profile_prism_connector_events)),
                                )
                                .service(
                                    web::resource("routing_event_logs")
                                        .route(web::get().to(get_profile_routing_events)),
                                )
                                .service(
                                    web::resource("outgoing_webhook_event_logs")
                                        .route(web::get().to(get_profile_outgoing_webhook_events)),
                                )
                                .service(
                                    web::resource("report/dispute")
                                        .route(web::post().to(generate_profile_dispute_report)),
                                )
                                .service(
                                    web::resource("report/refunds")
                                        .route(web::post().to(generate_profile_refund_report)),
                                )
                                .service(
                                    web::resource("report/relay")
                                        .route(web::post().to(generate_profile_relay_report)),
                                )
                                .service(
                                    web::resource("report/payments")
                                        .route(web::post().to(generate_profile_payment_report)),
                                )
                                .service(
                                    web::resource("report/payouts")
                                        .route(web::post().to(generate_profile_payout_report)),
                                )
                                .service(
                                    web::resource("report/authentications").route(
                                        web::post().to(generate_profile_authentication_report),
                                    ),
                                )
                                .service(
                                    web::resource("api_event_logs")
                                        .route(web::get().to(get_profile_api_events)),
                                )
                                .service(
                                    web::resource("sdk_event_logs")
                                        .route(web::post().to(get_profile_sdk_events)),
                                )
                                .service(
                                    web::resource("metrics/sankey")
                                        .route(web::post().to(get_profile_sankey)),
                                )
                                .service(
                                    web::resource("payment_intents/aggregate")
                                        .route(web::get().to(get_profile_payment_intent_aggregate)),
                                )
                                .service(
                                    web::resource("metrics/auth_events/sankey")
                                        .route(web::post().to(get_profile_auth_event_sankey)),
                                )
                                .service(web::resource("payments/list").route(
                                    web::post().to(get_profile_payment_list_from_opensearch),
                                )),
                        ),
                )
                .service(
                    web::scope("/v2")
                        .service(
                            web::resource("/metrics/payments")
                                .route(web::post().to(get_merchant_payment_intent_metrics)),
                        )
                        .service(
                            web::resource("/filters/payments")
                                .route(web::post().to(get_merchant_payment_intents_filters)),
                        )
                        .service(
                            web::scope("/merchant")
                                .service(
                                    web::resource("/metrics/payments")
                                        .route(web::post().to(get_merchant_payment_intent_metrics)),
                                )
                                .service(
                                    web::resource("/filters/payments").route(
                                        web::post().to(get_merchant_payment_intents_filters),
                                    ),
                                ),
                        )
                        .service(
                            web::scope("/org").service(
                                web::resource("/metrics/payments")
                                    .route(web::post().to(get_org_payment_intent_metrics)),
                            ),
                        )
                        .service(
                            web::scope("/profile").service(
                                web::resource("/metrics/payments")
                                    .route(web::post().to(get_profile_payment_intent_metrics)),
                            ),
                        ),
                )
        }
    }

    pub async fn get_info(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        domain: web::Path<analytics::AnalyticsDomain>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetInfo;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            domain.into_inner(),
            |_, _: (), domain: analytics::AnalyticsDomain, _| async {
                analytics::core::get_domain_info(domain)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::NoAuth,
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetPaymentMetricRequest` element.
    pub async fn get_merchant_payment_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_attempt: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payments::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetPaymentMetricRequest` element.
    pub async fn get_org_payment_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();

                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_attempt: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payments::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetPaymentMetricRequest` element.
    pub async fn get_profile_payment_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info: AuthInfo = auth.platform.to_profile_level_auth_info(profile_id);

                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_attempt: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payments::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element.
    pub async fn get_merchant_payment_intent_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentIntentMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentIntentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentIntentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();

                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_intent: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_merchant_payment_intent_aggregate(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        query: web::Query<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentIntentsAggregate;
        let payload = query.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                let status_with_count = state
                    .pool
                    .get_intent_status_with_count(&auth_info, &req)
                    .await
                    .change_context(AnalyticsError::UnknownError)?;
                Ok(ApplicationResponse::Json(
                    api_models::payments::PaymentsAggregateResponse { status_with_count },
                ))
            },
            &auth::JWTAuth {
                permission: Permission::MerchantPaymentRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_profile_payment_intent_aggregate(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        query: web::Query<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentIntentsAggregate;
        let payload = query.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                #[cfg(feature = "v1")]
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                #[cfg(feature = "v2")]
                let profile_id = auth.profile.get_id().clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                let status_with_count = state
                    .pool
                    .get_intent_status_with_count(&auth_info, &req)
                    .await
                    .change_context(AnalyticsError::UnknownError)?;
                Ok(ApplicationResponse::Json(
                    api_models::payments::PaymentsAggregateResponse { status_with_count },
                ))
            },
            &auth::JWTAuth {
                permission: Permission::ProfilePaymentRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element.
    pub async fn get_org_payment_intent_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentIntentMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentIntentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentIntentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();

                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_intent: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element.
    pub async fn get_profile_payment_intent_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentIntentMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentIntentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentIntentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);

                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_intent: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element.
    pub async fn get_merchant_refund_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetRefundMetricRequest; 1]>,
    ) -> impl Responder {
        #[allow(clippy::expect_used)]
        // safety: This shouldn't panic owing to the data type
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetRefundMetricRequest");
        let flow = AnalyticsFlow::GetRefundsMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();

                let validator_response = request_validator(
                    AnalyticsRequest {
                        refund: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element.
    pub async fn get_org_refund_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetRefundMetricRequest; 1]>,
    ) -> impl Responder {
        #[allow(clippy::expect_used)]
        // safety: This shouldn't panic owing to the data type
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetRefundMetricRequest");
        let flow = AnalyticsFlow::GetRefundsMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();

                let validator_response = request_validator(
                    AnalyticsRequest {
                        refund: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element.
    pub async fn get_profile_refund_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetRefundMetricRequest; 1]>,
    ) -> impl Responder {
        #[allow(clippy::expect_used)]
        // safety: This shouldn't panic owing to the data type
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetRefundMetricRequest");
        let flow = AnalyticsFlow::GetRefundsMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);

                let validator_response = request_validator(
                    AnalyticsRequest {
                        refund: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetFrmMetricRequest` element.
    pub async fn get_frm_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetFrmMetricRequest; 1]>,
    ) -> impl Responder {
        #[allow(clippy::expect_used)]
        // safety: This shouldn't panic owing to the data type
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetFrmMetricRequest");
        let flow = AnalyticsFlow::GetFrmMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                analytics::frm::get_metrics(
                    &state.pool,
                    auth.platform.get_processor().get_account().get_id(),
                    req,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: false,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetSdkEventMetricRequest` element.
    pub async fn get_sdk_event_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetSdkEventMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetSdkEventMetricRequest");
        let flow = AnalyticsFlow::GetSdkMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                analytics::sdk_events::get_metrics(
                    &state.pool,
                    &auth.platform.get_processor().get_account().publishable_key,
                    req,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetActivePaymentsMetricRequest` element.
    pub async fn get_active_payments_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetActivePaymentsMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetActivePaymentsMetricRequest");
        let flow = AnalyticsFlow::GetActivePaymentsMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                analytics::active_payments::get_metrics(
                    &state.pool,
                    &auth.platform.get_processor().get_account().publishable_key,
                    auth.platform.get_processor().get_account().get_id(),
                    req,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: false,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetAuthEventMetricRequest` element.
    pub async fn get_merchant_auth_event_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetAuthEventMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetAuthEventMetricRequest");
        let flow = AnalyticsFlow::GetAuthMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();

                analytics::auth_events::get_metrics(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetAuthEventMetricRequest` element.
    pub async fn get_profile_auth_event_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetAuthEventMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetAuthEventMetricRequest");
        let flow = AnalyticsFlow::GetAuthMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                analytics::auth_events::get_metrics(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetAuthEventMetricRequest` element.
    pub async fn get_org_auth_event_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetAuthEventMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetAuthEventMetricRequest");
        let flow = AnalyticsFlow::GetAuthMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();
                analytics::auth_events::get_metrics(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_merchant_payment_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetPaymentFiltersRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                analytics::payments::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_merchant_auth_events_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetAuthEventFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetAuthEventFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                analytics::auth_events::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_org_auth_events_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetAuthEventFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetAuthEventFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();
                analytics::auth_events::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_profile_auth_events_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetAuthEventFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetAuthEventFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                analytics::auth_events::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_org_payment_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetPaymentFiltersRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();
                analytics::payments::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_profile_payment_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetPaymentFiltersRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                analytics::payments::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_merchant_payment_intents_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetPaymentIntentFiltersRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentIntentFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                analytics::payment_intents::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::MerchantAnalyticsRead,
                    allow_connected: false,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_merchant_refund_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetRefundFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetRefundFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req: GetRefundFilterRequest, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                analytics::refunds::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_org_refund_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetRefundFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetRefundFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req: GetRefundFilterRequest, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();
                analytics::refunds::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_profile_refund_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetRefundFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetRefundFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req: GetRefundFilterRequest, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                analytics::refunds::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_frm_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetFrmFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetFrmFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req: GetFrmFilterRequest, _| async move {
                analytics::frm::get_filters(
                    &state.pool,
                    req,
                    auth.platform.get_processor().get_account().get_id(),
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: false,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_sdk_event_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetSdkEventFiltersRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSdkEventFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                analytics::sdk_events::get_filters(
                    &state.pool,
                    req,
                    &auth.platform.get_processor().get_account().publishable_key,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_profile_api_events(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Query<api_models::analytics::api_event::ApiLogsRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetApiEvents;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let (payment_id, payout_id) = match req.query_param.clone() {
                    QueryType::Payment { payment_id } => (Some(payment_id), None),
                    QueryType::Refund { payment_id, .. } => (Some(payment_id), None),
                    QueryType::Dispute { payment_id, .. } => (Some(payment_id), None),
                    QueryType::Payout { payout_id } => (None, Some(payout_id)),
                };
                #[cfg(feature = "v1")]
                let profile_id = auth.profile.map(|profile| profile.get_id().clone());
                #[cfg(feature = "v2")]
                let profile_id = Some(auth.profile.get_id().clone());
                utils::check_if_profile_id_is_present_in_intent_table(
                    payment_id,
                    payout_id,
                    &state,
                    auth.platform.get_processor(),
                    profile_id,
                )
                .await
                .change_context(AnalyticsError::AccessForbiddenError)?;
                api_events_core(
                    &state.pool,
                    req,
                    auth.platform.get_processor().get_account().get_id(),
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_profile_outgoing_webhook_events(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Query<
            api_models::analytics::outgoing_webhook_event::OutgoingWebhookLogsRequest,
        >,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetOutgoingWebhookEvents;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                #[cfg(feature = "v1")]
                let profile_id = auth.profile.map(|profile| profile.get_id().clone());
                #[cfg(feature = "v2")]
                let profile_id = Some(auth.profile.get_id().clone());
                utils::check_if_profile_id_is_present_in_intent_table(
                    req.payment_id.clone(),
                    req.payout_id.clone(),
                    &state,
                    auth.platform.get_processor(),
                    profile_id,
                )
                .await
                .change_context(AnalyticsError::AccessForbiddenError)?;
                outgoing_webhook_events_core(
                    &state.pool,
                    req,
                    auth.platform.get_processor().get_account().get_id(),
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_profile_sdk_events(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<api_models::analytics::sdk_events::SdkEventsRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSdkEvents;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth.profile.map(|profile| profile.get_id().clone());
                utils::check_if_profile_id_is_present_in_payment_intent(
                    req.payment_id.clone(),
                    &state,
                    auth.platform.get_processor(),
                    profile_id,
                )
                .await
                .change_context(AnalyticsError::AccessForbiddenError)?;
                sdk_events_core(
                    &state.pool,
                    req,
                    &auth.platform.get_processor().get_account().publishable_key,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_merchant_refund_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateRefundReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_merchant_level_auth_info();
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.refund_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: true,
                },
                &auth::JWTAuth {
                    permission: Permission::MerchantReportRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_org_refund_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateRefundReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_org_level_auth_info();

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: None,
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: None,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.refund_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::OrganizationReportRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_profile_refund_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateRefundReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();

                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.refund_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: false,
                },
                &auth::JWTAuth {
                    permission: Permission::ProfileReportRead,
                    allow_connected: true,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }
    #[cfg(feature = "v1")]
    pub async fn generate_merchant_relay_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateRelayReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_merchant_level_auth_info();
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;

                let lambda_result = invoke_lambda(
                    &state.conf.report_download_config.relay_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await;

                match &lambda_result {
                    Ok(_) => logger::info!(
                        merchant_id = ?lambda_req.merchant_id,
                        "Successfully invoked relay report lambda function"
                    ),
                    Err(error) => logger::error!(
                        merchant_id = ?lambda_req.merchant_id,
                        ?error,
                        "Failed to invoke relay report lambda function"
                    ),
                }

                lambda_result.map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: false,
                },
                &auth::JWTAuth {
                    permission: Permission::MerchantReportRead,
                    allow_connected: true,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_profile_relay_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateRelayReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();

                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;

                let lambda_result = invoke_lambda(
                    &state.conf.report_download_config.relay_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await;

                match &lambda_result {
                    Ok(_) => logger::info!(
                        merchant_id = ?lambda_req.merchant_id,
                        "Successfully invoked relay report lambda function"
                    ),
                    Err(error) => logger::error!(
                        merchant_id = ?lambda_req.merchant_id,
                        ?error,
                        "Failed to invoke relay report lambda function"
                    ),
                }

                lambda_result.map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: false,
                },
                &auth::JWTAuth {
                    permission: Permission::ProfileReportRead,
                    allow_connected: true,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_merchant_dispute_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateDisputeReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_merchant_level_auth_info();
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.dispute_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: true,
                },
                &auth::JWTAuth {
                    permission: Permission::MerchantReportRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }
    #[cfg(feature = "v1")]
    pub async fn generate_org_dispute_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateDisputeReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_org_level_auth_info();

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: None,
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: None,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.dispute_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::OrganizationReportRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_profile_dispute_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateDisputeReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.dispute_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: false,
                },
                &auth::JWTAuth {
                    permission: Permission::ProfileReportRead,
                    allow_connected: true,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_merchant_payout_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GeneratePayoutReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_merchant_level_auth_info();
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.payout_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: true,
                },
                &auth::JWTAuth {
                    permission: Permission::MerchantReportRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_org_payout_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GeneratePayoutReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_org_level_auth_info();

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: None,
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: None,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.payout_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::OrganizationReportRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_profile_payout_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GeneratePayoutReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.payout_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: false,
                },
                &auth::JWTAuth {
                    permission: Permission::ProfileReportRead,
                    allow_connected: true,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn generate_merchant_payment_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GeneratePaymentReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_merchant_level_auth_info();
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.payment_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type::<auth::AuthenticationDataWithUserId, _>(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: true,
                },
                &auth::JWTAuth {
                    permission: Permission::MerchantReportRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn generate_org_payment_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GeneratePaymentReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_org_level_auth_info();

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: None,
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: None,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.payment_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::OrganizationReportRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn generate_profile_payment_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GeneratePaymentReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };
                let profile_id = {
                    #[cfg(feature = "v1")]
                    {
                        auth.profile
                            .ok_or(report!(UserErrors::JwtProfileIdMissing))
                            .change_context(AnalyticsError::AccessForbiddenError)?
                            .get_id()
                            .clone()
                    }
                    #[cfg(feature = "v2")]
                    {
                        auth.profile.get_id().clone()
                    }
                };

                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.payment_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type::<auth::AuthenticationDataWithUserId, _>(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: false,
                },
                &auth::JWTAuth {
                    permission: Permission::ProfileReportRead,
                    allow_connected: true,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_merchant_authentication_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateAuthenticationReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_merchant_level_auth_info();
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.authentication_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: true,
                },
                &auth::JWTAuth {
                    permission: Permission::MerchantReportRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_org_authentication_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateAuthenticationReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };

                let auth_info = auth.platform.to_org_level_auth_info();

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: None,
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: None,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.authentication_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::OrganizationReportRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn generate_profile_authentication_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateAuthenticationReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let (user_email, optional_emails) = match user_id {
                    Some(user_id) => {
                        let user = state
                            .global_store
                            .find_active_user_by_user_id(&user_id)
                            .await
                            .change_context(AnalyticsError::UnknownError)?;

                        let user_email = Email::try_from(
                            UserEmail::from_pii_email(user.email)
                                .change_context(AnalyticsError::UnknownError)?
                                .get_secret()
                                .expose()
                                .to_string(),
                        )
                        .change_context(AnalyticsError::MissingEmail)?;

                        (user_email, payload.emails)
                    }
                    None => {
                        validate_report_request(&payload)?;
                        let (primary_email, other_emails) = payload
                            .emails
                            .and_then(|mut emails| {
                                if emails.is_empty() {
                                    None
                                } else {
                                    let primary_email = emails.remove(0);
                                    Some((primary_email, emails))
                                }
                            })
                            .ok_or(AnalyticsError::MissingEmail)?;

                        (primary_email, Some(other_emails))
                    }
                };

                let payload = ReportRequest {
                    emails: optional_emails,
                    ..payload
                };
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                let hash_key = match &payload.return_url {
                    Some(_) => {
                        get_payment_response_hash_key(
                            state.store.as_ref(),
                            auth.platform.get_processor().get_key_store(),
                            &auth_info,
                        )
                        .await?
                    }
                    None => None,
                };

                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(auth.platform.get_processor().get_account().get_id().clone()),
                    auth: auth_info,
                    email: user_email,
                    payment_response_hash_key: hash_key,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.authentication_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::ApiKeyAuth {
                    allow_connected_scope_operation: true,
                    allow_platform_self_operation: false,
                },
                &auth::JWTAuth {
                    permission: Permission::ProfileReportRead,
                    allow_connected: true,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetApiEventMetricRequest` element.
    pub async fn get_merchant_api_events_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetApiEventMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetApiEventMetricRequest");
        let flow = AnalyticsFlow::GetApiEventMetrics;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                analytics::api_event::get_api_event_metrics(
                    &state.pool,
                    auth.platform.get_processor().get_account().get_id(),
                    req,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_merchant_api_event_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetApiEventFiltersRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetApiEventFilters;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                analytics::api_event::get_filters(
                    &state.pool,
                    req,
                    auth.platform.get_processor().get_account().get_id(),
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    async fn connector_event_logs(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Query<api_models::analytics::connector_events::ConnectorEventsRequest>,
        flow: AnalyticsFlow,
        source: ConnectorEventSource,
    ) -> impl Responder {
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            move |state, auth: AuthenticationData, req, _| async move {
                #[cfg(feature = "v1")]
                let profile_id = auth.profile.map(|profile| profile.get_id().clone());
                #[cfg(feature = "v2")]
                let profile_id = Some(auth.profile.get_id().clone());
                utils::check_if_profile_id_is_present_in_intent_table(
                    req.payment_id.clone(),
                    req.payout_id.clone(),
                    &state,
                    auth.platform.get_processor(),
                    profile_id,
                )
                .await
                .change_context(AnalyticsError::AccessForbiddenError)?;
                connector_events_core(
                    &state.pool,
                    req,
                    auth.platform.get_processor().get_account().get_id(),
                    source,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_profile_connector_events(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Query<api_models::analytics::connector_events::ConnectorEventsRequest>,
    ) -> impl Responder {
        connector_event_logs(
            state,
            req,
            json_payload,
            AnalyticsFlow::GetConnectorEvents,
            ConnectorEventSource::Hyperswitch,
        )
        .await
    }

    pub async fn get_profile_prism_connector_events(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Query<api_models::analytics::connector_events::ConnectorEventsRequest>,
    ) -> impl Responder {
        connector_event_logs(
            state,
            req,
            json_payload,
            AnalyticsFlow::GetPrismConnectorEvents,
            ConnectorEventSource::Prism,
        )
        .await
    }

    pub async fn get_profile_routing_events(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Query<api_models::analytics::routing_events::RoutingEventsRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetRoutingEvents;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                #[cfg(feature = "v1")]
                let profile_id = auth.profile.map(|profile| profile.get_id().clone());
                #[cfg(feature = "v2")]
                let profile_id = Some(auth.profile.get_id().clone());
                utils::check_if_profile_id_is_present_in_payment_intent(
                    req.payment_id.clone(),
                    &state,
                    auth.platform.get_processor(),
                    profile_id,
                )
                .await
                .change_context(AnalyticsError::AccessForbiddenError)?;
                routing_events_core(
                    &state.pool,
                    req,
                    auth.platform.get_processor().get_account().get_id(),
                )
                .await
                .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::HeaderAuth(auth::ApiKeyAuth {
                    allow_connected_scope_operation: false,
                    allow_platform_self_operation: false,
                }),
                &auth::JWTAuth {
                    permission: Permission::ProfileAnalyticsRead,
                    allow_connected: true,
                    allow_platform: false,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_payment_list_from_opensearch(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<api_models::payments::PaymentListFilterConstraints>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentListFromOpenSearch;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            payload,
            |state, auth: AuthenticationData, constraints, _| async move {
                let req_merchant_id = auth.platform.get_processor().get_account().get_id().clone();
                common_utils::metrics::utils::record_operation_time(
                    Box::pin(async move {
                        let auth_info = vec![auth.platform.to_merchant_level_auth_info()];

                        let filters: SearchFilters = (&constraints).into();

                        let search_req = GetSearchRequestWithIndex {
                            index: SearchIndex::SessionizerPaymentIntents,
                            search_req: GetSearchRequest {
                                query: constraints.query.unwrap_or_default(),
                                filters: Some(filters),
                                time_range: constraints.time_range,
                                offset: constraints.offset.map(i64::from).unwrap_or(0),
                                count: i64::from(constraints.limit),
                                order: Some(constraints.order),
                            },
                        };

                        analytics::search::search_results(
                            state
                                .opensearch_client
                                .as_ref()
                                .ok_or_else(|| error_stack::report!(OpenSearchError::NotEnabled))?,
                            search_req,
                            auth_info,
                        )
                        .await
                        .map(|response| {
                            logger::info!(
                                count = response.hits.len(),
                                total = response.count,
                                "Successfully retrieved payments from OpenSearch"
                            );
                            ApplicationResponse::Json(response)
                        })
                    }),
                    &metrics::PAYMENT_LIST_OPENSEARCH_LATENCY,
                    router_env::metric_attributes!(("merchant_id", req_merchant_id.clone())),
                )
                .await
            },
            &auth::JWTAuth {
                permission: Permission::MerchantPaymentRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_profile_payment_list_from_opensearch(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<api_models::payments::PaymentListFilterConstraints>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentListFromOpenSearch;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            payload,
            |state, auth: AuthenticationData, constraints, _| async move {
                let req_merchant_id = auth.platform.get_processor().get_account().get_id().clone();
                common_utils::metrics::utils::record_operation_time(
                    Box::pin(async move {
                        let profile_id = auth
                            .profile
                            .ok_or(report!(UserErrors::JwtProfileIdMissing))
                            .change_context(OpenSearchError::AccessForbiddenError)?
                            .get_id()
                            .clone();

                        let auth_info = vec![auth.platform.to_profile_level_auth_info(profile_id)];

                        let filters: SearchFilters = (&constraints).into();

                        let search_req = GetSearchRequestWithIndex {
                            index: SearchIndex::SessionizerPaymentIntents,
                            search_req: GetSearchRequest {
                                query: constraints.query.unwrap_or_default(),
                                filters: Some(filters),
                                time_range: constraints.time_range,
                                offset: constraints.offset.map(i64::from).unwrap_or(0),
                                count: i64::from(constraints.limit),
                                order: Some(constraints.order),
                            },
                        };

                        analytics::search::search_results(
                            state
                                .opensearch_client
                                .as_ref()
                                .ok_or_else(|| error_stack::report!(OpenSearchError::NotEnabled))?,
                            search_req,
                            auth_info,
                        )
                        .await
                        .map(|response| {
                            logger::info!(
                                count = response.hits.len(),
                                total = response.count,
                                "Successfully retrieved payments for profile from OpenSearch"
                            );
                            ApplicationResponse::Json(response)
                        })
                    }),
                    &metrics::PAYMENT_LIST_OPENSEARCH_LATENCY,
                    router_env::metric_attributes!(("merchant_id", req_merchant_id.clone())),
                )
                .await
            },
            &auth::JWTAuth {
                permission: Permission::ProfilePaymentRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_global_search_results(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetGlobalSearchRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetGlobalSearchResults;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, auth: UserFromToken, req, _| async move {
                let role_id = auth.role_id;
                let role_info = RoleInfo::from_role_id_org_id_tenant_id(
                    &state,
                    &role_id,
                    &auth.org_id,
                    auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id),
                )
                .await
                .change_context(UserErrors::InternalServerError)
                .change_context(OpenSearchError::UnknownError)?;
                let permission_groups = role_info.get_permission_groups();
                if !permission_groups.contains(&common_enums::PermissionGroup::OperationsView) {
                    Err(OpenSearchError::AccessForbiddenError)?;
                }
                let state = Arc::new(state);

                let search_params: Vec<AuthInfo> = if role_info.is_internal() {
                    vec![AuthInfo::MerchantLevel {
                        org_id: auth.org_id.clone(),
                        merchant_ids: vec![auth.merchant_id.clone()],
                        processor_merchant_ids: None,
                    }]
                } else {
                    let user_roles: HashSet<UserRole> = match role_info.get_entity_type() {
                        EntityType::Tenant => state
                            .global_store
                            .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                                user_id: &auth.user_id,
                                tenant_id: auth
                                    .tenant_id
                                    .as_ref()
                                    .unwrap_or(&state.tenant.tenant_id),
                                org_id: None,
                                merchant_id: None,
                                profile_id: None,
                                entity_id: None,
                                version: None,
                                status: None,
                                limit: None,
                            })
                            .await
                            .change_context(UserErrors::InternalServerError)
                            .change_context(OpenSearchError::UnknownError)?
                            .into_iter()
                            .collect(),
                        EntityType::Organization | EntityType::Merchant | EntityType::Profile => {
                            state
                                .global_store
                                .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                                    user_id: &auth.user_id,
                                    tenant_id: auth
                                        .tenant_id
                                        .as_ref()
                                        .unwrap_or(&state.tenant.tenant_id),
                                    org_id: Some(&auth.org_id),
                                    merchant_id: None,
                                    profile_id: None,
                                    entity_id: None,
                                    version: None,
                                    status: None,
                                    limit: None,
                                })
                                .await
                                .change_context(UserErrors::InternalServerError)
                                .change_context(OpenSearchError::UnknownError)?
                                .into_iter()
                                .collect()
                        }
                    };

                    let role_info_map: HashMap<String, RoleInfo> = user_roles
                        .iter()
                        .map(|user_role| {
                            let state = Arc::clone(&state);
                            let role_id = user_role.role_id.clone();
                            let org_id = user_role.org_id.clone().unwrap_or_default();
                            let tenant_id = &user_role.tenant_id;
                            async move {
                                RoleInfo::from_role_id_org_id_tenant_id(
                                    &state, &role_id, &org_id, tenant_id,
                                )
                                .await
                                .change_context(UserErrors::InternalServerError)
                                .change_context(OpenSearchError::UnknownError)
                                .map(|role_info| (role_id, role_info))
                            }
                        })
                        .collect::<FuturesUnordered<_>>()
                        .collect::<Vec<_>>()
                        .await
                        .into_iter()
                        .collect::<Result<HashMap<_, _>, _>>()?;

                    let filtered_user_roles: Vec<&UserRole> = user_roles
                        .iter()
                        .filter(|user_role| {
                            let user_role_id = &user_role.role_id;
                            if let Some(role_info) = role_info_map.get(user_role_id) {
                                let permissions = role_info.get_permission_groups();
                                permissions.contains(&common_enums::PermissionGroup::OperationsView)
                            } else {
                                false
                            }
                        })
                        .collect();

                    filtered_user_roles
                        .iter()
                        .filter_map(|user_role| {
                            user_role
                                .get_entity_id_and_type()
                                .and_then(|(_, entity_type)| match entity_type {
                                    EntityType::Profile => Some(AuthInfo::ProfileLevel {
                                        org_id: user_role.org_id.clone()?,
                                        merchant_id: user_role.merchant_id.clone()?,
                                        profile_ids: vec![user_role.profile_id.clone()?],
                                        processor_merchant_id: None,
                                    }),
                                    EntityType::Merchant => Some(AuthInfo::MerchantLevel {
                                        org_id: user_role.org_id.clone()?,
                                        merchant_ids: vec![user_role.merchant_id.clone()?],
                                        processor_merchant_ids: None,
                                    }),
                                    EntityType::Organization => Some(AuthInfo::OrgLevel {
                                        org_id: user_role.org_id.clone()?,
                                    }),
                                    EntityType::Tenant => Some(AuthInfo::OrgLevel {
                                        org_id: auth.org_id.clone(),
                                    }),
                                })
                        })
                        .collect()
                };

                analytics::search::msearch_results(
                    state
                        .opensearch_client
                        .as_ref()
                        .ok_or_else(|| error_stack::report!(OpenSearchError::NotEnabled))?,
                    req,
                    search_params,
                    SEARCH_INDEXES.to_vec(),
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_search_results(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetSearchRequest>,
        index: web::Path<SearchIndex>,
    ) -> impl Responder {
        let index = index.into_inner();
        let flow = AnalyticsFlow::GetSearchResults;
        let indexed_req = GetSearchRequestWithIndex {
            search_req: json_payload.into_inner(),
            index,
        };
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            indexed_req,
            |state, auth: UserFromToken, req, _| async move {
                let role_id = auth.role_id;
                let role_info = RoleInfo::from_role_id_org_id_tenant_id(
                    &state,
                    &role_id,
                    &auth.org_id,
                    auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id),
                )
                .await
                .change_context(UserErrors::InternalServerError)
                .change_context(OpenSearchError::UnknownError)?;
                let permission_groups = role_info.get_permission_groups();
                if !permission_groups.contains(&common_enums::PermissionGroup::OperationsView) {
                    Err(OpenSearchError::AccessForbiddenError)?;
                }
                let state = Arc::new(state);

                let search_params: Vec<AuthInfo> = if role_info.is_internal() {
                    vec![AuthInfo::MerchantLevel {
                        org_id: auth.org_id.clone(),
                        merchant_ids: vec![auth.merchant_id.clone()],
                        processor_merchant_ids: None,
                    }]
                } else {
                    let user_roles: HashSet<UserRole> = match role_info.get_entity_type() {
                        EntityType::Tenant => state
                            .global_store
                            .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                                user_id: &auth.user_id,
                                tenant_id: auth
                                    .tenant_id
                                    .as_ref()
                                    .unwrap_or(&state.tenant.tenant_id),
                                org_id: None,
                                merchant_id: None,
                                profile_id: None,
                                entity_id: None,
                                version: None,
                                status: None,
                                limit: None,
                            })
                            .await
                            .change_context(UserErrors::InternalServerError)
                            .change_context(OpenSearchError::UnknownError)?
                            .into_iter()
                            .collect(),
                        EntityType::Organization | EntityType::Merchant | EntityType::Profile => {
                            state
                                .global_store
                                .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                                    user_id: &auth.user_id,
                                    tenant_id: auth
                                        .tenant_id
                                        .as_ref()
                                        .unwrap_or(&state.tenant.tenant_id),
                                    org_id: Some(&auth.org_id),
                                    merchant_id: None,
                                    profile_id: None,
                                    entity_id: None,
                                    version: None,
                                    status: None,
                                    limit: None,
                                })
                                .await
                                .change_context(UserErrors::InternalServerError)
                                .change_context(OpenSearchError::UnknownError)?
                                .into_iter()
                                .collect()
                        }
                    };

                    let role_info_map: HashMap<String, RoleInfo> = user_roles
                        .iter()
                        .map(|user_role| {
                            let state = Arc::clone(&state);
                            let role_id = user_role.role_id.clone();
                            let org_id = user_role.org_id.clone().unwrap_or_default();
                            let tenant_id = &user_role.tenant_id;
                            async move {
                                RoleInfo::from_role_id_org_id_tenant_id(
                                    &state, &role_id, &org_id, tenant_id,
                                )
                                .await
                                .change_context(UserErrors::InternalServerError)
                                .change_context(OpenSearchError::UnknownError)
                                .map(|role_info| (role_id, role_info))
                            }
                        })
                        .collect::<FuturesUnordered<_>>()
                        .collect::<Vec<_>>()
                        .await
                        .into_iter()
                        .collect::<Result<HashMap<_, _>, _>>()?;

                    let filtered_user_roles: Vec<&UserRole> = user_roles
                        .iter()
                        .filter(|user_role| {
                            let user_role_id = &user_role.role_id;
                            if let Some(role_info) = role_info_map.get(user_role_id) {
                                let permissions = role_info.get_permission_groups();
                                permissions.contains(&common_enums::PermissionGroup::OperationsView)
                            } else {
                                false
                            }
                        })
                        .collect();

                    filtered_user_roles
                        .iter()
                        .filter_map(|user_role| {
                            user_role
                                .get_entity_id_and_type()
                                .and_then(|(_, entity_type)| match entity_type {
                                    EntityType::Profile => Some(AuthInfo::ProfileLevel {
                                        org_id: user_role.org_id.clone()?,
                                        merchant_id: user_role.merchant_id.clone()?,
                                        profile_ids: vec![user_role.profile_id.clone()?],
                                        processor_merchant_id: None,
                                    }),
                                    EntityType::Merchant => Some(AuthInfo::MerchantLevel {
                                        org_id: user_role.org_id.clone()?,
                                        merchant_ids: vec![user_role.merchant_id.clone()?],
                                        processor_merchant_ids: None,
                                    }),
                                    EntityType::Organization => Some(AuthInfo::OrgLevel {
                                        org_id: user_role.org_id.clone()?,
                                    }),
                                    EntityType::Tenant => Some(AuthInfo::OrgLevel {
                                        org_id: auth.org_id.clone(),
                                    }),
                                })
                        })
                        .collect()
                };
                analytics::search::search_results(
                    state
                        .opensearch_client
                        .as_ref()
                        .ok_or_else(|| error_stack::report!(OpenSearchError::NotEnabled))?,
                    req,
                    search_params,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_merchant_dispute_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<api_models::analytics::GetDisputeFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetDisputeFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                analytics::disputes::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_profile_dispute_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<api_models::analytics::GetDisputeFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetDisputeFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                analytics::disputes::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_org_dispute_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<api_models::analytics::GetDisputeFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetDisputeFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();
                analytics::disputes::get_filters(&state.pool, req, &auth_info)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element.
    pub async fn get_merchant_dispute_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetDisputeMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetDisputeMetricRequest");
        let flow = AnalyticsFlow::GetDisputeMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                analytics::disputes::get_metrics(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element.
    pub async fn get_profile_dispute_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetDisputeMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetDisputeMetricRequest");
        let flow = AnalyticsFlow::GetDisputeMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                analytics::disputes::get_metrics(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    /// # Panics
    ///
    /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element.
    pub async fn get_org_dispute_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetDisputeMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetDisputeMetricRequest");
        let flow = AnalyticsFlow::GetDisputeMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();
                analytics::disputes::get_metrics(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_merchant_sankey(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSankey;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                analytics::payment_intents::get_sankey(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    pub async fn get_merchant_auth_event_sankey(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSankey;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_merchant_level_auth_info();
                analytics::auth_events::get_sankey(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
                allow_connected: true,
                allow_platform: true,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_org_auth_event_sankey(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSankey;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();
                analytics::auth_events::get_sankey(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_profile_auth_event_sankey(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSankey;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                analytics::auth_events::get_sankey(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_org_sankey(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSankey;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let auth_info = auth.platform.to_org_level_auth_info();
                analytics::payment_intents::get_sankey(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                    allow_connected: true,
                    allow_platform: true,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_profile_sankey(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSankey;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state: crate::routes::SessionState, auth: AuthenticationData, req, _| async move {
                let profile_id = auth
                    .profile
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?
                    .get_id()
                    .clone();
                let auth_info = auth.platform.to_profile_level_auth_info(profile_id);
                analytics::payment_intents::get_sankey(&state.pool, &auth_info, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
                allow_connected: true,
                allow_platform: false,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }
}
