// File: crates/router/src/core/revenue_recovery/types.rs
// Module: router
// Public functions: 9
// Public structs: 1

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

use api_models::{
    enums as api_enums,
    payments::{
        AmountDetails, PaymentRevenueRecoveryMetadata, PaymentsUpdateIntentRequest,
        ProxyPaymentsRequest,
    },
};
use common_utils::{
    self,
    ext_traits::{OptionExt, ValueExt},
    id_type,
};
use diesel_models::{
    enums, payment_intent, process_tracker::business_status, types as diesel_types,
};
use error_stack::{self, ResultExt};
use hyperswitch_domain_models::{
    api::ApplicationResponse,
    business_profile, merchant_connector_account,
    merchant_context::{Context, MerchantContext},
    payments::{
        self as domain_payments, payment_attempt::PaymentAttempt, PaymentConfirmData,
        PaymentIntent, PaymentIntentData, PaymentStatusData,
    },
    router_data_v2::{self, flow_common_types},
    router_flow_types,
    router_request_types::revenue_recovery as revenue_recovery_request,
    router_response_types::revenue_recovery as revenue_recovery_response,
    ApiModelToDieselModelConvertor,
};
use time::PrimitiveDateTime;

use super::errors::StorageErrorExt;
use crate::{
    core::{
        errors::{self, RouterResult},
        payments::{self, helpers, operations::Operation, transformers::GenerateResponse},
        revenue_recovery::{self as revenue_recovery_core, pcr, perform_calculate_workflow},
        webhooks::{
            create_event_and_trigger_outgoing_webhook, recovery_incoming as recovery_incoming_flow,
        },
    },
    db::StorageInterface,
    logger,
    routes::SessionState,
    services::{self, connector_integration_interface::RouterDataConversion},
    types::{
        self, api as api_types, api::payments as payments_types, domain, storage,
        transformers::ForeignInto,
    },
    workflows::{
        payment_sync,
        revenue_recovery::{self, get_schedule_time_to_retry_mit_payments},
    },
};

type RecoveryResult<T> = error_stack::Result<T, errors::RecoveryError>;
pub const REVENUE_RECOVERY: &str = "revenue_recovery";
/// The status of Passive Churn Payments
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub enum RevenueRecoveryPaymentsAttemptStatus {
    Succeeded,
    Failed,
    Processing,
    InvalidStatus(String),
    //  Cancelled,
}

impl RevenueRecoveryPaymentsAttemptStatus {
    pub(crate) async fn update_pt_status_based_on_attempt_status_for_execute_payment(
        &self,
        db: &dyn StorageInterface,
        execute_task_process: &storage::ProcessTracker,
    ) -> Result<(), errors::ProcessTrackerError> {
        logger::info!("Entering update_pt_status_based_on_attempt_status_for_execute_payment");
        match &self {
            Self::Succeeded | Self::Failed | Self::Processing => {
                // finish the current execute task
                db.finish_process_with_business_status(
                    execute_task_process.clone(),
                    business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_PSYNC,
                )
                .await?;
            }

            Self::InvalidStatus(action) => {
                logger::debug!(
                    "Invalid Attempt Status for the Recovery Payment : {}",
                    action
                );
                let pt_update = storage::ProcessTrackerUpdate::StatusUpdate {
                    status: enums::ProcessTrackerStatus::Review,
                    business_status: Some(String::from(business_status::EXECUTE_WORKFLOW_COMPLETE)),
                };
                // update the process tracker status as Review
                db.update_process(execute_task_process.clone(), pt_update)
                    .await?;
            }
        };
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    pub(crate) async fn update_pt_status_based_on_attempt_status_for_payments_sync(
        &self,
        state: &SessionState,
        payment_intent: &PaymentIntent,
        process_tracker: storage::ProcessTracker,
        profile: &domain::Profile,
        merchant_context: domain::MerchantContext,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        payment_attempt: PaymentAttempt,
        revenue_recovery_metadata: &mut PaymentRevenueRecoveryMetadata,
    ) -> Result<(), errors::ProcessTrackerError> {
        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 db = &*state.store;

        let recovery_payment_intent =
            hyperswitch_domain_models::revenue_recovery::RecoveryPaymentIntent::from(
                payment_intent,
            );

        let recovery_payment_attempt =
            hyperswitch_domain_models::revenue_recovery::RecoveryPaymentAttempt::from(
                &payment_attempt,
            );

        let recovery_payment_tuple = recovery_incoming_flow::RecoveryPaymentTuple::new(
            &recovery_payment_intent,
            &recovery_payment_attempt,
        );

        let used_token = get_payment_processor_token_id_from_payment_attempt(&payment_attempt);

        let retry_count = process_tracker.retry_count;

        let psync_response = revenue_recovery_payment_data
            .psync_data
            .as_ref()
            .ok_or(errors::RecoveryError::ValueNotFound)
            .attach_printable("Psync data not found in revenue recovery payment data")?;

        match self {
            Self::Succeeded => {
                // finish psync task as the payment was a success
                db.as_scheduler()
                    .finish_process_with_business_status(
                        process_tracker,
                        business_status::PSYNC_WORKFLOW_COMPLETE,
                    )
                    .await?;

                let event_status = common_enums::EventType::PaymentSucceeded;

                // publish events to kafka
                if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
                    state,
                    &recovery_payment_tuple,
                    Some(retry_count+1)
                )
                .await{
                    router_env::logger::error!(
                        "Failed to publish revenue recovery event to kafka: {:?}",
                        e
                    );
                };

                // update the status of token in redis
                let _update_error_code = storage::revenue_recovery_redis_operation::RedisTokenManager::update_payment_processor_token_error_code_from_process_tracker(
                    state,
                    &connector_customer_id,
                    &None,
                    // Since this is succeeded payment attempt, 'is_hard_decine' will be false.
                    &Some(false),
                    used_token.as_deref(),
                )
                .await;

                // unlocking the token
                let _unlock_the_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::unlock_connector_customer_status(
                    state,
                    &connector_customer_id,
                )
                .await;

                let payments_response = psync_response
                    .clone()
                    .generate_response(state, None, None, None, &merchant_context, profile, None)
                    .change_context(errors::RecoveryError::PaymentsResponseGenerationFailed)
                    .attach_printable("Failed while generating response for payment")?;

                RevenueRecoveryOutgoingWebhook::send_outgoing_webhook_based_on_revenue_recovery_status(
                    state,
                    common_enums::EventClass::Payments,
                    event_status,
                    payment_intent,
                    &merchant_context,
                    profile,
                    recovery_payment_attempt
                        .attempt_id
                        .get_string_repr()
                        .to_string(),
                    payments_response
                )
                .await?;

                // Record a successful transaction back to Billing Connector
                // TODO: Add support for retrying failed outgoing recordback webhooks
                record_back_to_billing_connector(
                    state,
                    &payment_attempt,
                    payment_intent,
                    &revenue_recovery_payment_data.billing_mca,
                )
                .await
                .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
                .attach_printable("Failed to update the process tracker")?;
            }
            Self::Failed => {
                // finish psync task
                db.as_scheduler()
                    .finish_process_with_business_status(
                        process_tracker.clone(),
                        business_status::PSYNC_WORKFLOW_COMPLETE,
                    )
                    .await?;
                // publish events to kafka
                if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
                    state,
                    &recovery_payment_tuple,
                    Some(retry_count+1)
                )
                .await{
                    router_env::logger::error!(
                        "Failed to publish revenue recovery event to kafka : {:?}", e
                    );
                };

                let error_code = recovery_payment_attempt.error_code;

                let is_hard_decline = revenue_recovery::check_hard_decline(state, &payment_attempt)
                    .await
                    .ok();

                // update the status of token in redis
                let _update_error_code = storage::revenue_recovery_redis_operation::RedisTokenManager::update_payment_processor_token_error_code_from_process_tracker(
                    state,
                    &connector_customer_id,
                    &error_code,
                    &is_hard_decline,
                    used_token.as_deref(),
                )
                .await;

                // unlocking the token
                let _unlock_the_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::unlock_connector_customer_status(
                    state,
                    &connector_customer_id,
                )
                .await;

                // Reopen calculate workflow on payment failure
                Box::pin(reopen_calculate_workflow_on_payment_failure(
                    state,
                    &process_tracker,
                    profile,
                    merchant_context,
                    payment_intent,
                    revenue_recovery_payment_data,
                    psync_response.payment_attempt.get_id(),
                ))
                .await?;
            }
            Self::Processing => {
                // do a psync payment
                let action = Box::pin(Action::payment_sync_call(
                    state,
                    revenue_recovery_payment_data,
                    payment_intent,
                    &process_tracker,
                    profile,
                    merchant_context,
                    payment_attempt,
                ))
                .await?;

                //handle the response
                Box::pin(action.psync_response_handler(
                    state,
                    payment_intent,
                    &process_tracker,
                    revenue_recovery_metadata,
                    revenue_recovery_payment_data,
                ))
                .await?;
            }
            Self::InvalidStatus(status) => logger::debug!(
                "Invalid Attempt Status for the Recovery Payment : {}",
                status
            ),
        }
        Ok(())
    }
}
pub enum Decision {
    Execute,
    Psync(enums::AttemptStatus, id_type::GlobalAttemptId),
    InvalidDecision,
    ReviewForSuccessfulPayment,
    ReviewForFailedPayment(enums::TriggeredBy),
}

impl Decision {
    pub async fn get_decision_based_on_params(
        state: &SessionState,
        intent_status: enums::IntentStatus,
        called_connector: enums::PaymentConnectorTransmission,
        active_attempt_id: Option<id_type::GlobalAttemptId>,
        revenue_recovery_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        payment_id: &id_type::GlobalPaymentId,
    ) -> RecoveryResult<Self> {
        logger::info!("Entering get_decision_based_on_params");

        Ok(match (intent_status, called_connector, active_attempt_id) {
            (
                enums::IntentStatus::Failed,
                enums::PaymentConnectorTransmission::ConnectorCallUnsuccessful,
                None,
            ) => Self::Execute,
            (
                enums::IntentStatus::Processing,
                enums::PaymentConnectorTransmission::ConnectorCallSucceeded,
                Some(_),
            ) => {
                let psync_data = revenue_recovery_core::api::call_psync_api(
                    state,
                    payment_id,
                    revenue_recovery_data,
                    true,
                    true,
                )
                .await
                .change_context(errors::RecoveryError::PaymentCallFailed)
                .attach_printable("Error while executing the Psync call")?;
                let payment_attempt = psync_data.payment_attempt;
                Self::Psync(payment_attempt.status, payment_attempt.get_id().clone())
            }
            (
                enums::IntentStatus::Failed,
                enums::PaymentConnectorTransmission::ConnectorCallUnsuccessful,
                Some(_),
            ) => {
                let psync_data = revenue_recovery_core::api::call_psync_api(
                    state,
                    payment_id,
                    revenue_recovery_data,
                    true,
                    true,
                )
                .await
                .change_context(errors::RecoveryError::PaymentCallFailed)
                .attach_printable("Error while executing the Psync call")?;

                let payment_attempt = psync_data.payment_attempt;

                let attempt_triggered_by = payment_attempt
                    .feature_metadata
                    .and_then(|metadata| {
                        metadata.revenue_recovery.map(|revenue_recovery_metadata| {
                            revenue_recovery_metadata.attempt_triggered_by
                        })
                    })
                    .get_required_value("Attempt Triggered By")
                    .change_context(errors::RecoveryError::ValueNotFound)?;
                Self::ReviewForFailedPayment(attempt_triggered_by)
            }
            (enums::IntentStatus::Succeeded, _, _) => Self::ReviewForSuccessfulPayment,
            _ => Self::InvalidDecision,
        })
    }
}

#[derive(Debug, Clone)]
pub enum Action {
    SyncPayment(PaymentAttempt),
    RetryPayment(PrimitiveDateTime),
    TerminalFailure(PaymentAttempt),
    SuccessfulPayment(PaymentAttempt),
    ReviewPayment,
    ManualReviewAction,
}
impl Action {
    #[allow(clippy::too_many_arguments)]
    pub async fn execute_payment(
        state: &SessionState,
        _merchant_id: &id_type::MerchantId,
        payment_intent: &PaymentIntent,
        process: &storage::ProcessTracker,
        profile: &domain::Profile,
        merchant_context: domain::MerchantContext,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        revenue_recovery_metadata: &PaymentRevenueRecoveryMetadata,
        latest_attempt_id: &id_type::GlobalAttemptId,
    ) -> RecoveryResult<Self> {
        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 tracking_data: pcr::RevenueRecoveryWorkflowTrackingData =
            serde_json::from_value(process.tracking_data.clone())
                .change_context(errors::RecoveryError::ValueNotFound)
                .attach_printable("Failed to deserialize the tracking data from process tracker")?;

        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 recovery_algorithm = tracking_data.revenue_recovery_retry;

        let scheduled_token = match storage::revenue_recovery_redis_operation::RedisTokenManager::get_token_based_on_retry_type(
            state,
            &connector_customer_id,
            recovery_algorithm,
            last_token_used.as_deref(),
        )
        .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) => {
                let response = revenue_recovery_core::api::call_proxy_api(
                    state,
                    payment_intent,
                    revenue_recovery_payment_data,
                    revenue_recovery_metadata,
                    &scheduled_token
                        .payment_processor_token_details
                        .payment_processor_token,
                )
                .await;

                let recovery_payment_intent =
                    hyperswitch_domain_models::revenue_recovery::RecoveryPaymentIntent::from(
                        payment_intent,
                    );
                // handle proxy api's response
                match response {
                    Ok(payment_data) => match payment_data.payment_attempt.status.foreign_into() {
                        RevenueRecoveryPaymentsAttemptStatus::Succeeded => {
                            let recovery_payment_attempt =
                                hyperswitch_domain_models::revenue_recovery::RecoveryPaymentAttempt::from(
                                    &payment_data.payment_attempt,
                                );

                            let recovery_payment_tuple =
                                recovery_incoming_flow::RecoveryPaymentTuple::new(
                                    &recovery_payment_intent,
                                    &recovery_payment_attempt,
                                );

                            // publish events to kafka
                            if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
                                state,
                                &recovery_payment_tuple,
                                Some(process.retry_count+1)
                            )
                            .await{
                                router_env::logger::error!(
                                    "Failed to publish revenue recovery event to kafka: {:?}",
                                    e
                                );
                            };

                            let is_hard_decline = revenue_recovery::check_hard_decline(
                                state,
                                &payment_data.payment_attempt,
                            )
                            .await
                            .ok();

                            // update the status of token in redis
                            let _update_error_code = storage::revenue_recovery_redis_operation::RedisTokenManager::update_payment_processor_token_error_code_from_process_tracker(
                                state,
                                &connector_customer_id,
                                &None,
                                &is_hard_decline,
                                Some(&scheduled_token.payment_processor_token_details.payment_processor_token),
                            )
                            .await;

                            // unlocking the token
                            let _unlock_the_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::unlock_connector_customer_status(
                                state,
                                &connector_customer_id,
                            )
                            .await;

                            let event_status = common_enums::EventType::PaymentSucceeded;

                            let payments_response = payment_data
                                .clone()
                                .generate_response(
                                    state,
                                    None,
                                    None,
                                    None,
                                    &merchant_context,
                                    profile,
                                    None,
                                )
                                .change_context(
                                    errors::RecoveryError::PaymentsResponseGenerationFailed,
                                )
                                .attach_printable("Failed while generating response for payment")?;

                            RevenueRecoveryOutgoingWebhook::send_outgoing_webhook_based_on_revenue_recovery_status(
                                state,
                                common_enums::EventClass::Payments,
                                event_status,
                                payment_intent,
                                &merchant_context,
                                profile,
                                payment_data.payment_attempt.id.get_string_repr().to_string(),
                                payments_response
                            )
                            .await?;

                            Ok(Self::SuccessfulPayment(
                                payment_data.payment_attempt.clone(),
                            ))
                        }
                        RevenueRecoveryPaymentsAttemptStatus::Failed => {
                            let recovery_payment_attempt =
                                hyperswitch_domain_models::revenue_recovery::RecoveryPaymentAttempt::from(
                                    &payment_data.payment_attempt,
                                );

                            let recovery_payment_tuple =
                                recovery_incoming_flow::RecoveryPaymentTuple::new(
                                    &recovery_payment_intent,
                                    &recovery_payment_attempt,
                                );

                            // publish events to kafka
                            if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
                                state,
                                &recovery_payment_tuple,
                                Some(process.retry_count+1)
                            )
                            .await{
                                router_env::logger::error!(
                                    "Failed to publish revenue recovery event to kafka: {:?}",
                                    e
                                );
                            };

                            let error_code = payment_data
                                .payment_attempt
                                .clone()
                                .error
                                .map(|error| error.code);

                            let is_hard_decline = revenue_recovery::check_hard_decline(
                                state,
                                &payment_data.payment_attempt,
                            )
                            .await
                            .ok();

                            let _update_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::update_payment_processor_token_error_code_from_process_tracker(
                                state,
                                &connector_customer_id,
                                &error_code,
                                &is_hard_decline,
                                Some(&scheduled_token
                                    .payment_processor_token_details
                                    .payment_processor_token)
                                    ,
                            )
                            .await;

                            // unlocking the token
                            let _unlock_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::unlock_connector_customer_status(
                                state,
                                &connector_customer_id,
                            )
                            .await;

                            // Reopen calculate workflow on payment failure
                            Box::pin(reopen_calculate_workflow_on_payment_failure(
                                state,
                                process,
                                profile,
                                merchant_context,
                                payment_intent,
                                revenue_recovery_payment_data,
                                latest_attempt_id,
                            ))
                            .await?;

                            // Return terminal failure to finish the current execute workflow
                            Ok(Self::TerminalFailure(payment_data.payment_attempt.clone()))
                        }

                        RevenueRecoveryPaymentsAttemptStatus::Processing => {
                            Ok(Self::SyncPayment(payment_data.payment_attempt.clone()))
                        }
                        RevenueRecoveryPaymentsAttemptStatus::InvalidStatus(action) => {
                            logger::info!(?action, "Invalid Payment Status For PCR Payment");
                            Ok(Self::ManualReviewAction)
                        }
                    },
                    Err(err) =>
                    // check for an active attempt being constructed or not
                    {
                        logger::error!(execute_payment_res=?err);
                        Ok(Self::ReviewPayment)
                    }
                }
            }
            None => {
                let response = revenue_recovery_core::api::call_psync_api(
                    state,
                    payment_intent.get_id(),
                    revenue_recovery_payment_data,
                    true,
                    true,
                )
                .await;

                let payment_status_data = response
                    .change_context(errors::RecoveryError::PaymentCallFailed)
                    .attach_printable("Error while executing the Psync call")?;

                let payment_attempt = payment_status_data.payment_attempt;

                logger::info!(
                    process_id = %process.id,
                    connector_customer_id = %connector_customer_id,
                    "No token available, finishing CALCULATE_WORKFLOW"
                );

                state
                    .store
                    .as_scheduler()
                    .finish_process_with_business_status(
                        process.clone(),
                        business_status::CALCULATE_WORKFLOW_FINISH,
                    )
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to finish CALCULATE_WORKFLOW")?;

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

    pub async fn execute_payment_task_response_handler(
        &self,
        state: &SessionState,
        payment_intent: &PaymentIntent,
        execute_task_process: &storage::ProcessTracker,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        revenue_recovery_metadata: &mut PaymentRevenueRecoveryMetadata,
    ) -> Result<(), errors::ProcessTrackerError> {
        logger::info!("Entering execute_payment_task_response_handler");

        let db = &*state.store;
        match self {
            Self::SyncPayment(payment_attempt) => {
                revenue_recovery_core::insert_psync_pcr_task_to_pt(
                    revenue_recovery_payment_data.billing_mca.get_id().clone(),
                    db,
                    revenue_recovery_payment_data
                        .merchant_account
                        .get_id()
                        .to_owned(),
                    payment_intent.id.clone(),
                    revenue_recovery_payment_data.profile.get_id().to_owned(),
                    payment_attempt.id.clone(),
                    storage::ProcessTrackerRunner::PassiveRecoveryWorkflow,
                    revenue_recovery_payment_data.retry_algorithm,
                )
                .await
                .change_context(errors::RecoveryError::ProcessTrackerFailure)
                .attach_printable("Failed to create a psync workflow in the process tracker")?;

                db.as_scheduler()
                    .finish_process_with_business_status(
                        execute_task_process.clone(),
                        business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_PSYNC,
                    )
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to update the process tracker")?;
                Ok(())
            }

            Self::RetryPayment(schedule_time) => {
                db.as_scheduler()
                    .retry_process(execute_task_process.clone(), *schedule_time)
                    .await?;

                // update the connector payment transmission field to Unsuccessful and unset active attempt id
                revenue_recovery_metadata.set_payment_transmission_field_for_api_request(
                    enums::PaymentConnectorTransmission::ConnectorCallUnsuccessful,
                );

                let payment_update_req =
                PaymentsUpdateIntentRequest::update_feature_metadata_and_active_attempt_with_api(
                    payment_intent
                        .feature_metadata
                        .clone()
                        .unwrap_or_default()
                        .convert_back()
                        .set_payment_revenue_recovery_metadata_using_api(
                            revenue_recovery_metadata.clone(),
                        ),
                    api_enums::UpdateActiveAttempt::Unset,
                );
                logger::info!(
                    "Call made to payments update intent api , with the request body {:?}",
                    payment_update_req
                );
                revenue_recovery_core::api::update_payment_intent_api(
                    state,
                    payment_intent.id.clone(),
                    revenue_recovery_payment_data,
                    payment_update_req,
                )
                .await
                .change_context(errors::RecoveryError::PaymentCallFailed)?;
                Ok(())
            }
            Self::TerminalFailure(payment_attempt) => {
                db.as_scheduler()
                    .finish_process_with_business_status(
                        execute_task_process.clone(),
                        business_status::EXECUTE_WORKFLOW_FAILURE,
                    )
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to update the process tracker")?;
                // TODO: Add support for retrying failed outgoing recordback webhooks
                Ok(())
            }
            Self::SuccessfulPayment(payment_attempt) => {
                db.as_scheduler()
                    .finish_process_with_business_status(
                        execute_task_process.clone(),
                        business_status::EXECUTE_WORKFLOW_COMPLETE,
                    )
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to update the process tracker")?;
                // Record back to billing connector for terminal status
                // TODO: Add support for retrying failed outgoing recordback webhooks
                record_back_to_billing_connector(
                    state,
                    payment_attempt,
                    payment_intent,
                    &revenue_recovery_payment_data.billing_mca,
                )
                .await
                .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
                .attach_printable("Failed to update the process tracker")?;
                Ok(())
            }
            Self::ReviewPayment => {
                // requeue the process tracker in case of error response
                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?;
                Ok(())
            }
            Self::ManualReviewAction => {
                logger::debug!("Invalid Payment Status For PCR Payment");
                let pt_update = storage::ProcessTrackerUpdate::StatusUpdate {
                    status: enums::ProcessTrackerStatus::Review,
                    business_status: Some(String::from(business_status::EXECUTE_WORKFLOW_COMPLETE)),
                };
                // update the process tracker status as Review
                db.as_scheduler()
                    .update_process(execute_task_process.clone(), pt_update)
                    .await?;
                Ok(())
            }
        }
    }

    pub async fn payment_sync_call(
        state: &SessionState,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        payment_intent: &PaymentIntent,
        process: &storage::ProcessTracker,
        profile: &domain::Profile,
        merchant_context: domain::MerchantContext,
        payment_attempt: PaymentAttempt,
    ) -> RecoveryResult<Self> {
        logger::info!("Entering payment_sync_call");

        let response = revenue_recovery_core::api::call_psync_api(
            state,
            payment_intent.get_id(),
            revenue_recovery_payment_data,
            true,
            true,
        )
        .await;
        let used_token = get_payment_processor_token_id_from_payment_attempt(&payment_attempt);

        match response {
            Ok(_payment_data) => match payment_attempt.status.foreign_into() {
                RevenueRecoveryPaymentsAttemptStatus::Succeeded => {
                    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")?;

                    // update the status of token in redis
                    let _update_error_code = storage::revenue_recovery_redis_operation::RedisTokenManager::update_payment_processor_token_error_code_from_process_tracker(
                    state,
                    &connector_customer_id,
                    &None,
                    // Since this is succeeded, 'hard_decine' will be false.
                    &Some(false),
                    used_token.as_deref(),
                )
                .await;

                    // unlocking the token
                    let _unlock_the_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::unlock_connector_customer_status(
                    state,
                    &connector_customer_id,
                )
                .await;

                    Ok(Self::SuccessfulPayment(payment_attempt))
                }
                RevenueRecoveryPaymentsAttemptStatus::Failed => {
                    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 error_code = payment_attempt.clone().error.map(|error| error.code);

                    let is_hard_decline =
                        revenue_recovery::check_hard_decline(state, &payment_attempt)
                            .await
                            .ok();

                    let _update_error_code = storage::revenue_recovery_redis_operation::RedisTokenManager::update_payment_processor_token_error_code_from_process_tracker(
                            state,
                            &connector_customer_id,
                            &error_code,
                            &is_hard_decline,
                            used_token.as_deref(),
                        )
                        .await;

                    // unlocking the token
                    let _unlock_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::unlock_connector_customer_status(
                        state,
                        &connector_customer_id,
                    )
                    .await;

                    // Reopen calculate workflow on payment failure
                    Box::pin(reopen_calculate_workflow_on_payment_failure(
                        state,
                        process,
                        profile,
                        merchant_context,
                        payment_intent,
                        revenue_recovery_payment_data,
                        payment_attempt.get_id(),
                    ))
                    .await?;

                    Ok(Self::TerminalFailure(payment_attempt.clone()))
                }

                RevenueRecoveryPaymentsAttemptStatus::Processing => {
                    Ok(Self::SyncPayment(payment_attempt))
                }
                RevenueRecoveryPaymentsAttemptStatus::InvalidStatus(action) => {
                    logger::info!(?action, "Invalid Payment Status For PCR PSync Payment");
                    Ok(Self::ManualReviewAction)
                }
            },
            Err(err) =>
            // if there is an error while psync we create a new Review Task
            {
                logger::error!(sync_payment_response=?err);
                Ok(Self::ReviewPayment)
            }
        }
    }
    pub async fn psync_response_handler(
        &self,
        state: &SessionState,
        payment_intent: &PaymentIntent,
        psync_task_process: &storage::ProcessTracker,
        revenue_recovery_metadata: &mut PaymentRevenueRecoveryMetadata,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
    ) -> Result<(), errors::ProcessTrackerError> {
        logger::info!("Entering psync_response_handler");

        let db = &*state.store;

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

        match self {
            Self::SyncPayment(payment_attempt) => {
                //  get a schedule time for psync
                // and retry the process if there is a schedule time
                // if None mark the pt status as Retries Exceeded and finish the task
                payment_sync::recovery_retry_sync_task(
                    state,
                    connector_customer_id,
                    revenue_recovery_metadata.connector.to_string(),
                    revenue_recovery_payment_data
                        .merchant_account
                        .get_id()
                        .clone(),
                    psync_task_process.clone(),
                )
                .await?;
                Ok(())
            }

            Self::RetryPayment(schedule_time) => {
                // finish the psync task
                db.as_scheduler()
                    .finish_process_with_business_status(
                        psync_task_process.clone(),
                        business_status::PSYNC_WORKFLOW_COMPLETE,
                    )
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to update the process tracker")?;

                // fetch the execute task
                let task = revenue_recovery_core::EXECUTE_WORKFLOW;
                let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;
                let process_tracker_id = payment_intent
                    .get_id()
                    .get_execute_revenue_recovery_id(task, runner);
                let execute_task_process = db
                    .as_scheduler()
                    .find_process_by_id(&process_tracker_id)
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)?
                    .get_required_value("Process Tracker")?;
                // retry the execute tasks
                db.as_scheduler()
                    .retry_process(execute_task_process, *schedule_time)
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to update the process tracker")?;
                Ok(())
            }

            Self::TerminalFailure(payment_attempt) => {
                // TODO: Add support for retrying failed outgoing recordback webhooks
                // finish the current psync task
                db.as_scheduler()
                    .finish_process_with_business_status(
                        psync_task_process.clone(),
                        business_status::PSYNC_WORKFLOW_COMPLETE,
                    )
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to update the process tracker")?;
                Ok(())
            }
            Self::SuccessfulPayment(payment_attempt) => {
                // finish the current psync task
                db.as_scheduler()
                    .finish_process_with_business_status(
                        psync_task_process.clone(),
                        business_status::PSYNC_WORKFLOW_COMPLETE,
                    )
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to update the process tracker")?;

                // Record a successful transaction back to Billing Connector
                // TODO: Add support for retrying failed outgoing recordback webhooks
                record_back_to_billing_connector(
                    state,
                    payment_attempt,
                    payment_intent,
                    &revenue_recovery_payment_data.billing_mca,
                )
                .await
                .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
                .attach_printable("Failed to update the process tracker")?;
                Ok(())
            }
            Self::ReviewPayment => {
                // requeue the process tracker task in case of psync api error
                let pt_update = storage::ProcessTrackerUpdate::StatusUpdate {
                    status: enums::ProcessTrackerStatus::Pending,
                    business_status: Some(String::from(business_status::PSYNC_WORKFLOW_REQUEUE)),
                };
                db.as_scheduler()
                    .update_process(psync_task_process.clone(), pt_update)
                    .await?;
                Ok(())
            }
            Self::ManualReviewAction => {
                logger::debug!("Invalid Payment Status For PCR Payment");
                let pt_update = storage::ProcessTrackerUpdate::StatusUpdate {
                    status: enums::ProcessTrackerStatus::Review,
                    business_status: Some(String::from(business_status::PSYNC_WORKFLOW_COMPLETE)),
                };
                // update the process tracker status as Review
                db.as_scheduler()
                    .update_process(psync_task_process.clone(), pt_update)
                    .await
                    .change_context(errors::RecoveryError::ProcessTrackerFailure)
                    .attach_printable("Failed to update the process tracker")?;

                Ok(())
            }
        }
    }

    pub(crate) async fn decide_retry_failure_action(
        state: &SessionState,
        merchant_id: &id_type::MerchantId,
        pt: storage::ProcessTracker,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        payment_attempt: &PaymentAttempt,
        payment_intent: &PaymentIntent,
    ) -> RecoveryResult<Self> {
        let db = &*state.store;
        let next_retry_count = pt.retry_count + 1;
        let error_message = payment_attempt
            .error
            .as_ref()
            .map(|details| details.message.clone());
        let error_code = payment_attempt
            .error
            .as_ref()
            .map(|details| details.code.clone());
        let connector_name = payment_attempt
            .connector
            .clone()
            .ok_or(errors::RecoveryError::ValueNotFound)
            .attach_printable("unable to derive payment connector from payment attempt")?;
        let gsm_record = helpers::get_gsm_record(
            state,
            error_code,
            error_message,
            connector_name,
            REVENUE_RECOVERY.to_string(),
        )
        .await;
        let is_hard_decline = gsm_record
            .and_then(|gsm_record| gsm_record.error_category)
            .map(|gsm_error_category| {
                gsm_error_category == common_enums::ErrorCategory::HardDecline
            })
            .unwrap_or(false);
        let schedule_time = revenue_recovery_payment_data
            .get_schedule_time_based_on_retry_type(
                state,
                merchant_id,
                next_retry_count,
                payment_attempt,
                payment_intent,
                is_hard_decline,
            )
            .await;

        match schedule_time {
            Some(schedule_time) => Ok(Self::RetryPayment(schedule_time)),

            None => Ok(Self::TerminalFailure(payment_attempt.clone())),
        }
    }
}

/// Reopen calculate workflow when payment fails
pub async fn reopen_calculate_workflow_on_payment_failure(
    state: &SessionState,
    process: &storage::ProcessTracker,
    profile: &domain::Profile,
    merchant_context: domain::MerchantContext,
    payment_intent: &PaymentIntent,
    revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
    latest_attempt_id: &id_type::GlobalAttemptId,
) -> RecoveryResult<()> {
    let db = &*state.store;
    let id = payment_intent.id.clone();
    let task = revenue_recovery_core::CALCULATE_WORKFLOW;
    let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;

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

    let new_tracking_data = pcr::RevenueRecoveryWorkflowTrackingData {
        payment_attempt_id: latest_attempt_id.clone(),
        ..old_tracking_data
    };


// Function: get_connector_metadata
// File: crates/hyperswitch_domain_models/src/router_response_types.rs
// Module: hyperswitch_domain_models

pub fn get_connector_metadata(&self) -> Option<masking::Secret<serde_json::Value>>


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

pub fn retrieve_default_routing_config(
    state: SessionState,
    profile_id: Option<common_utils::id_type::ProfileId>,
    merchant_context: domain::MerchantContext,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>>


// Function: construct_invoice_record_back_router_data
// File: crates/router/src/core/revenue_recovery/types.rs
// Module: router

pub fn construct_invoice_record_back_router_data(
    state: &SessionState,
    billing_mca: &merchant_connector_account::MerchantConnectorAccount,
    payment_attempt: &PaymentAttempt,
    payment_intent: &PaymentIntent,
) -> RecoveryResult<hyperswitch_domain_models::types::InvoiceRecordBackRouterData>


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

impl events::ApiEventMetric for for DecisionManagerRequest


// Function: handle_webhook_response
// File: crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs
// Module: hyperswitch_connectors

pub fn handle_webhook_response(
    payment_information: WebhookPaymentInformation,
    status_code: u16,
) -> CustomResult<
    (
        enums::AttemptStatus,
        Option<ErrorResponse>,
        PaymentsResponseData,
    ),
    errors::ConnectorError,
>


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

impl Responder


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

pub fn verify_email(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::VerifyEmailRequest>,
) -> HttpResponse


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

pub struct DeutschebankMeansOfPayment


// File: crates/common_utils/src/id_type/routing.rs
// Module: common_utils

crate::id_type!(
    RoutingId,
    " A type for routing_id that can be used for routing ids"
);

crate::impl_id_type_methods!(RoutingId, "routing_id");

// This is to display the `RoutingId` as RoutingId(abcd)
crate::impl_debug_id_type!(RoutingId);
crate::impl_try_from_cow_str_id_type!(RoutingId, "routing_id");

crate::impl_generate_id_id_type!(RoutingId, "routing");
crate::impl_serializable_secret_id_type!(RoutingId);
crate::impl_queryable_id_type!(RoutingId);
crate::impl_to_sql_from_sql_id_type!(RoutingId);

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


// Implementation: impl ClientSecretFetch for for PaymentMethodListRequest
// File: crates/router/src/services/authentication.rs
// Module: router
// Methods: 1 total (0 public)

impl ClientSecretFetch for for PaymentMethodListRequest


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

pub fn new(reference: String) -> Self


// Function: create_client
// File: crates/external_services/src/email/ses.rs
// Module: external_services

// Documentation: A helper function to create ses client

pub fn create_client(
        conf: &EmailSettings,
        ses_config: &SESConfig,
        proxy_url: Option<impl AsRef<str>>,
    ) -> CustomResult<Client, AwsSesError>


// Implementation: impl Tokenizable for for PaymentsAuthorizeData
// File: crates/router/src/types.rs
// Module: router
// Methods: 1 total (0 public)

impl Tokenizable for for PaymentsAuthorizeData


// Struct: PostProcessing
// File: crates/hyperswitch_domain_models/src/router_flow_types/payments.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PostProcessing


// Implementation: impl UserCompanyName
// File: crates/router/src/types/domain/user.rs
// Module: router
// Methods: 2 total (2 public)

impl UserCompanyName


// Struct: FrmRoutingAlgorithm
// File: crates/router/src/types/api/routing.rs
// Module: router
// Implementations: 0

pub struct FrmRoutingAlgorithm


// File: crates/diesel_models/src/process_tracker.rs
// Module: diesel_models
// Public functions: 2
// Public structs: 3

pub use common_enums::{enums::ProcessTrackerRunner, ApiVersion};
use common_utils::ext_traits::Encode;
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use error_stack::ResultExt;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{enums as storage_enums, errors, schema::process_tracker, StorageResult};

#[derive(
    Clone,
    Debug,
    Eq,
    PartialEq,
    Deserialize,
    Identifiable,
    Queryable,
    Selectable,
    Serialize,
    router_derive::DebugAsDisplay,
)]
#[diesel(table_name = process_tracker, check_for_backend(diesel::pg::Pg))]
pub struct ProcessTracker {
    pub id: String,
    pub name: Option<String>,
    #[diesel(deserialize_as = super::DieselArray<String>)]
    pub tag: Vec<String>,
    pub runner: Option<String>,
    pub retry_count: i32,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub schedule_time: Option<PrimitiveDateTime>,
    pub rule: String,
    pub tracking_data: serde_json::Value,
    pub business_status: String,
    pub status: storage_enums::ProcessTrackerStatus,
    #[diesel(deserialize_as = super::DieselArray<String>)]
    pub event: Vec<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub updated_at: PrimitiveDateTime,
    pub version: ApiVersion,
}

impl ProcessTracker {
    #[inline(always)]
    pub fn is_valid_business_status(&self, valid_statuses: &[&str]) -> bool {
        valid_statuses.iter().any(|&x| x == self.business_status)
    }
}

#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = process_tracker)]
pub struct ProcessTrackerNew {
    pub id: String,
    pub name: Option<String>,
    pub tag: Vec<String>,
    pub runner: Option<String>,
    pub retry_count: i32,
    pub schedule_time: Option<PrimitiveDateTime>,
    pub rule: String,
    pub tracking_data: serde_json::Value,
    pub business_status: String,
    pub status: storage_enums::ProcessTrackerStatus,
    pub event: Vec<String>,
    pub created_at: PrimitiveDateTime,
    pub updated_at: PrimitiveDateTime,
    pub version: ApiVersion,
}

impl ProcessTrackerNew {
    #[allow(clippy::too_many_arguments)]
    pub fn new<T>(
        process_tracker_id: impl Into<String>,
        task: impl Into<String>,
        runner: ProcessTrackerRunner,
        tag: impl IntoIterator<Item = impl Into<String>>,
        tracking_data: T,
        retry_count: Option<i32>,
        schedule_time: PrimitiveDateTime,
        api_version: ApiVersion,
    ) -> StorageResult<Self>
    where
        T: Serialize + std::fmt::Debug,
    {
        let current_time = common_utils::date_time::now();
        Ok(Self {
            id: process_tracker_id.into(),
            name: Some(task.into()),
            tag: tag.into_iter().map(Into::into).collect(),
            runner: Some(runner.to_string()),
            retry_count: retry_count.unwrap_or(0),
            schedule_time: Some(schedule_time),
            rule: String::new(),
            tracking_data: tracking_data
                .encode_to_value()
                .change_context(errors::DatabaseError::Others)
                .attach_printable("Failed to serialize process tracker tracking data")?,
            business_status: String::from(business_status::PENDING),
            status: storage_enums::ProcessTrackerStatus::New,
            event: vec![],
            created_at: current_time,
            updated_at: current_time,
            version: api_version,
        })
    }
}

#[derive(Debug)]
pub enum ProcessTrackerUpdate {
    Update {
        name: Option<String>,
        retry_count: Option<i32>,
        schedule_time: Option<PrimitiveDateTime>,
        tracking_data: Option<serde_json::Value>,
        business_status: Option<String>,
        status: Option<storage_enums::ProcessTrackerStatus>,
        updated_at: Option<PrimitiveDateTime>,
    },
    StatusUpdate {
        status: storage_enums::ProcessTrackerStatus,
        business_status: Option<String>,
    },
    StatusRetryUpdate {
        status: storage_enums::ProcessTrackerStatus,
        retry_count: i32,
        schedule_time: PrimitiveDateTime,
    },
}

#[derive(Debug, Clone, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = process_tracker)]
pub struct ProcessTrackerUpdateInternal {
    name: Option<String>,
    retry_count: Option<i32>,
    schedule_time: Option<PrimitiveDateTime>,
    tracking_data: Option<serde_json::Value>,
    business_status: Option<String>,
    status: Option<storage_enums::ProcessTrackerStatus>,
    updated_at: Option<PrimitiveDateTime>,
}

impl Default for ProcessTrackerUpdateInternal {
    fn default() -> Self {
        Self {
            name: Option::default(),
            retry_count: Option::default(),
            schedule_time: Option::default(),
            tracking_data: Option::default(),
            business_status: Option::default(),
            status: Option::default(),
            updated_at: Some(common_utils::date_time::now()),
        }
    }
}

impl From<ProcessTrackerUpdate> for ProcessTrackerUpdateInternal {
    fn from(process_tracker_update: ProcessTrackerUpdate) -> Self {
        match process_tracker_update {
            ProcessTrackerUpdate::Update {
                name,
                retry_count,
                schedule_time,
                tracking_data,
                business_status,
                status,
                updated_at,
            } => Self {
                name,
                retry_count,
                schedule_time,
                tracking_data,
                business_status,
                status,
                updated_at,
            },
            ProcessTrackerUpdate::StatusUpdate {
                status,
                business_status,
            } => Self {
                status: Some(status),
                business_status,
                ..Default::default()
            },
            ProcessTrackerUpdate::StatusRetryUpdate {
                status,
                retry_count,
                schedule_time,
            } => Self {
                status: Some(status),
                retry_count: Some(retry_count),
                schedule_time: Some(schedule_time),
                ..Default::default()
            },
        }
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::unwrap_used)]
    use common_utils::ext_traits::StringExt;

    use super::ProcessTrackerRunner;

    #[test]
    fn test_enum_to_string() {
        let string_format = "PAYMENTS_SYNC_WORKFLOW".to_string();
        let enum_format: ProcessTrackerRunner =
            string_format.parse_enum("ProcessTrackerRunner").unwrap();
        assert_eq!(enum_format, ProcessTrackerRunner::PaymentsSyncWorkflow);
    }
}

pub mod business_status {
    /// Indicates that an irrecoverable error occurred during the workflow execution.
    pub const GLOBAL_FAILURE: &str = "GLOBAL_FAILURE";

    /// Task successfully completed by consumer.
    /// A task that reaches this status should not be retried (rescheduled for execution) later.
    pub const COMPLETED_BY_PT: &str = "COMPLETED_BY_PT";

    /// An error occurred during the workflow execution which prevents further execution and
    /// retries.
    /// A task that reaches this status should not be retried (rescheduled for execution) later.
    pub const FAILURE: &str = "FAILURE";

    /// The resource associated with the task was removed, due to which further retries can/should
    /// not be done.
    pub const REVOKED: &str = "Revoked";

    /// The task was executed for the maximum possible number of times without a successful outcome.
    /// A task that reaches this status should not be retried (rescheduled for execution) later.
    pub const RETRIES_EXCEEDED: &str = "RETRIES_EXCEEDED";

    /// The outgoing webhook was successfully delivered in the initial attempt.
    /// Further retries of the task are not required.
    pub const INITIAL_DELIVERY_ATTEMPT_SUCCESSFUL: &str = "INITIAL_DELIVERY_ATTEMPT_SUCCESSFUL";

    /// Indicates that an error occurred during the workflow execution.
    /// This status is typically set by the workflow error handler.
    /// A task that reaches this status should not be retried (rescheduled for execution) later.
    pub const GLOBAL_ERROR: &str = "GLOBAL_ERROR";

    /// The resource associated with the task has been significantly modified since the task was
    /// created, due to which further retries of the current task are not required.
    /// A task that reaches this status should not be retried (rescheduled for execution) later.
    pub const RESOURCE_STATUS_MISMATCH: &str = "RESOURCE_STATUS_MISMATCH";

    /// Business status set for newly created tasks.
    pub const PENDING: &str = "Pending";

    /// For the PCR Workflow
    ///
    /// This status indicates the completion of a execute task
    pub const EXECUTE_WORKFLOW_COMPLETE: &str = "COMPLETED_EXECUTE_TASK";

    /// This status indicates the failure of a execute task
    pub const EXECUTE_WORKFLOW_FAILURE: &str = "FAILED_EXECUTE_TASK";

    /// This status indicates that the execute task was completed to trigger the psync task
    pub const EXECUTE_WORKFLOW_COMPLETE_FOR_PSYNC: &str = "COMPLETED_EXECUTE_TASK_TO_TRIGGER_PSYNC";

    /// This status indicates that the execute task was completed to trigger the review task
    pub const EXECUTE_WORKFLOW_COMPLETE_FOR_REVIEW: &str =
        "COMPLETED_EXECUTE_TASK_TO_TRIGGER_REVIEW";

    /// This status indicates that the requeue was triggered for execute task
    pub const EXECUTE_WORKFLOW_REQUEUE: &str = "TRIGGER_REQUEUE_FOR_EXECUTE_WORKFLOW";

    /// This status indicates the completion of a psync task
    pub const PSYNC_WORKFLOW_COMPLETE: &str = "COMPLETED_PSYNC_TASK";

    /// This status indicates that the psync task was completed to trigger the review task
    pub const PSYNC_WORKFLOW_COMPLETE_FOR_REVIEW: &str = "COMPLETED_PSYNC_TASK_TO_TRIGGER_REVIEW";

    /// This status indicates that the requeue was triggered for psync task
    pub const PSYNC_WORKFLOW_REQUEUE: &str = "TRIGGER_REQUEUE_FOR_PSYNC_WORKFLOW";

    /// This status indicates the completion of a review task
    pub const REVIEW_WORKFLOW_COMPLETE: &str = "COMPLETED_REVIEW_TASK";

    /// For the CALCULATE_WORKFLOW
    ///
    /// This status indicates an invoice is queued
    pub const CALCULATE_WORKFLOW_QUEUED: &str = "CALCULATE_WORKFLOW_QUEUED";

    /// This status indicates an invoice has been declined due to hard decline
    pub const CALCULATE_WORKFLOW_FINISH: &str = "FAILED_DUE_TO_HARD_DECLINE_ERROR";

    /// This status indicates that the invoice is scheduled with the best available token
    pub const CALCULATE_WORKFLOW_SCHEDULED: &str = "CALCULATE_WORKFLOW_SCHEDULED";

    /// This status indicates the invoice is in payment sync state
    pub const CALCULATE_WORKFLOW_PROCESSING: &str = "CALCULATE_WORKFLOW_PROCESSING";

    /// This status indicates the workflow has completed successfully when the invoice is paid
    pub const CALCULATE_WORKFLOW_COMPLETE: &str = "CALCULATE_WORKFLOW_COMPLETE";
}


// Struct: PoEligibility
// File: crates/hyperswitch_domain_models/src/router_flow_types/payouts.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PoEligibility


// Implementation: impl RemoveNewLine for for Option
// File: crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl RemoveNewLine for for Option


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

impl api::RefundExecute for for Tsys


// Function: new
// File: crates/test_utils/src/connector_auth.rs
// Module: test_utils

pub fn new() -> Self


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

pub fn new() -> &'static Self


// Struct: PartnerReferralResponse
// File: crates/router/src/types/api/connector_onboarding/paypal.rs
// Module: router
// Implementations: 1

pub struct PartnerReferralResponse


// Function: find_by_merchant_id_payout_id
// File: crates/diesel_models/src/query/payout_attempt.rs
// Module: diesel_models

pub fn find_by_merchant_id_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_id: &common_utils::id_type::PayoutId,
    ) -> StorageResult<Self>


// Struct: WiseRecipientCreateRequest
// File: crates/hyperswitch_connectors/src/connectors/wise/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct WiseRecipientCreateRequest


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

pub fn new() -> &'static Self


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

impl webhooks::IncomingWebhook for for Dwolla


// Implementation: impl quote::ToTokens for for FieldMeta
// File: crates/router_derive/src/macros/to_encryptable.rs
// Module: router_derive
// Methods: 1 total (0 public)

impl quote::ToTokens for for FieldMeta


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

// Public submodules:
pub mod transformers;


// Struct: StripeConnectPayoutCreateRequest
// File: crates/hyperswitch_connectors/src/connectors/stripe/transformers/connect.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct StripeConnectPayoutCreateRequest


// Implementation: impl PaymentIntentFetchConstraints
// File: crates/hyperswitch_domain_models/src/payments/payment_intent.rs
// Module: hyperswitch_domain_models
// Methods: 1 total (1 public)

impl PaymentIntentFetchConstraints


// Struct: CreateRoleRequest
// File: crates/api_models/src/user_role/role.rs
// Module: api_models
// Implementations: 0

pub struct CreateRoleRequest


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

impl Responder


// Implementation: impl Coinbase
// File: crates/hyperswitch_connectors/src/connectors/coinbase.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl Coinbase


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

impl ConnectorValidation for for Cryptopay


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

impl api::Refund for for Klarna


// File: crates/hyperswitch_connectors/src/connectors/inespay.rs
// Module: hyperswitch_connectors
// Public functions: 1
// Public structs: 1

pub mod transformers;

use std::sync::LazyLock;

use base64::Engine;
use common_enums::enums;
use common_utils::{
    consts::BASE64_ENGINE,
    crypto,
    errors::CustomResult,
    ext_traits::{ByteSliceExt, BytesExt},
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use masking::{ExposeInterface, Mask, Secret};
use ring::hmac;
use transformers as inespay;

use crate::{constants::headers, types::ResponseRouterData, utils};

#[derive(Clone)]
pub struct Inespay {
    amount_converter: &'static (dyn AmountConvertor<Output = StringMinorUnit> + Sync),
}

impl Inespay {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &StringMinorUnitForConnector,
        }
    }
}

impl api::Payment for Inespay {}
impl api::PaymentSession for Inespay {}
impl api::ConnectorAccessToken for Inespay {}
impl api::MandateSetup for Inespay {}
impl api::PaymentAuthorize for Inespay {}
impl api::PaymentSync for Inespay {}
impl api::PaymentCapture for Inespay {}
impl api::PaymentVoid for Inespay {}
impl api::Refund for Inespay {}
impl api::RefundExecute for Inespay {}
impl api::RefundSync for Inespay {}
impl api::PaymentToken for Inespay {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Inespay
{
    // Not Implemented (R)
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Inespay
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];
        let mut auth_headers = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut auth_headers);
        Ok(header)
    }
}

impl ConnectorCommon for Inespay {
    fn id(&self) -> &'static str {
        "inespay"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Minor
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.inespay.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = inespay::InespayAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![
            (
                headers::AUTHORIZATION.to_string(),
                auth.authorization.expose().into_masked(),
            ),
            (
                headers::X_API_KEY.to_string(),
                auth.api_key.expose().into_masked(),
            ),
        ])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: inespay::InespayErrorResponse = res
            .response
            .parse_struct("InespayErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.status,
            message: response.status_desc,
            reason: None,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Inespay {
    //TODO: implement functions when support enabled
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Inespay {
    //TODO: implement sessions flow
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Inespay {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Inespay {}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Inespay {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}/payins/single/init", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;

        match req.request.currency {
            common_enums::Currency::EUR => {
                let connector_router_data = inespay::InespayRouterData::from((amount, req));
                let connector_req =
                    inespay::InespayPaymentsRequest::try_from(&connector_router_data)?;
                Ok(RequestContent::Json(Box::new(connector_req)))
            }
            _ => Err(errors::ConnectorError::CurrencyNotSupported {
                message: req.request.currency.to_string(),
                connector: "Inespay",
            }
            .into()),
        }
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: inespay::InespayPaymentsResponse = res
            .response
            .parse_struct("Inespay PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Inespay {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_payment_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
        Ok(format!(
            "{}{}{}",
            self.base_url(connectors),
            "/payins/single/",
            connector_payment_id,
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: inespay::InespayPSyncResponse = res
            .response
            .parse_struct("inespay PaymentsSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Inespay {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsCaptureType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsCaptureType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        let response: inespay::InespayPaymentsResponse = res
            .response
            .parse_struct("Inespay PaymentsCaptureResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Inespay {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Inespay {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}/refunds/init", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let refund_amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;

        let connector_router_data = inespay::InespayRouterData::from((refund_amount, req));
        let connector_req = inespay::InespayRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&types::RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(types::RefundExecuteType::get_headers(
                self, req, connectors,
            )?)
            .set_body(types::RefundExecuteType::get_request_body(
                self, req, connectors,
            )?)
            .build();
        Ok(Some(request))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: inespay::InespayRefundsResponse = res
            .response
            .parse_struct("inespay InespayRefundsResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Inespay {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_refund_id = req
            .request
            .connector_refund_id
            .clone()
            .ok_or(errors::ConnectorError::MissingConnectorRefundID)?;
        Ok(format!(
            "{}{}{}",
            self.base_url(connectors),
            "/refunds/",
            connector_refund_id,
        ))
    }

    fn build_request(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::RefundSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::RefundSyncType::get_headers(self, req, connectors)?)
                .set_body(types::RefundSyncType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: inespay::InespayRSyncResponse = res
            .response
            .parse_struct("inespay RefundSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

fn get_webhook_body(
    body: &[u8],
) -> CustomResult<inespay::InespayWebhookEventData, errors::ConnectorError> {
    let notif_item: inespay::InespayWebhookEvent =
        serde_urlencoded::from_bytes::<inespay::InespayWebhookEvent>(body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
    let encoded_data_return = notif_item.data_return;
    let decoded_data_return = BASE64_ENGINE
        .decode(encoded_data_return)
        .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
    let data_return: inespay::InespayWebhookEventData = decoded_data_return
        .parse_struct("inespay InespayWebhookEventData")
        .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
    Ok(data_return)
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Inespay {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let notif_item = serde_urlencoded::from_bytes::<inespay::InespayWebhookEvent>(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(notif_item.signature_data_return.as_bytes().to_owned())
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let notif_item = serde_urlencoded::from_bytes::<inespay::InespayWebhookEvent>(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(notif_item.data_return.into_bytes())
    }

    async fn verify_webhook_source(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>,
        connector_label: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_label,
                connector_webhook_details,
            )
            .await?;
        let signature =
            self.get_webhook_source_verification_signature(request, &connector_webhook_secrets)?;

        let message = self.get_webhook_source_verification_message(
            request,
            merchant_id,
            &connector_webhook_secrets,
        )?;
        let secret = connector_webhook_secrets.secret;

        let signing_key = hmac::Key::new(hmac::HMAC_SHA256, &secret);
        let signed_message = hmac::sign(&signing_key, &message);
        let computed_signature = hex::encode(signed_message.as_ref());
        let payload_sign = BASE64_ENGINE.encode(computed_signature);
        Ok(payload_sign.as_bytes().eq(&signature))
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let data_return = get_webhook_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        match data_return {
            inespay::InespayWebhookEventData::Payment(data) => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::ConnectorTransactionId(
                        data.single_payin_id,
                    ),
                ))
            }
            inespay::InespayWebhookEventData::Refund(data) => {
                Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                    api_models::webhooks::RefundIdType::ConnectorRefundId(data.refund_id),
                ))
            }
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let data_return = get_webhook_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        Ok(api_models::webhooks::IncomingWebhookEvent::from(
            data_return,
        ))
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let data_return = get_webhook_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        Ok(match data_return {
            inespay::InespayWebhookEventData::Payment(payment_webhook_data) => {
                Box::new(payment_webhook_data)
            }
            inespay::InespayWebhookEventData::Refund(refund_webhook_data) => {
                Box::new(refund_webhook_data)
            }
        })
    }
}

static INESPAY_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> = LazyLock::new(|| {
    let supported_capture_methods = vec![enums::CaptureMethod::Automatic];
    let mut inespay_supported_payment_methods = SupportedPaymentMethods::new();

    inespay_supported_payment_methods.add(
        enums::PaymentMethod::BankDebit,
        enums::PaymentMethodType::Sepa,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );

    inespay_supported_payment_methods
});

static INESPAY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Inespay",
    description:
        "INESPAY is a payment method system that allows online shops to receive money in their bank accounts through a SEPA bank transfer ",
    connector_type: enums::HyperswitchConnectorCategory::BankAcquirer,
    integration_status: enums::ConnectorIntegrationStatus::Sandbox,
};

static INESPAY_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 2] =
    [enums::EventClass::Payments, enums::EventClass::Refunds];

impl ConnectorSpecifications for Inespay {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&INESPAY_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*INESPAY_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
        Some(&INESPAY_SUPPORTED_WEBHOOK_FLOWS)
    }
}


// Function: psync_response_handler
// File: crates/router/src/core/revenue_recovery/types.rs
// Module: router

pub fn psync_response_handler(
        &self,
        state: &SessionState,
        payment_intent: &PaymentIntent,
        psync_task_process: &storage::ProcessTracker,
        revenue_recovery_metadata: &mut PaymentRevenueRecoveryMetadata,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
    ) -> Result<(), errors::ProcessTrackerError>