// File: crates/router/src/core/revenue_recovery.rs
// Module: router
// Public functions: 8

pub mod api;
pub mod transformers;
pub mod types;
use std::marker::PhantomData;

use api_models::{
    enums,
    payments::{self as api_payments, PaymentsGetIntentRequest, PaymentsResponse},
    process_tracker::revenue_recovery,
    webhooks,
};
use common_utils::{
    self,
    errors::CustomResult,
    ext_traits::{AsyncExt, OptionExt, ValueExt},
    id_type,
};
use diesel_models::{enums as diesel_enum, process_tracker::business_status};
use error_stack::{self, report, ResultExt};
use hyperswitch_domain_models::{
    merchant_context,
    payments::{PaymentIntent, PaymentIntentData, PaymentStatusData},
    revenue_recovery as domain_revenue_recovery, ApiModelToDieselModelConvertor,
};
use scheduler::errors as sch_errors;

use crate::{
    core::{
        errors::{self, RouterResponse, RouterResult, StorageErrorExt},
        payments::{
            self,
            operations::{GetTrackerResponse, Operation},
            transformers::GenerateResponse,
        },
        revenue_recovery::types::RevenueRecoveryOutgoingWebhook,
    },
    db::StorageInterface,
    logger,
    routes::{app::ReqState, metrics, SessionState},
    services::ApplicationResponse,
    types::{
        api as router_api_types, domain,
        storage::{self, revenue_recovery as pcr},
        transformers::{ForeignFrom, ForeignInto},
    },
    workflows::revenue_recovery as revenue_recovery_workflow,
};
pub const CALCULATE_WORKFLOW: &str = "CALCULATE_WORKFLOW";
pub const PSYNC_WORKFLOW: &str = "PSYNC_WORKFLOW";
pub const EXECUTE_WORKFLOW: &str = "EXECUTE_WORKFLOW";

#[allow(clippy::too_many_arguments)]
pub async fn upsert_calculate_pcr_task(
    billing_connector_account: &domain::MerchantConnectorAccount,
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    recovery_intent_from_payment_intent: &domain_revenue_recovery::RecoveryPaymentIntent,
    business_profile: &domain::Profile,
    intent_retry_count: u16,
    payment_attempt_id: Option<id_type::GlobalAttemptId>,
    runner: storage::ProcessTrackerRunner,
    revenue_recovery_retry: diesel_enum::RevenueRecoveryAlgorithmType,
) -> CustomResult<webhooks::WebhookResponseTracker, errors::RevenueRecoveryError> {
    router_env::logger::info!("Starting calculate_job...");

    let task = "CALCULATE_WORKFLOW";

    let db = &*state.store;
    let payment_id = &recovery_intent_from_payment_intent.payment_id;

    // Create process tracker ID in the format: CALCULATE_WORKFLOW_{payment_intent_id}
    let process_tracker_id = format!("{runner}_{task}_{}", payment_id.get_string_repr());

    // Scheduled time is now because this will be the first entry in
    // process tracker and we dont want to wait
    let schedule_time = common_utils::date_time::now();

    let payment_attempt_id = payment_attempt_id
        .ok_or(error_stack::report!(
            errors::RevenueRecoveryError::PaymentAttemptIdNotFound
        ))
        .attach_printable("payment attempt id is required for calculate workflow tracking")?;

    // Check if a process tracker entry already exists for this payment intent
    let existing_entry = db
        .as_scheduler()
        .find_process_by_id(&process_tracker_id)
        .await
        .change_context(errors::RevenueRecoveryError::ProcessTrackerResponseError)
        .attach_printable(
            "Failed to check for existing calculate workflow process tracker entry",
        )?;

    match existing_entry {
        Some(existing_process) => {
            router_env::logger::error!(
                "Found existing CALCULATE_WORKFLOW task with  id: {}",
                existing_process.id
            );
        }
        None => {
            // No entry exists - create a new one
            router_env::logger::info!(
                "No existing CALCULATE_WORKFLOW task found for payment_intent_id: {}, creating new entry scheduled for 1 hour from now",
                payment_id.get_string_repr()
            );

            // Create tracking data
            let calculate_workflow_tracking_data = pcr::RevenueRecoveryWorkflowTrackingData {
                billing_mca_id: billing_connector_account.get_id(),
                global_payment_id: payment_id.clone(),
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                profile_id: business_profile.get_id().to_owned(),
                payment_attempt_id,
                revenue_recovery_retry,
                invoice_scheduled_time: None,
            };

            let tag = ["PCR"];
            let task = "CALCULATE_WORKFLOW";
            let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;

            let process_tracker_entry = storage::ProcessTrackerNew::new(
                process_tracker_id,
                task,
                runner,
                tag,
                calculate_workflow_tracking_data,
                Some(1),
                schedule_time,
                common_types::consts::API_VERSION,
            )
            .change_context(errors::RevenueRecoveryError::ProcessTrackerCreationError)
            .attach_printable("Failed to construct calculate workflow process tracker entry")?;

            // Insert into process tracker with status New
            db.as_scheduler()
                .insert_process(process_tracker_entry)
                .await
                .change_context(errors::RevenueRecoveryError::ProcessTrackerResponseError)
                .attach_printable(
                    "Failed to enter calculate workflow process_tracker_entry in DB",
                )?;

            router_env::logger::info!(
                "Successfully created new CALCULATE_WORKFLOW task for payment_intent_id: {}",
                payment_id.get_string_repr()
            );

            metrics::TASKS_ADDED_COUNT.add(
                1,
                router_env::metric_attributes!(("flow", "CalculateWorkflow")),
            );
        }
    }

    Ok(webhooks::WebhookResponseTracker::Payment {
        payment_id: payment_id.clone(),
        status: recovery_intent_from_payment_intent.status,
    })
}

pub async fn perform_execute_payment(
    state: &SessionState,
    execute_task_process: &storage::ProcessTracker,
    profile: &domain::Profile,
    merchant_context: domain::MerchantContext,
    tracking_data: &pcr::RevenueRecoveryWorkflowTrackingData,
    revenue_recovery_payment_data: &pcr::RevenueRecoveryPaymentData,
    payment_intent: &PaymentIntent,
) -> Result<(), sch_errors::ProcessTrackerError> {
    let db = &*state.store;

    let mut revenue_recovery_metadata = payment_intent
        .feature_metadata
        .as_ref()
        .and_then(|feature_metadata| feature_metadata.payment_revenue_recovery_metadata.clone())
        .get_required_value("Payment Revenue Recovery Metadata")?
        .convert_back();

    let decision = types::Decision::get_decision_based_on_params(
        state,
        payment_intent.status,
        revenue_recovery_metadata
            .payment_connector_transmission
            .unwrap_or_default(),
        payment_intent.active_attempt_id.clone(),
        revenue_recovery_payment_data,
        &tracking_data.global_payment_id,
    )
    .await?;

    // TODO decide if its a global failure or is it requeueable error
    match decision {
        types::Decision::Execute => {
            let connector_customer_id = revenue_recovery_metadata.get_connector_customer_id();

            let last_token_used = payment_intent
                .feature_metadata
                .as_ref()
                .and_then(|fm| fm.payment_revenue_recovery_metadata.as_ref())
                .map(|rr| {
                    rr.billing_connector_payment_details
                        .payment_processor_token
                        .clone()
                });

            let processor_token = storage::revenue_recovery_redis_operation::RedisTokenManager::get_token_based_on_retry_type(
                state,
                &connector_customer_id,
                tracking_data.revenue_recovery_retry,
                last_token_used.as_deref(),
                )
                .await
                .change_context(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "Failed to fetch token details from redis".to_string(),
                })?
                .ok_or(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "Failed to fetch token details from redis".to_string(),
            })?;
            logger::info!("Token fetched from redis success");
            let card_info =
                api_models::payments::AdditionalCardInfo::foreign_from(&processor_token);
            // record attempt call
            let record_attempt = api::record_internal_attempt_api(
                state,
                payment_intent,
                revenue_recovery_payment_data,
                &revenue_recovery_metadata,
                card_info,
                &processor_token
                    .payment_processor_token_details
                    .payment_processor_token,
            )
            .await;

            match record_attempt {
                Ok(record_attempt_response) => {
                    let action = Box::pin(types::Action::execute_payment(
                        state,
                        revenue_recovery_payment_data.merchant_account.get_id(),
                        payment_intent,
                        execute_task_process,
                        profile,
                        merchant_context,
                        revenue_recovery_payment_data,
                        &revenue_recovery_metadata,
                        &record_attempt_response.id,
                    ))
                    .await?;
                    Box::pin(action.execute_payment_task_response_handler(
                        state,
                        payment_intent,
                        execute_task_process,
                        revenue_recovery_payment_data,
                        &mut revenue_recovery_metadata,
                    ))
                    .await?;
                }
                Err(err) => {
                    logger::error!("Error while recording attempt: {:?}", err);
                    let pt_update = storage::ProcessTrackerUpdate::StatusUpdate {
                        status: enums::ProcessTrackerStatus::Pending,
                        business_status: Some(String::from(
                            business_status::EXECUTE_WORKFLOW_REQUEUE,
                        )),
                    };
                    db.as_scheduler()
                        .update_process(execute_task_process.clone(), pt_update)
                        .await?;
                }
            }
        }

        types::Decision::Psync(attempt_status, attempt_id) => {
            // find if a psync task is already present
            let task = PSYNC_WORKFLOW;
            let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;
            let process_tracker_id = attempt_id.get_psync_revenue_recovery_id(task, runner);
            let psync_process = db.find_process_by_id(&process_tracker_id).await?;

            match psync_process {
                Some(_) => {
                    let pcr_status: types::RevenueRecoveryPaymentsAttemptStatus =
                        attempt_status.foreign_into();

                    pcr_status
                        .update_pt_status_based_on_attempt_status_for_execute_payment(
                            db,
                            execute_task_process,
                        )
                        .await?;
                }

                None => {
                    // insert new psync task
                    insert_psync_pcr_task_to_pt(
                        revenue_recovery_payment_data.billing_mca.get_id().clone(),
                        db,
                        revenue_recovery_payment_data
                            .merchant_account
                            .get_id()
                            .clone(),
                        payment_intent.get_id().clone(),
                        revenue_recovery_payment_data.profile.get_id().clone(),
                        attempt_id.clone(),
                        storage::ProcessTrackerRunner::PassiveRecoveryWorkflow,
                        tracking_data.revenue_recovery_retry,
                    )
                    .await?;

                    // finish the current task
                    db.as_scheduler()
                        .finish_process_with_business_status(
                            execute_task_process.clone(),
                            business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_PSYNC,
                        )
                        .await?;
                }
            };
        }
        types::Decision::ReviewForSuccessfulPayment => {
            // Finish the current task since the payment was a success
            // And mark it as review as it might have happened through the external system
            db.finish_process_with_business_status(
                execute_task_process.clone(),
                business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_REVIEW,
            )
            .await?;
        }
        types::Decision::ReviewForFailedPayment(triggered_by) => {
            match triggered_by {
                enums::TriggeredBy::Internal => {
                    // requeue the current tasks to update the fields for rescheduling a payment
                    let pt_update = storage::ProcessTrackerUpdate::StatusUpdate {
                        status: enums::ProcessTrackerStatus::Pending,
                        business_status: Some(String::from(
                            business_status::EXECUTE_WORKFLOW_REQUEUE,
                        )),
                    };
                    db.as_scheduler()
                        .update_process(execute_task_process.clone(), pt_update)
                        .await?;
                }
                enums::TriggeredBy::External => {
                    logger::debug!("Failed Payment Attempt Triggered By External");
                    // Finish the current task since the payment was a failure by an external source
                    db.as_scheduler()
                        .finish_process_with_business_status(
                            execute_task_process.clone(),
                            business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_REVIEW,
                        )
                        .await?;
                }
            };
        }
        types::Decision::InvalidDecision => {
            db.finish_process_with_business_status(
                execute_task_process.clone(),
                business_status::EXECUTE_WORKFLOW_COMPLETE,
            )
            .await?;
            logger::warn!("Abnormal State Identified")
        }
    }

    Ok(())
}

#[allow(clippy::too_many_arguments)]
async fn insert_psync_pcr_task_to_pt(
    billing_mca_id: id_type::MerchantConnectorAccountId,
    db: &dyn StorageInterface,
    merchant_id: id_type::MerchantId,
    payment_id: id_type::GlobalPaymentId,
    profile_id: id_type::ProfileId,
    payment_attempt_id: id_type::GlobalAttemptId,
    runner: storage::ProcessTrackerRunner,
    revenue_recovery_retry: diesel_enum::RevenueRecoveryAlgorithmType,
) -> RouterResult<storage::ProcessTracker> {
    let task = PSYNC_WORKFLOW;
    let process_tracker_id = payment_attempt_id.get_psync_revenue_recovery_id(task, runner);
    let schedule_time = common_utils::date_time::now();
    let psync_workflow_tracking_data = pcr::RevenueRecoveryWorkflowTrackingData {
        billing_mca_id,
        global_payment_id: payment_id,
        merchant_id,
        profile_id,
        payment_attempt_id,
        revenue_recovery_retry,
        invoice_scheduled_time: Some(schedule_time),
    };
    let tag = ["REVENUE_RECOVERY"];
    let process_tracker_entry = storage::ProcessTrackerNew::new(
        process_tracker_id,
        task,
        runner,
        tag,
        psync_workflow_tracking_data,
        None,
        schedule_time,
        common_types::consts::API_VERSION,
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to construct delete tokenized data process tracker task")?;

    let response = db
        .insert_process(process_tracker_entry)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to construct delete tokenized data process tracker task")?;
    metrics::TASKS_ADDED_COUNT.add(
        1,
        router_env::metric_attributes!(("flow", "RevenueRecoveryPsync")),
    );

    Ok(response)
}

pub async fn perform_payments_sync(
    state: &SessionState,
    process: &storage::ProcessTracker,
    profile: &domain::Profile,
    merchant_context: domain::MerchantContext,
    tracking_data: &pcr::RevenueRecoveryWorkflowTrackingData,
    revenue_recovery_payment_data: &pcr::RevenueRecoveryPaymentData,
    payment_intent: &PaymentIntent,
) -> Result<(), errors::ProcessTrackerError> {
    let psync_data = api::call_psync_api(
        state,
        &tracking_data.global_payment_id,
        revenue_recovery_payment_data,
        true,
        true,
    )
    .await?;

    let payment_attempt = psync_data.payment_attempt.clone();
    let mut revenue_recovery_metadata = payment_intent
        .feature_metadata
        .as_ref()
        .and_then(|feature_metadata| feature_metadata.payment_revenue_recovery_metadata.clone())
        .get_required_value("Payment Revenue Recovery Metadata")?
        .convert_back();
    let pcr_status: types::RevenueRecoveryPaymentsAttemptStatus =
        payment_attempt.status.foreign_into();

    let new_revenue_recovery_payment_data = &pcr::RevenueRecoveryPaymentData {
        psync_data: Some(psync_data),
        ..revenue_recovery_payment_data.clone()
    };

    Box::pin(
        pcr_status.update_pt_status_based_on_attempt_status_for_payments_sync(
            state,
            payment_intent,
            process.clone(),
            profile,
            merchant_context,
            new_revenue_recovery_payment_data,
            payment_attempt,
            &mut revenue_recovery_metadata,
        ),
    )
    .await?;

    Ok(())
}

pub async fn perform_calculate_workflow(
    state: &SessionState,
    process: &storage::ProcessTracker,
    profile: &domain::Profile,
    merchant_context: domain::MerchantContext,
    tracking_data: &pcr::RevenueRecoveryWorkflowTrackingData,
    revenue_recovery_payment_data: &pcr::RevenueRecoveryPaymentData,
    payment_intent: &PaymentIntent,
) -> Result<(), sch_errors::ProcessTrackerError> {
    let db = &*state.store;
    let merchant_id = revenue_recovery_payment_data.merchant_account.get_id();
    let profile_id = revenue_recovery_payment_data.profile.get_id();
    let billing_mca_id = revenue_recovery_payment_data.billing_mca.get_id();

    let mut event_type: Option<common_enums::EventType> = None;

    logger::info!(
        process_id = %process.id,
        payment_id = %tracking_data.global_payment_id.get_string_repr(),
        "Starting CALCULATE_WORKFLOW..."
    );

    // 1. Extract connector_customer_id and token_list from tracking_data
    let connector_customer_id = payment_intent
        .extract_connector_customer_id_from_payment_intent()
        .change_context(errors::RecoveryError::ValueNotFound)
        .attach_printable("Failed to extract customer ID from payment intent")?;

    let merchant_context_from_revenue_recovery_payment_data =
        domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
            revenue_recovery_payment_data.merchant_account.clone(),
            revenue_recovery_payment_data.key_store.clone(),
        )));

    let retry_algorithm_type = match profile
        .revenue_recovery_retry_algorithm_type
        .filter(|retry_type|
             *retry_type != common_enums::RevenueRecoveryAlgorithmType::Monitoring) // ignore Monitoring in profile
        .unwrap_or(tracking_data.revenue_recovery_retry)                                                                  // fallback to tracking_data
    {
        common_enums::RevenueRecoveryAlgorithmType::Smart => common_enums::RevenueRecoveryAlgorithmType::Smart,
        common_enums::RevenueRecoveryAlgorithmType::Cascading => common_enums::RevenueRecoveryAlgorithmType::Cascading,
        common_enums::RevenueRecoveryAlgorithmType::Monitoring => {
            return Err(sch_errors::ProcessTrackerError::ProcessUpdateFailed);
        }
    };

    // External Payments which enter the calculate workflow for the first time will have active attempt id as None
    // Then we dont need to send an webhook to the merchant as its not a failure from our side.
    // Thus we dont need to a payment get call for such payments.
    let active_payment_attempt_id = payment_intent.active_attempt_id.as_ref();

    let payments_response = get_payment_response_using_payment_get_operation(
        state,
        &tracking_data.global_payment_id,
        revenue_recovery_payment_data,
        &merchant_context_from_revenue_recovery_payment_data,
        active_payment_attempt_id,
    )
    .await?;

    // 2. Get best available token
    let best_time_to_schedule =
        match revenue_recovery_workflow::get_token_with_schedule_time_based_on_retry_algorithm_type(
            state,
            &connector_customer_id,
            payment_intent,
            retry_algorithm_type,
            process.retry_count,
        )
        .await
        {
            Ok(token_opt) => token_opt,
            Err(e) => {
                logger::error!(
                    error = ?e,
                    connector_customer_id = %connector_customer_id,
                    "Failed to get best PSP token"
                );
                None
            }
        };

    match best_time_to_schedule {
        Some(scheduled_time) => {
            logger::info!(
                process_id = %process.id,
                connector_customer_id = %connector_customer_id,
                "Found best available token, creating EXECUTE_WORKFLOW task"
            );

            // reset active attmept id and payment connector transmission before going to execute workflow
            let  _ = Box::pin(reset_connector_transmission_and_active_attempt_id_before_pushing_to_execute_workflow(
                state,
                payment_intent,
                revenue_recovery_payment_data,
                active_payment_attempt_id
            )).await?;

            // 3. If token found: create EXECUTE_WORKFLOW task and finish CALCULATE_WORKFLOW
            insert_execute_pcr_task_to_pt(
                &tracking_data.billing_mca_id,
                state,
                &tracking_data.merchant_id,
                payment_intent,
                &tracking_data.profile_id,
                &tracking_data.payment_attempt_id,
                storage::ProcessTrackerRunner::PassiveRecoveryWorkflow,
                retry_algorithm_type,
                scheduled_time,
            )
            .await?;

            db.as_scheduler()
                .finish_process_with_business_status(
                    process.clone(),
                    business_status::CALCULATE_WORKFLOW_SCHEDULED,
                )
                .await
                .map_err(|e| {
                    logger::error!(
                        process_id = %process.id,
                        error = ?e,
                        "Failed to update CALCULATE_WORKFLOW status to complete"
                    );
                    sch_errors::ProcessTrackerError::ProcessUpdateFailed
                })?;

            logger::info!(
                process_id = %process.id,
                connector_customer_id = %connector_customer_id,
                "CALCULATE_WORKFLOW completed successfully"
            );
        }

        None => {
            let scheduled_token = match storage::revenue_recovery_redis_operation::
                RedisTokenManager::get_payment_processor_token_with_schedule_time(state, &connector_customer_id)
                .await {
                    Ok(scheduled_token_opt) => scheduled_token_opt,
                    Err(e) => {
                        logger::error!(
                            error = ?e,
                            connector_customer_id = %connector_customer_id,
                            "Failed to get PSP token status"
                        );
                        None
                    }
                };

            match scheduled_token {
                Some(scheduled_token) => {
                    // Update scheduled time to scheduled time + 15 minutes
                    // here scheduled_time is the wait time 15 minutes is a buffer time that we are adding
                    logger::info!(
                        process_id = %process.id,
                        connector_customer_id = %connector_customer_id,
                        "No token but time available, rescheduling for scheduled time + 15 mins"
                    );

                    update_calculate_job_schedule_time(
                        db,
                        process,
                        time::Duration::seconds(
                            state
                                .conf
                                .revenue_recovery
                                .recovery_timestamp
                                .job_schedule_buffer_time_in_seconds,
                        ),
                        scheduled_token.scheduled_at,
                        &connector_customer_id,
                    )
                    .await?;
                }
                None => {
                    let hard_decline_flag = storage::revenue_recovery_redis_operation::
                        RedisTokenManager::are_all_tokens_hard_declined(
                            state,
                            &connector_customer_id
                        )
                        .await
                        .ok()
                        .unwrap_or(false);

                    match hard_decline_flag {
                        false => {
                            logger::info!(
                                process_id = %process.id,
                                connector_customer_id = %connector_customer_id,
                                "Hard decline flag is false, rescheduling for scheduled time + 15 mins"
                            );

                            update_calculate_job_schedule_time(
                                db,
                                process,
                                time::Duration::seconds(
                                    state
                                        .conf
                                        .revenue_recovery
                                        .recovery_timestamp
                                        .job_schedule_buffer_time_in_seconds,
                                ),
                                Some(common_utils::date_time::now()),
                                &connector_customer_id,
                            )
                            .await?;
                        }
                        true => {
                            // Finish calculate workflow with CALCULATE_WORKFLOW_FINISH
                            logger::info!(
                                process_id = %process.id,
                                connector_customer_id = %connector_customer_id,
                                "No token available, finishing CALCULATE_WORKFLOW"
                            );

                            db.as_scheduler()
                                .finish_process_with_business_status(
                                    process.clone(),
                                    business_status::CALCULATE_WORKFLOW_FINISH,
                                )
                                .await
                                .map_err(|e| {
                                    logger::error!(
                                        process_id = %process.id,
                                        error = ?e,
                                        "Failed to finish CALCULATE_WORKFLOW"
                                    );
                                    sch_errors::ProcessTrackerError::ProcessUpdateFailed
                                })?;

                            event_type = Some(common_enums::EventType::PaymentFailed);

                            logger::info!(
                                process_id = %process.id,
                                connector_customer_id = %connector_customer_id,
                                "CALCULATE_WORKFLOW finished successfully"
                            );
                        }
                    }
                }
            }
        }
    }

    let _outgoing_webhook = event_type.and_then(|event_kind| {
        payments_response.map(|resp| Some((event_kind, resp)))
    })
    .flatten()
    .async_map(|(event_kind, response)| async move {
        let _ = RevenueRecoveryOutgoingWebhook::send_outgoing_webhook_based_on_revenue_recovery_status(
            state,
            common_enums::EventClass::Payments,
            event_kind,
            payment_intent,
            &merchant_context,
            profile,
            tracking_data.payment_attempt_id.get_string_repr().to_string(),
            response
        )
        .await
        .map_err(|e| {
            logger::error!(
                error = ?e,
                "Failed to send outgoing webhook"
            );
            e
        })
        .ok();
    }
    ).await;

    Ok(())
}

/// Update the schedule time for a CALCULATE_WORKFLOW process tracker
async fn update_calculate_job_schedule_time(
    db: &dyn StorageInterface,
    process: &storage::ProcessTracker,
    additional_time: time::Duration,
    base_time: Option<time::PrimitiveDateTime>,
    connector_customer_id: &str,
) -> Result<(), sch_errors::ProcessTrackerError> {
    let now = common_utils::date_time::now();

    let new_schedule_time = base_time.filter(|&t| t > now).unwrap_or(now) + additional_time;
    logger::info!(
        new_schedule_time = %new_schedule_time,
        process_id = %process.id,
        connector_customer_id = %connector_customer_id,
        "Rescheduling Calculate Job at "
    );
    let pt_update = storage::ProcessTrackerUpdate::Update {
        name: Some("CALCULATE_WORKFLOW".to_string()),
        retry_count: Some(process.clone().retry_count),
        schedule_time: Some(new_schedule_time),
        tracking_data: Some(process.clone().tracking_data),
        business_status: Some(String::from(business_status::PENDING)),
        status: Some(common_enums::ProcessTrackerStatus::Pending),
        updated_at: Some(common_utils::date_time::now()),
    };

    db.as_scheduler()
        .update_process(process.clone(), pt_update)
        .await
        .map_err(|e| {
            logger::error!(
                process_id = %process.id,
                error = ?e,
                "Failed to reschedule CALCULATE_WORKFLOW"
            );
            sch_errors::ProcessTrackerError::ProcessUpdateFailed
        })?;

    logger::info!(
        process_id = %process.id,
        connector_customer_id = %connector_customer_id,
        new_schedule_time = %new_schedule_time,
        additional_time = ?additional_time,
        "CALCULATE_WORKFLOW rescheduled successfully"
    );

    Ok(())
}

/// Insert Execute PCR Task to Process Tracker
#[allow(clippy::too_many_arguments)]
async fn insert_execute_pcr_task_to_pt(
    billing_mca_id: &id_type::MerchantConnectorAccountId,
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    payment_intent: &PaymentIntent,
    profile_id: &id_type::ProfileId,
    payment_attempt_id: &id_type::GlobalAttemptId,
    runner: storage::ProcessTrackerRunner,
    revenue_recovery_retry: diesel_enum::RevenueRecoveryAlgorithmType,
    schedule_time: time::PrimitiveDateTime,
) -> Result<storage::ProcessTracker, sch_errors::ProcessTrackerError> {
    let task = "EXECUTE_WORKFLOW";

    let payment_id = payment_intent.id.clone();

    let process_tracker_id = format!("{runner}_{task}_{}", payment_id.get_string_repr());

    // Check if a process tracker entry already exists for this payment intent
    let existing_entry = state
        .store
        .find_process_by_id(&process_tracker_id)
        .await
        .map_err(|e| {
            logger::error!(
                payment_id = %payment_id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                error = ?e,
                "Failed to check for existing execute workflow process tracker entry"
            );
            sch_errors::ProcessTrackerError::ProcessUpdateFailed
        })?;

    match existing_entry {
        Some(existing_process)
            if existing_process.business_status == business_status::EXECUTE_WORKFLOW_FAILURE
                || existing_process.business_status
                    == business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_PSYNC =>
        {
            // Entry exists with EXECUTE_WORKFLOW_COMPLETE status - update it
            logger::info!(
                payment_id = %payment_id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                current_retry_count = %existing_process.retry_count,
                "Found existing EXECUTE_WORKFLOW task with COMPLETE status, updating to PENDING with incremented retry count"
            );

            let mut tracking_data: pcr::RevenueRecoveryWorkflowTrackingData =
                serde_json::from_value(existing_process.tracking_data.clone())
                    .change_context(errors::RecoveryError::ValueNotFound)
                    .attach_printable(
                        "Failed to deserialize the tracking data from process tracker",
                    )?;

            tracking_data.revenue_recovery_retry = revenue_recovery_retry;

            let tracking_data_json = serde_json::to_value(&tracking_data)
                .change_context(errors::RecoveryError::ValueNotFound)
                .attach_printable("Failed to serialize the tracking data to json")?;

            let pt_update = storage::ProcessTrackerUpdate::Update {
                name: Some(task.to_string()),
                retry_count: Some(existing_process.clone().retry_count + 1),
                schedule_time: Some(schedule_time),
                tracking_data: Some(tracking_data_json),
                business_status: Some(String::from(business_status::PENDING)),
                status: Some(enums::ProcessTrackerStatus::Pending),
                updated_at: Some(common_utils::date_time::now()),
            };

            let updated_process = state
                .store
                .update_process(existing_process, pt_update)
                .await
                .map_err(|e| {
                    logger::error!(
                        payment_id = %payment_id.get_string_repr(),
                        process_tracker_id = %process_tracker_id,
                        error = ?e,
                        "Failed to update existing execute workflow process tracker entry"
                    );
                    sch_errors::ProcessTrackerError::ProcessUpdateFailed
                })?;

            logger::info!(
                payment_id = %payment_id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                new_retry_count = %updated_process.retry_count,
                "Successfully updated existing EXECUTE_WORKFLOW task"
            );

            Ok(updated_process)
        }
        Some(existing_process) => {
            // Entry exists but business status is not EXECUTE_WORKFLOW_COMPLETE
            logger::info!(
                payment_id = %payment_id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                current_business_status = %existing_process.business_status,
            );

            Ok(existing_process)
        }
        None => {
            // No entry exists - create a new one
            logger::info!(
                payment_id = %payment_id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                "No existing EXECUTE_WORKFLOW task found, creating new entry"
            );

            let execute_workflow_tracking_data = pcr::RevenueRecoveryWorkflowTrackingData {
                billing_mca_id: billing_mca_id.clone(),
                global_payment_id: payment_id.clone(),
                merchant_id: merchant_id.clone(),
                profile_id: profile_id.clone(),
                payment_attempt_id: payment_attempt_id.clone(),
                revenue_recovery_retry,
                invoice_scheduled_time: Some(schedule_time),
            };

            let tag = ["PCR"];
            let process_tracker_entry = storage::ProcessTrackerNew::new(
                process_tracker_id.clone(),
                task,
                runner,
                tag,
                execute_workflow_tracking_data,
                Some(1),
                schedule_time,
                common_types::consts::API_VERSION,
            )
            .map_err(|e| {
                logger::error!(
                    payment_id = %payment_id.get_string_repr(),
                    error = ?e,
                    "Failed to construct execute workflow process tracker entry"
                );
                sch_errors::ProcessTrackerError::ProcessUpdateFailed
            })?;

            let response = state
                .store
                .insert_process(process_tracker_entry)
                .await
                .map_err(|e| {
                    logger::error!(
                        payment_id = %payment_id.get_string_repr(),
                        error = ?e,
                        "Failed to insert execute workflow process tracker entry"
                    );
                    sch_errors::ProcessTrackerError::ProcessUpdateFailed
                })?;

            metrics::TASKS_ADDED_COUNT.add(
                1,
                router_env::metric_attributes!(("flow", "RevenueRecoveryExecute")),
            );

            logger::info!(
                payment_id = %payment_id.get_string_repr(),
                process_tracker_id = %response.id,
                "Successfully created new EXECUTE_WORKFLOW task"
            );

            Ok(response)
        }
    }
}

pub async fn retrieve_revenue_recovery_process_tracker(
    state: SessionState,
    id: id_type::GlobalPaymentId,
) -> RouterResponse<revenue_recovery::RevenueRecoveryResponse> {
    let db = &*state.store;
    let task = EXECUTE_WORKFLOW;
    let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;
    let process_tracker_id = id.get_execute_revenue_recovery_id(task, runner);

    let process_tracker = db
        .find_process_by_id(&process_tracker_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)
        .attach_printable("error retrieving the process tracker id")?
        .get_required_value("Process Tracker")
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Entry For the following id doesn't exists".to_owned(),
        })?;

    let tracking_data = process_tracker
        .tracking_data
        .clone()
        .parse_value::<pcr::RevenueRecoveryWorkflowTrackingData>("PCRWorkflowTrackingData")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to deserialize  Pcr Workflow Tracking Data")?;

    let psync_task = PSYNC_WORKFLOW;

    let process_tracker_id_for_psync = tracking_data
        .payment_attempt_id
        .get_psync_revenue_recovery_id(psync_task, runner);

    let process_tracker_for_psync = db
        .find_process_by_id(&process_tracker_id_for_psync)
        .await
        .map_err(|e| {
            logger::error!("Error while retrieving psync task : {:?}", e);
        })
        .ok()
        .flatten();

    let schedule_time_for_psync = process_tracker_for_psync.and_then(|pt| pt.schedule_time);

    let response = revenue_recovery::RevenueRecoveryResponse {
        id: process_tracker.id,
        name: process_tracker.name,
        schedule_time_for_payment: process_tracker.schedule_time,
        schedule_time_for_psync,
        status: process_tracker.status,
        business_status: process_tracker.business_status,
    };
    Ok(ApplicationResponse::Json(response))
}

pub async fn resume_revenue_recovery_process_tracker(
    state: SessionState,
    id: id_type::GlobalPaymentId,
    request_retrigger: revenue_recovery::RevenueRecoveryRetriggerRequest,
) -> RouterResponse<revenue_recovery::RevenueRecoveryResponse> {
    let db = &*state.store;
    let task = request_retrigger.revenue_recovery_task;
    let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;
    let process_tracker_id = id.get_execute_revenue_recovery_id(&task, runner);

    let process_tracker = db
        .find_process_by_id(&process_tracker_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)
        .attach_printable("error retrieving the process tracker id")?
        .get_required_value("Process Tracker")
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Entry For the following id doesn't exists".to_owned(),
        })?;

    let tracking_data = process_tracker
        .tracking_data
        .clone()
        .parse_value::<pcr::RevenueRecoveryWorkflowTrackingData>("PCRWorkflowTrackingData")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to deserialize  Pcr Workflow Tracking Data")?;

    //Call payment intent to check the status
    let request = PaymentsGetIntentRequest { id: id.clone() };
    let revenue_recovery_payment_data =
        revenue_recovery_workflow::extract_data_and_perform_action(&state, &tracking_data)
            .await
            .change_context(errors::ApiErrorResponse::GenericNotFoundError {
                message: "Failed to extract the revenue recovery data".to_owned(),
            })?;
    let merchant_context_from_revenue_recovery_payment_data =
        domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
            revenue_recovery_payment_data.merchant_account.clone(),
            revenue_recovery_payment_data.key_store.clone(),
        )));
    let create_intent_response = payments::payments_intent_core::<
        router_api_types::PaymentGetIntent,
        router_api_types::payments::PaymentsIntentResponse,
        _,
        _,
        PaymentIntentData<router_api_types::PaymentGetIntent>,
    >(
        state.clone(),
        state.get_req_state(),
        merchant_context_from_revenue_recovery_payment_data,
        revenue_recovery_payment_data.profile.clone(),
        payments::operations::PaymentGetIntent,
        request,
        tracking_data.global_payment_id.clone(),
        hyperswitch_domain_models::payments::HeaderPayload::default(),
    )
    .await?;

    let response = create_intent_response
        .get_json_body()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unexpected response from payments core")?;

    match response.status {
        enums::IntentStatus::Failed => {
            let pt_update = storage::ProcessTrackerUpdate::Update {
                name: process_tracker.name.clone(),
                tracking_data: Some(process_tracker.tracking_data.clone()),
                business_status: Some(request_retrigger.business_status.clone()),
                status: Some(request_retrigger.status),
                updated_at: Some(common_utils::date_time::now()),
                retry_count: Some(process_tracker.retry_count + 1),
                schedule_time: Some(request_retrigger.schedule_time.unwrap_or(
                    common_utils::date_time::now().saturating_add(time::Duration::seconds(600)),
                )),
            };


// Struct: DummyConnectorPaymentResponse
// File: crates/router/src/routes/dummy_connector/types.rs
// Module: router
// Implementations: 0

pub struct DummyConnectorPaymentResponse


// Struct: Config
// File: crates/router_env/src/logger/config.rs
// Module: router_env
// Implementations: 1

pub struct Config


// Struct: SantanderPixQRCodeErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/santander/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SantanderPixQRCodeErrorResponse


// Struct: Payments
// File: crates/router/src/routes/app.rs
// Module: router
// Implementations: 2

pub struct Payments


// Struct: BankInfo
// File: crates/hyperswitch_connectors/src/connectors/facilitapay/responses.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BankInfo


// Function: into_inner
// File: crates/hyperswitch_domain_models/src/type_encryption.rs
// Module: hyperswitch_domain_models

pub fn into_inner(self) -> T


// Implementation: impl ApiEventMetric for for ToggleBlocklistResponse
// File: crates/api_models/src/blocklist.rs
// Module: api_models
// Methods: 0 total (0 public)

impl ApiEventMetric for for ToggleBlocklistResponse


// Struct: BlockedRateAccumulator
// File: crates/analytics/src/frm/accumulator.rs
// Module: analytics
// Implementations: 1
// Traits: FrmMetricAccumulator

pub struct BlockedRateAccumulator


// Module Structure
// File: crates/router/src/events.rs
// Module: router

// Public submodules:
pub mod api_logs;
pub mod audit_events;
pub mod connector_api_logs;
pub mod event_logger;
pub mod outgoing_webhook_logs;
pub mod routing_api_logs;


// Implementation: impl IncomingWebhook for for Razorpay
// File: crates/hyperswitch_connectors/src/connectors/razorpay.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl IncomingWebhook for for Razorpay


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

pub struct PaymentMethodTypeCapRes


// Implementation: impl AddressConstructor for for BillingAddress
// File: crates/hyperswitch_connectors/src/connectors/nexixpay/transformers.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl AddressConstructor for for BillingAddress


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

impl api::Refund for for Amazonpay


// Struct: ApproveRequest
// File: crates/hyperswitch_connectors/src/connectors/worldline/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ApproveRequest


// Function: server
// File: crates/router/src/routes/app.rs
// Module: router

pub fn server(config: AppState) -> Scope


// Implementation: impl api::PaymentCapture for for Getnet
// File: crates/hyperswitch_connectors/src/connectors/getnet.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentCapture for for Getnet


// Function: get_refund_sync_process_schedule_time
// File: crates/router/src/core/refunds.rs
// Module: router

pub fn get_refund_sync_process_schedule_time(
    db: &dyn db::StorageInterface,
    connector: &str,
    merchant_id: &common_utils::id_type::MerchantId,
    retry_count: i32,
) -> Result<Option<time::PrimitiveDateTime>, errors::ProcessTrackerError>


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

impl ConnectorSpecifications for for Fiserv


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

impl Worldpay


// Struct: Card
// File: crates/hyperswitch_connectors/src/connectors/worldline/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Card


// Implementation: impl KafkaPaymentAttempt
// File: crates/router/src/services/kafka/payment_attempt.rs
// Module: router
// Methods: 1 total (1 public)

impl KafkaPaymentAttempt


// Function: health
// File: crates/router/src/bin/scheduler.rs
// Module: router

pub fn health() -> impl actix_web::Responder


// Implementation: impl api::RefundExecute for for Katapult
// File: crates/hyperswitch_connectors/src/connectors/katapult.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Katapult


// Struct: PeachPaymentsConnectorMetadataObject
// File: crates/hyperswitch_connectors/src/connectors/peachpayments/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PeachPaymentsConnectorMetadataObject


// Implementation: impl ProphetpayActionType
// File: crates/hyperswitch_connectors/src/connectors/prophetpay/transformers.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl ProphetpayActionType


// Function: get_decision_engine_configs
// File: crates/api_models/src/routing.rs
// Module: api_models

pub fn get_decision_engine_configs(
        &self,
    ) -> Result<open_router::DecisionEngineSuccessRateData, error_stack::Report<ValidationError>>


// Implementation: impl webhooks::IncomingWebhook for for Juspaythreedsserver
// File: crates/hyperswitch_connectors/src/connectors/juspaythreedsserver.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl webhooks::IncomingWebhook for for Juspaythreedsserver


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

impl FileUpload for for Adyen


// Implementation: impl std::ops::Deref for for PaymentsMandateReference
// File: crates/hyperswitch_domain_models/src/mandates.rs
// Module: hyperswitch_domain_models
// Methods: 1 total (0 public)

impl std::ops::Deref for for PaymentsMandateReference


// File: crates/api_models/src/analytics/payment_intents.rs
// Module: api_models
// Public functions: 1
// Public structs: 10

use std::{
    collections::hash_map::DefaultHasher,
    hash::{Hash, Hasher},
};

use common_utils::id_type;

use super::{ForexMetric, NameDescription, TimeRange};
use crate::enums::{
    AuthenticationType, Connector, Currency, IntentStatus, PaymentMethod, PaymentMethodType,
};

#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
pub struct PaymentIntentFilters {
    #[serde(default)]
    pub status: Vec<IntentStatus>,
    #[serde(default)]
    pub currency: Vec<Currency>,
    #[serde(default)]
    pub profile_id: Vec<id_type::ProfileId>,
    #[serde(default)]
    pub connector: Vec<Connector>,
    #[serde(default)]
    pub auth_type: Vec<AuthenticationType>,
    #[serde(default)]
    pub payment_method: Vec<PaymentMethod>,
    #[serde(default)]
    pub payment_method_type: Vec<PaymentMethodType>,
    #[serde(default)]
    pub card_network: Vec<String>,
    #[serde(default)]
    pub merchant_id: Vec<id_type::MerchantId>,
    #[serde(default)]
    pub card_last_4: Vec<String>,
    #[serde(default)]
    pub card_issuer: Vec<String>,
    #[serde(default)]
    pub error_reason: Vec<String>,
    #[serde(default)]
    pub customer_id: Vec<id_type::CustomerId>,
}

#[derive(
    Debug,
    serde::Serialize,
    serde::Deserialize,
    strum::AsRefStr,
    PartialEq,
    PartialOrd,
    Eq,
    Ord,
    strum::Display,
    strum::EnumIter,
    Clone,
    Copy,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PaymentIntentDimensions {
    #[strum(serialize = "status")]
    #[serde(rename = "status")]
    PaymentIntentStatus,
    Currency,
    ProfileId,
    Connector,
    #[strum(serialize = "authentication_type")]
    #[serde(rename = "authentication_type")]
    AuthType,
    PaymentMethod,
    PaymentMethodType,
    CardNetwork,
    MerchantId,
    #[strum(serialize = "card_last_4")]
    #[serde(rename = "card_last_4")]
    CardLast4,
    CardIssuer,
    ErrorReason,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumIter,
    strum::AsRefStr,
)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum PaymentIntentMetrics {
    SuccessfulSmartRetries,
    TotalSmartRetries,
    SmartRetriedAmount,
    PaymentIntentCount,
    PaymentsSuccessRate,
    PaymentProcessedAmount,
    SessionizedSuccessfulSmartRetries,
    SessionizedTotalSmartRetries,
    SessionizedSmartRetriedAmount,
    SessionizedPaymentIntentCount,
    SessionizedPaymentsSuccessRate,
    SessionizedPaymentProcessedAmount,
    SessionizedPaymentsDistribution,
}
impl ForexMetric for PaymentIntentMetrics {
    fn is_forex_metric(&self) -> bool {
        matches!(
            self,
            Self::PaymentProcessedAmount
                | Self::SmartRetriedAmount
                | Self::SessionizedPaymentProcessedAmount
                | Self::SessionizedSmartRetriedAmount
        )
    }
}

#[derive(Debug, Default, serde::Serialize)]
pub struct ErrorResult {
    pub reason: String,
    pub count: i64,
    pub percentage: f64,
}

pub mod metric_behaviour {
    pub struct SuccessfulSmartRetries;
    pub struct TotalSmartRetries;
    pub struct SmartRetriedAmount;
    pub struct PaymentIntentCount;
    pub struct PaymentsSuccessRate;
}

impl From<PaymentIntentMetrics> for NameDescription {
    fn from(value: PaymentIntentMetrics) -> Self {
        Self {
            name: value.to_string(),
            desc: String::new(),
        }
    }
}

impl From<PaymentIntentDimensions> for NameDescription {
    fn from(value: PaymentIntentDimensions) -> Self {
        Self {
            name: value.to_string(),
            desc: String::new(),
        }
    }
}

#[derive(Debug, serde::Serialize, Eq)]
pub struct PaymentIntentMetricsBucketIdentifier {
    pub status: Option<IntentStatus>,
    pub currency: Option<Currency>,
    pub profile_id: Option<String>,
    pub connector: Option<String>,
    pub auth_type: Option<AuthenticationType>,
    pub payment_method: Option<String>,
    pub payment_method_type: Option<String>,
    pub card_network: Option<String>,
    pub merchant_id: Option<String>,
    pub card_last_4: Option<String>,
    pub card_issuer: Option<String>,
    pub error_reason: Option<String>,
    #[serde(rename = "time_range")]
    pub time_bucket: TimeRange,
    #[serde(rename = "time_bucket")]
    #[serde(with = "common_utils::custom_serde::iso8601custom")]
    pub start_time: time::PrimitiveDateTime,
}

impl PaymentIntentMetricsBucketIdentifier {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        status: Option<IntentStatus>,
        currency: Option<Currency>,
        profile_id: Option<String>,
        connector: Option<String>,
        auth_type: Option<AuthenticationType>,
        payment_method: Option<String>,
        payment_method_type: Option<String>,
        card_network: Option<String>,
        merchant_id: Option<String>,
        card_last_4: Option<String>,
        card_issuer: Option<String>,
        error_reason: Option<String>,
        normalized_time_range: TimeRange,
    ) -> Self {
        Self {
            status,
            currency,
            profile_id,
            connector,
            auth_type,
            payment_method,
            payment_method_type,
            card_network,
            merchant_id,
            card_last_4,
            card_issuer,
            error_reason,
            time_bucket: normalized_time_range,
            start_time: normalized_time_range.start_time,
        }
    }
}

impl Hash for PaymentIntentMetricsBucketIdentifier {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.status.map(|i| i.to_string()).hash(state);
        self.currency.hash(state);
        self.profile_id.hash(state);
        self.connector.hash(state);
        self.auth_type.map(|i| i.to_string()).hash(state);
        self.payment_method.hash(state);
        self.payment_method_type.hash(state);
        self.card_network.hash(state);
        self.merchant_id.hash(state);
        self.card_last_4.hash(state);
        self.card_issuer.hash(state);
        self.error_reason.hash(state);
        self.time_bucket.hash(state);
    }
}

impl PartialEq for PaymentIntentMetricsBucketIdentifier {
    fn eq(&self, other: &Self) -> bool {
        let mut left = DefaultHasher::new();
        self.hash(&mut left);
        let mut right = DefaultHasher::new();
        other.hash(&mut right);
        left.finish() == right.finish()
    }
}

#[derive(Debug, serde::Serialize)]
pub struct PaymentIntentMetricsBucketValue {
    pub successful_smart_retries: Option<u64>,
    pub total_smart_retries: Option<u64>,
    pub smart_retried_amount: Option<u64>,
    pub smart_retried_amount_in_usd: Option<u64>,
    pub smart_retried_amount_without_smart_retries: Option<u64>,
    pub smart_retried_amount_without_smart_retries_in_usd: Option<u64>,
    pub payment_intent_count: Option<u64>,
    pub successful_payments: Option<u32>,
    pub successful_payments_without_smart_retries: Option<u32>,
    pub total_payments: Option<u32>,
    pub payments_success_rate: Option<f64>,
    pub payments_success_rate_without_smart_retries: Option<f64>,
    pub payment_processed_amount: Option<u64>,
    pub payment_processed_amount_in_usd: Option<u64>,
    pub payment_processed_count: Option<u64>,
    pub payment_processed_amount_without_smart_retries: Option<u64>,
    pub payment_processed_amount_without_smart_retries_in_usd: Option<u64>,
    pub payment_processed_count_without_smart_retries: Option<u64>,
    pub payments_success_rate_distribution_without_smart_retries: Option<f64>,
    pub payments_failure_rate_distribution_without_smart_retries: Option<f64>,
}

#[derive(Debug, serde::Serialize)]
pub struct MetricsBucketResponse {
    #[serde(flatten)]
    pub values: PaymentIntentMetricsBucketValue,
    #[serde(flatten)]
    pub dimensions: PaymentIntentMetricsBucketIdentifier,
}


// Function: get_merchant_fingerprint_secret_key
// File: crates/common_utils/src/id_type/merchant.rs
// Module: common_utils

// Documentation: get_merchant_fingerprint_secret_key

pub fn get_merchant_fingerprint_secret_key(&self) -> String


// Implementation: impl ConnectorValidation for for Razorpay
// File: crates/hyperswitch_connectors/src/connectors/razorpay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorValidation for for Razorpay


// Struct: RouterStore
// File: crates/storage_impl/src/lib.rs
// Module: storage_impl
// Implementations: 5
// Traits: DatabaseStore, RedisConnInterface, PayoutAttemptInterface, PayoutsInterface

pub struct RouterStore<T: DatabaseStore>

DatabaseStore RedisConnInterface PayoutAttemptInterface PayoutsInterface


// Struct: InvoiceNew
// File: crates/diesel_models/src/invoice.rs
// Module: diesel_models
// Implementations: 1

pub struct InvoiceNew


// Function: new
// File: crates/common_types/src/primitive_wrappers.rs
// Module: common_types

// Documentation: Creates a new instance of `OvercaptureEnabledBool`

pub fn new(value: bool) -> Self


// Struct: SurchargeDetailsOutput
// File: crates/api_models/src/surcharge_decision_configs.rs
// Module: api_models
// Implementations: 0

pub struct SurchargeDetailsOutput


// Implementation: impl ApiEventMetric for for AuthenticationAuthenticateRequest
// File: crates/api_models/src/authentication.rs
// Module: api_models
// Methods: 1 total (0 public)

impl ApiEventMetric for for AuthenticationAuthenticateRequest


#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)]
#[serde(deny_unknown_fields)]
pub struct PayoutListConstraints {
    /// The identifier for customer
    #[schema(value_type = Option<String>, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::CustomerId>,

    /// A cursor for use in pagination, fetch the next list after some object
    #[schema(example = "payout_fafa124123", value_type = Option<String>,)]
    pub starting_after: Option<id_type::PayoutId>,

    /// A cursor for use in pagination, fetch the previous list before some object
    #[schema(example = "payout_fafa124123", value_type = Option<String>,)]
    pub ending_before: Option<id_type::PayoutId>,

    /// limit on the number of objects to return
    #[schema(default = 10, maximum = 100)]
    #[serde(default = "default_payouts_list_limit")]
    pub limit: u32,

    /// The time at which payout is created
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<PrimitiveDateTime>,

    /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc).
    #[serde(flatten)]
    #[schema(value_type = Option<TimeRange>)]
    pub time_range: Option<common_utils::types::TimeRange>,
}

#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)]
#[serde(deny_unknown_fields)]
pub struct PayoutListFilterConstraints {
    /// The identifier for payout
    #[schema(
    value_type = Option<String>,
    min_length = 30,
    max_length = 30,
    example = "187282ab-40ef-47a9-9206-5099ba31e432"
)]
    pub payout_id: Option<id_type::PayoutId>,
    /// The merchant order reference ID for payout
    #[schema(value_type = Option<String>, max_length = 255, example = "merchant_order_ref_123")]
    pub merchant_order_reference_id: Option<String>,
    /// The identifier for business profile
    #[schema(value_type = Option<String>)]
    pub profile_id: Option<id_type::ProfileId>,
    /// The identifier for customer
    #[schema(value_type = Option<String>,example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::CustomerId>,
    /// The limit on the number of objects. The default limit is 10 and max limit is 20
    #[serde(default = "default_payouts_list_limit")]
    pub limit: u32,
    /// The starting point within a list of objects
    pub offset: Option<u32>,
    /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc).
    #[serde(flatten)]
    #[schema(value_type = Option<TimeRange>)]
    pub time_range: Option<common_utils::types::TimeRange>,
    /// The list of connectors to filter payouts list
    #[schema(value_type = Option<Vec<PayoutConnectors>>, max_length = 255, example = json!(["wise", "adyen"]))]
    pub connector: Option<Vec<api_enums::PayoutConnectors>>,
    /// The list of currencies to filter payouts list
    #[schema(value_type = Currency, example = "USD")]
    pub currency: Option<Vec<api_enums::Currency>>,
    /// The list of payout status to filter payouts list
    #[schema(value_type = Option<Vec<PayoutStatus>>, example = json!(["pending", "failed"]))]
    pub status: Option<Vec<api_enums::PayoutStatus>>,
    /// The list of payout methods to filter payouts list
    #[schema(value_type = Option<Vec<PayoutType>>, example = json!(["bank", "card"]))]
    pub payout_method: Option<Vec<common_enums::PayoutType>>,
    /// Type of recipient
    #[schema(value_type = PayoutEntityType, example = "Individual")]
    pub entity_type: Option<common_enums::PayoutEntityType>,
}

#[derive(Clone, Debug, serde::Serialize, ToSchema)]
pub struct PayoutListResponse {
    /// The number of payouts included in the list
    pub size: usize,
    /// The list of payouts response objects
    pub data: Vec<PayoutCreateResponse>,
    /// The total number of available payouts for given constraints
    #[serde(skip_serializing_if = "Option::is_none")]
    pub total_count: Option<i64>,
}

#[derive(Clone, Debug, serde::Serialize, ToSchema)]
pub struct PayoutListFilters {
    /// The list of available connector filters
    #[schema(value_type = Vec<PayoutConnectors>)]
    pub connector: Vec<api_enums::PayoutConnectors>,
    /// The list of available currency filters
    #[schema(value_type = Vec<Currency>)]
    pub currency: Vec<common_enums::Currency>,
    /// The list of available payout status filters
    #[schema(value_type = Vec<PayoutStatus>)]
    pub status: Vec<common_enums::PayoutStatus>,
    /// The list of available payout method filters
    #[schema(value_type = Vec<PayoutType>)]
    pub payout_method: Vec<common_enums::PayoutType>,
}

#[derive(Clone, Debug, serde::Serialize, ToSchema)]
pub struct PayoutLinkResponse {
    pub payout_link_id: String,
    #[schema(value_type = String)]
    pub link: Secret<url::Url>,
}

#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)]
pub struct PayoutLinkInitiateRequest {
    #[schema(value_type = String)]
    pub merchant_id: id_type::MerchantId,
    #[schema(value_type = String)]
    pub payout_id: id_type::PayoutId,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct PayoutLinkDetails {
    pub publishable_key: Secret<String>,
    pub client_secret: Secret<String>,
    pub payout_link_id: String,
    pub payout_id: id_type::PayoutId,
    pub customer_id: id_type::CustomerId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub session_expiry: PrimitiveDateTime,
    pub return_url: Option<url::Url>,
    #[serde(flatten)]
    pub ui_config: link_utils::GenericLinkUiConfigFormData,
    pub enabled_payment_methods: Vec<link_utils::EnabledPaymentMethod>,
    pub enabled_payment_methods_with_required_fields: Vec<PayoutEnabledPaymentMethodsInfo>,
    pub amount: common_utils::types::StringMajorUnit,
    pub currency: common_enums::Currency,
    pub locale: String,
    pub form_layout: Option<common_enums::UIWidgetFormLayout>,
    pub test_mode: bool,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct PayoutEnabledPaymentMethodsInfo {
    pub payment_method: common_enums::PaymentMethod,
    pub payment_method_types_info: Vec<PaymentMethodTypeInfo>,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct PaymentMethodTypeInfo {
    pub payment_method_type: common_enums::PaymentMethodType,
    pub required_fields: Option<HashMap<String, RequiredFieldInfo>>,
}

#[derive(Clone, Debug, serde::Serialize, FlatStruct)]
pub struct RequiredFieldsOverrideRequest {
    pub billing: Option<payments::Address>,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct PayoutLinkStatusDetails {
    pub payout_link_id: String,
    pub payout_id: id_type::PayoutId,
    pub customer_id: id_type::CustomerId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub session_expiry: PrimitiveDateTime,
    pub return_url: Option<url::Url>,
    pub status: api_enums::PayoutStatus,
    pub error_code: Option<UnifiedCode>,
    pub error_message: Option<UnifiedMessage>,
    #[serde(flatten)]
    pub ui_config: link_utils::GenericLinkUiConfigFormData,
    pub test_mode: bool,
}

impl From<Bank> for payout_method_utils::BankAdditionalData {
    fn from(bank_data: Bank) -> Self {
        match bank_data {
            Bank::Ach(AchBankTransfer {
                bank_name,
                bank_country_code,
                bank_city,
                bank_account_number,
                bank_routing_number,
            }) => Self::Ach(Box::new(
                payout_method_utils::AchBankTransferAdditionalData {
                    bank_name,
                    bank_country_code,
                    bank_city,
                    bank_account_number: bank_account_number.into(),
                    bank_routing_number: bank_routing_number.into(),
                },
            )),
            Bank::Bacs(BacsBankTransfer {
                bank_name,
                bank_country_code,
                bank_city,
                bank_account_number,
                bank_sort_code,
            }) => Self::Bacs(Box::new(
                payout_method_utils::BacsBankTransferAdditionalData {
                    bank_name,
                    bank_country_code,
                    bank_city,
                    bank_account_number: bank_account_number.into(),
                    bank_sort_code: bank_sort_code.into(),
                },
            )),
            Bank::Sepa(SepaBankTransfer {
                bank_name,
                bank_country_code,
                bank_city,
                iban,
                bic,
            }) => Self::Sepa(Box::new(
                payout_method_utils::SepaBankTransferAdditionalData {
                    bank_name,
                    bank_country_code,
                    bank_city,
                    iban: iban.into(),
                    bic: bic.map(From::from),
                },
            )),
            Bank::Pix(PixBankTransfer {
                bank_name,
                bank_branch,
                bank_account_number,
                pix_key,
                tax_id,
            }) => Self::Pix(Box::new(
                payout_method_utils::PixBankTransferAdditionalData {
                    bank_name,
                    bank_branch,
                    bank_account_number: bank_account_number.into(),
                    pix_key: pix_key.into(),
                    tax_id: tax_id.map(From::from),
                },
            )),
        }
    }
}

impl From<Wallet> for payout_method_utils::WalletAdditionalData {
    fn from(wallet_data: Wallet) -> Self {
        match wallet_data {
            Wallet::Paypal(Paypal {
                email,
                telephone_number,
                paypal_id,
            }) => Self::Paypal(Box::new(payout_method_utils::PaypalAdditionalData {
                email: email.map(ForeignFrom::foreign_from),
                telephone_number: telephone_number.map(From::from),
                paypal_id: paypal_id.map(From::from),
            })),
            Wallet::Venmo(Venmo { telephone_number }) => {
                Self::Venmo(Box::new(payout_method_utils::VenmoAdditionalData {
                    telephone_number: telephone_number.map(From::from),
                }))
            }
        }
    }
}

impl From<payout_method_utils::AdditionalPayoutMethodData> for PayoutMethodDataResponse {
    fn from(additional_data: payout_method_utils::AdditionalPayoutMethodData) -> Self {
        match additional_data {
            payout_method_utils::AdditionalPayoutMethodData::Card(card_data) => {
                Self::Card(card_data)
            }
            payout_method_utils::AdditionalPayoutMethodData::Bank(bank_data) => {
                Self::Bank(bank_data)
            }
            payout_method_utils::AdditionalPayoutMethodData::Wallet(wallet_data) => {
                Self::Wallet(wallet_data)
            }
        }
    }
}


// Struct: FiservemeaAuthType
// File: crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FiservemeaAuthType


// Function: payments_get_intent
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn payments_get_intent(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder


// Struct: SmithyMember
// File: crates/smithy-core/src/types.rs
// Module: smithy-core
// Implementations: 0

pub struct SmithyMember


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

pub struct CardDetail


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

impl api::MandateSetup for for Silverflow


// Struct: PayuPaymentStatusData
// File: crates/hyperswitch_connectors/src/connectors/payu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PayuPaymentStatusData


// Function: auth_type
// File: crates/router/src/services/authentication.rs
// Module: router

pub fn auth_type<'a, T, A>(
    default_auth: &'a dyn AuthenticateAndFetch<T, A>,
    jwt_auth_type: &'a dyn AuthenticateAndFetch<T, A>,
    headers: &HeaderMap,
) -> &'a dyn AuthenticateAndFetch<T, A>
where


// Struct: Feedback
// File: crates/api_models/src/user/dashboard_metadata.rs
// Module: api_models
// Implementations: 0

pub struct Feedback


// Struct: RoutingAlgorithmRef
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 1

pub struct RoutingAlgorithmRef


// Implementation: impl ApplePayPredecryptData
// File: crates/common_types/src/payments.rs
// Module: common_types
// Methods: 5 total (5 public)

impl ApplePayPredecryptData