// Implementation: impl GetPaymentMethodType for for MobilePaymentData
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Methods: 1 total (0 public)

impl GetPaymentMethodType for for MobilePaymentData


// Function: payments_list_by_filter
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn payments_list_by_filter(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Json<payment_types::PaymentListFilterConstraints>,
) -> impl Responder


// Implementation: impl api::RefundSync for for Square
// File: crates/hyperswitch_connectors/src/connectors/square.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundSync for for Square


// Implementation: impl api::Payment for for Iatapay
// File: crates/hyperswitch_connectors/src/connectors/iatapay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Payment for for Iatapay


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

pub fn get_total_amount(&self) -> MinorUnit


// File: crates/router/src/types/storage/fraud_check.rs
// Module: router

pub use diesel_models::fraud_check::{
    FraudCheck, FraudCheckNew, FraudCheckUpdate, FraudCheckUpdateInternal,
};


// File: crates/analytics/src/disputes/metrics/sessionized_metrics/dispute_status_metric.rs
// Module: analytics

use std::collections::HashSet;

use api_models::analytics::{
    disputes::{DisputeDimensions, DisputeFilters, DisputeMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::DisputeMetricRow;
use crate::{
    enums::AuthInfo,
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};
#[derive(Default)]
pub(crate) struct DisputeStatusMetric {}

#[async_trait::async_trait]
impl<T> super::DisputeMetric<T> for DisputeStatusMetric
where
    T: AnalyticsDataSource + super::DisputeMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[DisputeDimensions],
        auth: &AuthInfo,
        filters: &DisputeFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(DisputeMetricsBucketIdentifier, DisputeMetricRow)>>
    where
        T: AnalyticsDataSource + super::DisputeMetricAnalytics,
    {
        let mut query_builder = QueryBuilder::new(AnalyticsCollection::DisputeSessionized);

        for dim in dimensions {
            query_builder.add_select_column(dim).switch()?;
        }

        query_builder.add_select_column("dispute_status").switch()?;

        query_builder
            .add_select_column(Aggregate::Count {
                field: None,
                alias: Some("count"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        filters.set_filter_clause(&mut query_builder).switch()?;

        auth.set_filter_clause(&mut query_builder).switch()?;

        time_range.set_filter_clause(&mut query_builder).switch()?;

        for dim in dimensions {
            query_builder.add_group_by_clause(dim).switch()?;
        }

        query_builder
            .add_group_by_clause("dispute_status")
            .switch()?;

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .switch()?;
        }

        query_builder
            .execute_query::<DisputeMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    DisputeMetricsBucketIdentifier::new(
                        i.dispute_stage.as_ref().map(|i| i.0),
                        i.connector.clone(),
                        i.currency.as_ref().map(|i| i.0),
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(DisputeMetricsBucketIdentifier, DisputeMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


// Function: with_additional_payment_method_data
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models

pub fn with_additional_payment_method_data(
        additional_payment_method_data: AdditionalPaymentMethodConnectorResponse,
    ) -> Self


// Function: log_event
// File: crates/router/src/services/kafka.rs
// Module: router

pub fn log_event<T: KafkaMessage>(&self, event: &T) -> MQResult<()>


// Struct: SubscriptionPlans
// File: crates/hyperswitch_domain_models/src/router_response_types/subscriptions.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct SubscriptionPlans


// Struct: NexinetsAuthType
// File: crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NexinetsAuthType


// Struct: ReconToken
// File: crates/router/src/services/authentication.rs
// Module: router
// Implementations: 1

pub struct ReconToken


// Struct: ZenBrowserDetails
// File: crates/hyperswitch_connectors/src/connectors/zen/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ZenBrowserDetails


// Struct: RazorpayMetaData
// File: crates/hyperswitch_connectors/src/connectors/razorpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RazorpayMetaData


// Struct: EntityInfo
// File: crates/router/src/types/domain/user.rs
// Module: router
// Implementations: 0

pub struct EntityInfo


impl webhooks::IncomingWebhook for Nexixpay {
    fn get_webhook_object_reference_id(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}

lazy_static! {
    static ref NEXIXPAY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Nexixpay",
        description: "Nexixpay is an Italian bank that specialises in payment systems such as Nexi Payments (formerly known as CartaSi).",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Sandbox,
    };

    static ref NEXIXPAY_SUPPORTED_PAYMENT_METHODS: SupportedPaymentMethods = {

        let supported_capture_methods = vec![
                    enums::CaptureMethod::Automatic,
                    enums::CaptureMethod::Manual,
                    enums::CaptureMethod::SequentialAutomatic,
                ];

        let supported_card_network = vec![
                    common_enums::CardNetwork::Visa,
                    common_enums::CardNetwork::Mastercard,
                    common_enums::CardNetwork::AmericanExpress,
                    common_enums::CardNetwork::JCB,
                ];

        let mut nexixpay_supported_payment_methods = SupportedPaymentMethods::new();

        nexixpay_supported_payment_methods.add(
            enums::PaymentMethod::Card,
            enums::PaymentMethodType::Credit,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::Supported,
                refunds: common_enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::Supported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                )
            }
        );
        nexixpay_supported_payment_methods.add(
            enums::PaymentMethod::Card,
            enums::PaymentMethodType::Debit,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::Supported,
                refunds: common_enums::FeatureStatus::Supported,
                supported_capture_methods,
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::Supported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network,
                        }
                    }),
                )
            }
        );

        nexixpay_supported_payment_methods
    };

    static ref NEXIXPAY_SUPPORTED_WEBHOOK_FLOWS: Vec<enums::EventClass> = Vec::new();
}

impl ConnectorSpecifications for Nexixpay {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&*NEXIXPAY_CONNECTOR_INFO)
    }

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

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


// File: crates/hyperswitch_interfaces/src/api/payouts.rs
// Module: hyperswitch_interfaces

//! Payouts interface

use hyperswitch_domain_models::{
    router_flow_types::payouts::{
        PoCancel, PoCreate, PoEligibility, PoFulfill, PoQuote, PoRecipient, PoRecipientAccount,
        PoSync,
    },
    router_request_types::PayoutsData,
    router_response_types::PayoutsResponseData,
};

use super::ConnectorCommon;
use crate::api::ConnectorIntegration;

/// trait PayoutCancel
pub trait PayoutCancel: ConnectorIntegration<PoCancel, PayoutsData, PayoutsResponseData> {}

/// trait PayoutCreate
pub trait PayoutCreate: ConnectorIntegration<PoCreate, PayoutsData, PayoutsResponseData> {}

/// trait PayoutEligibility
pub trait PayoutEligibility:
    ConnectorIntegration<PoEligibility, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutFulfill
pub trait PayoutFulfill: ConnectorIntegration<PoFulfill, PayoutsData, PayoutsResponseData> {}

/// trait PayoutQuote
pub trait PayoutQuote: ConnectorIntegration<PoQuote, PayoutsData, PayoutsResponseData> {}

/// trait PayoutRecipient
pub trait PayoutRecipient:
    ConnectorIntegration<PoRecipient, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutRecipientAccount
pub trait PayoutRecipientAccount:
    ConnectorIntegration<PoRecipientAccount, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutSync
pub trait PayoutSync: ConnectorIntegration<PoSync, PayoutsData, PayoutsResponseData> {}

#[cfg(feature = "payouts")]
/// trait Payouts
pub trait Payouts:
    ConnectorCommon
    + PayoutCancel
    + PayoutCreate
    + PayoutEligibility
    + PayoutFulfill
    + PayoutQuote
    + PayoutRecipient
    + PayoutRecipientAccount
    + PayoutSync
{
}

/// Empty trait for when payouts feature is disabled
#[cfg(not(feature = "payouts"))]
pub trait Payouts {}


// Implementation: impl ClickhouseClient
// File: crates/analytics/src/clickhouse.rs
// Module: analytics
// Methods: 1 total (0 public)

impl ClickhouseClient


// Struct: BankAccountCredentialsOptions
// File: crates/pm_auth/src/connector/plaid/transformers.rs
// Module: pm_auth
// Implementations: 0

pub struct BankAccountCredentialsOptions


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

pub fn decide_gateway_open_router(
    state: SessionState,
    req_body: OpenRouterDecideGatewayRequest,
) -> RouterResponse<DecideGatewayResponse>


// Function: cancel_payout
// File: crates/router/src/core/payouts.rs
// Module: router

pub fn cancel_payout(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector_data: &api::ConnectorData,
    payout_data: &mut PayoutData,
) -> RouterResult<()>


// Struct: SkrillDetails
// File: crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SkrillDetails


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

pub fn get_valid_connectors_for_rule(rule: JsValue) -> JsResult


// Struct: SetupIntents
// File: crates/router/src/compatibility/stripe/app.rs
// Module: router
// Implementations: 1

pub struct SetupIntents


// Function: connector_transaction_id
// File: crates/hyperswitch_connectors/src/connectors/helcim.rs
// Module: hyperswitch_connectors

pub fn connector_transaction_id(
        &self,
        connector_meta: Option<&serde_json::Value>,
    ) -> CustomResult<Option<String>, errors::ConnectorError>


// Struct: RecipientCreateAddress
// File: crates/pm_auth/src/types.rs
// Module: pm_auth
// Implementations: 0

pub struct RecipientCreateAddress


// Struct: PlacetopayErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/placetopay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PlacetopayErrorResponse


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

impl api::Refund for for Forte


// File: crates/analytics/src/disputes/types.rs
// Module: analytics

use api_models::analytics::disputes::{DisputeDimensions, DisputeFilters};
use error_stack::ResultExt;

use crate::{
    query::{QueryBuilder, QueryFilter, QueryResult, ToSql},
    types::{AnalyticsCollection, AnalyticsDataSource},
};

impl<T> QueryFilter<T> for DisputeFilters
where
    T: AnalyticsDataSource,
    AnalyticsCollection: ToSql<T>,
{
    fn set_filter_clause(&self, builder: &mut QueryBuilder<T>) -> QueryResult<()> {
        if !self.connector.is_empty() {
            builder
                .add_filter_in_range_clause(DisputeDimensions::Connector, &self.connector)
                .attach_printable("Error adding connector filter")?;
        }

        if !self.dispute_stage.is_empty() {
            builder
                .add_filter_in_range_clause(DisputeDimensions::DisputeStage, &self.dispute_stage)
                .attach_printable("Error adding dispute stage filter")?;
        }

        if !self.currency.is_empty() {
            builder
                .add_filter_in_range_clause(DisputeDimensions::Currency, &self.currency)
                .attach_printable("Error adding currency filter")?;
        }

        Ok(())
    }
}


// Struct: ErrorResult
// File: crates/api_models/src/analytics/payments.rs
// Module: api_models
// Implementations: 0

pub struct ErrorResult


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

pub fn payouts_list_by_filter_profile()


// Struct: GetAuthEventFilterRequest
// File: crates/api_models/src/analytics.rs
// Module: api_models
// Implementations: 0

pub struct GetAuthEventFilterRequest


// Module Structure
// File: crates/router/src/routes/user.rs
// Module: router

// Public submodules:
pub mod theme;


// Implementation: impl User
// File: crates/router/src/routes/app.rs
// Module: router
// Methods: 1 total (1 public)

impl User


// Struct: RiskifiedFailedTransactionResponse
// File: crates/hyperswitch_connectors/src/connectors/riskified/transformers/api.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RiskifiedFailedTransactionResponse


// Function: deserialize_db_op
// File: crates/drainer/src/utils.rs
// Module: drainer

pub fn deserialize_db_op<'de, D>(deserializer: D) -> Result<kv::DBOperation, D::Error>
where
    D: serde::Deserializer<'de>,


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

impl api::PaymentAuthorize for for Wellsfargo


// Function: trigger_refund_to_gateway
// File: crates/router/src/core/refunds.rs
// Module: router

pub fn trigger_refund_to_gateway(
    state: &SessionState,
    refund: &diesel_refund::Refund,
    merchant_context: &domain::MerchantContext,
    payment_attempt: &storage::PaymentAttempt,
    payment_intent: &storage::PaymentIntent,
    creds_identifier: Option<String>,
    split_refunds: Option<SplitRefundsRequest>,
) -> RouterResult<diesel_refund::Refund>


// Struct: PaymentIntentsAnalyticsMetadata
// File: crates/api_models/src/analytics.rs
// Module: api_models
// Implementations: 0

pub struct PaymentIntentsAnalyticsMetadata


// Implementation: impl FraudCheckCheckout for for Riskified
// File: crates/hyperswitch_connectors/src/connectors/riskified.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl FraudCheckCheckout for for Riskified


// Struct: InfoCodes
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct InfoCodes


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

impl ConnectorValidation for for Hipay


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

pub struct DisputeMetricsBucketIdentifier


// Function: new_for_create
// File: crates/router/src/core/payments/types.rs
// Module: router

pub fn new_for_create(
        mut previous_captures: Vec<storage::Capture>,
        new_capture: storage::Capture,
    ) -> Self


// Function: generate_client_secret
// File: crates/common_utils/src/id_type/payment.rs
// Module: common_utils

// Documentation: Generate a client id for the payment id

pub fn generate_client_secret(&self) -> String


// Implementation: impl DummyConnector
// File: crates/router/src/routes/app.rs
// Module: router
// Methods: 1 total (1 public)

impl DummyConnector


// Struct: FraudCheck
// File: crates/diesel_models/src/fraud_check.rs
// Module: diesel_models
// Implementations: 0

pub struct FraudCheck


// Struct: PaymentAttemptRecordData
// File: crates/hyperswitch_domain_models/src/payments.rs
// Module: hyperswitch_domain_models
// Implementations: 1

pub struct PaymentAttemptRecordData<F>


// Function: new
// File: crates/diesel_models/src/subscription.rs
// Module: diesel_models

pub fn new(payment_method_id: Option<String>, status: Option<String>) -> Self


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

pub fn payouts_update()


// Implementation: impl api::PaymentToken for for Worldpay
// File: crates/hyperswitch_connectors/src/connectors/worldpay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Worldpay


// Implementation: impl ConnectorCommon for for Flexiti
// File: crates/hyperswitch_connectors/src/connectors/flexiti.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl ConnectorCommon for for Flexiti


// Function: get_santander_webhook_event
// File: crates/hyperswitch_connectors/src/connectors/santander/transformers.rs
// Module: hyperswitch_connectors

pub fn get_santander_webhook_event(
    event_type: FunctionType,
) -> api_models::webhooks::IncomingWebhookEvent


// Implementation: impl Bankofamerica
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica.rs
// Module: hyperswitch_connectors
// Methods: 2 total (2 public)

impl Bankofamerica


// Function: values
// File: crates/hyperswitch_constraint_graph/src/dense_map.rs
// Module: hyperswitch_constraint_graph

pub fn values(&self) -> slice::Iter<'_, V>


// Function: new_with_config_path
// File: crates/router_env/src/logger/config.rs
// Module: router_env

// Documentation: Constructor expecting config path set explicitly.

pub fn new_with_config_path(
        explicit_config_path: Option<PathBuf>,
    ) -> Result<Self, config::ConfigError>


// File: crates/euclid/src/dssa/state_machine.rs
// Module: euclid
// Public functions: 10
// Public structs: 3

use super::types::EuclidAnalysable;
use crate::{dssa::types, frontend::dir, types::Metadata};

#[derive(Debug, Clone, serde::Serialize, thiserror::Error)]
#[serde(tag = "type", content = "info", rename_all = "snake_case")]
pub enum StateMachineError {
    #[error("Index out of bounds: {0}")]
    IndexOutOfBounds(&'static str),
}

#[derive(Debug)]
struct ComparisonStateMachine<'a> {
    values: &'a [dir::DirValue],
    logic: &'a dir::DirComparisonLogic,
    metadata: &'a Metadata,
    count: usize,
    ctx_idx: usize,
}

impl<'a> ComparisonStateMachine<'a> {
    #[inline]
    fn is_finished(&self) -> bool {
        self.count + 1 >= self.values.len()
            || matches!(self.logic, dir::DirComparisonLogic::NegativeConjunction)
    }

    #[inline]
    fn advance(&mut self) {
        if let dir::DirComparisonLogic::PositiveDisjunction = self.logic {
            self.count = (self.count + 1) % self.values.len();
        }
    }

    #[inline]
    fn reset(&mut self) {
        self.count = 0;
    }

    #[inline]
    fn put(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        if let dir::DirComparisonLogic::PositiveDisjunction = self.logic {
            *context
                .get_mut(self.ctx_idx)
                .ok_or(StateMachineError::IndexOutOfBounds(
                    "in ComparisonStateMachine while indexing into context",
                ))? = types::ContextValue::assertion(
                self.values
                    .get(self.count)
                    .ok_or(StateMachineError::IndexOutOfBounds(
                        "in ComparisonStateMachine while indexing into values",
                    ))?,
                self.metadata,
            );
        }
        Ok(())
    }

    #[inline]
    fn push(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        match self.logic {
            dir::DirComparisonLogic::PositiveDisjunction => {
                context.push(types::ContextValue::assertion(
                    self.values
                        .get(self.count)
                        .ok_or(StateMachineError::IndexOutOfBounds(
                            "in ComparisonStateMachine while pushing",
                        ))?,
                    self.metadata,
                ));
            }

            dir::DirComparisonLogic::NegativeConjunction => {
                context.push(types::ContextValue::negation(self.values, self.metadata));
            }
        }
        Ok(())
    }
}

#[derive(Debug)]
struct ConditionStateMachine<'a> {
    state_machines: Vec<ComparisonStateMachine<'a>>,
    start_ctx_idx: usize,
}

impl<'a> ConditionStateMachine<'a> {
    fn new(condition: &'a [dir::DirComparison], start_idx: usize) -> Self {
        let mut machines = Vec::<ComparisonStateMachine<'a>>::with_capacity(condition.len());

        let mut machine_idx = start_idx;
        for cond in condition {
            let machine = ComparisonStateMachine {
                values: &cond.values,
                logic: &cond.logic,
                metadata: &cond.metadata,
                count: 0,
                ctx_idx: machine_idx,
            };
            machines.push(machine);
            machine_idx += 1;
        }

        Self {
            state_machines: machines,
            start_ctx_idx: start_idx,
        }
    }

    fn init(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        for machine in &self.state_machines {
            machine.push(context)?;
        }
        Ok(())
    }

    #[inline]
    fn destroy(&self, context: &mut types::ConjunctiveContext<'a>) {
        context.truncate(self.start_ctx_idx);
    }

    #[inline]
    fn is_finished(&self) -> bool {
        !self
            .state_machines
            .iter()
            .any(|machine| !machine.is_finished())
    }

    #[inline]
    fn get_next_ctx_idx(&self) -> usize {
        self.start_ctx_idx + self.state_machines.len()
    }

    fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        for machine in self.state_machines.iter_mut().rev() {
            if machine.is_finished() {
                machine.reset();
                machine.put(context)?;
            } else {
                machine.advance();
                machine.put(context)?;
                break;
            }
        }
        Ok(())
    }
}

#[derive(Debug)]
struct IfStmtStateMachine<'a> {
    condition_machine: ConditionStateMachine<'a>,
    nested: Vec<&'a dir::DirIfStatement>,
    nested_idx: usize,
}

impl<'a> IfStmtStateMachine<'a> {
    fn new(stmt: &'a dir::DirIfStatement, ctx_start_idx: usize) -> Self {
        let condition_machine = ConditionStateMachine::new(&stmt.condition, ctx_start_idx);
        let nested: Vec<&'a dir::DirIfStatement> = match &stmt.nested {
            None => Vec::new(),
            Some(nested_stmts) => nested_stmts.iter().collect(),
        };

        Self {
            condition_machine,
            nested,
            nested_idx: 0,
        }
    }

    fn init(
        &self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<Option<Self>, StateMachineError> {
        self.condition_machine.init(context)?;
        Ok(self
            .nested
            .first()
            .map(|nested| Self::new(nested, self.condition_machine.get_next_ctx_idx())))
    }

    #[inline]
    fn is_finished(&self) -> bool {
        self.nested_idx + 1 >= self.nested.len()
    }

    #[inline]
    fn is_condition_machine_finished(&self) -> bool {
        self.condition_machine.is_finished()
    }

    #[inline]
    fn destroy(&self, context: &mut types::ConjunctiveContext<'a>) {
        self.condition_machine.destroy(context);
    }

    #[inline]
    fn advance_condition_machine(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        self.condition_machine.advance(context)?;
        Ok(())
    }

    fn advance(&mut self) -> Result<Option<Self>, StateMachineError> {
        if self.nested.is_empty() {
            Ok(None)
        } else {
            self.nested_idx = (self.nested_idx + 1) % self.nested.len();
            Ok(Some(Self::new(
                self.nested
                    .get(self.nested_idx)
                    .ok_or(StateMachineError::IndexOutOfBounds(
                        "in IfStmtStateMachine while advancing",
                    ))?,
                self.condition_machine.get_next_ctx_idx(),
            )))
        }
    }
}

#[derive(Debug)]
struct RuleStateMachine<'a> {
    connector_selection_data: &'a [(dir::DirValue, Metadata)],
    connectors_added: bool,
    if_stmt_machines: Vec<IfStmtStateMachine<'a>>,
    running_stack: Vec<IfStmtStateMachine<'a>>,
}

impl<'a> RuleStateMachine<'a> {
    fn new<O>(
        rule: &'a dir::DirRule<O>,
        connector_selection_data: &'a [(dir::DirValue, Metadata)],
    ) -> Self {
        let mut if_stmt_machines: Vec<IfStmtStateMachine<'a>> =
            Vec::with_capacity(rule.statements.len());

        for stmt in rule.statements.iter().rev() {
            if_stmt_machines.push(IfStmtStateMachine::new(
                stmt,
                connector_selection_data.len(),
            ));
        }

        Self {
            connector_selection_data,
            connectors_added: false,
            if_stmt_machines,
            running_stack: Vec::new(),
        }
    }

    fn is_finished(&self) -> bool {
        self.if_stmt_machines.is_empty() && self.running_stack.is_empty()
    }

    fn init_next(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if self.if_stmt_machines.is_empty() || !self.running_stack.is_empty() {
            return Ok(());
        }

        if !self.connectors_added {
            for (dir_val, metadata) in self.connector_selection_data {
                context.push(types::ContextValue::assertion(dir_val, metadata));
            }
            self.connectors_added = true;
        }

        context.truncate(self.connector_selection_data.len());

        if let Some(mut next_running) = self.if_stmt_machines.pop() {
            while let Some(nested_running) = next_running.init(context)? {
                self.running_stack.push(next_running);
                next_running = nested_running;
            }

            self.running_stack.push(next_running);
        }

        Ok(())
    }

    fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        let mut condition_machines_finished = true;

        for stmt_machine in self.running_stack.iter_mut().rev() {
            if !stmt_machine.is_condition_machine_finished() {
                condition_machines_finished = false;
                stmt_machine.advance_condition_machine(context)?;
                break;
            } else {
                stmt_machine.advance_condition_machine(context)?;
            }
        }

        if !condition_machines_finished {
            return Ok(());
        }

        let mut maybe_next_running: Option<IfStmtStateMachine<'a>> = None;

        while let Some(last) = self.running_stack.last_mut() {
            if !last.is_finished() {
                maybe_next_running = last.advance()?;
                break;
            } else {
                last.destroy(context);
                self.running_stack.pop();
            }
        }

        if let Some(mut next_running) = maybe_next_running {
            while let Some(nested_running) = next_running.init(context)? {
                self.running_stack.push(next_running);
                next_running = nested_running;
            }

            self.running_stack.push(next_running);
        } else {
            self.init_next(context)?;
        }

        Ok(())
    }
}

#[derive(Debug)]
pub struct RuleContextManager<'a> {
    context: types::ConjunctiveContext<'a>,
    machine: RuleStateMachine<'a>,
    init: bool,
}

impl<'a> RuleContextManager<'a> {
    pub fn new<O>(
        rule: &'a dir::DirRule<O>,
        connector_selection_data: &'a [(dir::DirValue, Metadata)],
    ) -> Self {
        Self {
            context: Vec::new(),
            machine: RuleStateMachine::new(rule, connector_selection_data),
            init: false,
        }
    }

    pub fn advance(&mut self) -> Result<Option<&types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init_next(&mut self.context)?;
            Ok(Some(&self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&self.context))
            }
        }
    }

    pub fn advance_mut(
        &mut self,
    ) -> Result<Option<&mut types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init_next(&mut self.context)?;
            Ok(Some(&mut self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&mut self.context))
            }
        }
    }
}

#[derive(Debug)]
pub struct ProgramStateMachine<'a> {
    rule_machines: Vec<RuleStateMachine<'a>>,
    current_rule_machine: Option<RuleStateMachine<'a>>,
    is_init: bool,
}

impl<'a> ProgramStateMachine<'a> {
    pub fn new<O>(
        program: &'a dir::DirProgram<O>,
        connector_selection_data: &'a [Vec<(dir::DirValue, Metadata)>],
    ) -> Self {
        let mut rule_machines: Vec<RuleStateMachine<'a>> = program
            .rules
            .iter()
            .zip(connector_selection_data.iter())
            .rev()
            .map(|(rule, connector_selection_data)| {
                RuleStateMachine::new(rule, connector_selection_data)
            })
            .collect();

        Self {
            current_rule_machine: rule_machines.pop(),
            rule_machines,
            is_init: false,
        }
    }

    pub fn is_finished(&self) -> bool {
        self.current_rule_machine
            .as_ref()
            .is_none_or(|rsm| rsm.is_finished())
            && self.rule_machines.is_empty()
    }

    pub fn init(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if !self.is_init {
            if let Some(rsm) = self.current_rule_machine.as_mut() {
                rsm.init_next(context)?;
            }
            self.is_init = true;
        }

        Ok(())
    }

    pub fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if self
            .current_rule_machine
            .as_ref()
            .is_none_or(|rsm| rsm.is_finished())
        {
            self.current_rule_machine = self.rule_machines.pop();
            context.clear();
            if let Some(rsm) = self.current_rule_machine.as_mut() {
                rsm.init_next(context)?;
            }
        } else if let Some(rsm) = self.current_rule_machine.as_mut() {
            rsm.advance(context)?;
        }

        Ok(())
    }
}

pub struct AnalysisContextManager<'a> {
    context: types::ConjunctiveContext<'a>,
    machine: ProgramStateMachine<'a>,
    init: bool,
}

impl<'a> AnalysisContextManager<'a> {
    pub fn new<O>(
        program: &'a dir::DirProgram<O>,
        connector_selection_data: &'a [Vec<(dir::DirValue, Metadata)>],
    ) -> Self {
        let machine = ProgramStateMachine::new(program, connector_selection_data);
        let context: types::ConjunctiveContext<'a> = Vec::new();

        Self {
            context,
            machine,
            init: false,
        }
    }

    pub fn advance(&mut self) -> Result<Option<&types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init(&mut self.context)?;
            Ok(Some(&self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&self.context))
            }
        }
    }
}

pub fn make_connector_selection_data<O: EuclidAnalysable>(
    program: &dir::DirProgram<O>,
) -> Vec<Vec<(dir::DirValue, Metadata)>> {
    program
        .rules
        .iter()
        .map(|rule| {
            rule.connector_selection
                .get_dir_value_for_analysis(rule.name.clone())
        })
        .collect()
}

#[cfg(all(test, feature = "ast_parser"))]
mod tests {
    #![allow(clippy::expect_used)]

    use super::*;
    use crate::{dirval, frontend::ast, types::DummyOutput};

    #[test]
    fn test_correct_contexts() {
        let program_str = r#"
            default: ["stripe", "adyen"]

            stripe_first: ["stripe", "adyen"]
            {
                payment_method = wallet {
                    payment_method = (card, bank_redirect) {
                        currency = USD
                        currency = GBP
                    }

                    payment_method = pay_later {
                        capture_method = automatic
                        capture_method = manual
                    }
                }

                payment_method = card {
                    payment_method = (card, bank_redirect) & capture_method = (automatic, manual) {
                        currency = (USD, GBP)
                    }
                }
            }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let lowered = ast::lowering::lower_program(program).expect("Lowering");

        let selection_data = make_connector_selection_data(&lowered);
        let mut state_machine = ProgramStateMachine::new(&lowered, &selection_data);
        let mut ctx: types::ConjunctiveContext<'_> = Vec::new();
        state_machine.init(&mut ctx).expect("State machine init");

        let expected_contexts: Vec<Vec<dir::DirValue>> = vec![
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
                dirval!(CaptureMethod = Automatic),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
                dirval!(CaptureMethod = Manual),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = GBP),
            ],
        ];

        let mut expected_idx = 0usize;
        while !state_machine.is_finished() {
            let values = ctx
                .iter()
                .flat_map(|c| match c.value {
                    types::CtxValueKind::Assertion(val) => vec![val],
                    types::CtxValueKind::Negation(vals) => vals.iter().collect(),
                })
                .collect::<Vec<&dir::DirValue>>();
            assert_eq!(
                values,
                expected_contexts
                    .get(expected_idx)
                    .expect("Error deriving contexts")
                    .iter()
                    .collect::<Vec<&dir::DirValue>>()
            );
            expected_idx += 1;
            state_machine
                .advance(&mut ctx)
                .expect("State Machine advance");
        }

        assert_eq!(expected_idx, 14);

        let mut ctx_manager = AnalysisContextManager::new(&lowered, &selection_data);
        expected_idx = 0;
        while let Some(ctx) = ctx_manager.advance().expect("Context Manager Context") {
            let values = ctx
                .iter()
                .flat_map(|c| match c.value {
                    types::CtxValueKind::Assertion(val) => vec![val],
                    types::CtxValueKind::Negation(vals) => vals.iter().collect(),
                })
                .collect::<Vec<&dir::DirValue>>();
            assert_eq!(
                values,
                expected_contexts
                    .get(expected_idx)
                    .expect("Error deriving contexts")
                    .iter()
                    .collect::<Vec<&dir::DirValue>>()
            );
            expected_idx += 1;
        }

        assert_eq!(expected_idx, 14);
    }
}


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

impl ConnectorValidation for for Netcetera


// Implementation: impl api::ConnectorAccessToken for for Checkout
// File: crates/hyperswitch_connectors/src/connectors/checkout.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Checkout


// Struct: TrustpaymentsPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/trustpayments/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TrustpaymentsPaymentsRequest


// File: crates/test_utils/tests/connectors/checkout_ui.rs
// Module: test_utils

use serial_test::serial;
use thirtyfour::{prelude::*, WebDriver};

use crate::{selenium::*, tester};

struct CheckoutSeleniumTest;

impl SeleniumTest for CheckoutSeleniumTest {
    fn get_connector_name(&self) -> String {
        "checkout".to_string()
    }
}

async fn should_make_frictionless_3ds_payment(c: WebDriver) -> Result<(), WebDriverError> {
    let mycon = CheckoutSeleniumTest {};
    mycon
        .make_redirection_payment(
            c,
            vec![
                Event::Trigger(Trigger::Goto(&format!("{CHECKOUT_BASE_URL}/saved/18"))),
                Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
                Event::Assert(Assert::IsPresent("Google Search")),
                Event::Trigger(Trigger::Sleep(5)), //url gets updated only after some time, so need this timeout to solve the issue
                Event::Assert(Assert::ContainsAny(
                    Selector::QueryParamStr,
                    vec!["status=succeeded", "status=processing"],
                )),
            ],
        )
        .await?;
    Ok(())
}

async fn should_make_3ds_payment(c: WebDriver) -> Result<(), WebDriverError> {
    let mycon = CheckoutSeleniumTest {};
    mycon
        .make_redirection_payment(
            c,
            vec![
                Event::Trigger(Trigger::Goto(&format!("{CHECKOUT_BASE_URL}/saved/20"))),
                Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
                Event::Trigger(Trigger::Sleep(5)), //url gets updated only after some time, so need this timeout to solve the issue
                Event::Trigger(Trigger::SwitchFrame(By::Name("cko-3ds2-iframe"))),
                Event::Trigger(Trigger::SendKeys(By::Id("password"), "Checkout1!")),
                Event::Trigger(Trigger::Click(By::Id("txtButton"))),
                Event::Trigger(Trigger::Sleep(2)),
                Event::Assert(Assert::IsPresent("Google")),
                Event::Assert(Assert::ContainsAny(
                    Selector::QueryParamStr,
                    vec!["status=succeeded", "status=processing"],
                )),
            ],
        )
        .await?;
    Ok(())
}

async fn should_make_gpay_payment(c: WebDriver) -> Result<(), WebDriverError> {
    let mycon = CheckoutSeleniumTest {};
    mycon
        .make_redirection_payment(
            c,
            vec![
                Event::Trigger(Trigger::Goto(&format!("{CHECKOUT_BASE_URL}/saved/73"))),
                Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
                Event::Trigger(Trigger::Sleep(10)), //url gets updated only after some time, so need this timeout to solve the issue
                Event::Trigger(Trigger::SwitchFrame(By::Name("cko-3ds2-iframe"))),
                Event::Trigger(Trigger::SendKeys(By::Id("password"), "Checkout1!")),
                Event::Trigger(Trigger::Click(By::Id("txtButton"))),
                Event::Assert(Assert::IsPresent("Google")),
                Event::Assert(Assert::ContainsAny(
                    Selector::QueryParamStr,
                    vec!["status=succeeded", "status=processing"],
                )),
            ],
        )
        .await?;
    Ok(())
}

#[test]
#[serial]
fn should_make_frictionless_3ds_payment_test() {
    tester!(should_make_frictionless_3ds_payment);
}

#[test]
#[serial]
fn should_make_3ds_payment_test() {
    tester!(should_make_3ds_payment);
}

#[test]
#[serial]
#[ignore]
fn should_make_gpay_payment_test() {
    tester!(should_make_gpay_payment);
}


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

impl api::Refund for for Stax


    };

    // Algorithm was in disabled state
    let Some(algo_type_algorithm_id) = algo_type
        .clone()
        .get_algorithm_id_with_timestamp()
        .algorithm_id
    else {
        return default_specific_dynamic_routing_setup(
            state,
            key_store,
            business_profile,
            feature_to_enable,
            dynamic_routing_algo_ref,
            dynamic_routing_type,
        )
        .await;
    };
    let db = state.store.as_ref();
    let profile_id = business_profile.get_id().clone();
    let algo_type_enabled_features = algo_type.get_enabled_features();
    if *algo_type_enabled_features == feature_to_enable {
        // algorithm already has the required feature
        let routing_algorithm = db
            .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algo_type_algorithm_id)
            .await
            .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
        let updated_routing_record = routing_algorithm.foreign_into();

        return Ok(ApplicationResponse::Json(updated_routing_record));
    };
    *algo_type_enabled_features = feature_to_enable;
    dynamic_routing_algo_ref.update_enabled_features(dynamic_routing_type, feature_to_enable);
    update_business_profile_active_dynamic_algorithm_ref(
        db,
        &state.into(),
        &key_store,
        business_profile,
        dynamic_routing_algo_ref.clone(),
    )
    .await?;

    let routing_algorithm = db
        .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algo_type_algorithm_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
    let updated_routing_record = routing_algorithm.foreign_into();
    core_metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.clone())),
    );
    Ok(ApplicationResponse::Json(updated_routing_record))
}

#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
#[instrument(skip_all)]
pub async fn default_specific_dynamic_routing_setup(
    state: &SessionState,
    key_store: domain::MerchantKeyStore,
    business_profile: domain::Profile,
    feature_to_enable: routing_types::DynamicRoutingFeatures,
    mut dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
    dynamic_routing_type: routing_types::DynamicRoutingType,
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
    let db = state.store.as_ref();
    let key_manager_state = &state.into();
    let profile_id = business_profile.get_id().clone();
    let merchant_id = business_profile.merchant_id.clone();
    let algorithm_id = common_utils::generate_routing_id_of_default_length();
    let timestamp = common_utils::date_time::now();

    let algo = match dynamic_routing_type {
        routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
            let default_success_based_routing_config =
                if state.conf.open_router.dynamic_routing_enabled {
                    routing_types::SuccessBasedRoutingConfig::open_router_config_default()
                } else {
                    routing_types::SuccessBasedRoutingConfig::default()
                };

            routing_algorithm::RoutingAlgorithm {
                algorithm_id: algorithm_id.clone(),
                profile_id: profile_id.clone(),
                merchant_id,
                name: SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
                description: None,
                kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
                algorithm_data: serde_json::json!(default_success_based_routing_config),
                created_at: timestamp,
                modified_at: timestamp,
                algorithm_for: common_enums::TransactionType::Payment,
                decision_engine_routing_id: None,
            }
        }
        routing_types::DynamicRoutingType::EliminationRouting => {
            let default_elimination_routing_config =
                if state.conf.open_router.dynamic_routing_enabled {
                    routing_types::EliminationRoutingConfig::open_router_config_default()
                } else {
                    routing_types::EliminationRoutingConfig::default()
                };

            routing_algorithm::RoutingAlgorithm {
                algorithm_id: algorithm_id.clone(),
                profile_id: profile_id.clone(),
                merchant_id,
                name: ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
                description: None,
                kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
                algorithm_data: serde_json::json!(default_elimination_routing_config),
                created_at: timestamp,
                modified_at: timestamp,
                algorithm_for: common_enums::TransactionType::Payment,
                decision_engine_routing_id: None,
            }
        }

        routing_types::DynamicRoutingType::ContractBasedRouting => {
            return Err((errors::ApiErrorResponse::InvalidRequestData {
                message: "Contract routing cannot be set as default".to_string(),
            })
            .into())
        }
    };

    // Call to DE here
    // Need to map out the cases if this call should always be made or not
    if state.conf.open_router.dynamic_routing_enabled {
        enable_decision_engine_dynamic_routing_setup(
            state,
            business_profile.get_id(),
            dynamic_routing_type,
            &mut dynamic_routing_algo_ref,
            None,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to setup decision engine dynamic routing")?;
    }

    let record = db
        .insert_routing_algorithm(algo)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to insert record in routing algorithm table")?;

    dynamic_routing_algo_ref.update_algorithm_id(
        algorithm_id,
        feature_to_enable,
        dynamic_routing_type,
    );
    update_business_profile_active_dynamic_algorithm_ref(
        db,
        key_manager_state,
        &key_store,
        business_profile,
        dynamic_routing_algo_ref,
    )
    .await?;

    let new_record = record.foreign_into();

    core_metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.clone())),
    );
    Ok(ApplicationResponse::Json(new_record))
}

#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
#[instrument(skip_all)]
pub async fn create_specific_dynamic_routing_setup(
    state: &SessionState,
    key_store: domain::MerchantKeyStore,
    business_profile: domain::Profile,
    feature_to_enable: routing_types::DynamicRoutingFeatures,
    mut dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
    dynamic_routing_type: routing_types::DynamicRoutingType,
    payload: routing_types::DynamicRoutingPayload,
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
    let db = state.store.as_ref();
    let key_manager_state = &state.into();
    let profile_id = business_profile.get_id().clone();
    let merchant_id = business_profile.merchant_id.clone();
    let algorithm_id = common_utils::generate_routing_id_of_default_length();
    let timestamp = common_utils::date_time::now();

    let algo = match dynamic_routing_type {
        routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
            let success_config = match &payload {
                routing_types::DynamicRoutingPayload::SuccessBasedRoutingPayload(config) => config,
                _ => {
                    return Err((errors::ApiErrorResponse::InvalidRequestData {
                        message: "Invalid payload type for Success Rate Based Routing".to_string(),
                    })
                    .into())
                }
            };

            routing_algorithm::RoutingAlgorithm {
                algorithm_id: algorithm_id.clone(),
                profile_id: profile_id.clone(),
                merchant_id,
                name: SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
                description: None,
                kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
                algorithm_data: serde_json::json!(success_config),
                created_at: timestamp,
                modified_at: timestamp,
                algorithm_for: common_enums::TransactionType::Payment,
                decision_engine_routing_id: None,
            }
        }
        routing_types::DynamicRoutingType::EliminationRouting => {
            let elimination_config = match &payload {
                routing_types::DynamicRoutingPayload::EliminationRoutingPayload(config) => config,
                _ => {
                    return Err((errors::ApiErrorResponse::InvalidRequestData {
                        message: "Invalid payload type for Elimination Routing".to_string(),
                    })
                    .into())
                }
            };

            routing_algorithm::RoutingAlgorithm {
                algorithm_id: algorithm_id.clone(),
                profile_id: profile_id.clone(),
                merchant_id,
                name: ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
                description: None,
                kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
                algorithm_data: serde_json::json!(elimination_config),
                created_at: timestamp,
                modified_at: timestamp,
                algorithm_for: common_enums::TransactionType::Payment,
                decision_engine_routing_id: None,
            }
        }

        routing_types::DynamicRoutingType::ContractBasedRouting => {
            return Err((errors::ApiErrorResponse::InvalidRequestData {
                message: "Contract routing cannot be set as default".to_string(),
            })
            .into())
        }
    };

    let record = db
        .insert_routing_algorithm(algo)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to insert record in routing algorithm table")?;

    dynamic_routing_algo_ref.update_feature(feature_to_enable, dynamic_routing_type);
    update_business_profile_active_dynamic_algorithm_ref(
        db,
        key_manager_state,
        &key_store,
        business_profile,
        dynamic_routing_algo_ref,
    )
    .await?;

    let new_record = record.foreign_into();

    core_metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.clone())),
    );
    Ok(ApplicationResponse::Json(new_record))
}

#[derive(Debug, Clone)]
pub struct DynamicRoutingConfigParamsInterpolator {
    pub payment_method: Option<common_enums::PaymentMethod>,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub authentication_type: Option<common_enums::AuthenticationType>,
    pub currency: Option<common_enums::Currency>,
    pub country: Option<common_enums::CountryAlpha2>,
    pub card_network: Option<String>,
    pub card_bin: Option<String>,
}

impl DynamicRoutingConfigParamsInterpolator {
    pub fn new(
        payment_method: Option<common_enums::PaymentMethod>,
        payment_method_type: Option<common_enums::PaymentMethodType>,
        authentication_type: Option<common_enums::AuthenticationType>,
        currency: Option<common_enums::Currency>,
        country: Option<common_enums::CountryAlpha2>,
        card_network: Option<String>,
        card_bin: Option<String>,
    ) -> Self {
        Self {
            payment_method,
            payment_method_type,
            authentication_type,
            currency,
            country,
            card_network,
            card_bin,
        }
    }

    pub fn get_string_val(
        &self,
        params: &Vec<routing_types::DynamicRoutingConfigParams>,
    ) -> String {
        let mut parts: Vec<String> = Vec::new();
        for param in params {
            let val = match param {
                routing_types::DynamicRoutingConfigParams::PaymentMethod => self
                    .payment_method
                    .as_ref()
                    .map_or(String::new(), |pm| pm.to_string()),
                routing_types::DynamicRoutingConfigParams::PaymentMethodType => self
                    .payment_method_type
                    .as_ref()
                    .map_or(String::new(), |pmt| pmt.to_string()),
                routing_types::DynamicRoutingConfigParams::AuthenticationType => self
                    .authentication_type
                    .as_ref()
                    .map_or(String::new(), |at| at.to_string()),
                routing_types::DynamicRoutingConfigParams::Currency => self
                    .currency
                    .as_ref()
                    .map_or(String::new(), |cur| cur.to_string()),
                routing_types::DynamicRoutingConfigParams::Country => self
                    .country
                    .as_ref()
                    .map_or(String::new(), |cn| cn.to_string()),
                routing_types::DynamicRoutingConfigParams::CardNetwork => {
                    self.card_network.clone().unwrap_or_default()
                }
                routing_types::DynamicRoutingConfigParams::CardBin => {
                    self.card_bin.clone().unwrap_or_default()
                }
            };
            if !val.is_empty() {
                parts.push(val);
            }
        }
        parts.join(":")
    }
}

#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
#[instrument(skip_all)]
pub async fn enable_decision_engine_dynamic_routing_setup(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    dynamic_routing_type: routing_types::DynamicRoutingType,
    dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
    payload: Option<routing_types::DynamicRoutingPayload>,
) -> RouterResult<()> {
    logger::debug!(
        "performing call with open_router for profile {}",
        profile_id.get_string_repr()
    );

    let decision_engine_config_request = match dynamic_routing_type {
        routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
            let success_based_routing_config = payload
                .and_then(|p| match p {
                    routing_types::DynamicRoutingPayload::SuccessBasedRoutingPayload(config) => {
                        Some(config)
                    }
                    _ => None,
                })
                .unwrap_or_else(
                    routing_types::SuccessBasedRoutingConfig::open_router_config_default,
                );

            open_router::DecisionEngineConfigSetupRequest {
                merchant_id: profile_id.get_string_repr().to_string(),
                config: open_router::DecisionEngineConfigVariant::SuccessRate(
                    success_based_routing_config
                        .get_decision_engine_configs()
                        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
                            message: "Decision engine config not found".to_string(),
                        })
                        .attach_printable("Decision engine config not found")?,
                ),
            }
        }
        routing_types::DynamicRoutingType::EliminationRouting => {
            let elimination_based_routing_config = payload
                .and_then(|p| match p {
                    routing_types::DynamicRoutingPayload::EliminationRoutingPayload(config) => {
                        Some(config)
                    }
                    _ => None,
                })
                .unwrap_or_else(
                    routing_types::EliminationRoutingConfig::open_router_config_default,
                );

            open_router::DecisionEngineConfigSetupRequest {
                merchant_id: profile_id.get_string_repr().to_string(),
                config: open_router::DecisionEngineConfigVariant::Elimination(
                    elimination_based_routing_config
                        .get_decision_engine_configs()
                        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
                            message: "Decision engine config not found".to_string(),
                        })
                        .attach_printable("Decision engine config not found")?,
                ),
            }
        }
        routing_types::DynamicRoutingType::ContractBasedRouting => {
            return Err((errors::ApiErrorResponse::InvalidRequestData {
                message: "Contract routing cannot be set as default".to_string(),
            })
            .into())
        }
    };

    // Create merchant in Decision Engine if it is not already created
    create_merchant_in_decision_engine_if_not_exists(state, profile_id, dynamic_routing_algo_ref)
        .await;

    routing_utils::ConfigApiClient::send_decision_engine_request::<_, serde_json::Value>(
        state,
        services::Method::Post,
        DECISION_ENGINE_RULE_CREATE_ENDPOINT,
        Some(decision_engine_config_request),
        None,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Unable to setup decision engine dynamic routing")?;

    Ok(())
}

#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
#[instrument(skip_all)]
pub async fn update_decision_engine_dynamic_routing_setup(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    request: serde_json::Value,
    dynamic_routing_type: routing_types::DynamicRoutingType,
    dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
) -> RouterResult<()> {
    logger::debug!(
        "performing call with open_router for profile {}",
        profile_id.get_string_repr()
    );

    let decision_engine_request = match dynamic_routing_type {
        routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
            let success_rate_config: routing_types::SuccessBasedRoutingConfig = request
                .parse_value("SuccessBasedRoutingConfig")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("unable to deserialize SuccessBasedRoutingConfig")?;

            open_router::DecisionEngineConfigSetupRequest {
                merchant_id: profile_id.get_string_repr().to_string(),
                config: open_router::DecisionEngineConfigVariant::SuccessRate(
                    success_rate_config
                        .get_decision_engine_configs()
                        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
                            message: "Decision engine config not found".to_string(),
                        })
                        .attach_printable("Decision engine config not found")?,
                ),
            }
        }
        routing_types::DynamicRoutingType::EliminationRouting => {
            let elimination_config: routing_types::EliminationRoutingConfig = request
                .parse_value("EliminationRoutingConfig")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("unable to deserialize EliminationRoutingConfig")?;

            open_router::DecisionEngineConfigSetupRequest {
                merchant_id: profile_id.get_string_repr().to_string(),
                config: open_router::DecisionEngineConfigVariant::Elimination(
                    elimination_config
                        .get_decision_engine_configs()
                        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
                            message: "Decision engine config not found".to_string(),
                        })
                        .attach_printable("Decision engine config not found")?,
                ),
            }
        }
        routing_types::DynamicRoutingType::ContractBasedRouting => {
            return Err((errors::ApiErrorResponse::InvalidRequestData {
                message: "Contract routing cannot be set as default".to_string(),
            })
            .into())
        }
    };

    // Create merchant in Decision Engine if it is not already created
    create_merchant_in_decision_engine_if_not_exists(state, profile_id, dynamic_routing_algo_ref)
        .await;

    routing_utils::ConfigApiClient::send_decision_engine_request::<_, serde_json::Value>(
        state,
        services::Method::Post,
        DECISION_ENGINE_RULE_UPDATE_ENDPOINT,
        Some(decision_engine_request),
        None,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Unable to update decision engine dynamic routing")?;

    Ok(())
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn get_decision_engine_active_dynamic_routing_algorithm(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    dynamic_routing_type: open_router::DecisionEngineDynamicAlgorithmType,
) -> RouterResult<Option<open_router::DecisionEngineConfigSetupRequest>> {
    logger::debug!(
        "decision_engine_euclid: GET api call for decision active {:?} routing algorithm",
        dynamic_routing_type
    );
    let request = open_router::GetDecisionEngineConfigRequest {
        merchant_id: profile_id.get_string_repr().to_owned(),
        algorithm: dynamic_routing_type,
    };
    let response: Option<open_router::DecisionEngineConfigSetupRequest> =
        routing_utils::ConfigApiClient::send_decision_engine_request(
            state,
            services::Method::Post,
            DECISION_ENGINE_RULE_GET_ENDPOINT,
            Some(request),
            None,
            None,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get active dynamic algorithm from decision engine")?
        .response;

    Ok(response)
}

#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
#[instrument(skip_all)]
pub async fn disable_decision_engine_dynamic_routing_setup(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    dynamic_routing_type: routing_types::DynamicRoutingType,
    dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
) -> RouterResult<()> {
    logger::debug!(
        "performing call with open_router for profile {}",
        profile_id.get_string_repr()
    );

    let decision_engine_request = open_router::FetchRoutingConfig {
        merchant_id: profile_id.get_string_repr().to_string(),
        algorithm: match dynamic_routing_type {
            routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
                open_router::AlgorithmType::SuccessRate
            }
            routing_types::DynamicRoutingType::EliminationRouting => {
                open_router::AlgorithmType::Elimination
            }
            routing_types::DynamicRoutingType::ContractBasedRouting => {
                return Err((errors::ApiErrorResponse::InvalidRequestData {
                    message: "Contract routing is not enabled for decision engine".to_string(),
                })
                .into())
            }
        },
    };

    // Create merchant in Decision Engine if it is not already created
    create_merchant_in_decision_engine_if_not_exists(state, profile_id, dynamic_routing_algo_ref)
        .await;

    routing_utils::ConfigApiClient::send_decision_engine_request::<_, serde_json::Value>(
        state,
        services::Method::Post,
        DECISION_ENGINE_RULE_DELETE_ENDPOINT,
        Some(decision_engine_request),
        None,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Unable to disable decision engine dynamic routing")?;

    Ok(())
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn create_merchant_in_decision_engine_if_not_exists(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
) {
    if !dynamic_routing_algo_ref.is_merchant_created_in_decision_engine {
        logger::debug!(
            "Creating merchant_account in decision engine for profile {}",
            profile_id.get_string_repr()
        );

        create_decision_engine_merchant(state, profile_id)
            .await
            .map_err(|err| {
                logger::warn!("Merchant creation error in decision_engine: {err:?}");
            })
            .ok();

        // TODO: Update the status based on the status code or error message from the API call
        dynamic_routing_algo_ref.update_merchant_creation_status_in_decision_engine(true);
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn create_decision_engine_merchant(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
) -> RouterResult<()> {
    let merchant_account_req = open_router::MerchantAccount {
        merchant_id: profile_id.get_string_repr().to_string(),
        gateway_success_rate_based_decider_input: None,
    };

    routing_utils::ConfigApiClient::send_decision_engine_request::<_, serde_json::Value>(
        state,
        services::Method::Post,
        DECISION_ENGINE_MERCHANT_CREATE_ENDPOINT,
        Some(merchant_account_req),
        None,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to create merchant account on decision engine")?;

    Ok(())
}

#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
#[instrument(skip_all)]
pub async fn delete_decision_engine_merchant(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
) -> RouterResult<()> {
    let path = format!(
        "{}/{}",
        DECISION_ENGINE_MERCHANT_BASE_ENDPOINT,
        profile_id.get_string_repr()
    );
    routing_utils::ConfigApiClient::send_decision_engine_request::<_, serde_json::Value>(
        state,
        services::Method::Delete,
        &path,
        None::<id_type::ProfileId>,
        None,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to delete merchant account on decision engine")?;

    Ok(())
}