// File: crates/hyperswitch_interfaces/src/conversion_impls.rs

use common_utils::{errors::CustomResult, id_type};
use error_stack::ResultExt;
#[cfg(feature = "frm")]
use hyperswitch_domain_models::router_data_v2::flow_common_types::FrmFlowData;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::router_data_v2::flow_common_types::PayoutFlowData;
use hyperswitch_domain_models::{
    payment_address::PaymentAddress,
    router_data::{self, RouterData},
    router_data_v2::{
        flow_common_types::{
            AccessTokenFlowData, AuthenticationTokenFlowData, BillingConnectorInvoiceSyncFlowData,
            BillingConnectorPaymentsSyncFlowData, DisputesFlowData, ExternalAuthenticationFlowData,
            ExternalVaultProxyFlowData, FilesFlowData, GetSubscriptionEstimateData,
            GetSubscriptionPlanPricesData, GetSubscriptionPlansData, GiftCardBalanceCheckFlowData,
            InvoiceRecordBackData, MandateRevokeFlowData, PaymentFlowData, RefundFlowData,
            SubscriptionCancelData, SubscriptionCreateData, SubscriptionCustomerData,
            SubscriptionPauseData, SubscriptionResumeData, UasFlowData, VaultConnectorFlowData,
            WebhookSourceVerifyData,
        },
        RouterDataV2,
    },
};

use crate::{connector_integration_interface::RouterDataConversion, errors::ConnectorError};

fn get_irrelevant_id_string(id_name: &str, flow_name: &str) -> String {
    format!("irrelevant {id_name} in {flow_name} flow")
}
fn get_default_router_data<F, Req, Resp>(
    tenant_id: id_type::TenantId,
    flow_name: &str,
    request: Req,
    response: Result<Resp, router_data::ErrorResponse>,
) -> RouterData<F, Req, Resp> {
    RouterData {
        tenant_id,
        flow: std::marker::PhantomData,
        merchant_id: id_type::MerchantId::get_irrelevant_merchant_id(),
        customer_id: None,
        connector_customer: None,
        connector: get_irrelevant_id_string("connector", flow_name),
        payment_id: id_type::PaymentId::get_irrelevant_id(flow_name)
            .get_string_repr()
            .to_owned(),
        attempt_id: get_irrelevant_id_string("attempt_id", flow_name),
        status: common_enums::AttemptStatus::default(),
        payment_method: common_enums::PaymentMethod::default(),
        connector_auth_type: router_data::ConnectorAuthType::default(),
        description: None,
        address: PaymentAddress::default(),
        auth_type: common_enums::AuthenticationType::default(),
        connector_meta_data: None,
        connector_wallets_details: None,
        amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_token: None,
        recurring_mandate_payment_data: None,
        preprocessing_id: None,
        payment_method_balance: None,
        connector_api_version: None,
        request,
        response,
        connector_request_reference_id: get_irrelevant_id_string(
            "connector_request_reference_id",
            flow_name,
        ),
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        test_mode: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        dispute_id: None,
        refund_id: None,
        connector_response: None,
        payment_method_status: None,
        minor_amount_captured: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload: None,
        connector_mandate_request_reference_id: None,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        payment_method_type: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
        authorized_amount: None,
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for AuthenticationTokenFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {} = new_router_data.resource_common_data;
        let request = new_router_data.request.clone();
        let response = new_router_data.response.clone();
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "authentication token",
            request,
            response,
        );
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for AccessTokenFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {} = new_router_data.resource_common_data;
        let request = new_router_data.request.clone();
        let response = new_router_data.response.clone();
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "access token",
            request,
            response,
        );
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for GiftCardBalanceCheckFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self;
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let request = new_router_data.request.clone();
        let response = new_router_data.response.clone();
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "gift card balance check",
            request,
            response,
        );
        router_data.connector_auth_type = new_router_data.connector_auth_type;
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for PaymentFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone(),
            connector_customer: old_router_data.connector_customer.clone(),
            connector: old_router_data.connector.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            status: old_router_data.status,
            payment_method: old_router_data.payment_method,
            description: old_router_data.description.clone(),
            address: old_router_data.address.clone(),
            auth_type: old_router_data.auth_type,
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
            access_token: old_router_data.access_token.clone(),
            session_token: old_router_data.session_token.clone(),
            reference_id: old_router_data.reference_id.clone(),
            payment_method_token: old_router_data.payment_method_token.clone(),
            recurring_mandate_payment_data: old_router_data.recurring_mandate_payment_data.clone(),
            preprocessing_id: old_router_data.preprocessing_id.clone(),
            payment_method_balance: old_router_data.payment_method_balance.clone(),
            connector_api_version: old_router_data.connector_api_version.clone(),
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            test_mode: old_router_data.test_mode,
            connector_http_status_code: old_router_data.connector_http_status_code,
            external_latency: old_router_data.external_latency,
            apple_pay_flow: old_router_data.apple_pay_flow.clone(),
            connector_response: old_router_data.connector_response.clone(),
            payment_method_status: old_router_data.payment_method_status,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            connector_customer,
            connector,
            payment_id,
            attempt_id,
            status,
            payment_method,
            description,
            address,
            auth_type,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
            access_token,
            session_token,
            reference_id,
            payment_method_token,
            recurring_mandate_payment_data,
            preprocessing_id,
            payment_method_balance,
            connector_api_version,
            connector_request_reference_id,
            test_mode,
            connector_http_status_code,
            external_latency,
            apple_pay_flow,
            connector_response,
            payment_method_status,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "payment",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = customer_id;
        router_data.connector_customer = connector_customer;
        router_data.connector = connector;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.status = status;
        router_data.payment_method = payment_method;
        router_data.description = description;
        router_data.address = address;
        router_data.auth_type = auth_type;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;
        router_data.access_token = access_token;
        router_data.session_token = session_token;
        router_data.reference_id = reference_id;
        router_data.payment_method_token = payment_method_token;
        router_data.recurring_mandate_payment_data = recurring_mandate_payment_data;
        router_data.preprocessing_id = preprocessing_id;
        router_data.payment_method_balance = payment_method_balance;
        router_data.connector_api_version = connector_api_version;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.test_mode = test_mode;
        router_data.connector_http_status_code = connector_http_status_code;
        router_data.external_latency = external_latency;
        router_data.apple_pay_flow = apple_pay_flow;
        router_data.connector_response = connector_response;
        router_data.payment_method_status = payment_method_status;
        router_data.connector_auth_type = new_router_data.connector_auth_type;
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for RefundFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            status: old_router_data.status,
            payment_method: old_router_data.payment_method,
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            refund_id: old_router_data.refund_id.clone().ok_or(
                ConnectorError::MissingRequiredField {
                    field_name: "refund_id",
                },
            )?,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            payment_id,
            attempt_id,
            status,
            payment_method,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
            connector_request_reference_id,
            refund_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "refund",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = customer_id;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.status = status;
        router_data.payment_method = payment_method;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.refund_id = Some(refund_id);
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for DisputesFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            payment_method: old_router_data.payment_method,
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            dispute_id: old_router_data.dispute_id.clone().ok_or(
                ConnectorError::MissingRequiredField {
                    field_name: "dispute_id",
                },
            )?,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            payment_id,
            attempt_id,
            payment_method,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
            connector_request_reference_id,
            dispute_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "Disputes",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.payment_method = payment_method;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.dispute_id = Some(dispute_id);
        Ok(router_data)
    }
}

#[cfg(feature = "frm")]
impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for FrmFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            payment_method: old_router_data.payment_method,
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            auth_type: old_router_data.auth_type,
            connector_wallets_details: old_router_data.connector_wallets_details.clone(),
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
        };
        Ok(RouterDataV2 {
            tenant_id: old_router_data.tenant_id.clone(),
            flow: std::marker::PhantomData,
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            payment_id,
            attempt_id,
            payment_method,
            connector_request_reference_id,
            auth_type,
            connector_wallets_details,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "frm",
            new_router_data.request,
            new_router_data.response,
        );

        router_data.merchant_id = merchant_id;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.payment_method = payment_method;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.auth_type = auth_type;
        router_data.connector_wallets_details = connector_wallets_details;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;

        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for FilesFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            payment_id,
            attempt_id,
            connector_meta_data,
            connector_request_reference_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "files",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.connector_meta_data = connector_meta_data;
        router_data.connector_request_reference_id = connector_request_reference_id;

        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for WebhookSourceVerifyData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
        };
        Ok(RouterDataV2 {
            tenant_id: old_router_data.tenant_id.clone(),
            flow: std::marker::PhantomData,
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self { merchant_id } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "webhook source verify",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for MandateRevokeFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone().ok_or(
                ConnectorError::MissingRequiredField {
                    field_name: "customer_id",
                },
            )?,
            payment_id: Some(old_router_data.payment_id.clone()),
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            payment_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "mandate revoke",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = Some(customer_id);
        router_data.payment_id = payment_id
            .unwrap_or_else(|| {
                id_type::PaymentId::get_irrelevant_id("mandate revoke")
                    .get_string_repr()
                    .to_owned()
            })
            .to_owned();
        Ok(router_data)
    }
}

#[cfg(feature = "payouts")]
impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for PayoutFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone(),
            connector_customer: old_router_data.connector_customer.clone(),
            address: old_router_data.address.clone(),
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            connector_wallets_details: old_router_data.connector_wallets_details.clone(),
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            payout_method_data: old_router_data.payout_method_data.clone(),
            quote_id: old_router_data.quote_id.clone(),
        };
        Ok(RouterDataV2 {
            tenant_id: old_router_data.tenant_id.clone(),
            flow: std::marker::PhantomData,
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            connector_customer,
            address,
            connector_meta_data,
            connector_wallets_details,
            connector_request_reference_id,
            payout_method_data,
            quote_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "payout",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = customer_id;
        router_data.connector_customer = connector_customer;
        router_data.address = address;
        router_data.connector_meta_data = connector_meta_data;
        router_data.connector_wallets_details = connector_wallets_details;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.payout_method_data = payout_method_data;
        router_data.quote_id = quote_id;
        Ok(router_data)
    }
}
impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for ExternalAuthenticationFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            address: old_router_data.address.clone(),
        };
        Ok(RouterDataV2 {
            tenant_id: old_router_data.tenant_id.clone(),
            flow: std::marker::PhantomData,
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            connector_meta_data,
            address,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "external authentication",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.connector_meta_data = connector_meta_data;
        router_data.address = address;
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for InvoiceRecordBackData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            connector_meta_data: old_router_data.connector_meta_data.clone(),
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            connector_meta_data,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "recovery_record_back",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.connector_meta_data = connector_meta_data;
        router_data.connector_auth_type = new_router_data.connector_auth_type.clone();
        Ok(router_data)
    }
}

macro_rules! default_router_data_conversion {
    ($flow_name:ty) => {
        impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for $flow_name {
            fn from_old_router_data(
                old_router_data: &RouterData<T, Req, Resp>,
            ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
            where
                Self: Sized,
            {
                let resource_common_data = Self {
                    connector_meta_data: old_router_data.connector_meta_data.clone(),
                };
                Ok(RouterDataV2 {
                    flow: std::marker::PhantomData,
                    tenant_id: old_router_data.tenant_id.clone(),
                    resource_common_data,
                    connector_auth_type: old_router_data.connector_auth_type.clone(),
                    request: old_router_data.request.clone(),
                    response: old_router_data.response.clone(),
                })
            }

            fn to_old_router_data(
                new_router_data: RouterDataV2<T, Self, Req, Resp>,
            ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
            where
                Self: Sized,
            {
                let Self {
                    connector_meta_data,
                } = new_router_data.resource_common_data;
                let mut router_data = get_default_router_data(
                    new_router_data.tenant_id.clone(),
                    stringify!($flow_name),
                    new_router_data.request,
                    new_router_data.response,
                );
                router_data.connector_meta_data = connector_meta_data;
                router_data.connector_auth_type = new_router_data.connector_auth_type;

                Ok(router_data)
            }
        }
    };
}
default_router_data_conversion!(GetSubscriptionPlansData);
default_router_data_conversion!(GetSubscriptionPlanPricesData);
default_router_data_conversion!(SubscriptionCreateData);
default_router_data_conversion!(SubscriptionCustomerData);
default_router_data_conversion!(GetSubscriptionEstimateData);
default_router_data_conversion!(SubscriptionResumeData);
default_router_data_conversion!(SubscriptionPauseData);
default_router_data_conversion!(SubscriptionCancelData);

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for UasFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            authenticate_by: old_router_data.connector.clone(),
            source_authentication_id: old_router_data
                .authentication_id
                .clone()
                .ok_or(ConnectorError::MissingRequiredField {
                    field_name: "source_authentication_id",
                })
                .attach_printable("missing authentication id for uas")?,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            authenticate_by,
            source_authentication_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "uas",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.connector = authenticate_by;
        router_data.authentication_id = Some(source_authentication_id);
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for BillingConnectorPaymentsSyncFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "BillingConnectorPaymentsSync",
            new_router_data.request,
            new_router_data.response,
        );
        Ok(RouterData {
            connector_auth_type: new_router_data.connector_auth_type.clone(),
            ..router_data
        })
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for BillingConnectorInvoiceSyncFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "BillingConnectorInvoiceSync",
            new_router_data.request,
            new_router_data.response,
        );
        Ok(RouterData {
            connector_auth_type: new_router_data.connector_auth_type.clone(),
            ..router_data
        })
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for VaultConnectorFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "VaultConnector",
            new_router_data.request,
            new_router_data.response,
        );
        Ok(RouterData {
            connector_auth_type: new_router_data.connector_auth_type.clone(),
            ..router_data
        })
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for ExternalVaultProxyFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone(),
            connector_customer: old_router_data.connector_customer.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            status: old_router_data.status,
            payment_method: old_router_data.payment_method,
            description: old_router_data.description.clone(),
            address: old_router_data.address.clone(),
            auth_type: old_router_data.auth_type,
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
            access_token: old_router_data.access_token.clone(),
            session_token: old_router_data.session_token.clone(),
            reference_id: old_router_data.reference_id.clone(),
            payment_method_token: old_router_data.payment_method_token.clone(),
            recurring_mandate_payment_data: old_router_data.recurring_mandate_payment_data.clone(),
            preprocessing_id: old_router_data.preprocessing_id.clone(),
            payment_method_balance: old_router_data.payment_method_balance.clone(),
            connector_api_version: old_router_data.connector_api_version.clone(),
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            test_mode: old_router_data.test_mode,
            connector_http_status_code: old_router_data.connector_http_status_code,
            external_latency: old_router_data.external_latency,
            apple_pay_flow: old_router_data.apple_pay_flow.clone(),
            connector_response: old_router_data.connector_response.clone(),
            payment_method_status: old_router_data.payment_method_status,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            connector_customer,
            payment_id,
            attempt_id,
            status,
            payment_method,
            description,
            address,
            auth_type,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
            access_token,
            session_token,
            reference_id,
            payment_method_token,
            recurring_mandate_payment_data,
            preprocessing_id,
            payment_method_balance,
            connector_api_version,
            connector_request_reference_id,
            test_mode,
            connector_http_status_code,
            external_latency,
            apple_pay_flow,
            connector_response,
            payment_method_status,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "external vault proxy",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = customer_id;
        router_data.connector_customer = connector_customer;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.status = status;
        router_data.payment_method = payment_method;
        router_data.description = description;
        router_data.address = address;
        router_data.auth_type = auth_type;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;
        router_data.access_token = access_token;
        router_data.session_token = session_token;
        router_data.reference_id = reference_id;
        router_data.payment_method_token = payment_method_token;
        router_data.recurring_mandate_payment_data = recurring_mandate_payment_data;
        router_data.preprocessing_id = preprocessing_id;
        router_data.payment_method_balance = payment_method_balance;
        router_data.connector_api_version = connector_api_version;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.test_mode = test_mode;
        router_data.connector_http_status_code = connector_http_status_code;
        router_data.external_latency = external_latency;
        router_data.apple_pay_flow = apple_pay_flow;
        router_data.connector_response = connector_response;
        router_data.payment_method_status = payment_method_status;
        Ok(router_data)
    }
}


// File: crates/hyperswitch_interfaces/src/metrics.rs

//! Metrics interface

use router_env::{counter_metric, global_meter};

global_meter!(GLOBAL_METER, "ROUTER_API");

counter_metric!(UNIMPLEMENTED_FLOW, GLOBAL_METER);

counter_metric!(CONNECTOR_CALL_COUNT, GLOBAL_METER); // Attributes needed

counter_metric!(RESPONSE_DESERIALIZATION_FAILURE, GLOBAL_METER);
counter_metric!(CONNECTOR_ERROR_RESPONSE_COUNT, GLOBAL_METER);
// Connector Level Metric
counter_metric!(REQUEST_BUILD_FAILURE, GLOBAL_METER);


// File: crates/hyperswitch_interfaces/src/unified_connector_service.rs

use common_enums::AttemptStatus;
use common_utils::errors::CustomResult;
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::ErrorResponse, router_response_types::PaymentsResponseData,
};
use unified_connector_service_client::payments as payments_grpc;

use crate::helpers::ForeignTryFrom;

/// Unified Connector Service (UCS) related transformers
pub mod transformers;

pub use transformers::{
    UnifiedConnectorServiceError, WebhookTransformData, WebhookTransformationStatus,
};

/// Type alias for return type used by unified connector service response handlers
type UnifiedConnectorServiceResult = CustomResult<
    (
        Result<(PaymentsResponseData, AttemptStatus), ErrorResponse>,
        u16,
    ),
    UnifiedConnectorServiceError,
>;

#[allow(missing_docs)]
pub fn handle_unified_connector_service_response_for_payment_get(
    response: payments_grpc::PaymentServiceGetResponse,
) -> UnifiedConnectorServiceResult {
    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;

    let router_data_response =
        Result::<(PaymentsResponseData, AttemptStatus), ErrorResponse>::foreign_try_from(response)?;

    Ok((router_data_response, status_code))
}

/// Extracts the payments response from UCS webhook content
pub fn get_payments_response_from_ucs_webhook_content(
    webhook_content: payments_grpc::WebhookResponseContent,
) -> CustomResult<payments_grpc::PaymentServiceGetResponse, UnifiedConnectorServiceError> {
    match webhook_content.content {
        Some(unified_connector_service_client::payments::webhook_response_content::Content::PaymentsResponse(payments_response)) => {
            Ok(payments_response)
        },
        Some(unified_connector_service_client::payments::webhook_response_content::Content::RefundsResponse(_)) => {
            Err(UnifiedConnectorServiceError::WebhookProcessingFailure)
                .attach_printable("UCS webhook contains refunds response but payments response was expected")?
        },
        Some(unified_connector_service_client::payments::webhook_response_content::Content::DisputesResponse(_)) => {
            Err(UnifiedConnectorServiceError::WebhookProcessingFailure)
                .attach_printable("UCS webhook contains disputes response but payments response was expected")?
        },
        Some(unified_connector_service_client::payments::webhook_response_content::Content::IncompleteTransformation(_)) => {
            Err(UnifiedConnectorServiceError::WebhookProcessingFailure)
                .attach_printable("UCS webhook contains incomplete transformation but payments response was expected")?
        },
        None => {
            Err(UnifiedConnectorServiceError::WebhookProcessingFailure)
                .attach_printable("Missing payments response in UCS webhook content")?
        }
    }
}


// File: crates/hyperswitch_interfaces/src/integrity.rs

use common_utils::errors::IntegrityCheckError;
use hyperswitch_domain_models::router_request_types::{
    AuthoriseIntegrityObject, CaptureIntegrityObject, PaymentsAuthorizeData, PaymentsCaptureData,
    PaymentsSyncData, RefundIntegrityObject, RefundsData, SyncIntegrityObject,
};

/// Connector Integrity trait to check connector data integrity
pub trait FlowIntegrity {
    /// Output type for the connector
    type IntegrityObject;
    /// helps in connector integrity check
    fn compare(
        req_integrity_object: Self::IntegrityObject,
        res_integrity_object: Self::IntegrityObject,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError>;
}

/// Trait to get connector integrity object based on request and response
pub trait GetIntegrityObject<T: FlowIntegrity> {
    /// function to get response integrity object
    fn get_response_integrity_object(&self) -> Option<T::IntegrityObject>;
    /// function to get request integrity object
    fn get_request_integrity_object(&self) -> T::IntegrityObject;
}

/// Trait to check flow type, based on which various integrity checks will be performed
pub trait CheckIntegrity<Request, T> {
    /// Function to check to initiate integrity check
    fn check_integrity(
        &self,
        request: &Request,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError>;
}

impl<T, Request> CheckIntegrity<Request, T> for RefundsData
where
    T: FlowIntegrity,
    Request: GetIntegrityObject<T>,
{
    fn check_integrity(
        &self,
        request: &Request,
        connector_refund_id: Option<String>,
    ) -> Result<(), IntegrityCheckError> {
        match request.get_response_integrity_object() {
            Some(res_integrity_object) => {
                let req_integrity_object = request.get_request_integrity_object();
                T::compare(
                    req_integrity_object,
                    res_integrity_object,
                    connector_refund_id,
                )
            }
            None => Ok(()),
        }
    }
}

impl<T, Request> CheckIntegrity<Request, T> for PaymentsAuthorizeData
where
    T: FlowIntegrity,
    Request: GetIntegrityObject<T>,
{
    fn check_integrity(
        &self,
        request: &Request,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError> {
        match request.get_response_integrity_object() {
            Some(res_integrity_object) => {
                let req_integrity_object = request.get_request_integrity_object();
                T::compare(
                    req_integrity_object,
                    res_integrity_object,
                    connector_transaction_id,
                )
            }
            None => Ok(()),
        }
    }
}

impl<T, Request> CheckIntegrity<Request, T> for PaymentsCaptureData
where
    T: FlowIntegrity,
    Request: GetIntegrityObject<T>,
{
    fn check_integrity(
        &self,
        request: &Request,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError> {
        match request.get_response_integrity_object() {
            Some(res_integrity_object) => {
                let req_integrity_object = request.get_request_integrity_object();
                T::compare(
                    req_integrity_object,
                    res_integrity_object,
                    connector_transaction_id,
                )
            }
            None => Ok(()),
        }
    }
}

impl<T, Request> CheckIntegrity<Request, T> for PaymentsSyncData
where
    T: FlowIntegrity,
    Request: GetIntegrityObject<T>,
{
    fn check_integrity(
        &self,
        request: &Request,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError> {
        match request.get_response_integrity_object() {
            Some(res_integrity_object) => {
                let req_integrity_object = request.get_request_integrity_object();
                T::compare(
                    req_integrity_object,
                    res_integrity_object,
                    connector_transaction_id,
                )
            }
            None => Ok(()),
        }
    }
}

impl FlowIntegrity for RefundIntegrityObject {
    type IntegrityObject = Self;
    fn compare(
        req_integrity_object: Self,
        res_integrity_object: Self,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError> {
        let mut mismatched_fields = Vec::new();

        if req_integrity_object.currency != res_integrity_object.currency {
            mismatched_fields.push(format_mismatch(
                "currency",
                &req_integrity_object.currency.to_string(),
                &res_integrity_object.currency.to_string(),
            ));
        }

        if req_integrity_object.refund_amount != res_integrity_object.refund_amount {
            mismatched_fields.push(format_mismatch(
                "refund_amount",
                &req_integrity_object.refund_amount.to_string(),
                &res_integrity_object.refund_amount.to_string(),
            ));
        }

        if mismatched_fields.is_empty() {
            Ok(())
        } else {
            let field_names = mismatched_fields.join(", ");

            Err(IntegrityCheckError {
                field_names,
                connector_transaction_id,
            })
        }
    }
}

impl FlowIntegrity for AuthoriseIntegrityObject {
    type IntegrityObject = Self;
    fn compare(
        req_integrity_object: Self,
        res_integrity_object: Self,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError> {
        let mut mismatched_fields = Vec::new();

        if req_integrity_object.amount != res_integrity_object.amount {
            mismatched_fields.push(format_mismatch(
                "amount",
                &req_integrity_object.amount.to_string(),
                &res_integrity_object.amount.to_string(),
            ));
        }

        if req_integrity_object.currency != res_integrity_object.currency {
            mismatched_fields.push(format_mismatch(
                "currency",
                &req_integrity_object.currency.to_string(),
                &res_integrity_object.currency.to_string(),
            ));
        }

        if mismatched_fields.is_empty() {
            Ok(())
        } else {
            let field_names = mismatched_fields.join(", ");

            Err(IntegrityCheckError {
                field_names,
                connector_transaction_id,
            })
        }
    }
}

impl FlowIntegrity for SyncIntegrityObject {
    type IntegrityObject = Self;
    fn compare(
        req_integrity_object: Self,
        res_integrity_object: Self,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError> {
        let mut mismatched_fields = Vec::new();

        res_integrity_object
            .amount
            .zip(req_integrity_object.amount)
            .map(|(res_amount, req_amount)| {
                if res_amount != req_amount {
                    mismatched_fields.push(format_mismatch(
                        "amount",
                        &req_amount.to_string(),
                        &res_amount.to_string(),
                    ));
                }
            });

        res_integrity_object
            .currency
            .zip(req_integrity_object.currency)
            .map(|(res_currency, req_currency)| {
                if res_currency != req_currency {
                    mismatched_fields.push(format_mismatch(
                        "currency",
                        &req_currency.to_string(),
                        &res_currency.to_string(),
                    ));
                }
            });

        if mismatched_fields.is_empty() {
            Ok(())
        } else {
            let field_names = mismatched_fields.join(", ");

            Err(IntegrityCheckError {
                field_names,
                connector_transaction_id,
            })
        }
    }
}

impl FlowIntegrity for CaptureIntegrityObject {
    type IntegrityObject = Self;
    fn compare(
        req_integrity_object: Self,
        res_integrity_object: Self,
        connector_transaction_id: Option<String>,
    ) -> Result<(), IntegrityCheckError> {
        let mut mismatched_fields = Vec::new();

        res_integrity_object
            .capture_amount
            .zip(req_integrity_object.capture_amount)
            .map(|(res_amount, req_amount)| {
                if res_amount != req_amount {
                    mismatched_fields.push(format_mismatch(
                        "capture_amount",
                        &req_amount.to_string(),
                        &res_amount.to_string(),
                    ));
                }
            });

        if req_integrity_object.currency != res_integrity_object.currency {
            mismatched_fields.push(format_mismatch(
                "currency",
                &req_integrity_object.currency.to_string(),
                &res_integrity_object.currency.to_string(),
            ));
        }

        if mismatched_fields.is_empty() {
            Ok(())
        } else {
            let field_names = mismatched_fields.join(", ");

            Err(IntegrityCheckError {
                field_names,
                connector_transaction_id,
            })
        }
    }
}

impl GetIntegrityObject<CaptureIntegrityObject> for PaymentsCaptureData {
    fn get_response_integrity_object(&self) -> Option<CaptureIntegrityObject> {
        self.integrity_object.clone()
    }

    fn get_request_integrity_object(&self) -> CaptureIntegrityObject {
        CaptureIntegrityObject {
            capture_amount: Some(self.minor_amount_to_capture),
            currency: self.currency,
        }
    }
}

impl GetIntegrityObject<RefundIntegrityObject> for RefundsData {
    fn get_response_integrity_object(&self) -> Option<RefundIntegrityObject> {
        self.integrity_object.clone()
    }

    fn get_request_integrity_object(&self) -> RefundIntegrityObject {
        RefundIntegrityObject {
            currency: self.currency,
            refund_amount: self.minor_refund_amount,
        }
    }
}

impl GetIntegrityObject<AuthoriseIntegrityObject> for PaymentsAuthorizeData {
    fn get_response_integrity_object(&self) -> Option<AuthoriseIntegrityObject> {
        self.integrity_object.clone()
    }

    fn get_request_integrity_object(&self) -> AuthoriseIntegrityObject {
        AuthoriseIntegrityObject {
            amount: self.minor_amount,
            currency: self.currency,
        }
    }
}

impl GetIntegrityObject<SyncIntegrityObject> for PaymentsSyncData {
    fn get_response_integrity_object(&self) -> Option<SyncIntegrityObject> {
        self.integrity_object.clone()
    }

    fn get_request_integrity_object(&self) -> SyncIntegrityObject {
        SyncIntegrityObject {
            amount: Some(self.amount),
            currency: Some(self.currency),
        }
    }
}

#[inline]
fn format_mismatch(field: &str, expected: &str, found: &str) -> String {
    format!("{field} expected {expected} but found {found}")
}


// File: crates/hyperswitch_interfaces/src/connector_integration_interface.rs

use api_models::webhooks::{IncomingWebhookEvent, ObjectReferenceId};
use common_enums::PaymentAction;
use common_utils::{crypto, errors::CustomResult, request::Request};
use hyperswitch_domain_models::{
    api::ApplicationResponse,
    connector_endpoints::Connectors,
    errors::api_error_response::ApiErrorResponse,
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_data_v2::RouterDataV2,
    router_response_types::{ConnectorInfo, SupportedPaymentMethods},
};

use crate::{
    api,
    api::{
        BoxedConnectorIntegration, CaptureSyncMethod, Connector, ConnectorAccessTokenSuffix,
        ConnectorCommon, ConnectorIntegration, ConnectorRedirectResponse, ConnectorSpecifications,
        ConnectorValidation, CurrencyUnit,
    },
    authentication::ExternalAuthenticationPayload,
    connector_integration_v2::{BoxedConnectorIntegrationV2, ConnectorIntegrationV2, ConnectorV2},
    disputes, errors,
    events::connector_api_logs::ConnectorEvent,
    types,
    webhooks::{IncomingWebhook, IncomingWebhookFlowError, IncomingWebhookRequestDetails},
};

/// RouterDataConversion trait
///
/// This trait must be implemented for conversion between Router data and RouterDataV2
pub trait RouterDataConversion<T, Req: Clone, Resp: Clone> {
    /// Convert RouterData to RouterDataV2
    ///
    /// # Arguments
    ///
    /// * `old_router_data` - A reference to the old RouterData
    ///
    /// # Returns
    ///
    /// A `CustomResult` containing the new RouterDataV2 or a ConnectorError
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, errors::ConnectorError>
    where
        Self: Sized;
    /// Convert RouterDataV2 back to RouterData
    ///
    /// # Arguments
    ///
    /// * `new_router_data` - The new RouterDataV2
    ///
    /// # Returns
    ///
    /// A `CustomResult` containing the old RouterData or a ConnectorError
    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, errors::ConnectorError>
    where
        Self: Sized;
}
/// Alias for Box<&'static (dyn Connector + Sync)>
pub type BoxedConnector = Box<&'static (dyn Connector + Sync)>;
/// Alias for Box<&'static (dyn ConnectorV2 + Sync)>
pub type BoxedConnectorV2 = Box<&'static (dyn ConnectorV2 + Sync)>;

/// Enum representing the Connector
#[derive(Clone)]
pub enum ConnectorEnum {
    /// Old connector type
    Old(BoxedConnector),
    /// New connector type
    New(BoxedConnectorV2),
}

impl std::fmt::Debug for ConnectorEnum {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Old(_) => f
                .debug_tuple("Old")
                .field(&std::any::type_name::<BoxedConnector>().to_string())
                .finish(),
            Self::New(_) => f
                .debug_tuple("New")
                .field(&std::any::type_name::<BoxedConnectorV2>().to_string())
                .finish(),
        }
    }
}

#[allow(missing_debug_implementations)]
/// Enum representing the Connector Integration
#[derive(Clone)]
pub enum ConnectorIntegrationEnum<'a, F, ResourceCommonData, Req, Resp> {
    /// Old connector integration type
    Old(BoxedConnectorIntegration<'a, F, Req, Resp>),
    /// New connector integration type
    New(BoxedConnectorIntegrationV2<'a, F, ResourceCommonData, Req, Resp>),
}

/// Alias for Box<dyn ConnectorIntegrationInterface>
pub type BoxedConnectorIntegrationInterface<F, ResourceCommonData, Req, Resp> =
    Box<dyn ConnectorIntegrationInterface<F, ResourceCommonData, Req, Resp> + Send + Sync>;

impl ConnectorEnum {
    /// Get the connector integration
    ///
    /// # Returns
    ///
    /// A `BoxedConnectorIntegrationInterface` containing the connector integration
    pub fn get_connector_integration<F, ResourceCommonData, Req, Resp>(
        &self,
    ) -> BoxedConnectorIntegrationInterface<F, ResourceCommonData, Req, Resp>
    where
        dyn Connector + Sync: ConnectorIntegration<F, Req, Resp>,
        dyn ConnectorV2 + Sync: ConnectorIntegrationV2<F, ResourceCommonData, Req, Resp>,
        ResourceCommonData: RouterDataConversion<F, Req, Resp> + Clone + 'static,
        F: Clone + 'static,
        Req: Clone + 'static,
        Resp: Clone + 'static,
    {
        match self {
            Self::Old(old_integration) => Box::new(ConnectorIntegrationEnum::Old(
                old_integration.get_connector_integration(),
            )),
            Self::New(new_integration) => Box::new(ConnectorIntegrationEnum::New(
                new_integration.get_connector_integration_v2(),
            )),
        }
    }
    /// validates the file upload
    pub fn validate_file_upload(
        &self,
        purpose: api::files::FilePurpose,
        file_size: i32,
        file_type: mime::Mime,
    ) -> CustomResult<(), errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.validate_file_upload(purpose, file_size, file_type),
            Self::New(connector) => {
                connector.validate_file_upload_v2(purpose, file_size, file_type)
            }
        }
    }
}

#[async_trait::async_trait]
impl IncomingWebhook for ConnectorEnum {
    fn get_webhook_body_decoding_algorithm(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::DecodeMessage + Send>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_webhook_body_decoding_algorithm(request),
            Self::New(connector) => connector.get_webhook_body_decoding_algorithm(request),
        }
    }

    fn get_webhook_body_decoding_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_webhook_body_decoding_message(request),
            Self::New(connector) => connector.get_webhook_body_decoding_message(request),
        }
    }

    async fn decode_webhook_body(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        connector_name: &str,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => {
                connector
                    .decode_webhook_body(
                        request,
                        merchant_id,
                        connector_webhook_details,
                        connector_name,
                    )
                    .await
            }
            Self::New(connector) => {
                connector
                    .decode_webhook_body(
                        request,
                        merchant_id,
                        connector_webhook_details,
                        connector_name,
                    )
                    .await
            }
        }
    }

    fn get_webhook_source_verification_algorithm(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_webhook_source_verification_algorithm(request),
            Self::New(connector) => connector.get_webhook_source_verification_algorithm(request),
        }
    }

    async fn get_webhook_source_verification_merchant_secret(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_name: &str,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<api_models::webhooks::ConnectorWebhookSecrets, errors::ConnectorError> {
        match self {
            Self::Old(connector) => {
                connector
                    .get_webhook_source_verification_merchant_secret(
                        merchant_id,
                        connector_name,
                        connector_webhook_details,
                    )
                    .await
            }
            Self::New(connector) => {
                connector
                    .get_webhook_source_verification_merchant_secret(
                        merchant_id,
                        connector_name,
                        connector_webhook_details,
                    )
                    .await
            }
        }
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector
                .get_webhook_source_verification_signature(request, connector_webhook_secrets),
            Self::New(connector) => connector
                .get_webhook_source_verification_signature(request, connector_webhook_secrets),
        }
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_webhook_source_verification_message(
                request,
                merchant_id,
                connector_webhook_secrets,
            ),
            Self::New(connector) => connector.get_webhook_source_verification_message(
                request,
                merchant_id,
                connector_webhook_secrets,
            ),
        }
    }

    async fn verify_webhook_source(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        connector_account_details: crypto::Encryptable<masking::Secret<serde_json::Value>>,
        connector_name: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        match self {
            Self::Old(connector) => {
                connector
                    .verify_webhook_source(
                        request,
                        merchant_id,
                        connector_webhook_details,
                        connector_account_details,
                        connector_name,
                    )
                    .await
            }
            Self::New(connector) => {
                connector
                    .verify_webhook_source(
                        request,
                        merchant_id,
                        connector_webhook_details,
                        connector_account_details,
                        connector_name,
                    )
                    .await
            }
        }
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_webhook_object_reference_id(request),
            Self::New(connector) => connector.get_webhook_object_reference_id(request),
        }
    }

    #[cfg(feature = "payouts")]
    fn get_payout_webhook_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::PayoutWebhookUpdate, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_payout_webhook_details(request),
            Self::New(connector) => connector.get_payout_webhook_details(request),
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_webhook_event_type(request),
            Self::New(connector) => connector.get_webhook_event_type(request),
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_webhook_resource_object(request),
            Self::New(connector) => connector.get_webhook_resource_object(request),
        }
    }

    fn get_webhook_api_response(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        error_kind: Option<IncomingWebhookFlowError>,
    ) -> CustomResult<ApplicationResponse<serde_json::Value>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_webhook_api_response(request, error_kind),
            Self::New(connector) => connector.get_webhook_api_response(request, error_kind),
        }
    }

    fn get_dispute_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<disputes::DisputePayload, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_dispute_details(request),
            Self::New(connector) => connector.get_dispute_details(request),
        }
    }

    fn get_external_authentication_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ExternalAuthenticationPayload, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_external_authentication_details(request),
            Self::New(connector) => connector.get_external_authentication_details(request),
        }
    }

    fn get_mandate_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorMandateDetails>,
        errors::ConnectorError,
    > {
        match self {
            Self::Old(connector) => connector.get_mandate_details(request),
            Self::New(connector) => connector.get_mandate_details(request),
        }
    }

    fn get_network_txn_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId>,
        errors::ConnectorError,
    > {
        match self {
            Self::Old(connector) => connector.get_network_txn_id(request),
            Self::New(connector) => connector.get_network_txn_id(request),
        }
    }

    fn get_additional_payment_method_data(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<api_models::payment_methods::PaymentMethodUpdate>,
        errors::ConnectorError,
    > {
        match self {
            Self::Old(connector) => connector.get_additional_payment_method_data(request),
            Self::New(connector) => connector.get_additional_payment_method_data(request),
        }
    }

    #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
    fn get_revenue_recovery_invoice_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        hyperswitch_domain_models::revenue_recovery::RevenueRecoveryInvoiceData,
        errors::ConnectorError,
    > {
        match self {
            Self::Old(connector) => connector.get_revenue_recovery_invoice_details(request),
            Self::New(connector) => connector.get_revenue_recovery_invoice_details(request),
        }
    }

    #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
    fn get_revenue_recovery_attempt_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        hyperswitch_domain_models::revenue_recovery::RevenueRecoveryAttemptData,
        errors::ConnectorError,
    > {
        match self {
            Self::Old(connector) => connector.get_revenue_recovery_attempt_details(request),
            Self::New(connector) => connector.get_revenue_recovery_attempt_details(request),
        }
    }
    fn get_subscription_mit_payment_data(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        hyperswitch_domain_models::router_flow_types::SubscriptionMitPaymentData,
        errors::ConnectorError,
    > {
        match self {
            Self::Old(connector) => connector.get_subscription_mit_payment_data(request),
            Self::New(connector) => connector.get_subscription_mit_payment_data(request),
        }
    }
}

impl ConnectorRedirectResponse for ConnectorEnum {
    fn get_flow_type(
        &self,
        query_params: &str,
        json_payload: Option<serde_json::Value>,
        action: PaymentAction,
    ) -> CustomResult<common_enums::CallConnectorAction, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_flow_type(query_params, json_payload, action),
            Self::New(connector) => connector.get_flow_type(query_params, json_payload, action),
        }
    }
}

impl ConnectorValidation for ConnectorEnum {
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<common_enums::CaptureMethod>,
        payment_method: common_enums::PaymentMethod,
        pmt: Option<common_enums::PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.validate_connector_against_payment_request(
                capture_method,
                payment_method,
                pmt,
            ),
            Self::New(connector) => connector.validate_connector_against_payment_request(
                capture_method,
                payment_method,
                pmt,
            ),
        }
    }

    fn validate_mandate_payment(
        &self,
        pm_type: Option<common_enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.validate_mandate_payment(pm_type, pm_data),
            Self::New(connector) => connector.validate_mandate_payment(pm_type, pm_data),
        }
    }

    fn validate_psync_reference_id(
        &self,
        data: &hyperswitch_domain_models::router_request_types::PaymentsSyncData,
        is_three_ds: bool,
        status: common_enums::enums::AttemptStatus,
        connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.validate_psync_reference_id(
                data,
                is_three_ds,
                status,
                connector_meta_data,
            ),
            Self::New(connector) => connector.validate_psync_reference_id(
                data,
                is_three_ds,
                status,
                connector_meta_data,
            ),
        }
    }

    fn is_webhook_source_verification_mandatory(&self) -> bool {
        match self {
            Self::Old(connector) => connector.is_webhook_source_verification_mandatory(),
            Self::New(connector) => connector.is_webhook_source_verification_mandatory(),
        }
    }
}

impl ConnectorSpecifications for ConnectorEnum {
    fn decide_should_continue_after_preprocessing(
        &self,
        current_flow: api::CurrentFlowInfo<'_>,
        pre_processing_flow_name: api::PreProcessingFlowName,
        preprocessing_flow_response: api::PreProcessingFlowResponse<'_>,
    ) -> bool {
        match self {
            Self::Old(connector) => connector.decide_should_continue_after_preprocessing(
                current_flow,
                pre_processing_flow_name,
                preprocessing_flow_response,
            ),
            Self::New(connector) => connector.decide_should_continue_after_preprocessing(
                current_flow,
                pre_processing_flow_name,
                preprocessing_flow_response,
            ),
        }
    }
    fn get_preprocessing_flow_if_needed(
        &self,
        current_flow_info: api::CurrentFlowInfo<'_>,
    ) -> Option<api::PreProcessingFlowName> {
        match self {
            Self::Old(connector) => connector.get_preprocessing_flow_if_needed(current_flow_info),
            Self::New(connector) => connector.get_preprocessing_flow_if_needed(current_flow_info),
        }
    }
    fn get_alternate_flow_if_needed(
        &self,
        current_flow: api::CurrentFlowInfo<'_>,
    ) -> Option<api::AlternateFlow> {
        match self {
            Self::Old(connector) => connector.get_alternate_flow_if_needed(current_flow),
            Self::New(connector) => connector.get_alternate_flow_if_needed(current_flow),
        }
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        match self {
            Self::Old(connector) => connector.get_supported_payment_methods(),
            Self::New(connector) => connector.get_supported_payment_methods(),
        }
    }

    /// Supported webhooks flows
    fn get_supported_webhook_flows(&self) -> Option<&'static [common_enums::EventClass]> {
        match self {
            Self::Old(connector) => connector.get_supported_webhook_flows(),
            Self::New(connector) => connector.get_supported_webhook_flows(),
        }
    }

    /// Details related to connector
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        match self {
            Self::Old(connector) => connector.get_connector_about(),
            Self::New(connector) => connector.get_connector_about(),
        }
    }

    /// Check if connector supports authentication token
    fn authentication_token_for_token_creation(&self) -> bool {
        match self {
            Self::Old(connector) => connector.authentication_token_for_token_creation(),
            Self::New(connector) => connector.authentication_token_for_token_creation(),
        }
    }

    /// If connector supports session token generation
    fn is_sdk_client_token_generation_enabled(&self) -> bool {
        match self {
            Self::Old(connector) => connector.is_sdk_client_token_generation_enabled(),
            Self::New(connector) => connector.is_sdk_client_token_generation_enabled(),
        }
    }

    /// Supported payment methods for session token generation
    fn supported_payment_method_types_for_sdk_client_token_generation(
        &self,
    ) -> Vec<common_enums::PaymentMethodType> {
        match self {
            Self::Old(connector) => {
                connector.supported_payment_method_types_for_sdk_client_token_generation()
            }
            Self::New(connector) => {
                connector.supported_payment_method_types_for_sdk_client_token_generation()
            }
        }
    }

    /// Validate whether session token is generated for payment payment type
    fn validate_sdk_session_token_for_payment_method(
        &self,
        current_core_payment_method_type: &common_enums::PaymentMethodType,
    ) -> bool {
        match self {
            Self::Old(connector) => connector
                .validate_sdk_session_token_for_payment_method(current_core_payment_method_type),
            Self::New(connector) => connector
                .validate_sdk_session_token_for_payment_method(current_core_payment_method_type),
        }
    }

    #[cfg(feature = "v1")]
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
        is_config_enabled_to_send_payment_id_as_connector_request_id: bool,
    ) -> String {
        match self {
            Self::Old(connector) => connector.generate_connector_request_reference_id(
                payment_intent,
                payment_attempt,
                is_config_enabled_to_send_payment_id_as_connector_request_id,
            ),
            Self::New(connector) => connector.generate_connector_request_reference_id(
                payment_intent,
                payment_attempt,
                is_config_enabled_to_send_payment_id_as_connector_request_id,
            ),
        }
    }

    #[cfg(feature = "v2")]
    /// Generate connector request reference ID
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> String {
        match self {
            Self::Old(connector) => {
                connector.generate_connector_request_reference_id(payment_intent, payment_attempt)
            }
            Self::New(connector) => {
                connector.generate_connector_request_reference_id(payment_intent, payment_attempt)
            }
        }
    }

    #[cfg(feature = "v1")]
    fn generate_connector_customer_id(
        &self,
        customer_id: &Option<common_utils::id_type::CustomerId>,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> Option<String> {
        match self {
            Self::Old(connector) => {
                connector.generate_connector_customer_id(customer_id, merchant_id)
            }
            Self::New(connector) => {
                connector.generate_connector_customer_id(customer_id, merchant_id)
            }
        }
    }

    #[cfg(feature = "v2")]
    fn generate_connector_customer_id(
        &self,
        customer_id: &Option<common_utils::id_type::CustomerId>,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> Option<String> {
        todo!()
    }

    /// Check if connector requires create customer call
    fn should_call_connector_customer(
        &self,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> bool {
        match self {
            Self::Old(connector) => connector.should_call_connector_customer(payment_attempt),
            Self::New(connector) => connector.should_call_connector_customer(payment_attempt),
        }
    }

    fn should_call_tokenization_before_setup_mandate(&self) -> bool {
        match self {
            Self::Old(connector) => connector.should_call_tokenization_before_setup_mandate(),
            Self::New(connector) => connector.should_call_tokenization_before_setup_mandate(),
        }
    }
}

impl ConnectorCommon for ConnectorEnum {
    fn id(&self) -> &'static str {
        match self {
            Self::Old(connector) => connector.id(),
            Self::New(connector) => connector.id(),
        }
    }

    fn get_currency_unit(&self) -> CurrencyUnit {
        match self {
            Self::Old(connector) => connector.get_currency_unit(),
            Self::New(connector) => connector.get_currency_unit(),
        }
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.get_auth_header(auth_type),
            Self::New(connector) => connector.get_auth_header(auth_type),
        }
    }

    fn common_get_content_type(&self) -> &'static str {
        match self {
            Self::Old(connector) => connector.common_get_content_type(),
            Self::New(connector) => connector.common_get_content_type(),
        }
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        match self {
            Self::Old(connector) => connector.base_url(connectors),
            Self::New(connector) => connector.base_url(connectors),
        }
    }

    fn build_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.build_error_response(res, event_builder),
            Self::New(connector) => connector.build_error_response(res, event_builder),
        }
    }
}

impl ConnectorAccessTokenSuffix for ConnectorEnum {
    fn get_access_token_key<F, Req, Res>(
        &self,
        router_data: &RouterData<F, Req, Res>,
        merchant_connector_id_or_connector_name: String,
    ) -> CustomResult<String, errors::ConnectorError> {
        match self {
            Self::Old(connector) => {
                connector.get_access_token_key(router_data, merchant_connector_id_or_connector_name)
            }
            Self::New(connector) => {
                connector.get_access_token_key(router_data, merchant_connector_id_or_connector_name)
            }
        }
    }
}

/// Trait representing the connector integration interface
///
/// This trait defines the methods required for a connector integration interface.
pub trait ConnectorIntegrationInterface<F, ResourceCommonData, Req, Resp>: Send + Sync {
    /// Clone the connector integration interface
    ///
    /// # Returns
    ///
    /// A `Box` containing the cloned connector integration interface
    fn clone_box(
        &self,
    ) -> Box<dyn ConnectorIntegrationInterface<F, ResourceCommonData, Req, Resp> + Send + Sync>;
    /// Get the multiple capture sync method
    ///
    /// # Returns
    ///
    /// A `CustomResult` containing the `CaptureSyncMethod` or a `ConnectorError`
    fn get_multiple_capture_sync_method(
        &self,
    ) -> CustomResult<CaptureSyncMethod, errors::ConnectorError>;
    /// Build a request for the connector integration
    ///
    /// # Arguments
    ///
    /// * `req` - A reference to the RouterData
    /// # Returns
    ///
    /// A `CustomResult` containing an optional Request or a ConnectorError
    fn build_request(
        &self,
        req: &RouterData<F, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError>;
    /// handles response from the connector
    fn handle_response(
        &self,
        data: &RouterData<F, Req, Resp>,
        event_builder: Option<&mut ConnectorEvent>,
        _res: types::Response,
    ) -> CustomResult<RouterData<F, Req, Resp>, errors::ConnectorError>
    where
        F: Clone,
        Req: Clone,
        Resp: Clone;
    /// Get the error response
    ///
    /// # Arguments
    ///
    /// * `res` - The response
    /// * `event_builder` - An optional event builder
    ///
    /// # Returns
    ///
    /// A `CustomResult` containing the `ErrorResponse` or a `ConnectorError`
    fn get_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError>;
    /// Get the 5xx error response
    ///
    /// # Arguments
    ///
    /// * `res` - The response
    /// * `event_builder` - An optional event builder
    ///
    /// # Returns
    ///
    /// A `CustomResult` containing the `ErrorResponse` or a `ConnectorError`
    fn get_5xx_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError>;
}

impl<T: 'static, ResourceCommonData: 'static, Req: 'static, Resp: 'static>
    ConnectorIntegrationInterface<T, ResourceCommonData, Req, Resp>
    for ConnectorIntegrationEnum<'static, T, ResourceCommonData, Req, Resp>
where
    ResourceCommonData: RouterDataConversion<T, Req, Resp> + Clone,
    T: Clone,
    Req: Clone,
    Resp: Clone,
{
    fn get_multiple_capture_sync_method(
        &self,
    ) -> CustomResult<CaptureSyncMethod, errors::ConnectorError> {
        match self {
            ConnectorIntegrationEnum::Old(old_integration) => {
                old_integration.get_multiple_capture_sync_method()
            }
            ConnectorIntegrationEnum::New(new_integration) => {
                new_integration.get_multiple_capture_sync_method()
            }
        }
    }
    fn build_request(
        &self,
        req: &RouterData<T, Req, Resp>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        match self {
            ConnectorIntegrationEnum::Old(old_integration) => {
                old_integration.build_request(req, connectors)
            }
            ConnectorIntegrationEnum::New(new_integration) => {
                let new_router_data = ResourceCommonData::from_old_router_data(req)?;
                new_integration.build_request_v2(&new_router_data)
            }
        }
    }
    fn handle_response(
        &self,
        data: &RouterData<T, Req, Resp>,
        event_builder: Option<&mut ConnectorEvent>,
        res: types::Response,
    ) -> CustomResult<RouterData<T, Req, Resp>, errors::ConnectorError>
    where
        T: Clone,
        Req: Clone,
        Resp: Clone,
    {
        match self {
            ConnectorIntegrationEnum::Old(old_integration) => {
                old_integration.handle_response(data, event_builder, res)
            }
            ConnectorIntegrationEnum::New(new_integration) => {
                let new_router_data = ResourceCommonData::from_old_router_data(data)?;
                new_integration
                    .handle_response_v2(&new_router_data, event_builder, res)
                    .map(ResourceCommonData::to_old_router_data)?
            }
        }
    }
    fn get_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        match self {
            ConnectorIntegrationEnum::Old(old_integration) => {
                old_integration.get_error_response(res, event_builder)
            }
            ConnectorIntegrationEnum::New(new_integration) => {
                new_integration.get_error_response_v2(res, event_builder)
            }
        }
    }
    fn get_5xx_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        match self {
            ConnectorIntegrationEnum::Old(old_integration) => {
                old_integration.get_5xx_error_response(res, event_builder)
            }
            ConnectorIntegrationEnum::New(new_integration) => {
                new_integration.get_5xx_error_response(res, event_builder)
            }
        }
    }

    fn clone_box(
        &self,
    ) -> Box<dyn ConnectorIntegrationInterface<T, ResourceCommonData, Req, Resp> + Send + Sync>
    {
        Box::new(self.clone())
    }
}

impl api::ConnectorTransactionId for ConnectorEnum {
    /// Get the connector transaction ID
    ///
    /// # Arguments
    ///
    /// * `payment_attempt` - The payment attempt
    ///
    /// # Returns
    ///
    /// A `Result` containing an optional transaction ID or an ApiErrorResponse
    fn connector_transaction_id(
        &self,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> Result<Option<String>, ApiErrorResponse> {
        match self {
            Self::Old(connector) => connector.connector_transaction_id(payment_attempt),
            Self::New(connector) => connector.connector_transaction_id(payment_attempt),
        }
    }
}