// File: crates/router/src/core/payments/flows/capture_flow.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_utils::{id_type, types::MinorUnit, ucs_types};
use error_stack::ResultExt;
use external_services::grpc_client;
use hyperswitch_domain_models::{
    errors::api_error_response::ApiErrorResponse, payments as domain_payments,
};
use unified_connector_service_client::payments as payments_grpc;

use super::ConstructFlowSpecificData;
use crate::{
    core::{
        errors::{ConnectorErrorExt, RouterResult},
        payments::{self, access_token, helpers, transformers, Feature, PaymentData},
        unified_connector_service::{
            build_unified_connector_service_auth_metadata,
            handle_unified_connector_service_response_for_payment_capture, ucs_logging_wrapper,
        },
    },
    routes::SessionState,
    services::{self, logger},
    types::{self, api, domain, transformers::ForeignTryFrom},
};

#[cfg(feature = "v1")]
#[async_trait]
impl
    ConstructFlowSpecificData<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
    for PaymentData<api::Capture>
{
    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<types::MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
        _payment_method: Option<common_enums::PaymentMethod>,
        _payment_method_type: Option<common_enums::PaymentMethodType>,
    ) -> RouterResult<types::PaymentsCaptureRouterData> {
        Box::pin(transformers::construct_payment_router_data::<
            api::Capture,
            types::PaymentsCaptureData,
        >(
            state,
            self.clone(),
            connector_id,
            platform,
            customer,
            merchant_connector_account,
            merchant_recipient_data,
            header_payload,
            None,
            None,
        ))
        .await
    }
}

#[cfg(feature = "v2")]
#[async_trait]
impl
    ConstructFlowSpecificData<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
    for hyperswitch_domain_models::payments::PaymentCaptureData<api::Capture>
{
    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<types::MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
    ) -> RouterResult<
        types::RouterData<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>,
    > {
        Box::pin(transformers::construct_payment_router_data_for_capture(
            state,
            self.clone(),
            connector_id,
            platform,
            customer,
            merchant_connector_account,
            merchant_recipient_data,
            header_payload,
        ))
        .await
    }
}

#[async_trait]
impl Feature<api::Capture, types::PaymentsCaptureData>
    for types::RouterData<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
{
    async fn decide_flows<'a>(
        self,
        state: &SessionState,
        connector: &api::ConnectorData,
        call_connector_action: payments::CallConnectorAction,
        connector_request: Option<services::Request>,
        _business_profile: &domain::Profile,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
        return_raw_connector_response: Option<bool>,
        _gateway_context: payments::gateway::context::RouterGatewayContext,
    ) -> RouterResult<Self> {
        let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
            api::Capture,
            types::PaymentsCaptureData,
            types::PaymentsResponseData,
        > = connector.connector.get_connector_integration();

        let mut new_router_data = services::execute_connector_processing_step(
            state,
            connector_integration,
            &self,
            call_connector_action,
            connector_request,
            return_raw_connector_response,
        )
        .await
        .to_payment_failed_response()?;

        // Initiating Integrity check
        let integrity_result = helpers::check_integrity_based_on_flow(
            &new_router_data.request,
            &new_router_data.response,
        );
        new_router_data.integrity_check = integrity_result;

        Ok(new_router_data)
    }

    async fn add_access_token<'a>(
        &self,
        state: &SessionState,
        connector: &api::ConnectorData,
        _platform: &domain::Platform,
        creds_identifier: Option<&str>,
    ) -> RouterResult<types::AddAccessTokenResult> {
        Box::pin(access_token::add_access_token(
            state,
            connector,
            self,
            creds_identifier,
        ))
        .await
    }

    async fn build_flow_specific_connector_request(
        &mut self,
        state: &SessionState,
        connector: &api::ConnectorData,
        call_connector_action: payments::CallConnectorAction,
    ) -> RouterResult<(Option<services::Request>, bool)> {
        let request = match call_connector_action {
            payments::CallConnectorAction::Trigger => {
                let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
                    api::Capture,
                    types::PaymentsCaptureData,
                    types::PaymentsResponseData,
                > = connector.connector.get_connector_integration();

                connector_integration
                    .build_request(self, &state.conf.connectors)
                    .to_payment_failed_response()?
            }
            _ => None,
        };

        Ok((request, true))
    }

    async fn call_unified_connector_service<'a>(
        &mut self,
        state: &SessionState,
        header_payload: &domain_payments::HeaderPayload,
        lineage_ids: grpc_client::LineageIds,
        #[cfg(feature = "v1")] merchant_connector_account: helpers::MerchantConnectorAccountType,
        #[cfg(feature = "v2")]
        merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
        platform: &domain::Platform,
        _connector_data: &api::ConnectorData,
        unified_connector_service_execution_mode: common_enums::ExecutionMode,
        merchant_order_reference_id: Option<String>,
        _call_connector_action: common_enums::CallConnectorAction,
        _creds_identifier: Option<String>,
    ) -> RouterResult<()> {
        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let payment_capture_request =
            payments_grpc::PaymentServiceCaptureRequest::foreign_try_from(&*self)
                .change_context(ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to construct Payment Capture Request")?;

        let connector_auth_metadata =
            build_unified_connector_service_auth_metadata(merchant_connector_account, platform)
                .change_context(ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to construct request metadata")?;
        let merchant_reference_id = header_payload
            .x_reference_id
            .clone()
            .or(merchant_order_reference_id)
            .map(|id| id_type::PaymentReferenceId::from_str(id.as_str()))
            .transpose()
            .inspect_err(|err| logger::warn!(error=?err, "Invalid Merchant ReferenceId found"))
            .ok()
            .flatten()
            .map(ucs_types::UcsReferenceId::Payment);
        let header_payload = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(merchant_reference_id)
            .lineage_ids(lineage_ids);
        let (updated_router_data, _) = Box::pin(ucs_logging_wrapper(
            self.clone(),
            state,
            payment_capture_request,
            header_payload,
            |mut router_data, payment_capture_request, grpc_headers| async move {
                let response = client
                    .payment_capture(
                        payment_capture_request,
                        connector_auth_metadata,
                        grpc_headers,
                    )
                    .await
                    .change_context(ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to capture payment")?;

                let payment_capture_response = response.into_inner();

                let (router_data_response, status_code) =
                    handle_unified_connector_service_response_for_payment_capture(
                        payment_capture_response.clone(),
                    )
                    .change_context(ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to deserialize UCS response")?;

                let router_data_response = router_data_response.map(|(response, status)| {
                    router_data.status = status;
                    response
                });
                router_data.response = router_data_response;
                router_data.amount_captured = payment_capture_response.captured_amount;
                router_data.minor_amount_captured = payment_capture_response
                    .minor_captured_amount
                    .map(MinorUnit::new);
                router_data.connector_http_status_code = Some(status_code);

                Ok((router_data, (), payment_capture_response))
            },
        ))
        .await?;

        // Copy back the updated data
        *self = updated_router_data;
        Ok(())
    }
}


// File: crates/router/src/core/payments/gateway/psync_gateway.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_enums::{CallConnectorAction, ExecutionPath};
use common_utils::{errors::CustomResult, id_type, request::Request, ucs_types};
use error_stack::ResultExt;
use hyperswitch_domain_models::{router_data::RouterData, router_flow_types as domain};
use hyperswitch_interfaces::{
    api::gateway as payment_gateway,
    connector_integration_interface::{BoxedConnectorIntegrationInterface, RouterDataConversion},
    errors::ConnectorError,
    unified_connector_service::handle_unified_connector_service_response_for_payment_get,
};
use unified_connector_service_client::payments as payments_grpc;
use unified_connector_service_masking::ExposeInterface as UcsMaskingExposeInterface;

use crate::{
    core::{payments::gateway::context::RouterGatewayContext, unified_connector_service},
    routes::SessionState,
    services::logger,
    types::{self, transformers::ForeignTryFrom, MinorUnit},
};

// =============================================================================
// PaymentGateway Implementation for domain::PSync
// =============================================================================

/// Implementation of PaymentGateway for api::PSync flow
#[async_trait]
impl<RCD>
    payment_gateway::PaymentGateway<
        SessionState,
        RCD,
        Self,
        types::PaymentsSyncData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::PSync
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::PaymentsSyncData, types::PaymentsResponseData>,
{
    async fn execute(
        self: Box<Self>,
        state: &SessionState,
        _connector_integration: BoxedConnectorIntegrationInterface<
            Self,
            RCD,
            types::PaymentsSyncData,
            types::PaymentsResponseData,
        >,
        router_data: &RouterData<Self, types::PaymentsSyncData, types::PaymentsResponseData>,
        call_connector_action: CallConnectorAction,
        _connector_request: Option<Request>,
        _return_raw_connector_response: Option<bool>,
        context: RouterGatewayContext,
    ) -> CustomResult<
        RouterData<Self, types::PaymentsSyncData, types::PaymentsResponseData>,
        ConnectorError,
    > {
        let connector_name = router_data.connector.clone();
        let connector_enum = common_enums::connector_enums::Connector::from_str(&connector_name)
            .change_context(ConnectorError::InvalidConnectorName)?;
        let merchant_connector_account = context.merchant_connector_account;
        let creds_identifier = context.creds_identifier;
        let platform = context.platform;
        let lineage_ids = context.lineage_ids;
        let header_payload = context.header_payload;
        let unified_connector_service_execution_mode = context.execution_mode;
        let merchant_order_reference_id = header_payload.x_reference_id.clone();
        let is_ucs_psync_disabled = state
            .conf
            .grpc_client
            .unified_connector_service
            .as_ref()
            .is_some_and(|config| {
                config
                    .ucs_psync_disabled_connectors
                    .contains(&connector_enum)
            });

        if is_ucs_psync_disabled {
            logger::info!(
                "UCS PSync call disabled for connector: {}, skipping UCS call",
                connector_name
            );
            return Ok(router_data.clone());
        }
        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let payment_get_request = payments_grpc::PaymentServiceGetRequest::foreign_try_from((
            router_data,
            call_connector_action,
        ))
        .change_context(ConnectorError::RequestEncodingFailed)
        .attach_printable("Failed to construct Payment Get Request")?;

        let merchant_connector_id = merchant_connector_account.get_mca_id();

        let connector_auth_metadata =
            unified_connector_service::build_unified_connector_service_auth_metadata(
                merchant_connector_account,
                &platform,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct request metadata")?;
        let merchant_reference_id = header_payload
            .x_reference_id
            .clone()
            .or(merchant_order_reference_id)
            .map(|id| id_type::PaymentReferenceId::from_str(id.as_str()))
            .transpose()
            .inspect_err(|err| logger::warn!(error=?err, "Invalid Merchant ReferenceId found"))
            .ok()
            .flatten()
            .map(ucs_types::UcsReferenceId::Payment);
        let header_payload = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(merchant_reference_id)
            .lineage_ids(lineage_ids);
        let connector_name = router_data.connector.clone();
        let updated_router_data = Box::pin(unified_connector_service::ucs_logging_wrapper_new(
            router_data.clone(),
            state,
            payment_get_request,
            header_payload,
            |mut router_data, payment_get_request, grpc_headers| async move {
                let response = client
                    .payment_get(payment_get_request, connector_auth_metadata, grpc_headers)
                    .await
                    .attach_printable("Failed to get payment")?;

                let payment_get_response = response.into_inner();

                let (router_data_response, status_code) =
                    handle_unified_connector_service_response_for_payment_get(
                        payment_get_response.clone(),
                    )
                    .attach_printable("Failed to deserialize UCS response")?;

                // Extract and store access token if present
                if let Some(access_token) =
                    unified_connector_service::get_access_token_from_ucs_response(
                        state,
                        &platform,
                        &connector_name,
                        merchant_connector_id.as_ref(),
                        creds_identifier.clone(),
                        payment_get_response.state.as_ref(),
                    )
                    .await
                {
                    if let Err(error) = unified_connector_service::set_access_token_for_ucs(
                        state,
                        &platform,
                        &connector_name,
                        access_token,
                        merchant_connector_id.as_ref(),
                        creds_identifier,
                    )
                    .await
                    {
                        logger::error!(
                            ?error,
                            "Failed to store UCS access token from psync response"
                        );
                    } else {
                        logger::debug!("Successfully stored access token from UCS psync response");
                    }
                }

                let router_data_response = router_data_response.map(|(response, status)| {
                    router_data.status = status;
                    response
                });
                router_data.response = router_data_response;
                router_data.amount_captured = payment_get_response.captured_amount;
                router_data.minor_amount_captured = payment_get_response
                    .minor_captured_amount
                    .map(MinorUnit::new);
                router_data.raw_connector_response = payment_get_response
                    .raw_connector_response
                    .clone()
                    .map(|raw_connector_response| raw_connector_response.expose().into());
                router_data.connector_http_status_code = Some(status_code);

                Ok((router_data, payment_get_response))
            },
        ))
        .await
        .change_context(ConnectorError::ResponseHandlingFailed)?;

        Ok(updated_router_data)
    }
}

/// Implementation of FlowGateway for api::PSync
///
/// This allows the flow to provide its specific gateway based on execution path
impl<RCD>
    payment_gateway::FlowGateway<
        SessionState,
        RCD,
        types::PaymentsSyncData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::PSync
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::PaymentsSyncData, types::PaymentsResponseData>,
{
    fn get_gateway(
        execution_path: ExecutionPath,
    ) -> Box<
        dyn payment_gateway::PaymentGateway<
            SessionState,
            RCD,
            Self,
            types::PaymentsSyncData,
            types::PaymentsResponseData,
            RouterGatewayContext,
        >,
    > {
        match execution_path {
            ExecutionPath::Direct => Box::new(payment_gateway::DirectGateway),
            ExecutionPath::UnifiedConnectorService
            | ExecutionPath::ShadowUnifiedConnectorService => Box::new(Self),
        }
    }
}


// File: crates/router/src/core/payments/gateway/setup_mandate.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_enums::{CallConnectorAction, ExecutionPath};
use common_utils::{errors::CustomResult, id_type, request::Request, ucs_types};
use error_stack::ResultExt;
use hyperswitch_domain_models::{router_data::RouterData, router_flow_types as domain};
use hyperswitch_interfaces::{
    api::gateway as payment_gateway,
    connector_integration_interface::{BoxedConnectorIntegrationInterface, RouterDataConversion},
    errors::ConnectorError,
};
use unified_connector_service_client::payments as payments_grpc;
use unified_connector_service_masking::ExposeInterface as UcsMaskingExposeInterface;

use crate::{
    core::{
        payments::gateway::context::RouterGatewayContext, unified_connector_service,
        unified_connector_service::handle_unified_connector_service_response_for_payment_authorize,
    },
    routes::SessionState,
    services::logger,
    types::{self, transformers::ForeignTryFrom, MinorUnit},
};

// =============================================================================
// PaymentGateway Implementation for domain::SetupMandate
// =============================================================================

/// Implementation of PaymentGateway for api::PSync flow
#[async_trait]
impl<RCD>
    payment_gateway::PaymentGateway<
        SessionState,
        RCD,
        Self,
        types::SetupMandateRequestData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::SetupMandate
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::SetupMandateRequestData, types::PaymentsResponseData>,
{
    async fn execute(
        self: Box<Self>,
        state: &SessionState,
        _connector_integration: BoxedConnectorIntegrationInterface<
            Self,
            RCD,
            types::SetupMandateRequestData,
            types::PaymentsResponseData,
        >,
        router_data: &RouterData<Self, types::SetupMandateRequestData, types::PaymentsResponseData>,
        call_connector_action: CallConnectorAction,
        _connector_request: Option<Request>,
        _return_raw_connector_response: Option<bool>,
        context: RouterGatewayContext,
    ) -> CustomResult<
        RouterData<Self, types::SetupMandateRequestData, types::PaymentsResponseData>,
        ConnectorError,
    > {
        let merchant_connector_account = context.merchant_connector_account;
        let platform = context.platform;
        let lineage_ids = context.lineage_ids;
        let header_payload = context.header_payload;
        let unified_connector_service_execution_mode = context.execution_mode;
        let merchant_order_reference_id = header_payload.x_reference_id.clone();
        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let setup_mandate_request =
            payments_grpc::PaymentServiceAuthorizeOnlyRequest::foreign_try_from((
                router_data,
                call_connector_action,
            ))
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct Payment Get Request")?;

        let connector_auth_metadata =
            unified_connector_service::build_unified_connector_service_auth_metadata(
                merchant_connector_account,
                &platform,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct request metadata")?;
        let merchant_reference_id = header_payload
            .x_reference_id
            .clone()
            .or(merchant_order_reference_id)
            .map(|id| id_type::PaymentReferenceId::from_str(id.as_str()))
            .transpose()
            .inspect_err(|err| logger::warn!(error=?err, "Invalid Merchant ReferenceId found"))
            .ok()
            .flatten()
            .map(ucs_types::UcsReferenceId::Payment);
        let header_payload = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(merchant_reference_id)
            .lineage_ids(lineage_ids);
        let updated_router_data = Box::pin(unified_connector_service::ucs_logging_wrapper_new(
            router_data.clone(),
            state,
            setup_mandate_request,
            header_payload,
            |mut router_data, setup_mandate_request, grpc_headers| async move {
                let response = Box::pin(client.payment_setup_mandate_granular(
                    setup_mandate_request,
                    connector_auth_metadata,
                    grpc_headers,
                ))
                .await
                .attach_printable("Failed to get payment")?;

                let setup_mandate_response = response.into_inner();

                let ucs_data = handle_unified_connector_service_response_for_payment_authorize(
                    setup_mandate_response.clone(),
                )
                .attach_printable("Failed to deserialize UCS response")?;

                let router_data_response =
                    ucs_data.router_data_response.map(|(response, status)| {
                        router_data.status = status;
                        response
                    });
                router_data.response = router_data_response;
                router_data.amount_captured = setup_mandate_response.captured_amount;
                router_data.minor_amount_captured = setup_mandate_response
                    .minor_captured_amount
                    .map(MinorUnit::new);
                router_data.raw_connector_response = setup_mandate_response
                    .raw_connector_response
                    .clone()
                    .map(|raw_connector_response| raw_connector_response.expose().into());
                router_data.connector_http_status_code = Some(ucs_data.status_code);

                // Populate connector_customer_id if present
                ucs_data.connector_customer_id.map(|connector_customer_id| {
                    router_data.connector_customer = Some(connector_customer_id);
                });

                ucs_data.connector_response.map(|customer_response| {
                    router_data.connector_response = Some(customer_response);
                });

                Ok((router_data, setup_mandate_response))
            },
        ))
        .await
        .change_context(ConnectorError::ResponseHandlingFailed)?;

        Ok(updated_router_data)
    }
}

/// Implementation of FlowGateway for api::PSync
///
/// This allows the flow to provide its specific gateway based on execution path
impl<RCD>
    payment_gateway::FlowGateway<
        SessionState,
        RCD,
        types::SetupMandateRequestData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::SetupMandate
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::SetupMandateRequestData, types::PaymentsResponseData>,
{
    fn get_gateway(
        execution_path: ExecutionPath,
    ) -> Box<
        dyn payment_gateway::PaymentGateway<
            SessionState,
            RCD,
            Self,
            types::SetupMandateRequestData,
            types::PaymentsResponseData,
            RouterGatewayContext,
        >,
    > {
        match execution_path {
            ExecutionPath::Direct => Box::new(payment_gateway::DirectGateway),
            ExecutionPath::UnifiedConnectorService
            | ExecutionPath::ShadowUnifiedConnectorService => Box::new(Self),
        }
    }
}


// File: crates/router/src/core/payments/gateway/access_token_gateway.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_enums::{CallConnectorAction, ExecutionPath};
use common_utils::{errors::CustomResult, id_type, request::Request, ucs_types};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::RouterData, router_flow_types as domain,
    router_request_types::AccessTokenRequestData,
};
use hyperswitch_interfaces::{
    api::gateway as payment_gateway,
    connector_integration_interface::{BoxedConnectorIntegrationInterface, RouterDataConversion},
    errors::ConnectorError,
};
use unified_connector_service_client::payments as payments_grpc;

use crate::{
    core::{payments::gateway::context::RouterGatewayContext, unified_connector_service},
    routes::SessionState,
    services::logger,
    types::transformers::ForeignTryFrom,
};

// =============================================================================
// PaymentGateway Implementation for domain::AccessTokenAuth
// =============================================================================

/// Implementation of PaymentGateway for AccessTokenAuth flow
#[async_trait]
impl<RCD>
    payment_gateway::PaymentGateway<
        SessionState,
        RCD,
        Self,
        AccessTokenRequestData,
        hyperswitch_domain_models::router_data::AccessToken,
        RouterGatewayContext,
    > for domain::access_token_auth::AccessTokenAuth
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<
            Self,
            AccessTokenRequestData,
            hyperswitch_domain_models::router_data::AccessToken,
        >,
{
    async fn execute(
        self: Box<Self>,
        state: &SessionState,
        _connector_integration: BoxedConnectorIntegrationInterface<
            Self,
            RCD,
            AccessTokenRequestData,
            hyperswitch_domain_models::router_data::AccessToken,
        >,
        router_data: &RouterData<
            Self,
            AccessTokenRequestData,
            hyperswitch_domain_models::router_data::AccessToken,
        >,
        call_connector_action: CallConnectorAction,
        _connector_request: Option<Request>,
        _return_raw_connector_response: Option<bool>,
        context: RouterGatewayContext,
    ) -> CustomResult<
        RouterData<
            Self,
            AccessTokenRequestData,
            hyperswitch_domain_models::router_data::AccessToken,
        >,
        ConnectorError,
    > {
        let merchant_connector_account = context.merchant_connector_account;
        let platform = context.platform;
        let lineage_ids = context.lineage_ids;
        let header_payload = context.header_payload;
        let unified_connector_service_execution_mode = context.execution_mode;

        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let create_access_token_request =
            payments_grpc::PaymentServiceCreateAccessTokenRequest::foreign_try_from((
                router_data,
                call_connector_action,
            ))
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct Create Access Token Request")?;

        let connector_auth_metadata =
            unified_connector_service::build_unified_connector_service_auth_metadata(
                merchant_connector_account.clone(),
                &platform,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct request metadata")?;

        let merchant_reference_id = header_payload
            .x_reference_id
            .clone()
            .map(|id| id_type::PaymentReferenceId::from_str(id.as_str()))
            .transpose()
            .inspect_err(|err| logger::warn!(error=?err, "Invalid Merchant ReferenceId found"))
            .ok()
            .flatten()
            .map(ucs_types::UcsReferenceId::Payment);

        let header_payload = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(merchant_reference_id)
            .lineage_ids(lineage_ids);

        let updated_router_data = Box::pin(unified_connector_service::ucs_logging_wrapper_new(
            router_data.clone(),
            state,
            create_access_token_request,
            header_payload,
            |mut router_data, create_access_token_request, grpc_headers| async move {
                let response = client
                    .create_access_token(
                        create_access_token_request,
                        connector_auth_metadata,
                        grpc_headers,
                    )
                    .await
                    .attach_printable("Failed to create access token")?;

                let create_access_token_response = response.into_inner();

                let (access_token_result, status_code) =
                    unified_connector_service::handle_unified_connector_service_response_for_create_access_token(
                        create_access_token_response.clone(),
                    )
                    .attach_printable("Failed to deserialize UCS response")?;

                // Update router_data with access token
                match &access_token_result {
                    Ok(access_token) => {
                        router_data.access_token = Some(access_token.clone());
                    }
                    Err(_) => {
                        // Error case - access_token remains None
                    }
                }

                router_data.response = access_token_result;
                router_data.connector_http_status_code = Some(status_code);

                Ok((router_data, create_access_token_response))
            },
        ))
        .await
        .change_context(ConnectorError::ResponseHandlingFailed)?;

        Ok(updated_router_data)
    }
}

/// Implementation of FlowGateway for AccessTokenAuth
///
/// This allows the flow to provide its specific gateway based on execution path
impl<RCD>
    payment_gateway::FlowGateway<
        SessionState,
        RCD,
        AccessTokenRequestData,
        hyperswitch_domain_models::router_data::AccessToken,
        RouterGatewayContext,
    > for domain::access_token_auth::AccessTokenAuth
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<
            Self,
            AccessTokenRequestData,
            hyperswitch_domain_models::router_data::AccessToken,
        >,
{
    fn get_gateway(
        execution_path: ExecutionPath,
    ) -> Box<
        dyn payment_gateway::PaymentGateway<
            SessionState,
            RCD,
            Self,
            AccessTokenRequestData,
            hyperswitch_domain_models::router_data::AccessToken,
            RouterGatewayContext,
        >,
    > {
        match execution_path {
            ExecutionPath::Direct => Box::new(payment_gateway::DirectGateway),
            ExecutionPath::UnifiedConnectorService
            | ExecutionPath::ShadowUnifiedConnectorService => Box::new(Self),
        }
    }
}


// File: crates/router/src/core/payments/gateway/session_token_gateway.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_enums::{CallConnectorAction, ExecutionPath};
use common_utils::{errors::CustomResult, id_type, request::Request, ucs_types};
use error_stack::ResultExt;
use hyperswitch_domain_models::{router_data::RouterData, router_flow_types as domain};
use hyperswitch_interfaces::{
    api::gateway as payment_gateway,
    connector_integration_interface::{BoxedConnectorIntegrationInterface, RouterDataConversion},
    errors::ConnectorError,
};
use unified_connector_service_client::payments as payments_grpc;

use crate::{
    core::{
        payments::gateway::context::RouterGatewayContext, unified_connector_service,
        unified_connector_service::handle_unified_connector_service_response_for_session_token_create,
    },
    routes::SessionState,
    services::logger,
    types::{self, transformers::ForeignTryFrom},
};

// =============================================================================
// PaymentGateway Implementation for domain::AuthorizeSessionToken
// =============================================================================

/// Implementation of PaymentGateway for api::PSync flow
#[async_trait]
impl<RCD>
    payment_gateway::PaymentGateway<
        SessionState,
        RCD,
        Self,
        types::AuthorizeSessionTokenData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::AuthorizeSessionToken
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::AuthorizeSessionTokenData, types::PaymentsResponseData>,
{
    async fn execute(
        self: Box<Self>,
        state: &SessionState,
        _connector_integration: BoxedConnectorIntegrationInterface<
            Self,
            RCD,
            types::AuthorizeSessionTokenData,
            types::PaymentsResponseData,
        >,
        router_data: &RouterData<
            Self,
            types::AuthorizeSessionTokenData,
            types::PaymentsResponseData,
        >,
        _call_connector_action: CallConnectorAction,
        _connector_request: Option<Request>,
        _return_raw_connector_response: Option<bool>,
        context: RouterGatewayContext,
    ) -> CustomResult<
        RouterData<Self, types::AuthorizeSessionTokenData, types::PaymentsResponseData>,
        ConnectorError,
    > {
        let merchant_connector_account = context.merchant_connector_account;
        let platform = context.platform;
        let lineage_ids = context.lineage_ids;
        let header_payload = context.header_payload;
        let unified_connector_service_execution_mode = context.execution_mode;
        let merchant_order_reference_id = header_payload.x_reference_id.clone();

        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let authorize_session_token_request =
            payments_grpc::PaymentServiceCreateSessionTokenRequest::foreign_try_from(router_data)
                .change_context(ConnectorError::RequestEncodingFailed)
                .attach_printable("Failed to construct Payment Session Create Request")?;

        let connector_auth_metadata =
            unified_connector_service::build_unified_connector_service_auth_metadata(
                merchant_connector_account,
                &platform,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct request metadata")?;
        let merchant_reference_id = header_payload
            .x_reference_id
            .clone()
            .or(merchant_order_reference_id)
            .map(|id| id_type::PaymentReferenceId::from_str(id.as_str()))
            .transpose()
            .inspect_err(|err| logger::warn!(error=?err, "Invalid Merchant ReferenceId found"))
            .ok()
            .flatten()
            .map(ucs_types::UcsReferenceId::Payment);
        let header_payload = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(merchant_reference_id)
            .lineage_ids(lineage_ids);
        let updated_router_data = Box::pin(unified_connector_service::ucs_logging_wrapper_new(
            router_data.clone(),
            state,
            authorize_session_token_request,
            header_payload,
            |mut router_data, authorize_session_token_request, grpc_headers| async move {
                let response = client
                    .payment_session_token_create(
                        authorize_session_token_request,
                        connector_auth_metadata,
                        grpc_headers,
                    )
                    .await
                    .attach_printable("Failed to get payment")?;

                let payment_session_token_response = response.into_inner();

                let (router_data_response, status_code) =
                    handle_unified_connector_service_response_for_session_token_create(
                        payment_session_token_response.clone(),
                    )
                    .attach_printable("Failed to deserialize UCS response")?;

                let router_data_response = router_data_response.map(|(response, status)| {
                    router_data.status = status;
                    response
                });
                router_data.response = router_data_response;
                router_data.connector_http_status_code = Some(status_code);

                Ok((router_data, payment_session_token_response))
            },
        ))
        .await
        .change_context(ConnectorError::ResponseHandlingFailed)?;

        Ok(updated_router_data)
    }
}

/// Implementation of FlowGateway for api::PSync
///
/// This allows the flow to provide its specific gateway based on execution path
impl<RCD>
    payment_gateway::FlowGateway<
        SessionState,
        RCD,
        types::AuthorizeSessionTokenData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::AuthorizeSessionToken
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::AuthorizeSessionTokenData, types::PaymentsResponseData>,
{
    fn get_gateway(
        execution_path: ExecutionPath,
    ) -> Box<
        dyn payment_gateway::PaymentGateway<
            SessionState,
            RCD,
            Self,
            types::AuthorizeSessionTokenData,
            types::PaymentsResponseData,
            RouterGatewayContext,
        >,
    > {
        match execution_path {
            ExecutionPath::Direct => Box::new(payment_gateway::DirectGateway),
            ExecutionPath::UnifiedConnectorService
            | ExecutionPath::ShadowUnifiedConnectorService => Box::new(Self),
        }
    }
}


// File: crates/router/src/core/payments/gateway/authorize_gateway.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_enums::{CallConnectorAction, ExecutionPath};
use common_utils::{errors::CustomResult, id_type, request::Request, ucs_types};
use error_stack::ResultExt;
use hyperswitch_domain_models::{router_data::RouterData, router_flow_types as domain};
use hyperswitch_interfaces::{
    api::gateway as payment_gateway,
    connector_integration_interface::{BoxedConnectorIntegrationInterface, RouterDataConversion},
    errors::ConnectorError,
};
use unified_connector_service_client::payments as payments_grpc;
use unified_connector_service_masking::ExposeInterface as UcsMaskingExposeInterface;

use crate::{
    core::{
        payments::gateway::context::RouterGatewayContext, unified_connector_service,
        unified_connector_service::handle_unified_connector_service_response_for_payment_authorize,
    },
    routes::SessionState,
    services::logger,
    types::{self, transformers::ForeignTryFrom, MinorUnit},
};

// =============================================================================
// PaymentGateway Implementation for domain::Authorize
// =============================================================================

/// Implementation of PaymentGateway for api::PSync flow
#[async_trait]
impl<RCD>
    payment_gateway::PaymentGateway<
        SessionState,
        RCD,
        Self,
        types::PaymentsAuthorizeData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::Authorize
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
{
    async fn execute(
        self: Box<Self>,
        state: &SessionState,
        _connector_integration: BoxedConnectorIntegrationInterface<
            Self,
            RCD,
            types::PaymentsAuthorizeData,
            types::PaymentsResponseData,
        >,
        router_data: &RouterData<Self, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
        call_connector_action: CallConnectorAction,
        _connector_request: Option<Request>,
        _return_raw_connector_response: Option<bool>,
        context: RouterGatewayContext,
    ) -> CustomResult<
        RouterData<Self, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
        ConnectorError,
    > {
        let merchant_connector_account = context.merchant_connector_account;
        let platform = context.platform;
        let lineage_ids = context.lineage_ids;
        let header_payload = context.header_payload;
        let unified_connector_service_execution_mode = context.execution_mode;
        let merchant_order_reference_id = header_payload.x_reference_id.clone();
        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let granular_authorize_request =
            payments_grpc::PaymentServiceAuthorizeOnlyRequest::foreign_try_from((
                router_data,
                call_connector_action,
            ))
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct Payment Get Request")?;

        let connector_auth_metadata =
            unified_connector_service::build_unified_connector_service_auth_metadata(
                merchant_connector_account,
                &platform,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct request metadata")?;
        let merchant_reference_id = header_payload
            .x_reference_id
            .clone()
            .or(merchant_order_reference_id)
            .map(|id| id_type::PaymentReferenceId::from_str(id.as_str()))
            .transpose()
            .inspect_err(|err| logger::warn!(error=?err, "Invalid Merchant ReferenceId found"))
            .ok()
            .flatten()
            .map(ucs_types::UcsReferenceId::Payment);
        let header_payload = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(merchant_reference_id)
            .lineage_ids(lineage_ids);
        let updated_router_data = Box::pin(unified_connector_service::ucs_logging_wrapper_new(
            router_data.clone(),
            state,
            granular_authorize_request,
            header_payload,
            |mut router_data, granular_authorize_request, grpc_headers| async move {
                let response = Box::pin(client.payment_authorize_granular(
                    granular_authorize_request,
                    connector_auth_metadata,
                    grpc_headers,
                ))
                .await
                .attach_printable("Failed to get payment")?;

                let payment_authorize_response = response.into_inner();

                let ucs_data = handle_unified_connector_service_response_for_payment_authorize(
                    payment_authorize_response.clone(),
                )
                .attach_printable("Failed to deserialize UCS response")?;

                let router_data_response =
                    ucs_data.router_data_response.map(|(response, status)| {
                        router_data.status = status;
                        response
                    });
                router_data.response = router_data_response;
                router_data.amount_captured = payment_authorize_response.captured_amount;
                router_data.minor_amount_captured = payment_authorize_response
                    .minor_captured_amount
                    .map(MinorUnit::new);
                router_data.raw_connector_response = payment_authorize_response
                    .raw_connector_response
                    .clone()
                    .map(|raw_connector_response| raw_connector_response.expose().into());
                router_data.connector_http_status_code = Some(ucs_data.status_code);

                // Populate connector_customer_id if present
                ucs_data.connector_customer_id.map(|connector_customer_id| {
                    router_data.connector_customer = Some(connector_customer_id);
                });

                ucs_data.connector_response.map(|customer_response| {
                    router_data.connector_response = Some(customer_response);
                });

                Ok((router_data, payment_authorize_response))
            },
        ))
        .await
        .change_context(ConnectorError::ResponseHandlingFailed)?;

        Ok(updated_router_data)
    }
}

/// Implementation of FlowGateway for api::PSync
///
/// This allows the flow to provide its specific gateway based on execution path
impl<RCD>
    payment_gateway::FlowGateway<
        SessionState,
        RCD,
        types::PaymentsAuthorizeData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::Authorize
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
{
    fn get_gateway(
        execution_path: ExecutionPath,
    ) -> Box<
        dyn payment_gateway::PaymentGateway<
            SessionState,
            RCD,
            Self,
            types::PaymentsAuthorizeData,
            types::PaymentsResponseData,
            RouterGatewayContext,
        >,
    > {
        match execution_path {
            ExecutionPath::Direct => Box::new(payment_gateway::DirectGateway),
            ExecutionPath::UnifiedConnectorService
            | ExecutionPath::ShadowUnifiedConnectorService => Box::new(Self),
        }
    }
}


// File: crates/router/src/core/payments/gateway/payment_method_token_create_gateway.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_enums::{CallConnectorAction, ExecutionPath};
use common_utils::{errors::CustomResult, id_type, request::Request, ucs_types};
use error_stack::ResultExt;
use hyperswitch_domain_models::{router_data::RouterData, router_flow_types as domain};
use hyperswitch_interfaces::{
    api::gateway as payment_gateway,
    connector_integration_interface::{BoxedConnectorIntegrationInterface, RouterDataConversion},
    errors::ConnectorError,
};
use unified_connector_service_client::payments as payments_grpc;

use crate::{
    core::{
        payments::gateway::context::RouterGatewayContext, unified_connector_service,
        unified_connector_service::handle_unified_connector_service_response_for_payment_method_token_create,
    },
    routes::SessionState,
    services::logger,
    types::{self, transformers::ForeignTryFrom},
};

// =============================================================================
// PaymentGateway Implementation for domain::PaymentMethodToken
// =============================================================================

/// Implementation of PaymentGateway for api::PaymentMethodToken flow
#[async_trait]
impl<RCD>
    payment_gateway::PaymentGateway<
        SessionState,
        RCD,
        Self,
        types::PaymentMethodTokenizationData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::PaymentMethodToken
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<
            Self,
            types::PaymentMethodTokenizationData,
            types::PaymentsResponseData,
        >,
{
    async fn execute(
        self: Box<Self>,
        state: &SessionState,
        _connector_integration: BoxedConnectorIntegrationInterface<
            Self,
            RCD,
            types::PaymentMethodTokenizationData,
            types::PaymentsResponseData,
        >,
        router_data: &RouterData<
            Self,
            types::PaymentMethodTokenizationData,
            types::PaymentsResponseData,
        >,
        _call_connector_action: CallConnectorAction,
        _connector_request: Option<Request>,
        _return_raw_connector_response: Option<bool>,
        context: RouterGatewayContext,
    ) -> CustomResult<
        RouterData<Self, types::PaymentMethodTokenizationData, types::PaymentsResponseData>,
        ConnectorError,
    > {
        let merchant_connector_account = context.merchant_connector_account;
        let platform = context.platform;
        let lineage_ids = context.lineage_ids;
        let header_payload = context.header_payload;
        let unified_connector_service_execution_mode = context.execution_mode;
        let merchant_order_reference_id = header_payload.x_reference_id.clone();
        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let pm_token_create_request =
            payments_grpc::PaymentServiceCreatePaymentMethodTokenRequest::foreign_try_from(
                router_data,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct Payment Get Request")?;

        let connector_auth_metadata =
            unified_connector_service::build_unified_connector_service_auth_metadata(
                merchant_connector_account,
                &platform,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct request metadata")?;
        let merchant_reference_id = header_payload
            .x_reference_id
            .clone()
            .or(merchant_order_reference_id)
            .map(|id| id_type::PaymentReferenceId::from_str(id.as_str()))
            .transpose()
            .inspect_err(|err| logger::warn!(error=?err, "Invalid Merchant ReferenceId found"))
            .ok()
            .flatten()
            .map(ucs_types::UcsReferenceId::Payment);
        let header_payload = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(merchant_reference_id)
            .lineage_ids(lineage_ids);
        let updated_router_data = Box::pin(unified_connector_service::ucs_logging_wrapper_new(
            router_data.clone(),
            state,
            pm_token_create_request,
            header_payload,
            |mut router_data, pm_token_create_request, grpc_headers| async move {
                let response = Box::pin(client.payment_method_token_create(
                    pm_token_create_request,
                    connector_auth_metadata,
                    grpc_headers,
                ))
                .await
                .attach_printable("Failed to get payment")?;

                let pm_token_create_response = response.into_inner();

                let (router_data_response, status_code) =
                    handle_unified_connector_service_response_for_payment_method_token_create(
                        pm_token_create_response.clone(),
                    )
                    .attach_printable("Failed to deserialize UCS response")?;

                let router_data_response = router_data_response.map(|(response, status)| {
                    router_data.status = status;
                    response
                });
                router_data.response = router_data_response;
                router_data.payment_method_token = Some(
                    hyperswitch_domain_models::router_data::PaymentMethodToken::Token(
                        masking::Secret::new(pm_token_create_response.payment_method_token.clone()),
                    ),
                );
                router_data.connector_http_status_code = Some(status_code);

                Ok((router_data, pm_token_create_response))
            },
        ))
        .await
        .change_context(ConnectorError::ResponseHandlingFailed)?;

        Ok(updated_router_data)
    }
}

/// Implementation of FlowGateway for api::PaymentMethodToken
///
/// This allows the flow to provide its specific gateway based on execution path
impl<RCD>
    payment_gateway::FlowGateway<
        SessionState,
        RCD,
        types::PaymentMethodTokenizationData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::PaymentMethodToken
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<
            Self,
            types::PaymentMethodTokenizationData,
            types::PaymentsResponseData,
        >,
{
    fn get_gateway(
        execution_path: ExecutionPath,
    ) -> Box<
        dyn payment_gateway::PaymentGateway<
            SessionState,
            RCD,
            Self,
            types::PaymentMethodTokenizationData,
            types::PaymentsResponseData,
            RouterGatewayContext,
        >,
    > {
        match execution_path {
            ExecutionPath::Direct => Box::new(payment_gateway::DirectGateway),
            ExecutionPath::UnifiedConnectorService
            | ExecutionPath::ShadowUnifiedConnectorService => Box::new(Self),
        }
    }
}


// File: crates/router/src/core/payments/gateway/create_order_gateway.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_enums::{CallConnectorAction, ExecutionPath};
use common_utils::{errors::CustomResult, id_type, request::Request, ucs_types};
use error_stack::ResultExt;
use hyperswitch_domain_models::{router_data::RouterData, router_flow_types as domain};
use hyperswitch_interfaces::{
    api::gateway as payment_gateway,
    connector_integration_interface::{BoxedConnectorIntegrationInterface, RouterDataConversion},
    errors::ConnectorError,
};
use unified_connector_service_client::payments as payments_grpc;

use crate::{
    core::{
        payments::gateway::context::RouterGatewayContext,
        unified_connector_service::{
            self, handle_unified_connector_service_response_for_create_order,
        },
    },
    routes::SessionState,
    services::logger,
    types::{self, transformers::ForeignTryFrom},
};

// =============================================================================
// PaymentGateway Implementation for domain::CreateOrder
// =============================================================================

/// Implementation of PaymentGateway for api::CreateOrder flow
#[async_trait]
impl<RCD>
    payment_gateway::PaymentGateway<
        SessionState,
        RCD,
        Self,
        types::CreateOrderRequestData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::CreateOrder
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::CreateOrderRequestData, types::PaymentsResponseData>,
{
    async fn execute(
        self: Box<Self>,
        state: &SessionState,
        _connector_integration: BoxedConnectorIntegrationInterface<
            Self,
            RCD,
            types::CreateOrderRequestData,
            types::PaymentsResponseData,
        >,
        router_data: &RouterData<Self, types::CreateOrderRequestData, types::PaymentsResponseData>,
        _call_connector_action: CallConnectorAction,
        _connector_request: Option<Request>,
        _return_raw_connector_response: Option<bool>,
        context: RouterGatewayContext,
    ) -> CustomResult<
        RouterData<Self, types::CreateOrderRequestData, types::PaymentsResponseData>,
        ConnectorError,
    > {
        let merchant_connector_account = context.merchant_connector_account;
        let platform = context.platform;
        let lineage_ids = context.lineage_ids;
        let header_payload = context.header_payload;
        let unified_connector_service_execution_mode = context.execution_mode;
        let merchant_order_reference_id = header_payload.x_reference_id.clone();
        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let create_order_request =
            payments_grpc::PaymentServiceCreateOrderRequest::foreign_try_from(router_data)
                .change_context(ConnectorError::RequestEncodingFailed)
                .attach_printable("Failed to construct Payment Create Order Request")?;

        let connector_auth_metadata =
            unified_connector_service::build_unified_connector_service_auth_metadata(
                merchant_connector_account,
                &platform,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct request metadata")?;
        let merchant_reference_id = header_payload
            .x_reference_id
            .clone()
            .or(merchant_order_reference_id)
            .map(|id| id_type::PaymentReferenceId::from_str(id.as_str()))
            .transpose()
            .inspect_err(|err| logger::warn!(error=?err, "Invalid Merchant ReferenceId found"))
            .ok()
            .flatten()
            .map(ucs_types::UcsReferenceId::Payment);
        let header_payload = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(merchant_reference_id)
            .lineage_ids(lineage_ids);
        let updated_router_data = Box::pin(unified_connector_service::ucs_logging_wrapper_new(
            router_data.clone(),
            state,
            create_order_request,
            header_payload,
            |mut router_data, create_order_request, grpc_headers| async move {
                let response = Box::pin(client.payment_create_order(
                    create_order_request,
                    connector_auth_metadata,
                    grpc_headers,
                ))
                .await
                .attach_printable("Failed to create order")?;

                let create_order_response = response.into_inner();

                let (router_data_response, _status_code) =
                    handle_unified_connector_service_response_for_create_order(
                        create_order_response.clone(),
                    )
                    .attach_printable("Failed to deserialize UCS response")?;

                let router_data_response = router_data_response.map(|(response, status)| {
                    router_data.status = status;
                    response
                });
                router_data.response = router_data_response;
                Ok((router_data, create_order_response))
            },
        ))
        .await
        .change_context(ConnectorError::ResponseHandlingFailed)?;

        Ok(updated_router_data)
    }
}

/// Implementation of FlowGateway for api::CreateOrder
///
/// This allows the flow to provide its specific gateway based on execution path
impl<RCD>
    payment_gateway::FlowGateway<
        SessionState,
        RCD,
        types::CreateOrderRequestData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::CreateOrder
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, types::CreateOrderRequestData, types::PaymentsResponseData>,
{
    fn get_gateway(
        execution_path: ExecutionPath,
    ) -> Box<
        dyn payment_gateway::PaymentGateway<
            SessionState,
            RCD,
            Self,
            types::CreateOrderRequestData,
            types::PaymentsResponseData,
            RouterGatewayContext,
        >,
    > {
        match execution_path {
            ExecutionPath::Direct => Box::new(payment_gateway::DirectGateway),
            ExecutionPath::UnifiedConnectorService
            | ExecutionPath::ShadowUnifiedConnectorService => Box::new(Self),
        }
    }
}


// File: crates/router/src/core/payments/gateway/create_customer_gateway.rs

use std::str::FromStr;

use async_trait::async_trait;
use common_enums::{CallConnectorAction, ExecutionPath};
use common_utils::{errors::CustomResult, request::Request};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::RouterData, router_flow_types as domain,
    router_request_types::ConnectorCustomerData,
};
use hyperswitch_interfaces::{
    api::gateway as payment_gateway,
    connector_integration_interface::{BoxedConnectorIntegrationInterface, RouterDataConversion},
    errors::ConnectorError,
};
use unified_connector_service_client::payments as payments_grpc;

use crate::{
    core::{
        payments::gateway::context::RouterGatewayContext, unified_connector_service,
        unified_connector_service::handle_unified_connector_service_response_for_create_connector_customer,
    },
    routes::SessionState,
    types::{self, transformers::ForeignTryFrom},
};

// =============================================================================
// PaymentGateway Implementation for domain::CreateConnectorCustomer
// =============================================================================

/// Implementation of PaymentGateway for CreateConnectorCustomer flow
#[async_trait]
impl<RCD>
    payment_gateway::PaymentGateway<
        SessionState,
        RCD,
        Self,
        ConnectorCustomerData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::CreateConnectorCustomer
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, ConnectorCustomerData, types::PaymentsResponseData>,
{
    async fn execute(
        self: Box<Self>,
        state: &SessionState,
        _connector_integration: BoxedConnectorIntegrationInterface<
            Self,
            RCD,
            ConnectorCustomerData,
            types::PaymentsResponseData,
        >,
        router_data: &RouterData<Self, ConnectorCustomerData, types::PaymentsResponseData>,
        call_connector_action: CallConnectorAction,
        _connector_request: Option<Request>,
        _return_raw_connector_response: Option<bool>,
        context: RouterGatewayContext,
    ) -> CustomResult<
        RouterData<Self, ConnectorCustomerData, types::PaymentsResponseData>,
        ConnectorError,
    > {
        let connector_name = router_data.connector.clone();
        let _connector_enum = common_enums::connector_enums::Connector::from_str(&connector_name)
            .change_context(ConnectorError::InvalidConnectorName)?;
        let merchant_connector_account = context.merchant_connector_account;
        let platform = context.platform;
        let lineage_ids = context.lineage_ids;
        let unified_connector_service_execution_mode = context.execution_mode;

        let client = state
            .grpc_client
            .unified_connector_service_client
            .clone()
            .ok_or(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to fetch Unified Connector Service client")?;

        let create_connector_customer_request =
            payments_grpc::PaymentServiceCreateConnectorCustomerRequest::foreign_try_from((
                router_data,
                call_connector_action,
            ))
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct Create Connector Customer Request")?;

        let connector_auth_metadata =
            unified_connector_service::build_unified_connector_service_auth_metadata(
                merchant_connector_account,
                &platform,
            )
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to construct request metadata")?;

        let grpc_headers = state
            .get_grpc_headers_ucs(unified_connector_service_execution_mode)
            .external_vault_proxy_metadata(None)
            .merchant_reference_id(None)
            .lineage_ids(lineage_ids);
        let updated_router_data = Box::pin(unified_connector_service::ucs_logging_wrapper_new(
            router_data.clone(),
            state,
            create_connector_customer_request,
            grpc_headers,
            |mut router_data, create_connector_customer_request, grpc_headers| async move {
                let response = Box::pin(client.create_connector_customer(
                    create_connector_customer_request,
                    connector_auth_metadata,
                    grpc_headers,
                ))
                .await
                .attach_printable("Failed to create connector customer")?;

                let create_connector_customer_response = response.into_inner();

                let (connector_customer_result, status_code) =
                    handle_unified_connector_service_response_for_create_connector_customer(
                        create_connector_customer_response.clone(),
                    )
                    .attach_printable("Failed to deserialize UCS response")?;

                router_data.response = connector_customer_result;
                router_data.connector_http_status_code = Some(status_code);

                Ok((router_data, create_connector_customer_response))
            },
        ))
        .await
        .change_context(ConnectorError::ResponseHandlingFailed)?;

        Ok(updated_router_data)
    }
}

/// Implementation of FlowGateway for CreateConnectorCustomer
///
/// This allows the flow to provide its specific gateway based on execution path
impl<RCD>
    payment_gateway::FlowGateway<
        SessionState,
        RCD,
        ConnectorCustomerData,
        types::PaymentsResponseData,
        RouterGatewayContext,
    > for domain::CreateConnectorCustomer
where
    RCD: Clone
        + Send
        + Sync
        + 'static
        + RouterDataConversion<Self, ConnectorCustomerData, types::PaymentsResponseData>,
{
    fn get_gateway(
        execution_path: ExecutionPath,
    ) -> Box<
        dyn payment_gateway::PaymentGateway<
            SessionState,
            RCD,
            Self,
            ConnectorCustomerData,
            types::PaymentsResponseData,
            RouterGatewayContext,
        >,
    > {
        match execution_path {
            ExecutionPath::Direct => Box::new(payment_gateway::DirectGateway),
            ExecutionPath::UnifiedConnectorService
            | ExecutionPath::ShadowUnifiedConnectorService => Box::new(Self),
        }
    }
}


// File: crates/router/src/core/payments/gateway/context.rs

//! Gateway execution context for router crate
//!
//! This module defines the RouterGatewayContext type, which contains all the
//! information needed for executing payment operations through either direct
//! connector integration or Unified Connector Service (UCS).

use common_enums::{ExecutionMode, ExecutionPath, GatewaySystem};
use external_services::grpc_client::LineageIds;
use hyperswitch_domain_models::{payments::HeaderPayload, platform::Platform};
use hyperswitch_interfaces::api::gateway::GatewayContext;

use crate::core::payments::helpers;

/// Router's gateway execution context
///
/// This is the router crate's implementation of gateway context. It contains
/// all the information needed for both direct connector execution and UCS execution.
#[derive(Clone, Debug)]
pub struct RouterGatewayContext {
    pub creds_identifier: Option<String>,
    /// Merchant context (merchant_id, profile_id, etc.)
    pub platform: Platform,

    /// Header payload (x-reference-id, etc.)
    pub header_payload: HeaderPayload,

    /// Lineage IDs for distributed tracing
    pub lineage_ids: LineageIds,

    /// Merchant connector account details
    #[cfg(feature = "v1")]
    pub merchant_connector_account: helpers::MerchantConnectorAccountType,

    /// Merchant connector account details (v2)
    #[cfg(feature = "v2")]
    pub merchant_connector_account:
        hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccountTypeDetails,

    /// Execution mode (Primary, Shadow, etc.)
    pub execution_mode: ExecutionMode,

    /// Execution path (Direct, UCS, or Shadow)
    pub execution_path: ExecutionPath,
}

impl RouterGatewayContext {
    /// Create a new router gateway context
    pub fn new(
        platform: Platform,
        header_payload: HeaderPayload,
        lineage_ids: LineageIds,
        #[cfg(feature = "v1")] merchant_connector_account: helpers::MerchantConnectorAccountType,
        #[cfg(feature = "v2")]
        merchant_connector_account: hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccountTypeDetails,
        execution_mode: ExecutionMode,
        execution_path: ExecutionPath,
        creds_identifier: Option<String>,
    ) -> Self {
        Self {
            platform,
            header_payload,
            lineage_ids,
            merchant_connector_account,
            execution_mode,
            execution_path,
            creds_identifier,
        }
    }
}

/// Implementation of GatewayContext trait for RouterGatewayContext
///
/// This allows the framework to extract execution metadata without knowing
/// the concrete structure of RouterGatewayContext.
impl GatewayContext for RouterGatewayContext {
    fn execution_path(&self) -> ExecutionPath {
        self.execution_path
    }

    /// Get the execution mode (Primary, Shadow, etc.)
    fn execution_mode(&self) -> ExecutionMode {
        self.execution_mode
    }
}
impl RouterGatewayContext {
    /// Get the gateway system (Direct, UnifiedConnectorService, etc.)
    pub fn get_gateway_system(&self) -> GatewaySystem {
        match self.execution_path {
            ExecutionPath::Direct => GatewaySystem::Direct,
            ExecutionPath::UnifiedConnectorService => GatewaySystem::UnifiedConnectorService,
            ExecutionPath::ShadowUnifiedConnectorService => GatewaySystem::Direct,
        }
    }
}


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

pub mod fraud_check_post;
pub mod fraud_check_pre;
use async_trait::async_trait;
use common_enums::FrmSuggestion;
use error_stack::{report, ResultExt};

pub use self::{fraud_check_post::FraudCheckPost, fraud_check_pre::FraudCheckPre};
use super::{
    types::{ConnectorDetailsCore, FrmConfigsObject, PaymentToFrmData},
    FrmData,
};
use crate::{
    core::errors::{self, RouterResult},
    routes::{app::ReqState, SessionState},
    types::{domain, fraud_check::FrmRouterData},
};

pub type BoxedFraudCheckOperation<F, D> = Box<dyn FraudCheckOperation<F, D> + Send + Sync>;

pub trait FraudCheckOperation<F, D>: Send + std::fmt::Debug {
    fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<PaymentToFrmData> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("get tracker interface not found for {self:?}"))
    }
    fn to_domain(&self) -> RouterResult<&dyn Domain<F, D>> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("domain interface not found for {self:?}"))
    }
    fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<FrmData, F, D> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("get tracker interface not found for {self:?}"))
    }
}

#[async_trait]
pub trait GetTracker<D>: Send {
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: D,
        frm_connector_details: ConnectorDetailsCore,
    ) -> RouterResult<Option<FrmData>>;
}

#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait Domain<F, D>: Send + Sync {
    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>>
    where
        F: Send + Clone;

    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>
    where
        F: Send + Clone;

    // To execute several tasks conditionally based on the result of post_flow.
    // Eg: If the /sale(post flow) is returning the transaction as fraud we can execute refund in post task
    #[allow(clippy::too_many_arguments)]
    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>>
    where
        F: Send + Clone,
    {
        return Ok(Some(frm_data.to_owned()));
    }
}

#[async_trait]
pub trait UpdateTracker<Fd, F: Clone, D>: Send {
    async fn update_tracker<'b>(
        &'b self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        frm_data: Fd,
        payment_data: &mut D,
        _frm_suggestion: Option<FrmSuggestion>,
        frm_router_data: FrmRouterData,
    ) -> RouterResult<Fd>;
}


// File: crates/router/src/core/fraud_check/types.rs

use api_models::{
    enums as api_enums,
    enums::{PaymentMethod, PaymentMethodType},
    payments::Amount,
    refunds::RefundResponse,
};
use common_enums::FrmSuggestion;
use common_utils::pii::SecretSerdeValue;
use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent};
pub use hyperswitch_domain_models::{
    router_request_types::fraud_check::{
        Address, Destination, FrmFulfillmentRequest, FulfillmentStatus, Fulfillments, Product,
    },
    types::OrderDetailsWithAmount,
};
use masking::Serialize;
use serde::Deserialize;
use utoipa::ToSchema;

use super::operation::BoxedFraudCheckOperation;
use crate::types::{
    domain::MerchantAccount,
    storage::{enums as storage_enums, fraud_check::FraudCheck},
    PaymentAddress,
};

#[derive(Clone, Default, Debug)]
pub struct PaymentIntentCore {
    pub payment_id: common_utils::id_type::PaymentId,
}

#[derive(Clone, Debug)]
pub struct PaymentAttemptCore {
    pub attempt_id: String,
    pub payment_details: Option<PaymentDetails>,
    pub amount: Amount,
}

#[derive(Clone, Debug, Serialize)]
pub struct PaymentDetails {
    pub amount: i64,
    pub currency: Option<storage_enums::Currency>,
    pub payment_method: Option<PaymentMethod>,
    pub payment_method_type: Option<PaymentMethodType>,
    pub refund_transaction_id: Option<String>,
}
#[derive(Clone, Default, Debug)]
pub struct FrmMerchantAccount {
    pub merchant_id: common_utils::id_type::MerchantId,
}

#[derive(Clone, Debug)]
pub struct FrmData {
    pub payment_intent: PaymentIntent,
    pub payment_attempt: PaymentAttempt,
    pub merchant_account: MerchantAccount,
    pub fraud_check: FraudCheck,
    pub address: PaymentAddress,
    pub connector_details: ConnectorDetailsCore,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub refund: Option<RefundResponse>,
    pub frm_metadata: Option<SecretSerdeValue>,
}

#[derive(Debug)]
pub struct FrmInfo<F, D> {
    pub fraud_check_operation: BoxedFraudCheckOperation<F, D>,
    pub frm_data: Option<FrmData>,
    pub suggested_action: Option<FrmSuggestion>,
}

#[derive(Clone, Debug)]
pub struct ConnectorDetailsCore {
    pub connector_name: String,
    pub profile_id: common_utils::id_type::ProfileId,
}
#[derive(Clone)]
pub struct PaymentToFrmData {
    pub amount: Amount,
    pub payment_intent: PaymentIntent,
    pub payment_attempt: PaymentAttempt,
    pub merchant_account: MerchantAccount,
    pub address: PaymentAddress,
    pub connector_details: ConnectorDetailsCore,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub frm_metadata: Option<SecretSerdeValue>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrmConfigsObject {
    pub frm_enabled_pm: Option<PaymentMethod>,
    pub frm_enabled_gateway: Option<api_models::enums::Connector>,
    pub frm_preferred_flow_type: api_enums::FrmPreferredFlowTypes,
}

#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "camelCase")]
pub struct FrmFulfillmentSignifydApiRequest {
    ///unique order_id for the order_details in the transaction
    #[schema(max_length = 255, example = "pay_qiYfHcDou1ycIaxVXKHF")]
    pub order_id: String,
    ///denotes the status of the fulfillment... can be one of PARTIAL, COMPLETE, REPLACEMENT, CANCELED
    #[schema(value_type = Option<FulfillmentStatus>, example = "COMPLETE")]
    pub fulfillment_status: Option<FulfillmentStatus>,
    ///contains details of the fulfillment
    #[schema(value_type = Vec<Fulfillments>)]
    pub fulfillments: Vec<Fulfillments>,
}

#[derive(Debug, ToSchema, Clone, Serialize)]
pub struct FrmFulfillmentResponse {
    ///unique order_id for the transaction
    #[schema(max_length = 255, example = "pay_qiYfHcDou1ycIaxVXKHF")]
    pub order_id: String,
    ///shipment_ids used in the fulfillment overall...also data from previous fulfillments for the same transactions/order is sent
    #[schema(example = r#"["ship_101", "ship_102"]"#)]
    pub shipment_ids: Vec<String>,
}

#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "camelCase")]
pub struct FrmFulfillmentSignifydApiResponse {
    ///unique order_id for the transaction
    #[schema(max_length = 255, example = "pay_qiYfHcDou1ycIaxVXKHF")]
    pub order_id: String,
    ///shipment_ids used in the fulfillment overall...also data from previous fulfillments for the same transactions/order is sent
    #[schema(example = r#"["ship_101","ship_102"]"#)]
    pub shipment_ids: Vec<String>,
}

pub const CANCEL_INITIATED: &str = "Cancel Initiated with the processor";


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

pub mod checkout_flow;
pub mod fulfillment_flow;
pub mod record_return;
pub mod sale_flow;
pub mod transaction_flow;

use async_trait::async_trait;

use crate::{
    core::{
        errors::RouterResult,
        payments::{self, flows::ConstructFlowSpecificData},
    },
    routes::SessionState,
    services,
    types::{
        api::{Connector, FraudCheckConnectorData},
        domain,
        fraud_check::FraudCheckResponseData,
    },
};

#[async_trait]
pub trait FeatureFrm<F, T> {
    async fn decide_frm_flows<'a>(
        self,
        state: &SessionState,
        connector: &FraudCheckConnectorData,
        call_connector_action: payments::CallConnectorAction,
        platform: &domain::Platform,
    ) -> RouterResult<Self>
    where
        Self: Sized,
        F: Clone,
        dyn Connector: services::ConnectorIntegration<F, T, FraudCheckResponseData>;
}


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

use async_trait::async_trait;
use common_enums::FrmSuggestion;
use common_utils::ext_traits::Encode;
use diesel_models::enums::FraudCheckLastStep;
use router_env::{instrument, tracing};
use uuid::Uuid;

use super::{Domain, FraudCheckOperation, GetTracker, UpdateTracker};
use crate::{
    core::{
        errors::RouterResult,
        fraud_check::{
            self as frm_core,
            types::{FrmData, PaymentDetails, PaymentToFrmData},
            ConnectorDetailsCore,
        },
        payments,
    },
    errors,
    routes::app::ReqState,
    types::{
        api::fraud_check as frm_api,
        domain,
        fraud_check::{
            FraudCheckCheckoutData, FraudCheckResponseData, FraudCheckTransactionData, FrmRequest,
            FrmResponse, FrmRouterData,
        },
        storage::{
            enums::{FraudCheckStatus, FraudCheckType},
            fraud_check::{FraudCheckNew, FraudCheckUpdate},
        },
        ResponseId,
    },
    SessionState,
};

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

impl<F, D> FraudCheckOperation<F, D> for &FraudCheckPre
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<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 FraudCheckPre
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<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 FraudCheckPre {
    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    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: Uuid::new_v4().simple().to_string(),
                    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::PreFrm,
                    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 FraudCheckPre
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    #[cfg(feature = "v2")]
    #[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>> {
        todo!()
    }

    #[cfg(feature = "v1")]
    #[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>> {
        let router_data = frm_core::call_frm_service::<F, frm_api::Transaction, _, D>(
            state,
            payment_data,
            &mut frm_data.to_owned(),
            platform,
            customer,
        )
        .await?;
        frm_data.fraud_check.last_step = FraudCheckLastStep::TransactionOrRecordRefund;
        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::Transaction(FraudCheckTransactionData {
                amount: router_data.request.amount,
                order_details: router_data.request.order_details,
                currency: router_data.request.currency,
                payment_method: Some(router_data.payment_method),
                error_code: router_data.request.error_code,
                error_message: router_data.request.error_message,
                connector_transaction_id: router_data.request.connector_transaction_id,
                connector: router_data.request.connector,
            }),
            response: FrmResponse::Transaction(router_data.response),
        }))
    }

    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::Checkout, _, D>(
            state,
            payment_data,
            &mut frm_data.to_owned(),
            platform,
            customer,
        )
        .await?;
        frm_data.fraud_check.last_step = FraudCheckLastStep::CheckoutOrSale;
        Ok(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::Checkout(Box::new(FraudCheckCheckoutData {
                amount: router_data.request.amount,
                order_details: router_data.request.order_details,
                currency: router_data.request.currency,
                browser_info: router_data.request.browser_info,
                payment_method_data: router_data.request.payment_method_data,
                email: router_data.request.email,
                gateway: router_data.request.gateway,
            })),
            response: FrmResponse::Checkout(router_data.response),
        })
    }
}

#[async_trait]
impl<F, D> UpdateTracker<FrmData, F, D> for FraudCheckPre
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    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 frm_check_update = match frm_router_data.response {
            FrmResponse::Checkout(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: _,
                    } => Some(FraudCheckUpdate::ErrorUpdate {
                        status: FraudCheckStatus::TransactionFailure,
                        error_message: Some(Some(
                            "Error: Got Record Return Response response in current Checkout flow"
                                .to_string(),
                        )),
                    }),
                },
            },
            FrmResponse::Transaction(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 frm_status = payment_data
                            .get_frm_message()
                            .as_ref()
                            .map_or(status, |frm_data| frm_data.frm_status);

                        let fraud_check_update = FraudCheckUpdate::ResponseUpdate {
                            frm_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: None,
                        };
                        Some(fraud_check_update)
                    }
                    FraudCheckResponseData::FulfillmentResponse {
                        order_id: _,
                        shipment_ids: _,
                    } => None,
                    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 Checkout flow"
                                .to_string(),
                        )),
                    }),
                },
            },
            FrmResponse::Sale(_response)
            | FrmResponse::Fulfillment(_response)
            | FrmResponse::RecordReturn(_response) => Some(FraudCheckUpdate::ErrorUpdate {
                status: FraudCheckStatus::TransactionFailure,
                error_message: Some(Some(
                    "Error: Got Pre(Sale) flow response in current post flow".to_string(),
                )),
            }),
        };

        let db = &*state.store;
        frm_data.fraud_check = match frm_check_update {
            Some(fraud_check_update) => db
                .update_fraud_check_response_with_attempt_id(
                    frm_data.clone().fraud_check,
                    fraud_check_update,
                )
                .await
                .map_err(|error| error.change_context(errors::ApiErrorResponse::PaymentNotFound))?,
            None => frm_data.clone().fraud_check,
        };

        Ok(frm_data)
    }
}