// File: crates/router/src/core/fraud_check/operation/fraud_check_post.rs

use async_trait::async_trait;
use common_enums::{CaptureMethod, FrmSuggestion};
use common_utils::ext_traits::Encode;
use hyperswitch_domain_models::payments::{
    payment_attempt::PaymentAttemptUpdate, payment_intent::PaymentIntentUpdate, HeaderPayload,
};
use router_env::{instrument, logger, tracing};

use super::{Domain, FraudCheckOperation, GetTracker, UpdateTracker};
use crate::{
    consts,
    core::{
        errors::{RouterResult, StorageErrorExt},
        fraud_check::{
            self as frm_core,
            types::{FrmData, PaymentDetails, PaymentToFrmData, CANCEL_INITIATED},
            ConnectorDetailsCore, FrmConfigsObject,
        },
        payments,
    },
    errors,
    routes::app::ReqState,
    services::{self, api},
    types::{
        api::{
            enums::{AttemptStatus, IntentStatus},
            fraud_check as frm_api, payments as payment_types, Capture, Void,
        },
        domain,
        fraud_check::{
            FraudCheckResponseData, FraudCheckSaleData, FrmRequest, FrmResponse, FrmRouterData,
        },
        storage::{
            enums::{FraudCheckLastStep, FraudCheckStatus, FraudCheckType, MerchantDecision},
            fraud_check::{FraudCheckNew, FraudCheckUpdate},
        },
        ResponseId,
    },
    utils, SessionState,
};

#[derive(Debug, Clone, Copy)]
pub struct FraudCheckPost;

impl<F, D> FraudCheckOperation<F, D> for &FraudCheckPost
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<PaymentToFrmData> + Send + Sync)> {
        Ok(*self)
    }
    fn to_domain(&self) -> RouterResult<&dyn Domain<F, D>> {
        Ok(*self)
    }
    fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<FrmData, F, D> + Send + Sync)> {
        Ok(*self)
    }
}

impl<F, D> FraudCheckOperation<F, D> for FraudCheckPost
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<PaymentToFrmData> + Send + Sync)> {
        Ok(self)
    }
    fn to_domain(&self) -> RouterResult<&dyn Domain<F, D>> {
        Ok(self)
    }
    fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<FrmData, F, D> + Send + Sync)> {
        Ok(self)
    }
}

#[async_trait]
impl GetTracker<PaymentToFrmData> for FraudCheckPost {
    #[cfg(feature = "v2")]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: PaymentToFrmData,
        frm_connector_details: ConnectorDetailsCore,
    ) -> RouterResult<Option<FrmData>> {
        todo!()
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: PaymentToFrmData,
        frm_connector_details: ConnectorDetailsCore,
    ) -> RouterResult<Option<FrmData>> {
        let db = &*state.store;

        let payment_details: Option<serde_json::Value> = PaymentDetails::from(payment_data.clone())
            .encode_to_value()
            .ok();
        let existing_fraud_check = db
            .find_fraud_check_by_payment_id_if_present(
                payment_data.payment_intent.get_id().to_owned(),
                payment_data.merchant_account.get_id().clone(),
            )
            .await
            .ok();
        let fraud_check = match existing_fraud_check {
            Some(Some(fraud_check)) => Ok(fraud_check),
            _ => {
                db.insert_fraud_check_response(FraudCheckNew {
                    frm_id: utils::generate_id(consts::ID_LENGTH, "frm"),
                    payment_id: payment_data.payment_intent.get_id().to_owned(),
                    merchant_id: payment_data.merchant_account.get_id().clone(),
                    attempt_id: payment_data.payment_attempt.attempt_id.clone(),
                    created_at: common_utils::date_time::now(),
                    frm_name: frm_connector_details.connector_name,
                    frm_transaction_id: None,
                    frm_transaction_type: FraudCheckType::PostFrm,
                    frm_status: FraudCheckStatus::Pending,
                    frm_score: None,
                    frm_reason: None,
                    frm_error: None,
                    payment_details,
                    metadata: None,
                    modified_at: common_utils::date_time::now(),
                    last_step: FraudCheckLastStep::Processing,
                    payment_capture_method: payment_data.payment_attempt.capture_method,
                })
                .await
            }
        };
        match fraud_check {
            Ok(fraud_check_value) => {
                let frm_data = FrmData {
                    payment_intent: payment_data.payment_intent,
                    payment_attempt: payment_data.payment_attempt,
                    merchant_account: payment_data.merchant_account,
                    address: payment_data.address,
                    fraud_check: fraud_check_value,
                    connector_details: payment_data.connector_details,
                    order_details: payment_data.order_details,
                    refund: None,
                    frm_metadata: payment_data.frm_metadata,
                };
                Ok(Some(frm_data))
            }
            Err(error) => {
                router_env::logger::error!("inserting into fraud_check table failed {error:?}");
                Ok(None)
            }
        }
    }
}

#[async_trait]
impl<F, D> Domain<F, D> for FraudCheckPost
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    #[instrument(skip_all)]
    async fn post_payment_frm<'a>(
        &'a self,
        state: &'a SessionState,
        _req_state: ReqState,
        payment_data: &mut D,
        frm_data: &mut FrmData,
        platform: &domain::Platform,
        customer: &Option<domain::Customer>,
    ) -> RouterResult<Option<FrmRouterData>> {
        if frm_data.fraud_check.last_step != FraudCheckLastStep::Processing {
            logger::debug!("post_flow::Sale Skipped");
            return Ok(None);
        }
        let router_data = frm_core::call_frm_service::<F, frm_api::Sale, _, D>(
            state,
            payment_data,
            &mut frm_data.to_owned(),
            platform,
            customer,
        )
        .await?;
        frm_data.fraud_check.last_step = FraudCheckLastStep::CheckoutOrSale;
        Ok(Some(FrmRouterData {
            merchant_id: router_data.merchant_id,
            connector: router_data.connector,
            payment_id: router_data.payment_id.clone(),
            attempt_id: router_data.attempt_id,
            request: FrmRequest::Sale(FraudCheckSaleData {
                amount: router_data.request.amount,
                order_details: router_data.request.order_details,
                currency: router_data.request.currency,
                email: router_data.request.email,
            }),
            response: FrmResponse::Sale(router_data.response),
        }))
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn execute_post_tasks(
        &self,
        _state: &SessionState,
        _req_state: ReqState,
        _frm_data: &mut FrmData,
        _platform: &domain::Platform,
        _frm_configs: FrmConfigsObject,
        _frm_suggestion: &mut Option<FrmSuggestion>,
        _payment_data: &mut D,
        _customer: &Option<domain::Customer>,
        _should_continue_capture: &mut bool,
    ) -> RouterResult<Option<FrmData>> {
        todo!()
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn execute_post_tasks(
        &self,
        state: &SessionState,
        req_state: ReqState,
        frm_data: &mut FrmData,
        platform: &domain::Platform,
        _frm_configs: FrmConfigsObject,
        frm_suggestion: &mut Option<FrmSuggestion>,
        payment_data: &mut D,
        customer: &Option<domain::Customer>,
        _should_continue_capture: &mut bool,
    ) -> RouterResult<Option<FrmData>> {
        if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Fraud)
            && matches!(
                frm_data.fraud_check.last_step,
                FraudCheckLastStep::CheckoutOrSale
            )
        {
            *frm_suggestion = Some(FrmSuggestion::FrmCancelTransaction);

            let cancel_req = api_models::payments::PaymentsCancelRequest {
                payment_id: frm_data.payment_intent.get_id().to_owned(),
                cancellation_reason: frm_data.fraud_check.frm_error.clone(),
                merchant_connector_details: None,
            };
            let cancel_res = Box::pin(payments::payments_core::<
                Void,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<Void>,
            >(
                state.clone(),
                req_state.clone(),
                platform.clone(),
                None,
                payments::PaymentCancel,
                cancel_req,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                None,
                HeaderPayload::default(),
            ))
            .await?;
            logger::debug!("payment_id : {:?} has been cancelled since it has been found fraudulent by configured frm connector",payment_data.get_payment_attempt().payment_id);
            if let services::ApplicationResponse::JsonWithHeaders((payments_response, _)) =
                cancel_res
            {
                payment_data.set_payment_intent_status(payments_response.status);
            }
            let _router_data = frm_core::call_frm_service::<F, frm_api::RecordReturn, _, D>(
                state,
                payment_data,
                &mut frm_data.to_owned(),
                platform,
                customer,
            )
            .await?;
            frm_data.fraud_check.last_step = FraudCheckLastStep::TransactionOrRecordRefund;
        } else if matches!(
            frm_data.fraud_check.frm_status,
            FraudCheckStatus::ManualReview
        ) {
            *frm_suggestion = Some(FrmSuggestion::FrmManualReview);
        } else if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Legit)
            && matches!(
                frm_data.fraud_check.payment_capture_method,
                Some(CaptureMethod::Automatic) | Some(CaptureMethod::SequentialAutomatic)
            )
        {
            let capture_request = api_models::payments::PaymentsCaptureRequest {
                payment_id: frm_data.payment_intent.get_id().to_owned(),
                merchant_id: None,
                amount_to_capture: None,
                refund_uncaptured_amount: None,
                statement_descriptor_suffix: None,
                statement_descriptor_prefix: None,
                merchant_connector_details: None,
                all_keys_required: None,
            };
            let capture_response = Box::pin(payments::payments_core::<
                Capture,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<Capture>,
            >(
                state.clone(),
                req_state.clone(),
                platform.clone(),
                None,
                payments::PaymentCapture,
                capture_request,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                None,
                HeaderPayload::default(),
            ))
            .await?;
            logger::debug!("payment_id : {:?} has been captured since it has been found legit by configured frm connector",payment_data.get_payment_attempt().payment_id);
            if let services::ApplicationResponse::JsonWithHeaders((payments_response, _)) =
                capture_response
            {
                payment_data.set_payment_intent_status(payments_response.status);
            }
        };
        return Ok(Some(frm_data.to_owned()));
    }

    #[instrument(skip_all)]
    async fn pre_payment_frm<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: &mut D,
        frm_data: &mut FrmData,
        platform: &domain::Platform,
        customer: &Option<domain::Customer>,
    ) -> RouterResult<FrmRouterData> {
        let router_data = frm_core::call_frm_service::<F, frm_api::Sale, _, D>(
            state,
            payment_data,
            &mut frm_data.to_owned(),
            platform,
            customer,
        )
        .await?;
        Ok(FrmRouterData {
            merchant_id: router_data.merchant_id,
            connector: router_data.connector,
            payment_id: router_data.payment_id,
            attempt_id: router_data.attempt_id,
            request: FrmRequest::Sale(FraudCheckSaleData {
                amount: router_data.request.amount,
                order_details: router_data.request.order_details,
                currency: router_data.request.currency,
                email: router_data.request.email,
            }),
            response: FrmResponse::Sale(router_data.response),
        })
    }
}

#[async_trait]
impl<F, D> UpdateTracker<FrmData, F, D> for FraudCheckPost
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    #[cfg(feature = "v2")]
    async fn update_tracker<'b>(
        &'b self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        mut frm_data: FrmData,
        payment_data: &mut D,
        frm_suggestion: Option<FrmSuggestion>,
        frm_router_data: FrmRouterData,
    ) -> RouterResult<FrmData> {
        todo!()
    }

    #[cfg(feature = "v1")]
    async fn update_tracker<'b>(
        &'b self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        mut frm_data: FrmData,
        payment_data: &mut D,
        frm_suggestion: Option<FrmSuggestion>,
        frm_router_data: FrmRouterData,
    ) -> RouterResult<FrmData> {
        let db = &*state.store;
        let frm_check_update = match frm_router_data.response {
            FrmResponse::Sale(response) => match response {
                Err(err) => Some(FraudCheckUpdate::ErrorUpdate {
                    status: FraudCheckStatus::TransactionFailure,
                    error_message: Some(Some(err.message)),
                }),
                Ok(payments_response) => match payments_response {
                    FraudCheckResponseData::TransactionResponse {
                        resource_id,
                        connector_metadata,
                        status,
                        reason,
                        score,
                    } => {
                        let connector_transaction_id = match resource_id {
                            ResponseId::NoResponseId => None,
                            ResponseId::ConnectorTransactionId(id) => Some(id),
                            ResponseId::EncodedData(id) => Some(id),
                        };

                        let fraud_check_update = FraudCheckUpdate::ResponseUpdate {
                            frm_status: status,
                            frm_transaction_id: connector_transaction_id,
                            frm_reason: reason,
                            frm_score: score,
                            metadata: connector_metadata,
                            modified_at: common_utils::date_time::now(),
                            last_step: frm_data.fraud_check.last_step,
                            payment_capture_method: frm_data.fraud_check.payment_capture_method,
                        };
                        Some(fraud_check_update)
                    },
                    FraudCheckResponseData::RecordReturnResponse { resource_id: _, connector_metadata: _, return_id: _ } => {
                        Some(FraudCheckUpdate::ErrorUpdate {
                            status: FraudCheckStatus::TransactionFailure,
                            error_message: Some(Some(
                                "Error: Got Record Return Response response in current Sale flow".to_string(),
                            )),
                        })
                    }
                    FraudCheckResponseData::FulfillmentResponse {
                        order_id: _,
                        shipment_ids: _,
                    } => None,
                },
            },
            FrmResponse::Fulfillment(response) => match response {
                Err(err) => Some(FraudCheckUpdate::ErrorUpdate {
                    status: FraudCheckStatus::TransactionFailure,
                    error_message: Some(Some(err.message)),
                }),
                Ok(payments_response) => match payments_response {
                    FraudCheckResponseData::TransactionResponse {
                        resource_id,
                        connector_metadata,
                        status,
                        reason,
                        score,
                    } => {
                        let connector_transaction_id = match resource_id {
                            ResponseId::NoResponseId => None,
                            ResponseId::ConnectorTransactionId(id) => Some(id),
                            ResponseId::EncodedData(id) => Some(id),
                        };

                        let fraud_check_update = FraudCheckUpdate::ResponseUpdate {
                            frm_status: status,
                            frm_transaction_id: connector_transaction_id,
                            frm_reason: reason,
                            frm_score: score,
                            metadata: connector_metadata,
                            modified_at: common_utils::date_time::now(),
                            last_step: frm_data.fraud_check.last_step,
                            payment_capture_method: frm_data.fraud_check.payment_capture_method,
                        };
                        Some(fraud_check_update)
                    }
                    FraudCheckResponseData::FulfillmentResponse {
                        order_id: _,
                        shipment_ids: _,
                    } => None,
                    FraudCheckResponseData::RecordReturnResponse { resource_id: _, connector_metadata: _, return_id: _ } => None,

                },
            },

            FrmResponse::RecordReturn(response) => match response {
                Err(err) => Some(FraudCheckUpdate::ErrorUpdate {
                    status: FraudCheckStatus::TransactionFailure,
                    error_message: Some(Some(err.message)),
                }),
                Ok(payments_response) => match payments_response {
                    FraudCheckResponseData::TransactionResponse {
                        resource_id: _,
                        connector_metadata: _,
                        status: _,
                        reason: _,
                        score: _,
                    } => {
                        Some(FraudCheckUpdate::ErrorUpdate {
                            status: FraudCheckStatus::TransactionFailure,
                            error_message: Some(Some(
                                "Error: Got Transaction Response response in current Record Return flow".to_string(),
                            )),
                        })
                    },
                    FraudCheckResponseData::FulfillmentResponse {order_id: _, shipment_ids: _ } => {
                        None
                    },
                    FraudCheckResponseData::RecordReturnResponse { resource_id, connector_metadata, return_id: _ } => {
                        let connector_transaction_id = match resource_id {
                            ResponseId::NoResponseId => None,
                            ResponseId::ConnectorTransactionId(id) => Some(id),
                            ResponseId::EncodedData(id) => Some(id),
                        };

                        let fraud_check_update = FraudCheckUpdate::ResponseUpdate {
                            frm_status: frm_data.fraud_check.frm_status,
                            frm_transaction_id: connector_transaction_id,
                            frm_reason: frm_data.fraud_check.frm_reason.clone(),
                            frm_score: frm_data.fraud_check.frm_score,
                            metadata: connector_metadata,
                            modified_at: common_utils::date_time::now(),
                            last_step: frm_data.fraud_check.last_step,
                            payment_capture_method: frm_data.fraud_check.payment_capture_method,
                        };
                        Some(fraud_check_update)
                    }
                },
            },

            FrmResponse::Checkout(_) | FrmResponse::Transaction(_) => {
                Some(FraudCheckUpdate::ErrorUpdate {
                    status: FraudCheckStatus::TransactionFailure,
                    error_message: Some(Some(
                        "Error: Got Pre(Sale) flow response in current post flow".to_string(),
                    )),
                })
            }
        };

        if let Some(frm_suggestion) = frm_suggestion {
            let (payment_attempt_status, payment_intent_status, merchant_decision, error_message) =
                match frm_suggestion {
                    FrmSuggestion::FrmCancelTransaction => (
                        AttemptStatus::Failure,
                        IntentStatus::Failed,
                        Some(MerchantDecision::Rejected.to_string()),
                        Some(Some(CANCEL_INITIATED.to_string())),
                    ),
                    FrmSuggestion::FrmManualReview => (
                        AttemptStatus::Unresolved,
                        IntentStatus::RequiresMerchantAction,
                        None,
                        None,
                    ),
                    FrmSuggestion::FrmAuthorizeTransaction => (
                        AttemptStatus::Authorized,
                        IntentStatus::RequiresCapture,
                        None,
                        None,
                    ),
                };

            let payment_attempt_update = PaymentAttemptUpdate::RejectUpdate {
                status: payment_attempt_status,
                error_code: Some(Some(frm_data.fraud_check.frm_status.to_string())),
                error_message,
                updated_by: frm_data.merchant_account.storage_scheme.to_string(),
            };

            #[cfg(feature = "v1")]
            let payment_attempt = db
                .update_payment_attempt_with_attempt_id(
                    payment_data.get_payment_attempt().clone(),
                    payment_attempt_update,
                    frm_data.merchant_account.storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

            #[cfg(feature = "v2")]
            let payment_attempt = db
                .update_payment_attempt_with_attempt_id(
                    key_manager_state,
                    key_store,
                    payment_data.get_payment_attempt().clone(),
                    payment_attempt_update,
                    frm_data.merchant_account.storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

            payment_data.set_payment_attempt(payment_attempt);

            let payment_intent = db
                .update_payment_intent(
                    payment_data.get_payment_intent().clone(),
                    PaymentIntentUpdate::RejectUpdate {
                        status: payment_intent_status,
                        merchant_decision,
                        updated_by: frm_data.merchant_account.storage_scheme.to_string(),
                    },
                    key_store,
                    frm_data.merchant_account.storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

            payment_data.set_payment_intent(payment_intent);
        }
        frm_data.fraud_check = match frm_check_update {
            Some(fraud_check_update) => db
                .update_fraud_check_response_with_attempt_id(
                    frm_data.fraud_check.clone(),
                    fraud_check_update,
                )
                .await
                .map_err(|error| error.change_context(errors::ApiErrorResponse::PaymentNotFound))?,
            None => frm_data.fraud_check.clone(),
        };

        Ok(frm_data)
    }
}


// File: crates/router/src/core/fraud_check/flows/checkout_flow.rs

use async_trait::async_trait;
use common_utils::{ext_traits::ValueExt, pii::Email};
use error_stack::ResultExt;
use masking::ExposeInterface;

use super::{ConstructFlowSpecificData, FeatureFrm};
use crate::{
    core::{
        errors::{ConnectorErrorExt, RouterResult},
        fraud_check::types::FrmData,
        payments::{self, helpers},
    },
    errors, services,
    types::{
        api::fraud_check::{self as frm_api, FraudCheckConnectorData},
        domain,
        fraud_check::{FraudCheckCheckoutData, FraudCheckResponseData, FrmCheckoutRouterData},
        storage::enums as storage_enums,
        BrowserInformation, ConnectorAuthType, MerchantRecipientData, ResponseId, RouterData,
    },
    SessionState,
};

#[async_trait]
impl ConstructFlowSpecificData<frm_api::Checkout, FraudCheckCheckoutData, FraudCheckResponseData>
    for FrmData
{
    #[cfg(feature = "v2")]
    async fn construct_router_data<'a>(
        &self,
        _state: &SessionState,
        _connector_id: &str,
        _platform: &domain::Platform,
        _customer: &Option<domain::Customer>,
        _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
        _merchant_recipient_data: Option<MerchantRecipientData>,
        _header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
    ) -> RouterResult<RouterData<frm_api::Checkout, FraudCheckCheckoutData, FraudCheckResponseData>>
    {
        todo!()
    }

    #[cfg(feature = "v1")]
    async fn construct_router_data<'a>(
        &self,
        state: &SessionState,
        connector_id: &str,
        platform: &domain::Platform,
        customer: &Option<domain::Customer>,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
        _merchant_recipient_data: Option<MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
        _payment_method: Option<common_enums::PaymentMethod>,
        _payment_method_type: Option<common_enums::PaymentMethodType>,
    ) -> RouterResult<RouterData<frm_api::Checkout, FraudCheckCheckoutData, FraudCheckResponseData>>
    {
        use crate::connector::utils::PaymentsAttemptData;

        let status = storage_enums::AttemptStatus::Pending;

        let auth_type: ConnectorAuthType = merchant_connector_account
            .get_connector_account_details()
            .parse_value("ConnectorAuthType")
            .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                id: "ConnectorAuthType".to_string(),
            })?;

        let browser_info: Option<BrowserInformation> = self.payment_attempt.get_browser_info().ok();
        let customer_id = customer.to_owned().map(|customer| customer.customer_id);

        let router_data = RouterData {
            flow: std::marker::PhantomData,
            merchant_id: platform.get_processor().get_account().get_id().clone(),
            customer_id,
            tenant_id: state.tenant.tenant_id.clone(),
            connector: connector_id.to_string(),
            payment_id: self.payment_intent.payment_id.get_string_repr().to_owned(),
            attempt_id: self.payment_attempt.attempt_id.clone(),
            status,
            payment_method: self
                .payment_attempt
                .payment_method
                .ok_or(errors::ApiErrorResponse::PaymentMethodNotFound)?,
            payment_method_type: self.payment_attempt.payment_method_type,
            connector_auth_type: auth_type,
            description: None,
            payment_method_status: None,
            address: self.address.clone(),
            auth_type: storage_enums::AuthenticationType::NoThreeDs,
            connector_meta_data: None,
            connector_wallets_details: None,
            amount_captured: None,
            minor_amount_captured: None,
            request: FraudCheckCheckoutData {
                amount: self
                    .payment_attempt
                    .net_amount
                    .get_total_amount()
                    .get_amount_as_i64(),
                order_details: self.order_details.clone(),
                currency: self.payment_attempt.currency,
                browser_info,
                payment_method_data: self
                    .payment_attempt
                    .payment_method_data
                    .as_ref()
                    .map(|pm_data| {
                        pm_data
                            .clone()
                            .parse_value::<api_models::payments::AdditionalPaymentData>(
                                "AdditionalPaymentData",
                            )
                    })
                    .transpose()
                    .unwrap_or_default(),
                email: customer
                    .clone()
                    .and_then(|customer_data| {
                        customer_data
                            .email
                            .map(|email| Email::try_from(email.into_inner().expose()))
                    })
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InvalidDataValue {
                        field_name: "customer.customer_data.email",
                    })?,
                gateway: self.payment_attempt.connector.clone(),
            }, // self.order_details
            response: Ok(FraudCheckResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId("".to_string()),
                connector_metadata: None,
                status: storage_enums::FraudCheckStatus::Pending,
                score: None,
                reason: None,
            }),
            access_token: None,
            session_token: None,
            reference_id: None,
            payment_method_token: None,
            connector_customer: None,
            preprocessing_id: None,
            connector_request_reference_id: uuid::Uuid::new_v4().to_string(),
            test_mode: None,
            recurring_mandate_payment_data: None,
            #[cfg(feature = "payouts")]
            payout_method_data: None,
            #[cfg(feature = "payouts")]
            quote_id: None,
            payment_method_balance: None,
            connector_http_status_code: None,
            external_latency: None,
            connector_api_version: None,
            apple_pay_flow: None,
            frm_metadata: self.frm_metadata.clone(),
            refund_id: None,
            dispute_id: None,
            connector_response: None,
            integrity_check: Ok(()),
            additional_merchant_data: None,
            header_payload,
            connector_mandate_request_reference_id: None,
            authentication_id: None,
            psd2_sca_exemption_type: None,
            raw_connector_response: None,
            is_payment_id_from_merchant: None,
            l2_l3_data: None,
            minor_amount_capturable: None,
            authorized_amount: None,
        };

        Ok(router_data)
    }
}

#[async_trait]
impl FeatureFrm<frm_api::Checkout, FraudCheckCheckoutData> for FrmCheckoutRouterData {
    async fn decide_frm_flows<'a>(
        mut self,
        state: &SessionState,
        connector: &FraudCheckConnectorData,
        call_connector_action: payments::CallConnectorAction,
        platform: &domain::Platform,
    ) -> RouterResult<Self> {
        decide_frm_flow(&mut self, state, connector, call_connector_action, platform).await
    }
}

pub async fn decide_frm_flow(
    router_data: &mut FrmCheckoutRouterData,
    state: &SessionState,
    connector: &FraudCheckConnectorData,
    call_connector_action: payments::CallConnectorAction,
    _platform: &domain::Platform,
) -> RouterResult<FrmCheckoutRouterData> {
    let connector_integration: services::BoxedFrmConnectorIntegrationInterface<
        frm_api::Checkout,
        FraudCheckCheckoutData,
        FraudCheckResponseData,
    > = connector.connector.get_connector_integration();
    let resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        router_data,
        call_connector_action,
        None,
        None,
    )
    .await
    .to_payment_failed_response()?;

    Ok(resp)
}


// File: crates/router/src/core/fraud_check/flows/transaction_flow.rs

use async_trait::async_trait;
use common_utils::ext_traits::ValueExt;
use error_stack::ResultExt;

use crate::{
    core::{
        errors::{ConnectorErrorExt, RouterResult},
        fraud_check::{FeatureFrm, FrmData},
        payments::{self, flows::ConstructFlowSpecificData, helpers},
    },
    errors, services,
    types::{
        api::fraud_check as frm_api,
        domain,
        fraud_check::{
            FraudCheckResponseData, FraudCheckTransactionData, FrmTransactionRouterData,
        },
        storage::enums as storage_enums,
        ConnectorAuthType, MerchantRecipientData, ResponseId, RouterData,
    },
    SessionState,
};

#[async_trait]
impl
    ConstructFlowSpecificData<
        frm_api::Transaction,
        FraudCheckTransactionData,
        FraudCheckResponseData,
    > for FrmData
{
    #[cfg(feature = "v2")]
    async fn construct_router_data<'a>(
        &self,
        _state: &SessionState,
        _connector_id: &str,
        _platform: &domain::Platform,
        _customer: &Option<domain::Customer>,
        _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
        _merchant_recipient_data: Option<MerchantRecipientData>,
        _header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
    ) -> RouterResult<
        RouterData<frm_api::Transaction, FraudCheckTransactionData, FraudCheckResponseData>,
    > {
        todo!()
    }

    #[cfg(feature = "v1")]
    async fn construct_router_data<'a>(
        &self,
        state: &SessionState,
        connector_id: &str,
        platform: &domain::Platform,
        customer: &Option<domain::Customer>,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
        _merchant_recipient_data: Option<MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
        _payment_method: Option<common_enums::PaymentMethod>,
        _payment_method_type: Option<common_enums::PaymentMethodType>,
    ) -> RouterResult<
        RouterData<frm_api::Transaction, FraudCheckTransactionData, FraudCheckResponseData>,
    > {
        let status = storage_enums::AttemptStatus::Pending;

        let auth_type: ConnectorAuthType = merchant_connector_account
            .get_connector_account_details()
            .parse_value("ConnectorAuthType")
            .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                id: "ConnectorAuthType".to_string(),
            })?;

        let customer_id = customer.to_owned().map(|customer| customer.customer_id);

        let payment_method = self.payment_attempt.payment_method;
        let currency = self.payment_attempt.currency;

        let router_data = RouterData {
            flow: std::marker::PhantomData,
            merchant_id: platform.get_processor().get_account().get_id().clone(),
            tenant_id: state.tenant.tenant_id.clone(),
            customer_id,
            connector: connector_id.to_string(),
            payment_id: self.payment_intent.payment_id.get_string_repr().to_owned(),
            attempt_id: self.payment_attempt.attempt_id.clone(),
            status,
            payment_method: self
                .payment_attempt
                .payment_method
                .ok_or(errors::ApiErrorResponse::PaymentMethodNotFound)?,
            payment_method_type: self.payment_attempt.payment_method_type,

            connector_auth_type: auth_type,
            description: None,
            address: self.address.clone(),
            auth_type: storage_enums::AuthenticationType::NoThreeDs,
            connector_meta_data: None,
            connector_wallets_details: None,
            amount_captured: None,
            minor_amount_captured: None,
            request: FraudCheckTransactionData {
                amount: self
                    .payment_attempt
                    .net_amount
                    .get_total_amount()
                    .get_amount_as_i64(),
                order_details: self.order_details.clone(),
                currency,
                payment_method,
                error_code: self.payment_attempt.error_code.clone(),
                error_message: self.payment_attempt.error_message.clone(),
                connector_transaction_id: self
                    .payment_attempt
                    .get_connector_payment_id()
                    .map(ToString::to_string),
                connector: self.payment_attempt.connector.clone(),
            }, // self.order_details
            response: Ok(FraudCheckResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId("".to_string()),
                connector_metadata: None,
                status: storage_enums::FraudCheckStatus::Pending,
                score: None,
                reason: None,
            }),
            access_token: None,
            session_token: None,
            reference_id: None,
            payment_method_token: None,
            connector_customer: None,
            preprocessing_id: None,
            connector_request_reference_id: uuid::Uuid::new_v4().to_string(),
            test_mode: None,
            recurring_mandate_payment_data: None,
            #[cfg(feature = "payouts")]
            payout_method_data: None,
            #[cfg(feature = "payouts")]
            quote_id: None,
            payment_method_balance: None,
            connector_http_status_code: None,
            external_latency: None,
            connector_api_version: None,
            payment_method_status: None,
            apple_pay_flow: None,
            frm_metadata: self.frm_metadata.clone(),
            refund_id: None,
            dispute_id: None,
            connector_response: None,
            integrity_check: Ok(()),
            additional_merchant_data: None,
            header_payload,
            connector_mandate_request_reference_id: None,
            authentication_id: None,
            psd2_sca_exemption_type: None,
            raw_connector_response: None,
            is_payment_id_from_merchant: None,
            l2_l3_data: None,
            minor_amount_capturable: None,
            authorized_amount: None,
        };

        Ok(router_data)
    }
}

#[async_trait]
impl FeatureFrm<frm_api::Transaction, FraudCheckTransactionData> for FrmTransactionRouterData {
    async fn decide_frm_flows<'a>(
        mut self,
        state: &SessionState,
        connector: &frm_api::FraudCheckConnectorData,
        call_connector_action: payments::CallConnectorAction,
        platform: &domain::Platform,
    ) -> RouterResult<Self> {
        decide_frm_flow(&mut self, state, connector, call_connector_action, platform).await
    }
}

pub async fn decide_frm_flow(
    router_data: &mut FrmTransactionRouterData,
    state: &SessionState,
    connector: &frm_api::FraudCheckConnectorData,
    call_connector_action: payments::CallConnectorAction,
    _platform: &domain::Platform,
) -> RouterResult<FrmTransactionRouterData> {
    let connector_integration: services::BoxedFrmConnectorIntegrationInterface<
        frm_api::Transaction,
        FraudCheckTransactionData,
        FraudCheckResponseData,
    > = connector.connector.get_connector_integration();
    let resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        router_data,
        call_connector_action,
        None,
        None,
    )
    .await
    .to_payment_failed_response()?;

    Ok(resp)
}


// File: crates/router/src/core/fraud_check/flows/sale_flow.rs

use async_trait::async_trait;
use common_utils::{ext_traits::ValueExt, pii::Email};
use error_stack::ResultExt;
use masking::ExposeInterface;

use crate::{
    core::{
        errors::{ConnectorErrorExt, RouterResult},
        fraud_check::{FeatureFrm, FraudCheckConnectorData, FrmData},
        payments::{self, flows::ConstructFlowSpecificData, helpers},
    },
    errors, services,
    types::{
        api::fraud_check as frm_api,
        domain,
        fraud_check::{FraudCheckResponseData, FraudCheckSaleData, FrmSaleRouterData},
        storage::enums as storage_enums,
        ConnectorAuthType, MerchantRecipientData, ResponseId, RouterData,
    },
    SessionState,
};

#[async_trait]
impl ConstructFlowSpecificData<frm_api::Sale, FraudCheckSaleData, FraudCheckResponseData>
    for FrmData
{
    #[cfg(feature = "v2")]
    async fn construct_router_data<'a>(
        &self,
        _state: &SessionState,
        _connector_id: &str,
        _platform: &domain::Platform,
        _customer: &Option<domain::Customer>,
        _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
        _merchant_recipient_data: Option<MerchantRecipientData>,
        _header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
    ) -> RouterResult<RouterData<frm_api::Sale, FraudCheckSaleData, FraudCheckResponseData>> {
        todo!()
    }

    #[cfg(feature = "v1")]
    async fn construct_router_data<'a>(
        &self,
        state: &SessionState,
        connector_id: &str,
        platform: &domain::Platform,
        customer: &Option<domain::Customer>,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
        _merchant_recipient_data: Option<MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
        _payment_method: Option<common_enums::PaymentMethod>,
        _payment_method_type: Option<common_enums::PaymentMethodType>,
    ) -> RouterResult<RouterData<frm_api::Sale, FraudCheckSaleData, FraudCheckResponseData>> {
        let status = storage_enums::AttemptStatus::Pending;

        let auth_type: ConnectorAuthType = merchant_connector_account
            .get_connector_account_details()
            .parse_value("ConnectorAuthType")
            .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                id: "ConnectorAuthType".to_string(),
            })?;

        let customer_id = customer.to_owned().map(|customer| customer.customer_id);

        let router_data = RouterData {
            flow: std::marker::PhantomData,
            merchant_id: platform.get_processor().get_account().get_id().clone(),
            customer_id,
            connector: connector_id.to_string(),
            payment_id: self.payment_intent.payment_id.get_string_repr().to_owned(),
            attempt_id: self.payment_attempt.attempt_id.clone(),
            tenant_id: state.tenant.tenant_id.clone(),
            status,
            payment_method: self
                .payment_attempt
                .payment_method
                .ok_or(errors::ApiErrorResponse::PaymentMethodNotFound)?,
            payment_method_type: self.payment_attempt.payment_method_type,
            connector_auth_type: auth_type,
            description: None,
            address: self.address.clone(),
            auth_type: storage_enums::AuthenticationType::NoThreeDs,
            connector_meta_data: None,
            connector_wallets_details: None,
            amount_captured: None,
            minor_amount_captured: None,
            request: FraudCheckSaleData {
                amount: self
                    .payment_attempt
                    .net_amount
                    .get_total_amount()
                    .get_amount_as_i64(),
                order_details: self.order_details.clone(),
                currency: self.payment_attempt.currency,
                email: customer
                    .clone()
                    .and_then(|customer_data| {
                        customer_data
                            .email
                            .map(|email| Email::try_from(email.into_inner().expose()))
                    })
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InvalidDataValue {
                        field_name: "customer.customer_data.email",
                    })?,
            },
            response: Ok(FraudCheckResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId("".to_string()),
                connector_metadata: None,
                status: storage_enums::FraudCheckStatus::Pending,
                score: None,
                reason: None,
            }),
            access_token: None,
            session_token: None,
            reference_id: None,
            payment_method_token: None,
            connector_customer: None,
            preprocessing_id: None,
            payment_method_status: None,
            connector_request_reference_id: uuid::Uuid::new_v4().to_string(),
            test_mode: None,
            recurring_mandate_payment_data: None,
            #[cfg(feature = "payouts")]
            payout_method_data: None,
            #[cfg(feature = "payouts")]
            quote_id: None,
            payment_method_balance: None,
            connector_http_status_code: None,
            external_latency: None,
            connector_api_version: None,
            apple_pay_flow: None,
            frm_metadata: self.frm_metadata.clone(),
            refund_id: None,
            dispute_id: None,
            connector_response: None,
            integrity_check: Ok(()),
            additional_merchant_data: None,
            header_payload,
            connector_mandate_request_reference_id: None,
            authentication_id: None,
            psd2_sca_exemption_type: None,
            raw_connector_response: None,
            is_payment_id_from_merchant: None,
            l2_l3_data: None,
            minor_amount_capturable: None,
            authorized_amount: None,
        };

        Ok(router_data)
    }
}

#[async_trait]
impl FeatureFrm<frm_api::Sale, FraudCheckSaleData> for FrmSaleRouterData {
    async fn decide_frm_flows<'a>(
        mut self,
        state: &SessionState,
        connector: &FraudCheckConnectorData,
        call_connector_action: payments::CallConnectorAction,
        platform: &domain::Platform,
    ) -> RouterResult<Self> {
        decide_frm_flow(&mut self, state, connector, call_connector_action, platform).await
    }
}

pub async fn decide_frm_flow(
    router_data: &mut FrmSaleRouterData,
    state: &SessionState,
    connector: &FraudCheckConnectorData,
    call_connector_action: payments::CallConnectorAction,
    _platform: &domain::Platform,
) -> RouterResult<FrmSaleRouterData> {
    let connector_integration: services::BoxedFrmConnectorIntegrationInterface<
        frm_api::Sale,
        FraudCheckSaleData,
        FraudCheckResponseData,
    > = connector.connector.get_connector_integration();
    let resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        router_data,
        call_connector_action,
        None,
        None,
    )
    .await
    .to_payment_failed_response()?;

    Ok(resp)
}


// File: crates/router/src/core/fraud_check/flows/record_return.rs

use async_trait::async_trait;
use common_utils::ext_traits::ValueExt;
use error_stack::ResultExt;

use crate::{
    connector::signifyd::transformers::RefundMethod,
    core::{
        errors::{ConnectorErrorExt, RouterResult},
        fraud_check::{FeatureFrm, FraudCheckConnectorData, FrmData},
        payments::{self, flows::ConstructFlowSpecificData, helpers},
    },
    errors, services,
    types::{
        api::RecordReturn,
        domain,
        fraud_check::{
            FraudCheckRecordReturnData, FraudCheckResponseData, FrmRecordReturnRouterData,
        },
        storage::enums as storage_enums,
        ConnectorAuthType, MerchantRecipientData, ResponseId, RouterData,
    },
    utils, SessionState,
};

#[async_trait]
impl ConstructFlowSpecificData<RecordReturn, FraudCheckRecordReturnData, FraudCheckResponseData>
    for FrmData
{
    #[cfg(feature = "v2")]
    async fn construct_router_data<'a>(
        &self,
        _state: &SessionState,
        _connector_id: &str,
        _platform: &domain::Platform,
        _customer: &Option<domain::Customer>,
        _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
        _merchant_recipient_data: Option<MerchantRecipientData>,
        _header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
    ) -> RouterResult<RouterData<RecordReturn, FraudCheckRecordReturnData, FraudCheckResponseData>>
    {
        todo!()
    }

    #[cfg(feature = "v1")]
    async fn construct_router_data<'a>(
        &self,
        state: &SessionState,
        connector_id: &str,
        platform: &domain::Platform,
        customer: &Option<domain::Customer>,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
        _merchant_recipient_data: Option<MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
        _payment_method: Option<common_enums::PaymentMethod>,
        _payment_method_type: Option<common_enums::PaymentMethodType>,
    ) -> RouterResult<RouterData<RecordReturn, FraudCheckRecordReturnData, FraudCheckResponseData>>
    {
        let status = storage_enums::AttemptStatus::Pending;

        let auth_type: ConnectorAuthType = merchant_connector_account
            .get_connector_account_details()
            .parse_value("ConnectorAuthType")
            .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                id: "ConnectorAuthType".to_string(),
            })?;

        let customer_id = customer.to_owned().map(|customer| customer.customer_id);
        let currency = self.payment_attempt.clone().currency;
        let router_data = RouterData {
            flow: std::marker::PhantomData,
            merchant_id: platform.get_processor().get_account().get_id().clone(),
            tenant_id: state.tenant.tenant_id.clone(),
            customer_id,
            connector: connector_id.to_string(),
            payment_id: self.payment_intent.payment_id.get_string_repr().to_owned(),
            attempt_id: self.payment_attempt.attempt_id.clone(),
            status,
            payment_method: utils::OptionExt::get_required_value(
                self.payment_attempt.payment_method,
                "payment_method",
            )?,
            payment_method_type: self.payment_attempt.payment_method_type,
            connector_auth_type: auth_type,
            description: None,
            address: self.address.clone(),
            auth_type: storage_enums::AuthenticationType::NoThreeDs,
            connector_meta_data: None,
            connector_wallets_details: None,
            amount_captured: None,
            minor_amount_captured: None,
            request: FraudCheckRecordReturnData {
                amount: self
                    .payment_attempt
                    .net_amount
                    .get_total_amount()
                    .get_amount_as_i64(),
                refund_method: RefundMethod::OriginalPaymentInstrument, //we dont consume this data now in payments...hence hardcoded
                currency,
                refund_transaction_id: self.refund.clone().map(|refund| refund.refund_id),
            }, // self.order_details
            response: Ok(FraudCheckResponseData::RecordReturnResponse {
                resource_id: ResponseId::ConnectorTransactionId("".to_string()),
                connector_metadata: None,
                return_id: None,
            }),
            access_token: None,
            session_token: None,
            reference_id: None,
            payment_method_token: None,
            connector_customer: None,
            preprocessing_id: None,
            payment_method_status: None,
            connector_request_reference_id: uuid::Uuid::new_v4().to_string(),
            test_mode: None,
            recurring_mandate_payment_data: None,
            #[cfg(feature = "payouts")]
            payout_method_data: None,
            #[cfg(feature = "payouts")]
            quote_id: None,
            payment_method_balance: None,
            connector_http_status_code: None,
            external_latency: None,
            connector_api_version: None,
            apple_pay_flow: None,
            frm_metadata: None,
            refund_id: None,
            dispute_id: None,
            connector_response: None,
            integrity_check: Ok(()),
            additional_merchant_data: None,
            header_payload,
            connector_mandate_request_reference_id: None,
            authentication_id: None,
            psd2_sca_exemption_type: None,
            raw_connector_response: None,
            is_payment_id_from_merchant: None,
            l2_l3_data: None,
            minor_amount_capturable: None,
            authorized_amount: None,
        };

        Ok(router_data)
    }
}

#[async_trait]
impl FeatureFrm<RecordReturn, FraudCheckRecordReturnData> for FrmRecordReturnRouterData {
    async fn decide_frm_flows<'a>(
        mut self,
        state: &SessionState,
        connector: &FraudCheckConnectorData,
        call_connector_action: payments::CallConnectorAction,
        platform: &domain::Platform,
    ) -> RouterResult<Self> {
        decide_frm_flow(&mut self, state, connector, call_connector_action, platform).await
    }
}

pub async fn decide_frm_flow(
    router_data: &mut FrmRecordReturnRouterData,
    state: &SessionState,
    connector: &FraudCheckConnectorData,
    call_connector_action: payments::CallConnectorAction,
    _platform: &domain::Platform,
) -> RouterResult<FrmRecordReturnRouterData> {
    let connector_integration: services::BoxedFrmConnectorIntegrationInterface<
        RecordReturn,
        FraudCheckRecordReturnData,
        FraudCheckResponseData,
    > = connector.connector.get_connector_integration();
    let resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        router_data,
        call_connector_action,
        None,
        None,
    )
    .await
    .to_payment_failed_response()?;

    Ok(resp)
}


// File: crates/router/src/core/fraud_check/flows/fulfillment_flow.rs

use common_utils::ext_traits::{OptionExt, ValueExt};
use error_stack::ResultExt;
use router_env::tracing::{self, instrument};

use crate::{
    core::{
        errors::RouterResult, fraud_check::frm_core_types::FrmFulfillmentRequest,
        payments::helpers, utils as core_utils,
    },
    errors,
    types::{
        domain,
        fraud_check::{FraudCheckFulfillmentData, FrmFulfillmentRouterData},
        storage, ConnectorAuthType, ErrorResponse, PaymentAddress, RouterData,
    },
    utils, SessionState,
};

#[cfg(feature = "v2")]
pub async fn construct_fulfillment_router_data<'a>(
    _state: &'a SessionState,
    _payment_intent: &'a storage::PaymentIntent,
    _payment_attempt: &storage::PaymentAttempt,
    _platform: &domain::Platform,
    _connector: String,
    _fulfillment_request: FrmFulfillmentRequest,
) -> RouterResult<FrmFulfillmentRouterData> {
    todo!()
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn construct_fulfillment_router_data<'a>(
    state: &'a SessionState,
    payment_intent: &'a storage::PaymentIntent,
    payment_attempt: &storage::PaymentAttempt,
    platform: &domain::Platform,
    connector: String,
    fulfillment_request: FrmFulfillmentRequest,
) -> RouterResult<FrmFulfillmentRouterData> {
    let profile_id = payment_intent
        .profile_id
        .as_ref()
        .get_required_value("profile_id")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("profile_id is not set in payment_intent")?
        .clone();

    let connector_id = connector.clone();

    let merchant_connector_account = helpers::get_merchant_connector_account(
        state,
        platform.get_processor().get_account().get_id(),
        None,
        platform.get_processor().get_key_store(),
        &profile_id,
        &connector,
        None,
    )
    .await?;

    let test_mode: Option<bool> = merchant_connector_account.is_test_mode_on();
    let auth_type: ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .parse_value("ConnectorAuthType")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let payment_method =
        utils::OptionExt::get_required_value(payment_attempt.payment_method, "payment_method")?;

    let router_data = RouterData {
        flow: std::marker::PhantomData,
        merchant_id: platform.get_processor().get_account().get_id().clone(),
        tenant_id: state.tenant.tenant_id.clone(),
        connector,
        payment_id: payment_attempt.payment_id.get_string_repr().to_owned(),
        attempt_id: payment_attempt.attempt_id.clone(),
        status: payment_attempt.status,
        payment_method,
        payment_method_type: payment_attempt.payment_method_type,
        connector_auth_type: auth_type,
        description: None,
        address: PaymentAddress::default(),
        auth_type: payment_attempt.authentication_type.unwrap_or_default(),
        connector_meta_data: merchant_connector_account.get_metadata(),
        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
        amount_captured: payment_intent
            .amount_captured
            .map(|amt| amt.get_amount_as_i64()),
        minor_amount_captured: payment_intent.amount_captured,
        payment_method_status: None,
        request: FraudCheckFulfillmentData {
            amount: payment_attempt
                .net_amount
                .get_total_amount()
                .get_amount_as_i64(),
            order_details: payment_intent.order_details.clone(),
            fulfillment_req: fulfillment_request,
        },
        response: Err(ErrorResponse::default()),
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_token: None,
        connector_customer: None,
        customer_id: None,
        recurring_mandate_payment_data: None,
        preprocessing_id: None,
        payment_method_balance: None,
        connector_request_reference_id: core_utils::get_connector_request_reference_id(
            &state.conf,
            platform.get_processor().get_account().get_id(),
            payment_intent,
            payment_attempt,
            &connector_id,
        )?,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        test_mode,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload: None,
        connector_mandate_request_reference_id: None,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
        authorized_amount: None,
    };
    Ok(router_data)
}


// File: crates/router/src/core/proxy/utils.rs

use api_models::{payment_methods::PaymentMethodId, proxy as proxy_api_models};
use common_utils::{
    ext_traits::{Encode, OptionExt},
    id_type,
};
use error_stack::ResultExt;
use hyperswitch_domain_models::payment_methods;
use masking::Mask;
use serde_json::Value;
use x509_parser::nom::{
    bytes::complete::{tag, take_while1},
    character::complete::{char, multispace0},
    sequence::{delimited, preceded, terminated},
    IResult,
};

use crate::{
    core::{
        errors::{self, RouterResult},
        payment_methods::vault,
    },
    routes::SessionState,
    types::{domain, payment_methods as pm_types},
};

pub struct ProxyRequestWrapper(pub proxy_api_models::ProxyRequest);
pub enum ProxyRecord {
    PaymentMethodRecord(Box<domain::PaymentMethod>),
    TokenizationRecord(Box<domain::Tokenization>),
}

impl ProxyRequestWrapper {
    pub async fn get_proxy_record(
        &self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: common_enums::enums::MerchantStorageScheme,
    ) -> RouterResult<ProxyRecord> {
        let token = &self.0.token;

        match self.0.token_type {
            proxy_api_models::TokenType::PaymentMethodId => {
                let pm_id = PaymentMethodId {
                    payment_method_id: token.clone(),
                };
                let pm_id =
                    id_type::GlobalPaymentMethodId::generate_from_string(pm_id.payment_method_id)
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Unable to generate GlobalPaymentMethodId")?;

                let payment_method_record = state
                    .store
                    .find_payment_method(key_store, &pm_id, storage_scheme)
                    .await
                    .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)?;
                Ok(ProxyRecord::PaymentMethodRecord(Box::new(
                    payment_method_record,
                )))
            }
            proxy_api_models::TokenType::TokenizationId => {
                let token_id = id_type::GlobalTokenId::from_string(token.clone().as_str())
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable(
                        "Error while coneverting from string to GlobalTokenId type",
                    )?;
                let db = state.store.as_ref();

                let tokenization_record = db
                    .get_entity_id_vault_id_by_token_id(&token_id, key_store)
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Error while fetching tokenization record from vault")?;

                Ok(ProxyRecord::TokenizationRecord(Box::new(
                    tokenization_record,
                )))
            }
        }
    }

    pub fn get_headers(&self) -> Vec<(String, masking::Maskable<String>)> {
        self.0
            .headers
            .as_map()
            .iter()
            .map(|(key, value)| (key.clone(), value.clone().into_masked()))
            .collect()
    }

    pub fn get_destination_url(&self) -> &str {
        self.0.destination_url.as_str()
    }

    pub fn get_method(&self) -> common_utils::request::Method {
        self.0.method
    }
}

impl ProxyRecord {
    fn get_vault_id(&self) -> RouterResult<payment_methods::VaultId> {
        match self {
            Self::PaymentMethodRecord(payment_method) => payment_method
                .locker_id
                .clone()
                .get_required_value("vault_id")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Locker id not present in Payment Method Entry"),
            Self::TokenizationRecord(tokenization_record) => Ok(
                payment_methods::VaultId::generate(tokenization_record.locker_id.clone()),
            ),
        }
    }

    fn get_customer_id(&self) -> id_type::GlobalCustomerId {
        match self {
            Self::PaymentMethodRecord(payment_method) => payment_method.customer_id.clone(),
            Self::TokenizationRecord(tokenization_record) => {
                tokenization_record.customer_id.clone()
            }
        }
    }

    pub async fn get_vault_data(
        &self,
        state: &SessionState,
        platform: domain::Platform,
    ) -> RouterResult<Value> {
        match self {
            Self::PaymentMethodRecord(_) => {
                let vault_resp = vault::retrieve_payment_method_from_vault_internal(
                    state,
                    &platform,
                    &self.get_vault_id()?,
                    &self.get_customer_id(),
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error while fetching data from vault")?;

                Ok(vault_resp
                    .data
                    .encode_to_value()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to serialize vault data")?)
            }
            Self::TokenizationRecord(_) => {
                let vault_request = pm_types::VaultRetrieveRequest {
                    entity_id: self.get_customer_id(),
                    vault_id: self.get_vault_id()?,
                };

                let vault_data = vault::retrieve_value_from_vault(state, vault_request)
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to retrieve vault data")?;

                Ok(vault_data.get("data").cloned().unwrap_or(Value::Null))
            }
        }
    }
}

#[derive(Debug)]
pub struct TokenReference {
    pub field: String,
}

pub fn parse_token(input: &str) -> IResult<&str, TokenReference> {
    let (input, field) = delimited(
        tag("{{"),
        preceded(
            multispace0,
            preceded(
                char('$'),
                terminated(
                    take_while1(|c: char| c.is_alphanumeric() || c == '_'),
                    multispace0,
                ),
            ),
        ),
        tag("}}"),
    )(input)?;

    Ok((
        input,
        TokenReference {
            field: field.to_string(),
        },
    ))
}

pub fn contains_token(s: &str) -> bool {
    s.contains("{{") && s.contains("$") && s.contains("}}")
}


// File: crates/router/src/core/card_testing_guard/utils.rs

use error_stack::ResultExt;
use hyperswitch_domain_models::{
    card_testing_guard_data::CardTestingGuardData, router_request_types::BrowserInformation,
};
use masking::{PeekInterface, Secret};
use router_env::logger;

use super::errors;
use crate::{
    core::{errors::RouterResult, payments::helpers},
    routes::SessionState,
    services,
    types::domain,
    utils::crypto::{self, SignMessage},
};

pub async fn validate_card_testing_guard_checks(
    state: &SessionState,
    #[cfg(feature = "v1")] browser_info: Option<&serde_json::Value>,
    #[cfg(feature = "v2")] browser_info: Option<&BrowserInformation>,
    card_number: cards::CardNumber,
    customer_id: &Option<common_utils::id_type::CustomerId>,
    business_profile: &domain::Profile,
) -> RouterResult<Option<CardTestingGuardData>> {
    match &business_profile.card_testing_guard_config {
        Some(card_testing_guard_config) => {
            let fingerprint = generate_fingerprint(card_number, business_profile).await?;

            let card_testing_guard_expiry = card_testing_guard_config.card_testing_guard_expiry;

            let mut card_ip_blocking_cache_key = String::new();
            let mut guest_user_card_blocking_cache_key = String::new();
            let mut customer_id_blocking_cache_key = String::new();

            if card_testing_guard_config.is_card_ip_blocking_enabled {
                if let Some(browser_info) = browser_info {
                    #[cfg(feature = "v1")]
                    {
                        let browser_info =
                            serde_json::from_value::<BrowserInformation>(browser_info.clone())
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable("could not parse browser_info")?;

                        if let Some(browser_info_ip) = browser_info.ip_address {
                            card_ip_blocking_cache_key =
                                helpers::validate_card_ip_blocking_for_business_profile(
                                    state,
                                    browser_info_ip,
                                    fingerprint.clone(),
                                    card_testing_guard_config,
                                )
                                .await?;
                        }
                    }

                    #[cfg(feature = "v2")]
                    {
                        if let Some(browser_info_ip) = browser_info.ip_address {
                            card_ip_blocking_cache_key =
                                helpers::validate_card_ip_blocking_for_business_profile(
                                    state,
                                    browser_info_ip,
                                    fingerprint.clone(),
                                    card_testing_guard_config,
                                )
                                .await?;
                        }
                    }
                }
            }

            if card_testing_guard_config.is_guest_user_card_blocking_enabled {
                guest_user_card_blocking_cache_key =
                    helpers::validate_guest_user_card_blocking_for_business_profile(
                        state,
                        fingerprint.clone(),
                        customer_id.clone(),
                        card_testing_guard_config,
                    )
                    .await?;
            }

            if card_testing_guard_config.is_customer_id_blocking_enabled {
                if let Some(customer_id) = customer_id.clone() {
                    customer_id_blocking_cache_key =
                        helpers::validate_customer_id_blocking_for_business_profile(
                            state,
                            customer_id.clone(),
                            business_profile.get_id(),
                            card_testing_guard_config,
                        )
                        .await?;
                }
            }

            Ok(Some(CardTestingGuardData {
                is_card_ip_blocking_enabled: card_testing_guard_config.is_card_ip_blocking_enabled,
                card_ip_blocking_cache_key,
                is_guest_user_card_blocking_enabled: card_testing_guard_config
                    .is_guest_user_card_blocking_enabled,
                guest_user_card_blocking_cache_key,
                is_customer_id_blocking_enabled: card_testing_guard_config
                    .is_customer_id_blocking_enabled,
                customer_id_blocking_cache_key,
                card_testing_guard_expiry,
            }))
        }
        None => Ok(None),
    }
}

pub async fn generate_fingerprint(
    card_number: cards::CardNumber,
    business_profile: &domain::Profile,
) -> RouterResult<Secret<String>> {
    let card_testing_secret_key = &business_profile.card_testing_secret_key;

    match card_testing_secret_key {
        Some(card_testing_secret_key) => {
            let card_number_fingerprint = crypto::HmacSha512::sign_message(
                &crypto::HmacSha512,
                card_testing_secret_key.get_inner().peek().as_bytes(),
                card_number.clone().get_card_no().as_bytes(),
            )
            .attach_printable("error in pm fingerprint creation")
            .map_or_else(
                |err| {
                    logger::error!(error=?err);
                    None
                },
                Some,
            )
            .map(hex::encode);

            card_number_fingerprint.map(Secret::new).ok_or_else(|| {
                error_stack::report!(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Error while masking fingerprint")
            })
        }
        None => Err(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("card testing secret key not configured")?,
    }
}

pub async fn increment_blocked_count_in_cache(
    state: &SessionState,
    card_testing_guard_data: Option<CardTestingGuardData>,
) -> RouterResult<()> {
    if let Some(card_testing_guard_data) = card_testing_guard_data.clone() {
        if card_testing_guard_data.is_card_ip_blocking_enabled
            && !card_testing_guard_data
                .card_ip_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.card_ip_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }

        if card_testing_guard_data.is_guest_user_card_blocking_enabled
            && !card_testing_guard_data
                .guest_user_card_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.guest_user_card_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }

        if card_testing_guard_data.is_customer_id_blocking_enabled
            && !card_testing_guard_data
                .customer_id_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.customer_id_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }
    }
    Ok(())
}


// File: crates/router/src/core/connector_onboarding/paypal.rs

use api_models::{admin::MerchantConnectorUpdate, connector_onboarding as api};
use common_utils::ext_traits::Encode;
use error_stack::ResultExt;
pub use external_services::http_client;
use masking::{ExposeInterface, PeekInterface, Secret};

use crate::{
    core::{
        admin,
        errors::{ApiErrorResponse, RouterResult},
    },
    services::{ApplicationResponse, Request},
    types::{self as oss_types, api as oss_api_types, api::connector_onboarding as types},
    utils::connector_onboarding as utils,
    SessionState,
};

fn build_referral_url(state: SessionState) -> String {
    format!(
        "{}v2/customer/partner-referrals",
        state.conf.connectors.paypal.base_url
    )
}

async fn build_referral_request(
    state: SessionState,
    tracking_id: String,
    return_url: String,
) -> RouterResult<Request> {
    let access_token = utils::paypal::generate_access_token(state.clone()).await?;
    let request_body = types::paypal::PartnerReferralRequest::new(tracking_id, return_url);

    utils::paypal::build_paypal_post_request(
        build_referral_url(state),
        request_body,
        access_token.token.expose(),
    )
}

pub async fn get_action_url_from_paypal(
    state: SessionState,
    tracking_id: String,
    return_url: String,
) -> RouterResult<String> {
    let referral_request = Box::pin(build_referral_request(
        state.clone(),
        tracking_id,
        return_url,
    ))
    .await?;
    let referral_response = http_client::send_request(&state.conf.proxy, referral_request, None)
        .await
        .change_context(ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to send request to paypal referrals")?;

    let parsed_response: types::paypal::PartnerReferralResponse = referral_response
        .json()
        .await
        .change_context(ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to parse paypal response")?;

    parsed_response.extract_action_url()
}

fn merchant_onboarding_status_url(state: SessionState, tracking_id: String) -> String {
    let partner_id = state
        .conf
        .connector_onboarding
        .get_inner()
        .paypal
        .partner_id
        .to_owned();
    format!(
        "{}v1/customer/partners/{}/merchant-integrations?tracking_id={}",
        state.conf.connectors.paypal.base_url,
        partner_id.expose(),
        tracking_id
    )
}

pub async fn sync_merchant_onboarding_status(
    state: SessionState,
    tracking_id: String,
) -> RouterResult<api::OnboardingStatus> {
    let access_token = utils::paypal::generate_access_token(state.clone()).await?;

    let Some(seller_status_response) =
        find_paypal_merchant_by_tracking_id(state.clone(), tracking_id, &access_token).await?
    else {
        return Ok(api::OnboardingStatus::PayPal(
            api::PayPalOnboardingStatus::AccountNotFound,
        ));
    };

    let merchant_details_url = seller_status_response
        .extract_merchant_details_url(&state.conf.connectors.paypal.base_url)?;

    let merchant_details_request =
        utils::paypal::build_paypal_get_request(merchant_details_url, access_token.token.expose())?;

    let merchant_details_response =
        http_client::send_request(&state.conf.proxy, merchant_details_request, None)
            .await
            .change_context(ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to send request to paypal merchant details")?;

    let parsed_response: types::paypal::SellerStatusDetailsResponse = merchant_details_response
        .json()
        .await
        .change_context(ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to parse paypal merchant details response")?;

    let eligibity = parsed_response.get_eligibility_status().await?;
    Ok(api::OnboardingStatus::PayPal(eligibity))
}

async fn find_paypal_merchant_by_tracking_id(
    state: SessionState,
    tracking_id: String,
    access_token: &oss_types::AccessToken,
) -> RouterResult<Option<types::paypal::SellerStatusResponse>> {
    let seller_status_request = utils::paypal::build_paypal_get_request(
        merchant_onboarding_status_url(state.clone(), tracking_id),
        access_token.token.peek().to_string(),
    )?;
    let seller_status_response =
        http_client::send_request(&state.conf.proxy, seller_status_request, None)
            .await
            .change_context(ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to send request to paypal onboarding status")?;

    if seller_status_response.status().is_success() {
        return Ok(Some(
            seller_status_response
                .json()
                .await
                .change_context(ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to parse paypal onboarding status response")?,
        ));
    }
    Ok(None)
}

pub async fn update_mca(
    state: &SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    connector_id: common_utils::id_type::MerchantConnectorAccountId,
    auth_details: oss_types::ConnectorAuthType,
) -> RouterResult<oss_api_types::MerchantConnectorResponse> {
    let connector_auth_json = auth_details
        .encode_to_value()
        .change_context(ApiErrorResponse::InternalServerError)
        .attach_printable("Error while deserializing connector_account_details")?;
    #[cfg(feature = "v1")]
    let request = MerchantConnectorUpdate {
        connector_type: common_enums::ConnectorType::PaymentProcessor,
        connector_account_details: Some(Secret::new(connector_auth_json)),
        disabled: Some(false),
        status: Some(common_enums::ConnectorStatus::Active),
        connector_label: None,
        payment_methods_enabled: None,
        metadata: None,
        frm_configs: None,
        connector_webhook_details: None,
        pm_auth_config: None,
        test_mode: None,
        additional_merchant_data: None,
        connector_wallets_details: None,
    };
    #[cfg(feature = "v2")]
    let request = MerchantConnectorUpdate {
        connector_type: common_enums::ConnectorType::PaymentProcessor,
        connector_account_details: Some(Secret::new(connector_auth_json)),
        disabled: Some(false),
        status: Some(common_enums::ConnectorStatus::Active),
        connector_label: None,
        payment_methods_enabled: None,
        metadata: None,
        frm_configs: None,
        connector_webhook_details: None,
        pm_auth_config: None,
        merchant_id: merchant_id.clone(),
        additional_merchant_data: None,
        connector_wallets_details: None,
        feature_metadata: None,
    };
    let mca_response =
        admin::update_connector(state.clone(), merchant_id, None, &connector_id, request).await?;

    match mca_response {
        ApplicationResponse::Json(mca_data) => Ok(mca_data),
        _ => Err(ApiErrorResponse::InternalServerError.into()),
    }
}


// File: crates/router/src/core/disputes/transformers.rs

use api_models::disputes::EvidenceType;
use common_utils::errors::CustomResult;
use error_stack::ResultExt;

use crate::{
    core::{errors, files::helpers::retrieve_file_and_provider_file_id_from_file_id},
    routes::SessionState,
    types::{
        api::{self, DisputeEvidence},
        domain,
        transformers::ForeignFrom,
        SubmitEvidenceRequestData,
    },
};

pub async fn get_evidence_request_data(
    state: &SessionState,
    platform: &domain::Platform,
    evidence_request: api_models::disputes::SubmitEvidenceRequest,
    dispute: &diesel_models::dispute::Dispute,
) -> CustomResult<SubmitEvidenceRequestData, errors::ApiErrorResponse> {
    let cancellation_policy_file_info = retrieve_file_and_provider_file_id_from_file_id(
        state,
        evidence_request.cancellation_policy,
        None,
        platform,
        api::FileDataRequired::NotRequired,
    )
    .await?;
    let customer_communication_file_info = retrieve_file_and_provider_file_id_from_file_id(
        state,
        evidence_request.customer_communication,
        None,
        platform,
        api::FileDataRequired::NotRequired,
    )
    .await?;
    let customer_sifnature_file_info = retrieve_file_and_provider_file_id_from_file_id(
        state,
        evidence_request.customer_signature,
        None,
        platform,
        api::FileDataRequired::NotRequired,
    )
    .await?;
    let receipt_file_info = retrieve_file_and_provider_file_id_from_file_id(
        state,
        evidence_request.receipt,
        None,
        platform,
        api::FileDataRequired::NotRequired,
    )
    .await?;
    let refund_policy_file_info = retrieve_file_and_provider_file_id_from_file_id(
        state,
        evidence_request.refund_policy,
        None,
        platform,
        api::FileDataRequired::NotRequired,
    )
    .await?;
    let service_documentation_file_info = retrieve_file_and_provider_file_id_from_file_id(
        state,
        evidence_request.service_documentation,
        None,
        platform,
        api::FileDataRequired::NotRequired,
    )
    .await?;
    let shipping_documentation_file_info = retrieve_file_and_provider_file_id_from_file_id(
        state,
        evidence_request.shipping_documentation,
        None,
        platform,
        api::FileDataRequired::NotRequired,
    )
    .await?;
    let invoice_showing_distinct_transactions_file_info =
        retrieve_file_and_provider_file_id_from_file_id(
            state,
            evidence_request.invoice_showing_distinct_transactions,
            None,
            platform,
            api::FileDataRequired::NotRequired,
        )
        .await?;
    let recurring_transaction_agreement_file_info =
        retrieve_file_and_provider_file_id_from_file_id(
            state,
            evidence_request.recurring_transaction_agreement,
            None,
            platform,
            api::FileDataRequired::NotRequired,
        )
        .await?;
    let uncategorized_file_info = retrieve_file_and_provider_file_id_from_file_id(
        state,
        evidence_request.uncategorized_file,
        None,
        platform,
        api::FileDataRequired::NotRequired,
    )
    .await?;
    Ok(SubmitEvidenceRequestData {
        dispute_id: dispute.dispute_id.clone(),
        dispute_status: dispute.dispute_status,
        connector_dispute_id: dispute.connector_dispute_id.clone(),
        access_activity_log: evidence_request.access_activity_log,
        billing_address: evidence_request.billing_address,
        cancellation_policy: cancellation_policy_file_info.file_data,
        cancellation_policy_provider_file_id: cancellation_policy_file_info.provider_file_id,
        cancellation_policy_disclosure: evidence_request.cancellation_policy_disclosure,
        cancellation_rebuttal: evidence_request.cancellation_rebuttal,
        customer_communication: customer_communication_file_info.file_data,
        customer_communication_provider_file_id: customer_communication_file_info.provider_file_id,
        customer_email_address: evidence_request.customer_email_address,
        customer_name: evidence_request.customer_name,
        customer_purchase_ip: evidence_request.customer_purchase_ip,
        customer_signature: customer_sifnature_file_info.file_data,
        customer_signature_provider_file_id: customer_sifnature_file_info.provider_file_id,
        product_description: evidence_request.product_description,
        receipt: receipt_file_info.file_data,
        receipt_provider_file_id: receipt_file_info.provider_file_id,
        refund_policy: refund_policy_file_info.file_data,
        refund_policy_provider_file_id: refund_policy_file_info.provider_file_id,
        refund_policy_disclosure: evidence_request.refund_policy_disclosure,
        refund_refusal_explanation: evidence_request.refund_refusal_explanation,
        service_date: evidence_request.service_date,
        service_documentation: service_documentation_file_info.file_data,
        service_documentation_provider_file_id: service_documentation_file_info.provider_file_id,
        shipping_address: evidence_request.shipping_address,
        shipping_carrier: evidence_request.shipping_carrier,
        shipping_date: evidence_request.shipping_date,
        shipping_documentation: shipping_documentation_file_info.file_data,
        shipping_documentation_provider_file_id: shipping_documentation_file_info.provider_file_id,
        shipping_tracking_number: evidence_request.shipping_tracking_number,
        invoice_showing_distinct_transactions: invoice_showing_distinct_transactions_file_info
            .file_data,
        invoice_showing_distinct_transactions_provider_file_id:
            invoice_showing_distinct_transactions_file_info.provider_file_id,
        recurring_transaction_agreement: recurring_transaction_agreement_file_info.file_data,
        recurring_transaction_agreement_provider_file_id: recurring_transaction_agreement_file_info
            .provider_file_id,
        uncategorized_file: uncategorized_file_info.file_data,
        uncategorized_file_provider_file_id: uncategorized_file_info.provider_file_id,
        uncategorized_text: evidence_request.uncategorized_text,
        cancellation_policy_file_type: cancellation_policy_file_info.file_type,
        customer_communication_file_type: customer_communication_file_info.file_type,
        customer_signature_file_type: customer_sifnature_file_info.file_type,
        receipt_file_type: receipt_file_info.file_type,
        refund_policy_file_type: refund_policy_file_info.file_type,
        service_documentation_file_type: service_documentation_file_info.file_type,
        shipping_documentation_file_type: shipping_documentation_file_info.file_type,
        invoice_showing_distinct_transactions_file_type:
            invoice_showing_distinct_transactions_file_info.file_type,
        recurring_transaction_agreement_file_type: recurring_transaction_agreement_file_info
            .file_type,
        uncategorized_file_type: uncategorized_file_info.file_type,
    })
}

pub fn update_dispute_evidence(
    dispute_evidence: DisputeEvidence,
    evidence_type: api::EvidenceType,
    file_id: String,
) -> DisputeEvidence {
    match evidence_type {
        api::EvidenceType::CancellationPolicy => DisputeEvidence {
            cancellation_policy: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::CustomerCommunication => DisputeEvidence {
            customer_communication: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::CustomerSignature => DisputeEvidence {
            customer_signature: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::Receipt => DisputeEvidence {
            receipt: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::RefundPolicy => DisputeEvidence {
            refund_policy: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::ServiceDocumentation => DisputeEvidence {
            service_documentation: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::ShippingDocumentation => DisputeEvidence {
            shipping_documentation: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::InvoiceShowingDistinctTransactions => DisputeEvidence {
            invoice_showing_distinct_transactions: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::RecurringTransactionAgreement => DisputeEvidence {
            recurring_transaction_agreement: Some(file_id),
            ..dispute_evidence
        },
        api::EvidenceType::UncategorizedFile => DisputeEvidence {
            uncategorized_file: Some(file_id),
            ..dispute_evidence
        },
    }
}

pub async fn get_dispute_evidence_block(
    state: &SessionState,
    platform: &domain::Platform,
    evidence_type: EvidenceType,
    file_id: String,
) -> CustomResult<api_models::disputes::DisputeEvidenceBlock, errors::ApiErrorResponse> {
    let file_metadata = state
        .store
        .find_file_metadata_by_merchant_id_file_id(
            platform.get_processor().get_account().get_id(),
            &file_id,
        )
        .await
        .change_context(errors::ApiErrorResponse::FileNotFound)
        .attach_printable("Unable to retrieve file_metadata")?;
    let file_metadata_response =
        api_models::files::FileMetadataResponse::foreign_from(file_metadata);
    Ok(api_models::disputes::DisputeEvidenceBlock {
        evidence_type,
        file_metadata_response,
    })
}

pub fn delete_evidence_file(
    dispute_evidence: DisputeEvidence,
    evidence_type: EvidenceType,
) -> DisputeEvidence {
    match evidence_type {
        EvidenceType::CancellationPolicy => DisputeEvidence {
            cancellation_policy: None,
            ..dispute_evidence
        },
        EvidenceType::CustomerCommunication => DisputeEvidence {
            customer_communication: None,
            ..dispute_evidence
        },
        EvidenceType::CustomerSignature => DisputeEvidence {
            customer_signature: None,
            ..dispute_evidence
        },
        EvidenceType::Receipt => DisputeEvidence {
            receipt: None,
            ..dispute_evidence
        },
        EvidenceType::RefundPolicy => DisputeEvidence {
            refund_policy: None,
            ..dispute_evidence
        },
        EvidenceType::ServiceDocumentation => DisputeEvidence {
            service_documentation: None,
            ..dispute_evidence
        },
        EvidenceType::ShippingDocumentation => DisputeEvidence {
            shipping_documentation: None,
            ..dispute_evidence
        },
        EvidenceType::InvoiceShowingDistinctTransactions => DisputeEvidence {
            invoice_showing_distinct_transactions: None,
            ..dispute_evidence
        },
        EvidenceType::RecurringTransactionAgreement => DisputeEvidence {
            recurring_transaction_agreement: None,
            ..dispute_evidence
        },
        EvidenceType::UncategorizedFile => DisputeEvidence {
            uncategorized_file: None,
            ..dispute_evidence
        },
    }
}

pub async fn get_dispute_evidence_vec(
    state: &SessionState,
    platform: domain::Platform,
    dispute_evidence: DisputeEvidence,
) -> CustomResult<Vec<api_models::disputes::DisputeEvidenceBlock>, errors::ApiErrorResponse> {
    let mut dispute_evidence_blocks: Vec<api_models::disputes::DisputeEvidenceBlock> = vec![];
    if let Some(cancellation_policy_block) = dispute_evidence.cancellation_policy {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::CancellationPolicy,
                cancellation_policy_block,
            )
            .await?,
        )
    }
    if let Some(customer_communication_block) = dispute_evidence.customer_communication {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::CustomerCommunication,
                customer_communication_block,
            )
            .await?,
        )
    }
    if let Some(customer_signature_block) = dispute_evidence.customer_signature {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::CustomerSignature,
                customer_signature_block,
            )
            .await?,
        )
    }
    if let Some(receipt_block) = dispute_evidence.receipt {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(state, &platform, EvidenceType::Receipt, receipt_block)
                .await?,
        )
    }
    if let Some(refund_policy_block) = dispute_evidence.refund_policy {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::RefundPolicy,
                refund_policy_block,
            )
            .await?,
        )
    }
    if let Some(service_documentation_block) = dispute_evidence.service_documentation {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::ServiceDocumentation,
                service_documentation_block,
            )
            .await?,
        )
    }
    if let Some(shipping_documentation_block) = dispute_evidence.shipping_documentation {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::ShippingDocumentation,
                shipping_documentation_block,
            )
            .await?,
        )
    }
    if let Some(invoice_showing_distinct_transactions_block) =
        dispute_evidence.invoice_showing_distinct_transactions
    {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::InvoiceShowingDistinctTransactions,
                invoice_showing_distinct_transactions_block,
            )
            .await?,
        )
    }
    if let Some(recurring_transaction_agreement_block) =
        dispute_evidence.recurring_transaction_agreement
    {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::RecurringTransactionAgreement,
                recurring_transaction_agreement_block,
            )
            .await?,
        )
    }
    if let Some(uncategorized_file_block) = dispute_evidence.uncategorized_file {
        dispute_evidence_blocks.push(
            get_dispute_evidence_block(
                state,
                &platform,
                EvidenceType::UncategorizedFile,
                uncategorized_file_block,
            )
            .await?,
        )
    }
    Ok(dispute_evidence_blocks)
}