// Implementation: impl SellerStatusResponse
// File: crates/router/src/types/api/connector_onboarding/paypal.rs
// Module: router
// Methods: 1 total (1 public)

impl SellerStatusResponse


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

pub fn refresh_cgraph_cache(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    key: String,
    profile_id: &common_utils::id_type::ProfileId,
    transaction_type: &api_enums::TransactionType,
) -> RoutingResult<Arc<hyperswitch_constraint_graph::ConstraintGraph<euclid_dir::DirValue>>>


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

pub fn perform_straight_through_routing(
    algorithm: &routing_types::StraightThroughAlgorithm,
    creds_identifier: Option<&str>,
) -> RoutingResult<(Vec<routing_types::RoutableConnectorChoice>, bool)>


// Implementation: impl Default for for EliminationRoutingConfig
// File: crates/api_models/src/routing.rs
// Module: api_models
// Methods: 1 total (0 public)

impl Default for for EliminationRoutingConfig


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

impl Responder


// Implementation: impl NetworkTokenizationBuilder
// File: crates/router/src/core/payment_methods/tokenize/card_executor.rs
// Module: router
// Methods: 1 total (1 public)

impl NetworkTokenizationBuilder


// Function: get_viz_digraph_string
// File: crates/hyperswitch_constraint_graph/src/graph.rs
// Module: hyperswitch_constraint_graph

pub fn get_viz_digraph_string(&self) -> String


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

impl ConnectorValidation for for Juspaythreedsserver


// File: crates/hyperswitch_constraint_graph/src/error.rs
// Module: hyperswitch_constraint_graph
// Public functions: 1

use std::sync::{Arc, Weak};

use crate::types::{Metadata, NodeValue, Relation, RelationResolution, ValueNode};

#[derive(Debug, Clone, serde::Serialize)]
#[serde(tag = "type", content = "predecessor", rename_all = "snake_case")]
pub enum ValueTracePredecessor<V: ValueNode> {
    Mandatory(Box<Weak<AnalysisTrace<V>>>),
    OneOf(Vec<Weak<AnalysisTrace<V>>>),
}

#[derive(Debug, Clone, serde::Serialize)]
#[serde(tag = "type", content = "trace", rename_all = "snake_case")]
pub enum AnalysisTrace<V: ValueNode> {
    Value {
        value: NodeValue<V>,
        relation: Relation,
        predecessors: Option<ValueTracePredecessor<V>>,
        info: Option<&'static str>,
        metadata: Option<Arc<dyn Metadata>>,
    },

    AllAggregation {
        unsatisfied: Vec<Weak<AnalysisTrace<V>>>,
        info: Option<&'static str>,
        metadata: Option<Arc<dyn Metadata>>,
    },

    AnyAggregation {
        unsatisfied: Vec<Weak<AnalysisTrace<V>>>,
        info: Option<&'static str>,
        metadata: Option<Arc<dyn Metadata>>,
    },

    InAggregation {
        expected: Vec<V>,
        found: Option<V>,
        relation: Relation,
        info: Option<&'static str>,
        metadata: Option<Arc<dyn Metadata>>,
    },
    Contradiction {
        relation: RelationResolution,
    },
}

#[derive(Debug, Clone, serde::Serialize, thiserror::Error)]
#[serde(tag = "type", content = "info", rename_all = "snake_case")]
pub enum GraphError<V: ValueNode> {
    #[error("An edge was not found in the graph")]
    EdgeNotFound,
    #[error("Attempted to create a conflicting edge between two nodes")]
    ConflictingEdgeCreated,
    #[error("Cycle detected in graph")]
    CycleDetected,
    #[error("Domain wasn't found in the Graph")]
    DomainNotFound,
    #[error("Malformed Graph: {reason}")]
    MalformedGraph { reason: String },
    #[error("A node was not found in the graph")]
    NodeNotFound,
    #[error("A value node was not found: {0:#?}")]
    ValueNodeNotFound(V),
    #[error("No values provided for an 'in' aggregator node")]
    NoInAggregatorValues,
    #[error("Error during analysis: {0:#?}")]
    AnalysisError(Weak<AnalysisTrace<V>>),
}

impl<V: ValueNode> GraphError<V> {
    pub fn get_analysis_trace(self) -> Result<Weak<AnalysisTrace<V>>, Self> {
        match self {
            Self::AnalysisError(trace) => Ok(trace),
            _ => Err(self),
        }
    }
}


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

pub struct SessionRoutingChoice


// Function: seed_knowledge_graph
// File: crates/euclid_wasm/src/lib.rs
// Module: euclid_wasm

pub fn seed_knowledge_graph(mcas: JsValue) -> JsResult


// Struct: RelayNew
// File: crates/diesel_models/src/relay.rs
// Module: diesel_models
// Implementations: 0

pub struct RelayNew


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

pub mod transformers;
use std::sync::LazyLock;

use base64::Engine;
use common_enums::{enums, PaymentAction};
use common_utils::{
    crypto,
    errors::{CustomResult, ReportSwitchExt},
    ext_traits::ByteSliceExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{
        AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector, StringMajorUnit,
        StringMajorUnitForConnector, StringMinorUnit, StringMinorUnitForConnector,
    },
};
use error_stack::{Report, ResultExt};
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
        PreProcessing,
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData, PaymentsSessionData,
        PaymentsSyncData, RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsPreProcessingRouterData, PaymentsSyncRouterData,
        RefreshTokenRouterData, RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorRedirectResponse,
        ConnectorSpecifications, ConnectorValidation,
    },
    configs::Connectors,
    consts,
    disputes::DisputePayload,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{
        PaymentsAuthorizeType, PaymentsPreProcessingType, PaymentsSyncType, RefreshTokenType,
        RefundExecuteType, RefundSyncType, Response,
    },
    webhooks,
};
use masking::{Mask, PeekInterface};
use transformers as trustpay;

use crate::{
    constants::headers,
    types::ResponseRouterData,
    utils::{self, self as connector_utils, ConnectorErrorType, PaymentsPreProcessingRequestData},
};

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

impl Trustpay {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &StringMajorUnitForConnector,
            amount_converter_to_float_major_unit: &FloatMajorUnitForConnector,
            amount_converter_to_string_minor_unit: &StringMinorUnitForConnector,
        }
    }
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Trustpay
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        match req.payment_method {
            enums::PaymentMethod::BankRedirect | enums::PaymentMethod::BankTransfer => {
                let token = req
                    .access_token
                    .clone()
                    .ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
                Ok(vec![
                    (
                        headers::CONTENT_TYPE.to_string(),
                        "application/json".to_owned().into(),
                    ),
                    (
                        headers::AUTHORIZATION.to_string(),
                        format!("Bearer {}", token.token.peek()).into_masked(),
                    ),
                ])
            }
            _ => {
                let mut header = vec![(
                    headers::CONTENT_TYPE.to_string(),
                    self.get_content_type().to_string().into(),
                )];
                let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
                header.append(&mut api_key);
                Ok(header)
            }
        }
    }
}

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

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

    fn common_get_content_type(&self) -> &'static str {
        "application/x-www-form-urlencoded"
    }

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

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

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: Result<
            trustpay::TrustpayErrorResponse,
            Report<common_utils::errors::ParsingError>,
        > = res.response.parse_struct("trustpay ErrorResponse");

        match response {
            Ok(response_data) => {
                event_builder.map(|i| i.set_error_response_body(&response_data));
                router_env::logger::info!(connector_response=?response_data);
                let error_list = response_data.errors.clone().unwrap_or_default();
                let option_error_code_message =
                    utils::get_error_code_error_message_based_on_priority(
                        self.clone(),
                        error_list.into_iter().map(|errors| errors.into()).collect(),
                    );
                let reason = response_data.errors.map(|errors| {
                    errors
                        .iter()
                        .map(|error| error.description.clone())
                        .collect::<Vec<String>>()
                        .join(" & ")
                });
                Ok(ErrorResponse {
                    status_code: res.status_code,
                    code: option_error_code_message
                        .clone()
                        .map(|error_code_message| error_code_message.error_code)
                        .unwrap_or(consts::NO_ERROR_CODE.to_string()),
                    // message vary for the same code, so relying on code alone as it is unique
                    message: option_error_code_message
                        .map(|error_code_message| error_code_message.error_code)
                        .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
                    reason: reason
                        .or(response_data.description)
                        .or(response_data.payment_description),
                    attempt_status: None,
                    connector_transaction_id: response_data.instance_id,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                })
            }
            Err(error_msg) => {
                event_builder.map(|event| event.set_error(serde_json::json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
                router_env::logger::error!(deserialization_error =? error_msg);
                utils::handle_json_response_deserialization_failure(res, "trustpay")
            }
        }
    }
}

impl ConnectorValidation for Trustpay {}

impl api::Payment for Trustpay {}

impl api::PaymentToken for Trustpay {}

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

impl api::MandateSetup for Trustpay {}
impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for Trustpay
{
    fn build_request(
        &self,
        _req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(
            errors::ConnectorError::NotImplemented("Setup Mandate flow for Trustpay".to_string())
                .into(),
        )
    }
}

impl api::PaymentVoid for Trustpay {}

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

impl api::ConnectorAccessToken for Trustpay {}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Trustpay {
    fn get_url(
        &self,
        _req: &RefreshTokenRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}{}",
            connectors.trustpay.base_url_bank_redirects, "api/oauth2/token"
        ))
    }

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

    fn get_headers(
        &self,
        req: &RefreshTokenRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = trustpay::TrustpayAuthType::try_from(&req.connector_auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        let auth_value = auth
            .project_id
            .zip(auth.secret_key)
            .map(|(project_id, secret_key)| {
                format!(
                    "Basic {}",
                    common_utils::consts::BASE64_ENGINE
                        .encode(format!("{project_id}:{secret_key}"))
                )
            });
        Ok(vec![
            (
                headers::CONTENT_TYPE.to_string(),
                RefreshTokenType::get_content_type(self).to_string().into(),
            ),
            (headers::AUTHORIZATION.to_string(), auth_value.into_masked()),
        ])
    }

    fn get_request_body(
        &self,
        req: &RefreshTokenRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = trustpay::TrustpayAuthUpdateRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &RefreshTokenRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefreshTokenRouterData, errors::ConnectorError> {
        let response: trustpay::TrustpayAuthUpdateResponse = res
            .response
            .parse_struct("trustpay TrustpayAuthUpdateResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: trustpay::TrustpayAccessTokenErrorResponse = res
            .response
            .parse_struct("Trustpay AccessTokenErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.result_info.result_code.to_string(),
            // message vary for the same code, so relying on code alone as it is unique
            message: response.result_info.result_code.to_string(),
            reason: response.result_info.additional_info,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

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

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

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let id = req.request.connector_transaction_id.clone();
        match req.payment_method {
            enums::PaymentMethod::BankRedirect | enums::PaymentMethod::BankTransfer => Ok(format!(
                "{}{}/{}",
                connectors.trustpay.base_url_bank_redirects,
                "api/Payments/Payment",
                id.get_connector_transaction_id()
                    .change_context(errors::ConnectorError::MissingConnectorTransactionID)?
            )),
            _ => Ok(format!(
                "{}{}/{}",
                self.base_url(connectors),
                "api/v1/instance",
                id.get_connector_transaction_id()
                    .change_context(errors::ConnectorError::MissingConnectorTransactionID)?
            )),
        }
    }

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

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

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: trustpay::TrustpayPaymentsResponse = res
            .response
            .parse_struct("trustpay PaymentsResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        match &response {
            trustpay::TrustpayPaymentsResponse::WebhookResponse(webhook_response) => {
                let response_integrity_object = connector_utils::get_sync_integrity_object(
                    self.amount_converter_to_float_major_unit,
                    webhook_response.amount.amount,
                    webhook_response.amount.currency.to_string(),
                )?;

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

                RouterData::try_from(ResponseRouterData {
                    response,
                    data: data.clone(),
                    http_code: res.status_code,
                })
                .map(|mut router_data| {
                    router_data.request.integrity_object = Some(response_integrity_object);
                    router_data
                })
                .change_context(errors::ConnectorError::ResponseHandlingFailed)
            }

            trustpay::TrustpayPaymentsResponse::BankRedirectSync(bank_redirect_sync_response) => {
                let response_integrity_object = connector_utils::get_sync_integrity_object(
                    self.amount_converter_to_float_major_unit,
                    bank_redirect_sync_response
                        .payment_information
                        .amount
                        .amount,
                    bank_redirect_sync_response
                        .payment_information
                        .amount
                        .currency
                        .to_string(),
                )?;

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

                RouterData::try_from(ResponseRouterData {
                    response,
                    data: data.clone(),
                    http_code: res.status_code,
                })
                .map(|mut router_data| {
                    router_data.request.integrity_object = Some(response_integrity_object);
                    router_data
                })
                .change_context(errors::ConnectorError::ResponseHandlingFailed)
            }

            _ => {
                event_builder.map(|i| i.set_response_body(&response));
                router_env::logger::info!(connector_response=?response);
                RouterData::try_from(ResponseRouterData {
                    response,
                    data: data.clone(),
                    http_code: res.status_code,
                })
                .change_context(errors::ConnectorError::ResponseHandlingFailed)
            }
        }
    }
}

impl api::PaymentCapture for Trustpay {}
impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Trustpay {}

impl api::PaymentsPreProcessing for Trustpay {}

impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResponseData>
    for Trustpay
{
    fn get_headers(
        &self,
        req: &PaymentsPreProcessingRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PaymentsPreProcessingType::get_content_type(self)
                .to_string()
                .into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

    fn get_url(
        &self,
        _req: &PaymentsPreProcessingRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}{}", self.base_url(connectors), "api/v1/intent"))
    }

    fn get_request_body(
        &self,
        req: &PaymentsPreProcessingRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let req_currency = req.request.get_currency()?;
        let req_amount = req.request.get_minor_amount()?;

        let amount = utils::convert_amount(self.amount_converter, req_amount, req_currency)?;

        let connector_router_data = trustpay::TrustpayRouterData::try_from((amount, req))?;
        let connector_req =
            trustpay::TrustpayCreateIntentRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsPreProcessingRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsPreProcessingRouterData, errors::ConnectorError> {
        let response: trustpay::TrustpayCreateIntentResponse = res
            .response
            .parse_struct("TrustpayCreateIntentResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

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

impl api::PaymentSession for Trustpay {}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Trustpay {}

impl api::PaymentAuthorize for Trustpay {}

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

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

    fn get_url(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        match req.payment_method {
            enums::PaymentMethod::BankRedirect | enums::PaymentMethod::BankTransfer => Ok(format!(
                "{}{}",
                connectors.trustpay.base_url_bank_redirects, "api/Payments/Payment"
            )),
            _ => Ok(format!(
                "{}{}",
                self.base_url(connectors),
                "api/v1/purchase"
            )),
        }
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;
        let connector_router_data = trustpay::TrustpayRouterData::try_from((amount, req))?;
        let connector_req = trustpay::TrustpayPaymentsRequest::try_from(&connector_router_data)?;
        match req.payment_method {
            enums::PaymentMethod::BankRedirect | enums::PaymentMethod::BankTransfer => {
                Ok(RequestContent::Json(Box::new(connector_req)))
            }
            _ => Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))),
        }
    }

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

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: trustpay::TrustpayPaymentsResponse = res
            .response
            .parse_struct("trustpay PaymentsResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

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

impl api::Refund for Trustpay {}
impl api::RefundExecute for Trustpay {}
impl api::RefundSync for Trustpay {}

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

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

    fn get_url(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        match req.payment_method {
            enums::PaymentMethod::BankRedirect | enums::PaymentMethod::BankTransfer => Ok(format!(
                "{}{}{}{}",
                connectors.trustpay.base_url_bank_redirects,
                "api/Payments/Payment/",
                req.request.connector_transaction_id,
                "/Refund"
            )),
            _ => Ok(format!("{}{}", self.base_url(connectors), "api/v1/Refund")),
        }
    }

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

        let connector_router_data = trustpay::TrustpayRouterData::try_from((amount, req))?;
        let connector_req = trustpay::TrustpayRefundRequest::try_from(&connector_router_data)?;
        match req.payment_method {
            enums::PaymentMethod::BankRedirect | enums::PaymentMethod::BankTransfer => {
                Ok(RequestContent::Json(Box::new(connector_req)))
            }
            _ => Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))),
        }
    }

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

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: trustpay::RefundResponse = res
            .response
            .parse_struct("trustpay RefundResponse")
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

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

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

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

    fn get_url(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let id = req
            .request
            .connector_refund_id
            .to_owned()
            .ok_or(errors::ConnectorError::MissingConnectorRefundID)?;
        match req.payment_method {
            enums::PaymentMethod::BankRedirect | enums::PaymentMethod::BankTransfer => Ok(format!(
                "{}{}/{}",
                connectors.trustpay.base_url_bank_redirects, "api/Payments/Payment", id
            )),
            _ => Ok(format!(
                "{}{}/{}",
                self.base_url(connectors),
                "api/v1/instance",
                id
            )),
        }
    }

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

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: trustpay::RefundResponse = res
            .response
            .parse_struct("trustpay RefundResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        if let trustpay::RefundResponse::WebhookRefund(ref webhook_response) = response {
            let response_integrity_object = connector_utils::get_refund_integrity_object(
                self.amount_converter_to_float_major_unit,
                webhook_response.amount.amount,
                webhook_response.amount.currency.to_string(),
            )?;

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

            let new_router_data = RouterData::try_from(ResponseRouterData {
                response,
                data: data.clone(),
                http_code: res.status_code,
            });

            return new_router_data
                .map(|mut router_data| {
                    router_data.request.integrity_object = Some(response_integrity_object);
                    router_data
                })
                .change_context(errors::ConnectorError::ResponseHandlingFailed);
        }
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

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

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Trustpay {
    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let details: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        let payment_attempt_id = details
            .payment_information
            .references
            .merchant_reference
            .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?;

        match details.payment_information.credit_debit_indicator {
            trustpay::CreditDebitIndicator::Crdt => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::PaymentAttemptId(payment_attempt_id),
                ))
            }
            trustpay::CreditDebitIndicator::Dbit => {
                if details.payment_information.status == trustpay::WebhookStatus::Chargebacked {
                    Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                        api_models::payments::PaymentIdType::PaymentAttemptId(payment_attempt_id),
                    ))
                } else {
                    Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                        api_models::webhooks::RefundIdType::RefundId(payment_attempt_id),
                    ))
                }
            }
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let response: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        match (
            response.payment_information.credit_debit_indicator,
            response.payment_information.status,
        ) {
            (trustpay::CreditDebitIndicator::Crdt, trustpay::WebhookStatus::Paid) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess)
            }
            (trustpay::CreditDebitIndicator::Crdt, trustpay::WebhookStatus::Rejected) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure)
            }
            (trustpay::CreditDebitIndicator::Dbit, trustpay::WebhookStatus::Paid) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::RefundSuccess)
            }
            (trustpay::CreditDebitIndicator::Dbit, trustpay::WebhookStatus::Refunded) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::RefundSuccess)
            }
            (trustpay::CreditDebitIndicator::Dbit, trustpay::WebhookStatus::Rejected) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::RefundFailure)
            }
            (trustpay::CreditDebitIndicator::Dbit, trustpay::WebhookStatus::Chargebacked) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::DisputeLost)
            }

            (
                trustpay::CreditDebitIndicator::Dbit | trustpay::CreditDebitIndicator::Crdt,
                trustpay::WebhookStatus::Unknown,
            ) => Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported),
            (trustpay::CreditDebitIndicator::Crdt, trustpay::WebhookStatus::Refunded) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
            }
            (trustpay::CreditDebitIndicator::Crdt, trustpay::WebhookStatus::Chargebacked) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
            }
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let details: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        Ok(Box::new(details.payment_information))
    }

    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let response: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        hex::decode(response.signature)
            .change_context(errors::ConnectorError::WebhookSignatureNotFound)
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let trustpay_response: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        let response: serde_json::Value = request.body.parse_struct("Webhook Value").switch()?;
        let values = utils::collect_and_sort_values_by_removing_signature(
            &response,
            &trustpay_response.signature,
        );
        let payload = values.join("/");
        Ok(payload.into_bytes())
    }

    fn get_dispute_details(
        &self,


// File: crates/diesel_models/src/query/payout_attempt.rs
// Module: diesel_models
// Public functions: 9

use std::collections::HashSet;

use async_bb8_diesel::AsyncRunQueryDsl;
use diesel::{
    associations::HasTable,
    query_dsl::methods::{DistinctDsl, FilterDsl, SelectDsl},
    BoolExpressionMethods, ExpressionMethods,
};
use error_stack::{report, ResultExt};

use super::generics;
use crate::{
    enums,
    errors::DatabaseError,
    payout_attempt::{
        PayoutAttempt, PayoutAttemptNew, PayoutAttemptUpdate, PayoutAttemptUpdateInternal,
    },
    schema::{payout_attempt::dsl, payouts as payout_dsl},
    Payouts, PgPooledConn, StorageResult,
};

impl PayoutAttemptNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<PayoutAttempt> {
        generics::generic_insert(conn, self).await
    }
}

impl PayoutAttempt {
    pub async fn update_with_attempt_id(
        self,
        conn: &PgPooledConn,
        payout_attempt_update: PayoutAttemptUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::payout_attempt_id
                .eq(self.payout_attempt_id.to_owned())
                .and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
            PayoutAttemptUpdateInternal::from(payout_attempt_update),
        )
        .await
        {
            Err(error) => match error.current_context() {
                DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn find_by_merchant_id_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_id: &common_utils::id_type::PayoutId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_id.eq(payout_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_payout_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_attempt_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_attempt_id.eq(payout_attempt_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_connector_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_payout_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::connector_payout_id.eq(connector_payout_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_merchant_order_reference_id(
        conn: &PgPooledConn,
        merchant_id_input: &common_utils::id_type::MerchantId,
        merchant_order_reference_id_input: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id.eq(merchant_id_input.to_owned()).and(
                dsl::merchant_order_reference_id.eq(merchant_order_reference_id_input.to_owned()),
            ),
        )
        .await
    }

    pub async fn update_by_merchant_id_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_id: &common_utils::id_type::PayoutId,
        payout: PayoutAttemptUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_id.eq(payout_id.to_owned())),
            PayoutAttemptUpdateInternal::from(payout),
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(DatabaseError::NotFound).attach_printable("Error while updating payout")
        })
    }

    pub async fn update_by_merchant_id_payout_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_attempt_id: &str,
        payout: PayoutAttemptUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_attempt_id.eq(payout_attempt_id.to_owned())),
            PayoutAttemptUpdateInternal::from(payout),
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(DatabaseError::NotFound).attach_printable("Error while updating payout")
        })
    }

    pub async fn get_filters_for_payouts(
        conn: &PgPooledConn,
        payouts: &[Payouts],
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<(
        Vec<String>,
        Vec<enums::Currency>,
        Vec<enums::PayoutStatus>,
        Vec<enums::PayoutType>,
    )> {
        let active_attempt_ids = payouts
            .iter()
            .map(|payout| {
                format!(
                    "{}_{}",
                    payout.payout_id.get_string_repr(),
                    payout.attempt_count.clone()
                )
            })
            .collect::<Vec<String>>();

        let active_payout_ids = payouts
            .iter()
            .map(|payout| payout.payout_id.to_owned())
            .collect::<Vec<common_utils::id_type::PayoutId>>();

        let filter = <Self as HasTable>::table()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(dsl::payout_attempt_id.eq_any(active_attempt_ids));

        let payouts_filter = <Payouts as HasTable>::table()
            .filter(payout_dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(payout_dsl::payout_id.eq_any(active_payout_ids));

        let payout_status: Vec<enums::PayoutStatus> = payouts
            .iter()
            .map(|payout| payout.status)
            .collect::<HashSet<enums::PayoutStatus>>()
            .into_iter()
            .collect();

        let filter_connector = filter
            .clone()
            .select(dsl::connector)
            .distinct()
            .get_results_async::<Option<String>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by connector")?
            .into_iter()
            .flatten()
            .collect::<Vec<String>>();

        let filter_currency = payouts_filter
            .clone()
            .select(payout_dsl::destination_currency)
            .distinct()
            .get_results_async::<enums::Currency>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by currency")?
            .into_iter()
            .collect::<Vec<enums::Currency>>();

        let filter_payout_method = payouts_filter
            .clone()
            .select(payout_dsl::payout_type)
            .distinct()
            .get_results_async::<Option<enums::PayoutType>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by payout type")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::PayoutType>>();

        Ok((
            filter_connector,
            filter_currency,
            payout_status,
            filter_payout_method,
        ))
    }
}


// Struct: AuthorizedPaymentMethod
// File: crates/hyperswitch_connectors/src/connectors/klarna/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AuthorizedPaymentMethod


// Trait: TransitionTo
// File: crates/router/src/core/payment_methods/tokenize.rs
// Module: router

pub trait TransitionTo<S: State>


// Implementation: impl CardDetailUpdate
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Methods: 1 total (1 public)

impl CardDetailUpdate


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

pub struct ResetPasswordRequest


// Implementation: impl api::PaymentAuthorize for for Authorizedotnet
// File: crates/hyperswitch_connectors/src/connectors/authorizedotnet.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Authorizedotnet


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

impl api::PaymentCapture for for Payu


// Struct: MifinityErrorList
// File: crates/hyperswitch_connectors/src/connectors/mifinity/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MifinityErrorList


// Function: retrieve_card_info
// File: crates/router/src/core/cards_info.rs
// Module: router

pub fn retrieve_card_info(
    state: routes::SessionState,
    merchant_context: domain::MerchantContext,
    request: cards_info_api_types::CardsInfoRequest,
) -> RouterResponse<cards_info_api_types::CardInfoResponse>


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

impl ConnectorSpecifications for for Jpmorgan


// Struct: MerchantConnectorAccount
// File: crates/diesel_models/src/merchant_connector_account.rs
// Module: diesel_models
// Implementations: 2

pub struct MerchantConnectorAccount


// Function: concat_strings
// File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
// Module: hyperswitch_connectors

// Documentation: Concatenates a vector of strings without any separator This is useful for creating verification messages for webhooks

pub fn concat_strings(strings: &[String]) -> String


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

pub struct WiseAuthType


// Function: get_value
// File: crates/redis_interface/src/types.rs
// Module: redis_interface

pub fn get_value(&self) -> &T


// Function: get_vault_response_for_retrieve_payment_method_data
// File: crates/router/src/core/payment_methods/vault.rs
// Module: router

pub fn get_vault_response_for_retrieve_payment_method_data<F>(
    router_data: VaultRouterData<F>,
) -> RouterResult<pm_types::VaultRetrieveResponse>


// Struct: FrmMetricsBucketIdentifier
// File: crates/api_models/src/analytics/frm.rs
// Module: api_models
// Implementations: 3
// Traits: Hash, PartialEq

pub struct FrmMetricsBucketIdentifier


// Implementation: impl api::ConnectorRedirectResponse for for Noon
// File: crates/hyperswitch_connectors/src/connectors/noon.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl api::ConnectorRedirectResponse for for Noon


// Function: payouts_fulfill
// File: crates/openapi/src/routes/payouts.rs
// Module: openapi

pub fn payouts_fulfill()


// Struct: TrustpayCreateIntentResponse
// File: crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TrustpayCreateIntentResponse


// Function: is_merchant_connector_account_id_in_connector_mandate_details
// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs
// Module: hyperswitch_domain_models

pub fn is_merchant_connector_account_id_in_connector_mandate_details(
            &self,
            profile_id: Option<&id_type::ProfileId>,
            connector_mandate_details: &CommonMandateReference,
        ) -> bool


// Struct: GooglePayPaymentInformation
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GooglePayPaymentInformation


// Function: get_payment_method_data
// File: crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
// Module: hyperswitch_domain_models

pub fn get_payment_method_data(&self) -> Option<api_models::payments::AdditionalPaymentData>


// Struct: TokenexInsertResponse
// File: crates/hyperswitch_connectors/src/connectors/tokenex/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TokenexInsertResponse


// Function: update_role
// File: crates/router/src/core/user_role/role.rs
// Module: router

pub fn update_role(
    state: SessionState,
    user_from_token: UserFromToken,
    req: role_api::UpdateRoleRequest,
    role_id: &str,
) -> UserResponse<role_api::RoleInfoWithGroupsResponse>


// Struct: NetworkTokenPaymentInformation
// File: crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NetworkTokenPaymentInformation


pub struct Comparison {
    /// The left hand side which will always be a domain input identifier like "payment.method.cardtype"
    pub lhs: String,
    /// The comparison operator
    pub comparison: ComparisonType,
    /// The value to compare against
    pub value: ValueType,
    /// Additional metadata that the Static Analyzer and Backend does not touch.
    /// This can be used to store useful information for the frontend and is required for communication
    /// between the static analyzer and the frontend.
    // #[schema(value_type=HashMap<String, serde_json::Value>)]
    pub metadata: Metadata,
}

/// Represents all the conditions of an IF statement
/// eg:
///
/// ```text
/// payment.method = card & payment.method.cardtype = debit & payment.method.network = diners
/// ```
pub type IfCondition = Vec<Comparison>;

/// Represents an IF statement with conditions and optional nested IF statements
///
/// ```text
/// payment.method = card {
///     payment.method.cardtype = (credit, debit) {
///         payment.method.network = (amex, rupay, diners)
///     }
/// }
/// ```
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct IfStatement {
    // #[schema(value_type=Vec<Comparison>)]
    pub condition: IfCondition,
    pub nested: Option<Vec<IfStatement>>,
}

/// Represents a rule
///
/// ```text
/// rule_name: [stripe, adyen, checkout]
/// {
///     payment.method = card {
///         payment.method.cardtype = (credit, debit) {
///             payment.method.network = (amex, rupay, diners)
///         }
///
///         payment.method.cardtype = credit
///     }
/// }
/// ```
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
// #[aliases(RuleConnectorSelection = Rule<ConnectorSelection>)]
pub struct Rule {
    pub name: String,
    #[serde(alias = "routingType")]
    pub routing_type: RoutingType,
    #[serde(alias = "routingOutput")]
    pub output: Output,
    pub statements: Vec<IfStatement>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RoutingType {
    Priority,
    VolumeSplit,
    VolumeSplitPriority,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct VolumeSplit<T> {
    pub split: u8,
    pub output: T,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ConnectorInfo {
    pub gateway_name: String,
    pub gateway_id: Option<String>,
}

impl TryFrom<ConnectorInfo> for DeRoutableConnectorChoice {
    type Error = error_stack::Report<errors::RoutingError>;

    fn try_from(c: ConnectorInfo) -> Result<Self, Self::Error> {
        let gateway_id = c
            .gateway_id
            .map(|mca| {
                id_type::MerchantConnectorAccountId::wrap(mca)
                    .change_context(errors::RoutingError::GenericConversionError {
                        from: "String".to_string(),
                        to: "MerchantConnectorAccountId".to_string(),
                    })
                    .attach_printable("unable to convert MerchantConnectorAccountId from string")
            })
            .transpose()?;

        let gateway_name = RoutableConnectors::from_str(&c.gateway_name)
            .map_err(|_| errors::RoutingError::GenericConversionError {
                from: "String".to_string(),
                to: "RoutableConnectors".to_string(),
            })
            .attach_printable("unable to convert connector name to RoutableConnectors")?;

        Ok(Self {
            gateway_name,
            gateway_id,
        })
    }
}

impl ConnectorInfo {
    pub fn new(gateway_name: String, gateway_id: Option<String>) -> Self {
        Self {
            gateway_name,
            gateway_id,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Output {
    Single(ConnectorInfo),
    Priority(Vec<ConnectorInfo>),
    VolumeSplit(Vec<VolumeSplit<ConnectorInfo>>),
    VolumeSplitPriority(Vec<VolumeSplit<Vec<ConnectorInfo>>>),
}

pub type Globals = HashMap<String, HashSet<ValueType>>;

/// The program, having a default connector selection and
/// a bunch of rules. Also can hold arbitrary metadata.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
// #[aliases(ProgramConnectorSelection = Program<ConnectorSelection>)]
pub struct Program {
    pub globals: Globals,
    pub default_selection: Output,
    // #[schema(value_type=RuleConnectorSelection)]
    pub rules: Vec<Rule>,
    // #[schema(value_type=HashMap<String, serde_json::Value>)]
    pub metadata: Option<Metadata>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RoutingRule {
    pub rule_id: Option<String>,
    pub name: String,
    pub description: Option<String>,
    pub metadata: Option<RoutingMetadata>,
    pub created_by: String,
    #[serde(default)]
    pub algorithm_for: AlgorithmType,
    pub algorithm: StaticRoutingAlgorithm,
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, strum::Display)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum AlgorithmType {
    #[default]
    Payment,
    Payout,
    ThreeDsAuthentication,
}

impl From<TransactionType> for AlgorithmType {
    fn from(transaction_type: TransactionType) -> Self {
        match transaction_type {
            TransactionType::Payment => Self::Payment,
            TransactionType::Payout => Self::Payout,
            TransactionType::ThreeDsAuthentication => Self::ThreeDsAuthentication,
        }
    }
}

impl From<RoutableConnectorChoice> for ConnectorInfo {
    fn from(c: RoutableConnectorChoice) -> Self {
        Self {
            gateway_name: c.connector.to_string(),
            gateway_id: c
                .merchant_connector_id
                .map(|mca_id| mca_id.get_string_repr().to_string()),
        }
    }
}

impl From<Box<RoutableConnectorChoice>> for ConnectorInfo {
    fn from(c: Box<RoutableConnectorChoice>) -> Self {
        Self {
            gateway_name: c.connector.to_string(),
            gateway_id: c
                .merchant_connector_id
                .map(|mca_id| mca_id.get_string_repr().to_string()),
        }
    }
}

impl From<ConnectorVolumeSplit> for VolumeSplit<ConnectorInfo> {
    fn from(v: ConnectorVolumeSplit) -> Self {
        Self {
            split: v.split,
            output: ConnectorInfo {
                gateway_name: v.connector.connector.to_string(),
                gateway_id: v
                    .connector
                    .merchant_connector_id
                    .map(|mca_id| mca_id.get_string_repr().to_string()),
            },
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
pub enum StaticRoutingAlgorithm {
    Single(Box<ConnectorInfo>),
    Priority(Vec<ConnectorInfo>),
    VolumeSplit(Vec<VolumeSplit<ConnectorInfo>>),
    Advanced(Program),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RoutingMetadata {
    pub kind: enums::RoutingAlgorithmKind,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RoutingDictionaryRecord {
    pub rule_id: String,
    pub name: String,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RoutingAlgorithmRecord {
    pub id: id_type::RoutingId,
    pub name: String,
    pub description: Option<String>,
    pub created_by: id_type::ProfileId,
    pub algorithm_data: StaticRoutingAlgorithm,
    pub algorithm_for: TransactionType,
    pub metadata: Option<RoutingMetadata>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
}

impl From<RoutingAlgorithmRecord> for routing_algorithm::RoutingProfileMetadata {
    fn from(record: RoutingAlgorithmRecord) -> Self {
        let kind = match record.algorithm_data {
            StaticRoutingAlgorithm::Single(_) => enums::RoutingAlgorithmKind::Single,
            StaticRoutingAlgorithm::Priority(_) => enums::RoutingAlgorithmKind::Priority,
            StaticRoutingAlgorithm::VolumeSplit(_) => enums::RoutingAlgorithmKind::VolumeSplit,
            StaticRoutingAlgorithm::Advanced(_) => enums::RoutingAlgorithmKind::Advanced,
        };
        Self {
            profile_id: record.created_by,
            algorithm_id: record.id,
            name: record.name,
            description: record.description,
            kind,
            created_at: record.created_at,
            modified_at: record.modified_at,
            algorithm_for: record.algorithm_for,
        }
    }
}

impl TryFrom<ast::Program<ConnectorSelection>> for Program {
    type Error = error_stack::Report<errors::RoutingError>;

    fn try_from(p: ast::Program<ConnectorSelection>) -> Result<Self, Self::Error> {
        let rules = p
            .rules
            .into_iter()
            .map(convert_rule)
            .collect::<Result<Vec<_>, _>>()?;

        Ok(Self {
            globals: HashMap::new(),
            default_selection: convert_output(p.default_selection),
            rules,
            metadata: Some(p.metadata),
        })
    }
}

impl TryFrom<ast::Program<ConnectorSelection>> for StaticRoutingAlgorithm {
    type Error = error_stack::Report<errors::RoutingError>;

    fn try_from(p: ast::Program<ConnectorSelection>) -> Result<Self, Self::Error> {
        let internal_program: Program = p.try_into()?;
        Ok(Self::Advanced(internal_program))
    }
}

fn convert_rule(rule: ast::Rule<ConnectorSelection>) -> RoutingResult<Rule> {
    let routing_type = match &rule.connector_selection {
        ConnectorSelection::Priority(_) => RoutingType::Priority,
        ConnectorSelection::VolumeSplit(_) => RoutingType::VolumeSplit,
    };

    Ok(Rule {
        name: rule.name,
        routing_type,
        output: convert_output(rule.connector_selection),
        statements: rule
            .statements
            .into_iter()
            .map(convert_if_stmt)
            .collect::<RoutingResult<Vec<IfStatement>>>()?,
    })
}

fn convert_if_stmt(stmt: ast::IfStatement) -> RoutingResult<IfStatement> {
    Ok(IfStatement {
        condition: stmt
            .condition
            .into_iter()
            .map(convert_comparison)
            .collect::<RoutingResult<Vec<Comparison>>>()?,

        nested: stmt
            .nested
            .map(|v| {
                v.into_iter()
                    .map(convert_if_stmt)
                    .collect::<RoutingResult<Vec<IfStatement>>>()
            })
            .transpose()?,
    })
}

fn convert_comparison(c: ast::Comparison) -> RoutingResult<Comparison> {
    Ok(Comparison {
        lhs: c.lhs,
        comparison: convert_comparison_type(c.comparison),
        value: convert_value(c.value)?,
        metadata: c.metadata,
    })
}

fn convert_comparison_type(ct: ast::ComparisonType) -> ComparisonType {
    match ct {
        ast::ComparisonType::Equal => ComparisonType::Equal,
        ast::ComparisonType::NotEqual => ComparisonType::NotEqual,
        ast::ComparisonType::LessThan => ComparisonType::LessThan,
        ast::ComparisonType::LessThanEqual => ComparisonType::LessThanEqual,
        ast::ComparisonType::GreaterThan => ComparisonType::GreaterThan,
        ast::ComparisonType::GreaterThanEqual => ComparisonType::GreaterThanEqual,
    }
}

fn convert_value(v: ast::ValueType) -> RoutingResult<ValueType> {
    use ast::ValueType::*;
    match v {
        Number(n) => Ok(ValueType::Number(
            n.get_amount_as_i64().try_into().unwrap_or_default(),
        )),
        EnumVariant(e) => Ok(ValueType::EnumVariant(e)),
        MetadataVariant(m) => Ok(ValueType::MetadataVariant(MetadataValue {
            key: m.key,
            value: m.value,
        })),
        StrValue(s) => Ok(ValueType::StrValue(s)),

        NumberArray(arr) => Ok(ValueType::NumberArray(
            arr.into_iter()
                .map(|n| n.get_amount_as_i64().try_into().unwrap_or_default())
                .collect(),
        )),
        EnumVariantArray(arr) => Ok(ValueType::EnumVariantArray(arr)),
        NumberComparisonArray(arr) => Ok(ValueType::NumberComparisonArray(
            arr.into_iter()
                .map(|nc| NumberComparison {
                    comparison_type: convert_comparison_type(nc.comparison_type),
                    number: nc.number.get_amount_as_i64().try_into().unwrap_or_default(),
                })
                .collect(),
        )),
    }
}

fn convert_output(sel: ConnectorSelection) -> Output {
    match sel {
        ConnectorSelection::Priority(choices) => {
            Output::Priority(choices.into_iter().map(stringify_choice).collect())
        }
        ConnectorSelection::VolumeSplit(vs) => Output::VolumeSplit(
            vs.into_iter()
                .map(|v| VolumeSplit {
                    split: v.split,
                    output: stringify_choice(v.connector),
                })
                .collect(),
        ),
    }
}

fn stringify_choice(c: RoutableConnectorChoice) -> ConnectorInfo {
    ConnectorInfo::new(
        c.connector.to_string(),
        c.merchant_connector_id
            .map(|mca_id| mca_id.get_string_repr().to_string()),
    )
}

pub async fn select_routing_result<T>(
    state: &SessionState,
    business_profile: &business_profile::Profile,
    hyperswitch_result: T,
    de_result: T,
) -> T {
    let routing_result_source: Option<api_routing::RoutingResultSource> = state
        .store
        .find_config_by_key(&format!(
            "routing_result_source_{0}",
            business_profile.get_id().get_string_repr()
        ))
        .await
        .map(|c| c.config.parse_enum("RoutingResultSource").ok())
        .unwrap_or(None); //Ignore errors so that we can use the hyperswitch result as a fallback
    if let Some(api_routing::RoutingResultSource::DecisionEngine) = routing_result_source {
        logger::debug!(business_profile_id=?business_profile.get_id(), "Using Decision Engine routing result");
        de_result
    } else {
        logger::debug!(business_profile_id=?business_profile.get_id(), "Using Hyperswitch routing result");
        hyperswitch_result
    }
}
pub trait DecisionEngineErrorsInterface {
    fn get_error_message(&self) -> String;
    fn get_error_code(&self) -> String;
    fn get_error_data(&self) -> Option<String>;
}

#[derive(Debug)]
pub struct RoutingEventsWrapper<Req>
where
    Req: Serialize + Clone,
{
    pub tenant_id: id_type::TenantId,
    pub request_id: Option<RequestId>,
    pub payment_id: String,
    pub profile_id: id_type::ProfileId,
    pub merchant_id: id_type::MerchantId,
    pub flow: String,
    pub request: Option<Req>,
    pub parse_response: bool,
    pub log_event: bool,
    pub routing_event: Option<routing_events::RoutingEvent>,
}

#[derive(Debug)]
pub enum EventResponseType<Res>
where
    Res: Serialize + serde::de::DeserializeOwned + Clone,
{
    Structured(Res),
    String(String),
}

#[derive(Debug, Serialize)]
pub struct RoutingEventsResponse<Res>
where
    Res: Serialize + serde::de::DeserializeOwned + Clone,
{
    pub event: Option<routing_events::RoutingEvent>,
    pub response: Option<Res>,
}

impl<Res> RoutingEventsResponse<Res>
where
    Res: Serialize + serde::de::DeserializeOwned + Clone,
{
    pub fn new(event: Option<routing_events::RoutingEvent>, response: Option<Res>) -> Self {
        Self { event, response }
    }

    pub fn set_response(&mut self, response: Res) {
        self.response = Some(response);
    }

    pub fn set_event(&mut self, event: routing_events::RoutingEvent) {
        self.event = Some(event);
    }
}

impl<Req> RoutingEventsWrapper<Req>
where
    Req: Serialize + Clone,
{
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        tenant_id: id_type::TenantId,
        request_id: Option<RequestId>,
        payment_id: String,
        profile_id: id_type::ProfileId,
        merchant_id: id_type::MerchantId,
        flow: String,
        request: Option<Req>,
        parse_response: bool,
        log_event: bool,
    ) -> Self {
        Self {
            tenant_id,
            request_id,
            payment_id,
            profile_id,
            merchant_id,
            flow,
            request,
            parse_response,
            log_event,
            routing_event: None,
        }
    }

    pub fn construct_event_builder(
        self,
        url: String,
        routing_engine: routing_events::RoutingEngine,
        method: routing_events::ApiMethod,
    ) -> RoutingResult<Self> {
        let mut wrapper = self;
        let request = wrapper
            .request
            .clone()
            .ok_or(errors::RoutingError::RoutingEventsError {
                message: "Request body is missing".to_string(),
                status_code: 400,
            })?;

        let serialized_request = serde_json::to_value(&request)
            .change_context(errors::RoutingError::RoutingEventsError {
                message: "Failed to serialize RoutingRequest".to_string(),
                status_code: 500,
            })
            .attach_printable("Failed to serialize request body")?;

        let routing_event = routing_events::RoutingEvent::new(
            wrapper.tenant_id.clone(),
            "".to_string(),
            &wrapper.flow,
            serialized_request,
            url,
            method,
            wrapper.payment_id.clone(),
            wrapper.profile_id.clone(),
            wrapper.merchant_id.clone(),
            wrapper.request_id,
            routing_engine,
        );

        wrapper.set_routing_event(routing_event);

        Ok(wrapper)
    }

    pub async fn trigger_event<Res, F, Fut>(
        self,
        state: &SessionState,
        func: F,
    ) -> RoutingResult<RoutingEventsResponse<Res>>
    where
        F: FnOnce() -> Fut + Send,
        Res: Serialize + serde::de::DeserializeOwned + Clone,
        Fut: futures::Future<Output = RoutingResult<Option<Res>>> + Send,
    {
        let mut routing_event =
            self.routing_event
                .ok_or(errors::RoutingError::RoutingEventsError {
                    message: "Routing event is missing".to_string(),
                    status_code: 500,
                })?;

        let mut response = RoutingEventsResponse::new(None, None);

        let resp = func().await;
        match resp {
            Ok(ok_resp) => {
                if let Some(resp) = ok_resp {
                    routing_event.set_response_body(&resp);
                    // routing_event
                    //     .set_routable_connectors(ok_resp.get_routable_connectors().unwrap_or_default());
                    // routing_event.set_payment_connector(ok_resp.get_payment_connector());
                    routing_event.set_status_code(200);

                    response.set_response(resp.clone());
                    self.log_event
                        .then(|| state.event_handler().log_event(&routing_event));
                }
            }
            Err(err) => {
                // Need to figure out a generic way to log errors
                routing_event
                    .set_error(serde_json::json!({"error": err.current_context().to_string()}));

                match err.current_context() {
                    errors::RoutingError::RoutingEventsError { status_code, .. } => {
                        routing_event.set_status_code(*status_code);
                    }
                    _ => {
                        routing_event.set_status_code(500);
                    }
                }
                state.event_handler().log_event(&routing_event)
            }
        }

        response.set_event(routing_event);

        Ok(response)
    }

    pub fn set_log_event(&mut self, log_event: bool) {
        self.log_event = log_event;
    }

    pub fn set_request_body(&mut self, request: Req) {
        self.request = Some(request);
    }

    pub fn set_routing_event(&mut self, routing_event: routing_events::RoutingEvent) {
        self.routing_event = Some(routing_event);
    }
}

pub trait RoutingEventsInterface {
    fn get_routable_connectors(&self) -> Option<Vec<RoutableConnectorChoice>>;
    fn get_payment_connector(&self) -> Option<RoutableConnectorChoice>;
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalSuccessRateConfigEventRequest {
    pub min_aggregates_size: Option<u32>,
    pub default_success_rate: Option<f64>,
    pub specificity_level: api_routing::SuccessRateSpecificityLevel,
    pub exploration_percent: Option<f64>,
}

impl From<&api_routing::SuccessBasedRoutingConfigBody> for CalSuccessRateConfigEventRequest {
    fn from(value: &api_routing::SuccessBasedRoutingConfigBody) -> Self {
        Self {
            min_aggregates_size: value.min_aggregates_size,
            default_success_rate: value.default_success_rate,
            specificity_level: value.specificity_level,
            exploration_percent: value.exploration_percent,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalSuccessRateEventRequest {
    pub id: String,
    pub params: String,
    pub labels: Vec<String>,
    pub config: Option<CalSuccessRateConfigEventRequest>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct EliminationRoutingEventBucketConfig {
    pub bucket_size: Option<u64>,
    pub bucket_leak_interval_in_secs: Option<u64>,
}

impl From<&api_routing::EliminationAnalyserConfig> for EliminationRoutingEventBucketConfig {
    fn from(value: &api_routing::EliminationAnalyserConfig) -> Self {
        Self {
            bucket_size: value.bucket_size,
            bucket_leak_interval_in_secs: value.bucket_leak_interval_in_secs,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct EliminationRoutingEventRequest {
    pub id: String,
    pub params: String,
    pub labels: Vec<String>,
    pub config: Option<EliminationRoutingEventBucketConfig>,
}

/// API-1 types
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalContractScoreEventRequest {
    pub id: String,
    pub params: String,
    pub labels: Vec<String>,
    pub config: Option<api_routing::ContractBasedRoutingConfig>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct LabelWithScoreEventResponse {
    pub score: f64,
    pub label: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalSuccessRateEventResponse {
    pub labels_with_score: Vec<LabelWithScoreEventResponse>,
    pub routing_approach: RoutingApproach,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl TryFrom<&ir_client::success_rate_client::CalSuccessRateResponse>
    for CalSuccessRateEventResponse
{
    type Error = errors::RoutingError;

    fn try_from(
        value: &ir_client::success_rate_client::CalSuccessRateResponse,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            labels_with_score: value
                .labels_with_score
                .iter()
                .map(|l| LabelWithScoreEventResponse {
                    score: l.score,
                    label: l.label.clone(),
                })
                .collect(),
            routing_approach: match value.routing_approach {
                0 => RoutingApproach::Exploration,
                1 => RoutingApproach::Exploitation,
                _ => {
                    return Err(errors::RoutingError::GenericNotFoundError {
                        field: "unknown routing approach from dynamic routing service".to_string(),
                    })
                }
            },
        })
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RoutingApproach {
    Exploitation,
    Exploration,
    Elimination,
    ContractBased,
    StaticRouting,
    Default,
}

impl RoutingApproach {
    pub fn from_decision_engine_approach(approach: &str) -> Self {
        match approach {
            "SR_SELECTION_V3_ROUTING" => Self::Exploitation,
            "SR_V3_HEDGING" => Self::Exploration,
            _ => Self::Default,
        }
    }
}

impl From<RoutingApproach> for common_enums::RoutingApproach {
    fn from(approach: RoutingApproach) -> Self {
        match approach {
            RoutingApproach::Exploitation => Self::SuccessRateExploitation,
            RoutingApproach::Exploration => Self::SuccessRateExploration,
            RoutingApproach::ContractBased => Self::ContractBasedRouting,
            RoutingApproach::StaticRouting => Self::RuleBasedRouting,
            _ => Self::DefaultFallback,
        }
    }
}

impl std::fmt::Display for RoutingApproach {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Exploitation => write!(f, "Exploitation"),
            Self::Exploration => write!(f, "Exploration"),
            Self::Elimination => write!(f, "Elimination"),
            Self::ContractBased => write!(f, "ContractBased"),
            Self::StaticRouting => write!(f, "StaticRouting"),
            Self::Default => write!(f, "Default"),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct BucketInformationEventResponse {
    pub is_eliminated: bool,
    pub bucket_name: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct EliminationInformationEventResponse {
    pub entity: Option<BucketInformationEventResponse>,
    pub global: Option<BucketInformationEventResponse>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct LabelWithStatusEliminationEventResponse {
    pub label: String,
    pub elimination_information: Option<EliminationInformationEventResponse>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct EliminationEventResponse {
    pub labels_with_status: Vec<LabelWithStatusEliminationEventResponse>,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl From<&ir_client::elimination_based_client::EliminationResponse> for EliminationEventResponse {
    fn from(value: &ir_client::elimination_based_client::EliminationResponse) -> Self {
        Self {
            labels_with_status: value
                .labels_with_status
                .iter()
                .map(
                    |label_with_status| LabelWithStatusEliminationEventResponse {
                        label: label_with_status.label.clone(),
                        elimination_information: label_with_status
                            .elimination_information
                            .as_ref()
                            .map(|info| EliminationInformationEventResponse {
                                entity: info.entity.as_ref().map(|entity_info| {
                                    BucketInformationEventResponse {
                                        is_eliminated: entity_info.is_eliminated,
                                        bucket_name: entity_info.bucket_name.clone(),
                                    }
                                }),
                                global: info.global.as_ref().map(|global_info| {
                                    BucketInformationEventResponse {
                                        is_eliminated: global_info.is_eliminated,
                                        bucket_name: global_info.bucket_name.clone(),
                                    }
                                }),
                            }),
                    },
                )
                .collect(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ScoreDataEventResponse {
    pub score: f64,
    pub label: String,
    pub current_count: u64,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalContractScoreEventResponse {
    pub labels_with_score: Vec<ScoreDataEventResponse>,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl From<&ir_client::contract_routing_client::CalContractScoreResponse>
    for CalContractScoreEventResponse
{
    fn from(value: &ir_client::contract_routing_client::CalContractScoreResponse) -> Self {
        Self {
            labels_with_score: value
                .labels_with_score
                .iter()
                .map(|label_with_score| ScoreDataEventResponse {
                    score: label_with_score.score,
                    label: label_with_score.label.clone(),
                    current_count: label_with_score.current_count,
                })
                .collect(),
        }
    }
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalGlobalSuccessRateConfigEventRequest {
    pub entity_min_aggregates_size: u32,
    pub entity_default_success_rate: f64,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalGlobalSuccessRateEventRequest {
    pub entity_id: String,
    pub entity_params: String,
    pub entity_labels: Vec<String>,
    pub global_labels: Vec<String>,
    pub config: Option<CalGlobalSuccessRateConfigEventRequest>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateSuccessRateWindowConfig {
    pub max_aggregates_size: Option<u32>,
    pub current_block_threshold: Option<api_routing::CurrentBlockThreshold>,
}

impl From<&api_routing::SuccessBasedRoutingConfigBody> for UpdateSuccessRateWindowConfig {
    fn from(value: &api_routing::SuccessBasedRoutingConfigBody) -> Self {
        Self {
            max_aggregates_size: value.max_aggregates_size,
            current_block_threshold: value.current_block_threshold.clone(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateLabelWithStatusEventRequest {
    pub label: String,
    pub status: bool,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateSuccessRateWindowEventRequest {
    pub id: String,
    pub params: String,
    pub labels_with_status: Vec<UpdateLabelWithStatusEventRequest>,
    pub config: Option<UpdateSuccessRateWindowConfig>,
    pub global_labels_with_status: Vec<UpdateLabelWithStatusEventRequest>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateSuccessRateWindowEventResponse {
    pub status: UpdationStatusEventResponse,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl TryFrom<&ir_client::success_rate_client::UpdateSuccessRateWindowResponse>
    for UpdateSuccessRateWindowEventResponse
{
    type Error = errors::RoutingError;

    fn try_from(
        value: &ir_client::success_rate_client::UpdateSuccessRateWindowResponse,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: match value.status {
                0 => UpdationStatusEventResponse::WindowUpdationSucceeded,
                1 => UpdationStatusEventResponse::WindowUpdationFailed,
                _ => {
                    return Err(errors::RoutingError::GenericNotFoundError {
                        field: "unknown updation status from dynamic routing service".to_string(),
                    })
                }
            },
        })
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UpdationStatusEventResponse {
    WindowUpdationSucceeded,
    WindowUpdationFailed,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct LabelWithBucketNameEventRequest {
    pub label: String,
    pub bucket_name: String,
}

impl From<&api_routing::RoutableConnectorChoiceWithBucketName> for LabelWithBucketNameEventRequest {
    fn from(value: &api_routing::RoutableConnectorChoiceWithBucketName) -> Self {
        Self {
            label: value.routable_connector_choice.to_string(),
            bucket_name: value.bucket_name.clone(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateEliminationBucketEventRequest {
    pub id: String,
    pub params: String,
    pub labels_with_bucket_name: Vec<LabelWithBucketNameEventRequest>,
    pub config: Option<EliminationRoutingEventBucketConfig>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateEliminationBucketEventResponse {
    pub status: EliminationUpdationStatusEventResponse,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl TryFrom<&ir_client::elimination_based_client::UpdateEliminationBucketResponse>
    for UpdateEliminationBucketEventResponse
{
    type Error = errors::RoutingError;

    fn try_from(
        value: &ir_client::elimination_based_client::UpdateEliminationBucketResponse,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: match value.status {
                0 => EliminationUpdationStatusEventResponse::BucketUpdationSucceeded,
                1 => EliminationUpdationStatusEventResponse::BucketUpdationFailed,
                _ => {
                    return Err(errors::RoutingError::GenericNotFoundError {
                        field: "unknown updation status from dynamic routing service".to_string(),
                    })
                }
            },
        })
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EliminationUpdationStatusEventResponse {
    BucketUpdationSucceeded,
    BucketUpdationFailed,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ContractLabelInformationEventRequest {
    pub label: String,
    pub target_count: u64,
    pub target_time: u64,
    pub current_count: u64,
}

impl From<&api_routing::LabelInformation> for ContractLabelInformationEventRequest {
    fn from(value: &api_routing::LabelInformation) -> Self {
        Self {
            label: value.label.clone(),
            target_count: value.target_count,
            target_time: value.target_time,
            current_count: 1,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateContractRequestEventRequest {
    pub id: String,
    pub params: String,
    pub labels_information: Vec<ContractLabelInformationEventRequest>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateContractEventResponse {
    pub status: ContractUpdationStatusEventResponse,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl TryFrom<&ir_client::contract_routing_client::UpdateContractResponse>
    for UpdateContractEventResponse
{
    type Error = errors::RoutingError;

    fn try_from(
        value: &ir_client::contract_routing_client::UpdateContractResponse,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: match value.status {
                0 => ContractUpdationStatusEventResponse::ContractUpdationSucceeded,
                1 => ContractUpdationStatusEventResponse::ContractUpdationFailed,
                _ => {
                    return Err(errors::RoutingError::GenericNotFoundError {
                        field: "unknown updation status from dynamic routing service".to_string(),
                    })
                }
            },
        })
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContractUpdationStatusEventResponse {
    ContractUpdationSucceeded,
    ContractUpdationFailed,
}