// File: crates/euclid/src/frontend/dir.rs

//! Domain Intermediate Representation
pub mod enums;
pub mod lowering;
pub mod transformers;

use strum::IntoEnumIterator;

// use common_utils::types::MinorUnit;
use crate::{enums as euclid_enums, frontend::ast, types};

#[macro_export]
macro_rules! dirval {
    (Connector = $name:ident) => {
        $crate::frontend::dir::DirValue::Connector(Box::new(
            $crate::frontend::ast::ConnectorChoice {
                connector: $crate::enums::RoutableConnectors::$name,
            },
        ))
    };

    ($key:ident = $val:ident) => {{
        pub use $crate::frontend::dir::enums::*;

        $crate::frontend::dir::DirValue::$key($key::$val)
    }};

    ($key:ident = $num:literal) => {{
        $crate::frontend::dir::DirValue::$key($crate::types::NumValue {
            number: common_utils::types::MinorUnit::new($num),
            refinement: None,
        })
    }};

    ($key:ident s= $str:literal) => {{
        $crate::frontend::dir::DirValue::$key($crate::types::StrValue {
            value: $str.to_string(),
        })
    }};

    ($key:literal = $str:literal) => {{
        $crate::frontend::dir::DirValue::MetaData($crate::types::MetadataValue {
            key: $key.to_string(),
            value: $str.to_string(),
        })
    }};
}

#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize)]
pub struct DirKey {
    pub kind: DirKeyKind,
    pub value: Option<String>,
}

impl DirKey {
    pub fn new(kind: DirKeyKind, value: Option<String>) -> Self {
        Self { kind, value }
    }
}

#[derive(
    Debug,
    Clone,
    Hash,
    PartialEq,
    Eq,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::VariantNames,
    strum::EnumString,
    strum::EnumMessage,
    strum::EnumProperty,
)]
pub enum DirKeyKind {
    #[strum(
        serialize = "payment_method",
        detailed_message = "Different modes of payment - eg. cards, wallets, banks",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "payment_method")]
    PaymentMethod,
    #[strum(
        serialize = "card_bin",
        detailed_message = "First 4 to 6 digits of a payment card number",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "card_bin")]
    CardBin,
    #[strum(
        serialize = "extended_card_bin",
        detailed_message = "First 8 digits of a payment card number",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "extended_card_bin")]
    ExtendedCardBin,
    #[strum(
        serialize = "card_type",
        detailed_message = "Type of the payment card - eg. credit, debit",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "card_type")]
    CardType,
    #[strum(
        serialize = "card_network",
        detailed_message = "Network that facilitates payment card transactions",
        props(Category = "Payment Methods")
    )]
    #[serde(rename = "card_network")]
    CardNetwork,
    #[strum(
        serialize = "pay_later",
        detailed_message = "Supported types of Pay Later payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "pay_later")]
    PayLaterType,
    #[strum(
        serialize = "gift_card",
        detailed_message = "Supported types of Gift Card payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "gift_card")]
    GiftCardType,
    #[strum(
        serialize = "mandate_acceptance_type",
        detailed_message = "Mode of customer acceptance for mandates - online and offline",
        props(Category = "Payments")
    )]
    #[serde(rename = "mandate_acceptance_type")]
    MandateAcceptanceType,
    #[strum(
        serialize = "mandate_type",
        detailed_message = "Type of mandate acceptance - single use and multi use",
        props(Category = "Payments")
    )]
    #[serde(rename = "mandate_type")]
    MandateType,
    #[strum(
        serialize = "payment_type",
        detailed_message = "Indicates if a payment is mandate or non-mandate",
        props(Category = "Payments")
    )]
    #[serde(rename = "payment_type")]
    PaymentType,
    #[strum(
        serialize = "wallet",
        detailed_message = "Supported types of Wallet payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "wallet")]
    WalletType,
    #[strum(
        serialize = "upi",
        detailed_message = "Supported types of UPI payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "upi")]
    UpiType,
    #[strum(
        serialize = "voucher",
        detailed_message = "Supported types of Voucher payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "voucher")]
    VoucherType,
    #[strum(
        serialize = "bank_transfer",
        detailed_message = "Supported types of Bank Transfer payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "bank_transfer")]
    BankTransferType,
    #[strum(
        serialize = "bank_redirect",
        detailed_message = "Supported types of Bank Redirect payment methods",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "bank_redirect")]
    BankRedirectType,
    #[strum(
        serialize = "bank_debit",
        detailed_message = "Supported types of Bank Debit payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "bank_debit")]
    BankDebitType,
    #[strum(
        serialize = "crypto",
        detailed_message = "Supported types of Crypto payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "crypto")]
    CryptoType,
    #[strum(
        serialize = "metadata",
        detailed_message = "Aribitrary Key and value pair",
        props(Category = "Metadata")
    )]
    #[serde(rename = "metadata")]
    MetaData,
    #[strum(
        serialize = "reward",
        detailed_message = "Supported types of Reward payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "reward")]
    RewardType,
    #[strum(
        serialize = "amount",
        detailed_message = "Value of the transaction",
        props(Category = "Payments")
    )]
    #[serde(rename = "amount")]
    PaymentAmount,
    #[strum(
        serialize = "currency",
        detailed_message = "Currency used for the payment",
        props(Category = "Payments")
    )]
    #[serde(rename = "currency")]
    PaymentCurrency,
    #[strum(
        serialize = "authentication_type",
        detailed_message = "Type of authentication for the payment",
        props(Category = "Payments")
    )]
    #[serde(rename = "authentication_type")]
    AuthenticationType,
    #[strum(
        serialize = "capture_method",
        detailed_message = "Modes of capturing a payment",
        props(Category = "Payments")
    )]
    #[serde(rename = "capture_method")]
    CaptureMethod,
    #[strum(
        serialize = "country",
        serialize = "business_country",
        detailed_message = "Country of the business unit",
        props(Category = "Merchant")
    )]
    #[serde(rename = "business_country", alias = "country")]
    BusinessCountry,
    #[strum(
        serialize = "billing_country",
        detailed_message = "Country of the billing address of the customer",
        props(Category = "Customer")
    )]
    #[serde(rename = "billing_country")]
    BillingCountry,
    #[serde(skip_deserializing, rename = "connector")]
    Connector,
    #[strum(
        serialize = "business_label",
        detailed_message = "Identifier for business unit",
        props(Category = "Merchant")
    )]
    #[serde(rename = "business_label")]
    BusinessLabel,
    #[strum(
        serialize = "setup_future_usage",
        detailed_message = "Identifier for recurring payments",
        props(Category = "Payments")
    )]
    #[serde(rename = "setup_future_usage")]
    SetupFutureUsage,
    #[strum(
        serialize = "card_redirect",
        detailed_message = "Supported types of Card Redirect payment method",
        props(Category = "Payment Method Types")
    )]
    #[serde(rename = "card_redirect")]
    CardRedirectType,
    #[serde(rename = "real_time_payment")]
    #[strum(
        serialize = "real_time_payment",
        detailed_message = "Supported types of real time payment method",
        props(Category = "Payment Method Types")
    )]
    RealTimePaymentType,
    #[serde(rename = "open_banking")]
    #[strum(
        serialize = "open_banking",
        detailed_message = "Supported types of open banking payment method",
        props(Category = "Payment Method Types")
    )]
    OpenBankingType,
    #[serde(rename = "mobile_payment")]
    #[strum(
        serialize = "mobile_payment",
        detailed_message = "Supported types of mobile payment method",
        props(Category = "Payment Method Types")
    )]
    MobilePaymentType,
    #[strum(
        serialize = "issuer_name",
        detailed_message = "Name of the card issuing bank",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "issuer_name")]
    IssuerName,
    #[strum(
        serialize = "issuer_country",
        detailed_message = "Country of the card issuing bank",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "issuer_country")]
    IssuerCountry,
    #[strum(
        serialize = "customer_device_platform",
        detailed_message = "Platform of the customer's device (Web, Android, iOS)",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "customer_device_platform")]
    CustomerDevicePlatform,
    #[strum(
        serialize = "customer_device_type",
        detailed_message = "Type of the customer's device (Mobile, Tablet, Desktop, Gaming Console)",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "customer_device_type")]
    CustomerDeviceType,
    #[strum(
        serialize = "customer_device_display_size",
        detailed_message = "Display size of the customer's device (e.g., 500x600)",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "customer_device_display_size")]
    CustomerDeviceDisplaySize,
    #[strum(
        serialize = "acquirer_country",
        detailed_message = "Country of the acquiring bank",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "acquirer_country")]
    AcquirerCountry,
    #[strum(
        serialize = "acquirer_fraud_rate",
        detailed_message = "Fraud rate of the acquiring bank",
        props(Category = "3DS Decision")
    )]
    #[serde(rename = "acquirer_fraud_rate")]
    AcquirerFraudRate,
}

pub trait EuclidDirFilter: Sized
where
    Self: 'static,
{
    const ALLOWED: &'static [DirKeyKind];
    fn get_allowed_keys() -> &'static [DirKeyKind] {
        Self::ALLOWED
    }

    fn is_key_allowed(key: &DirKeyKind) -> bool {
        Self::ALLOWED.contains(key)
    }
}

impl DirKeyKind {
    pub fn get_type(&self) -> types::DataType {
        match self {
            Self::PaymentMethod => types::DataType::EnumVariant,
            Self::CardBin => types::DataType::StrValue,
            Self::ExtendedCardBin => types::DataType::StrValue,
            Self::CardType => types::DataType::EnumVariant,
            Self::CardNetwork => types::DataType::EnumVariant,
            Self::MetaData => types::DataType::MetadataValue,
            Self::MandateType => types::DataType::EnumVariant,
            Self::PaymentType => types::DataType::EnumVariant,
            Self::MandateAcceptanceType => types::DataType::EnumVariant,
            Self::PayLaterType => types::DataType::EnumVariant,
            Self::WalletType => types::DataType::EnumVariant,
            Self::UpiType => types::DataType::EnumVariant,
            Self::VoucherType => types::DataType::EnumVariant,
            Self::BankTransferType => types::DataType::EnumVariant,
            Self::GiftCardType => types::DataType::EnumVariant,
            Self::BankRedirectType => types::DataType::EnumVariant,
            Self::CryptoType => types::DataType::EnumVariant,
            Self::RewardType => types::DataType::EnumVariant,
            Self::PaymentAmount => types::DataType::Number,
            Self::PaymentCurrency => types::DataType::EnumVariant,
            Self::AuthenticationType => types::DataType::EnumVariant,
            Self::CaptureMethod => types::DataType::EnumVariant,
            Self::BusinessCountry => types::DataType::EnumVariant,
            Self::BillingCountry => types::DataType::EnumVariant,
            Self::Connector => types::DataType::EnumVariant,
            Self::BankDebitType => types::DataType::EnumVariant,
            Self::BusinessLabel => types::DataType::StrValue,
            Self::SetupFutureUsage => types::DataType::EnumVariant,
            Self::CardRedirectType => types::DataType::EnumVariant,
            Self::RealTimePaymentType => types::DataType::EnumVariant,
            Self::OpenBankingType => types::DataType::EnumVariant,
            Self::MobilePaymentType => types::DataType::EnumVariant,
            Self::IssuerName => types::DataType::StrValue,
            Self::IssuerCountry => types::DataType::EnumVariant,
            Self::CustomerDevicePlatform => types::DataType::EnumVariant,
            Self::CustomerDeviceType => types::DataType::EnumVariant,
            Self::CustomerDeviceDisplaySize => types::DataType::EnumVariant,
            Self::AcquirerCountry => types::DataType::EnumVariant,
            Self::AcquirerFraudRate => types::DataType::Number,
        }
    }
    pub fn get_value_set(&self) -> Option<Vec<DirValue>> {
        match self {
            Self::PaymentMethod => Some(
                enums::PaymentMethod::iter()
                    .map(DirValue::PaymentMethod)
                    .collect(),
            ),
            Self::CardBin => None,
            Self::ExtendedCardBin => None,
            Self::CardType => Some(enums::CardType::iter().map(DirValue::CardType).collect()),
            Self::MandateAcceptanceType => Some(
                euclid_enums::MandateAcceptanceType::iter()
                    .map(DirValue::MandateAcceptanceType)
                    .collect(),
            ),
            Self::PaymentType => Some(
                euclid_enums::PaymentType::iter()
                    .map(DirValue::PaymentType)
                    .collect(),
            ),
            Self::MandateType => Some(
                euclid_enums::MandateType::iter()
                    .map(DirValue::MandateType)
                    .collect(),
            ),
            Self::CardNetwork => Some(
                enums::CardNetwork::iter()
                    .map(DirValue::CardNetwork)
                    .collect(),
            ),
            Self::PayLaterType => Some(
                enums::PayLaterType::iter()
                    .map(DirValue::PayLaterType)
                    .collect(),
            ),
            Self::MetaData => None,
            Self::WalletType => Some(
                enums::WalletType::iter()
                    .map(DirValue::WalletType)
                    .collect(),
            ),
            Self::UpiType => Some(enums::UpiType::iter().map(DirValue::UpiType).collect()),
            Self::VoucherType => Some(
                enums::VoucherType::iter()
                    .map(DirValue::VoucherType)
                    .collect(),
            ),
            Self::BankTransferType => Some(
                enums::BankTransferType::iter()
                    .map(DirValue::BankTransferType)
                    .collect(),
            ),
            Self::GiftCardType => Some(
                enums::GiftCardType::iter()
                    .map(DirValue::GiftCardType)
                    .collect(),
            ),
            Self::BankRedirectType => Some(
                enums::BankRedirectType::iter()
                    .map(DirValue::BankRedirectType)
                    .collect(),
            ),
            Self::CryptoType => Some(
                enums::CryptoType::iter()
                    .map(DirValue::CryptoType)
                    .collect(),
            ),
            Self::RewardType => Some(
                enums::RewardType::iter()
                    .map(DirValue::RewardType)
                    .collect(),
            ),
            Self::PaymentAmount => None,
            Self::PaymentCurrency => Some(
                enums::PaymentCurrency::iter()
                    .map(DirValue::PaymentCurrency)
                    .collect(),
            ),
            Self::AuthenticationType => Some(
                enums::AuthenticationType::iter()
                    .map(DirValue::AuthenticationType)
                    .collect(),
            ),
            Self::CaptureMethod => Some(
                enums::CaptureMethod::iter()
                    .map(DirValue::CaptureMethod)
                    .collect(),
            ),
            Self::BankDebitType => Some(
                enums::BankDebitType::iter()
                    .map(DirValue::BankDebitType)
                    .collect(),
            ),
            Self::BusinessCountry => Some(
                enums::Country::iter()
                    .map(DirValue::BusinessCountry)
                    .collect(),
            ),
            Self::BillingCountry => Some(
                enums::Country::iter()
                    .map(DirValue::BillingCountry)
                    .collect(),
            ),
            Self::Connector => Some(
                common_enums::RoutableConnectors::iter()
                    .map(|connector| {
                        DirValue::Connector(Box::new(ast::ConnectorChoice { connector }))
                    })
                    .collect(),
            ),
            Self::BusinessLabel => None,
            Self::SetupFutureUsage => Some(
                enums::SetupFutureUsage::iter()
                    .map(DirValue::SetupFutureUsage)
                    .collect(),
            ),
            Self::CardRedirectType => Some(
                enums::CardRedirectType::iter()
                    .map(DirValue::CardRedirectType)
                    .collect(),
            ),
            Self::RealTimePaymentType => Some(
                enums::RealTimePaymentType::iter()
                    .map(DirValue::RealTimePaymentType)
                    .collect(),
            ),
            Self::OpenBankingType => Some(
                enums::OpenBankingType::iter()
                    .map(DirValue::OpenBankingType)
                    .collect(),
            ),
            Self::MobilePaymentType => Some(
                enums::MobilePaymentType::iter()
                    .map(DirValue::MobilePaymentType)
                    .collect(),
            ),
            Self::IssuerName => None,
            Self::IssuerCountry => Some(
                enums::Country::iter()
                    .map(DirValue::IssuerCountry)
                    .collect(),
            ),
            Self::CustomerDevicePlatform => Some(
                enums::CustomerDevicePlatform::iter()
                    .map(DirValue::CustomerDevicePlatform)
                    .collect(),
            ),
            Self::CustomerDeviceType => Some(
                enums::CustomerDeviceType::iter()
                    .map(DirValue::CustomerDeviceType)
                    .collect(),
            ),
            Self::CustomerDeviceDisplaySize => Some(
                enums::CustomerDeviceDisplaySize::iter()
                    .map(DirValue::CustomerDeviceDisplaySize)
                    .collect(),
            ),
            Self::AcquirerCountry => Some(
                enums::Country::iter()
                    .map(DirValue::AcquirerCountry)
                    .collect(),
            ),
            Self::AcquirerFraudRate => None,
        }
    }
}

#[derive(
    Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, strum::Display, strum::VariantNames,
)]
#[serde(tag = "key", content = "value")]
pub enum DirValue {
    #[serde(rename = "payment_method")]
    PaymentMethod(enums::PaymentMethod),
    #[serde(rename = "card_bin")]
    CardBin(types::StrValue),
    #[serde(rename = "extended_card_bin")]
    ExtendedCardBin(types::StrValue),
    #[serde(rename = "card_type")]
    CardType(enums::CardType),
    #[serde(rename = "card_network")]
    CardNetwork(enums::CardNetwork),
    #[serde(rename = "metadata")]
    MetaData(types::MetadataValue),
    #[serde(rename = "pay_later")]
    PayLaterType(enums::PayLaterType),
    #[serde(rename = "wallet")]
    WalletType(enums::WalletType),
    #[serde(rename = "acceptance_type")]
    MandateAcceptanceType(euclid_enums::MandateAcceptanceType),
    #[serde(rename = "mandate_type")]
    MandateType(euclid_enums::MandateType),
    #[serde(rename = "payment_type")]
    PaymentType(euclid_enums::PaymentType),
    #[serde(rename = "upi")]
    UpiType(enums::UpiType),
    #[serde(rename = "voucher")]
    VoucherType(enums::VoucherType),
    #[serde(rename = "bank_transfer")]
    BankTransferType(enums::BankTransferType),
    #[serde(rename = "bank_redirect")]
    BankRedirectType(enums::BankRedirectType),
    #[serde(rename = "bank_debit")]
    BankDebitType(enums::BankDebitType),
    #[serde(rename = "crypto")]
    CryptoType(enums::CryptoType),
    #[serde(rename = "reward")]
    RewardType(enums::RewardType),
    #[serde(rename = "gift_card")]
    GiftCardType(enums::GiftCardType),
    #[serde(rename = "amount")]
    PaymentAmount(types::NumValue),
    #[serde(rename = "currency")]
    PaymentCurrency(enums::PaymentCurrency),
    #[serde(rename = "authentication_type")]
    AuthenticationType(enums::AuthenticationType),
    #[serde(rename = "capture_method")]
    CaptureMethod(enums::CaptureMethod),
    #[serde(rename = "business_country", alias = "country")]
    BusinessCountry(enums::Country),
    #[serde(rename = "billing_country")]
    BillingCountry(enums::Country),
    #[serde(skip_deserializing, rename = "connector")]
    Connector(Box<ast::ConnectorChoice>),
    #[serde(rename = "business_label")]
    BusinessLabel(types::StrValue),
    #[serde(rename = "setup_future_usage")]
    SetupFutureUsage(enums::SetupFutureUsage),
    #[serde(rename = "card_redirect")]
    CardRedirectType(enums::CardRedirectType),
    #[serde(rename = "real_time_payment")]
    RealTimePaymentType(enums::RealTimePaymentType),
    #[serde(rename = "open_banking")]
    OpenBankingType(enums::OpenBankingType),
    #[serde(rename = "mobile_payment")]
    MobilePaymentType(enums::MobilePaymentType),
    #[serde(rename = "issuer_name")]
    IssuerName(types::StrValue),
    #[serde(rename = "issuer_country")]
    IssuerCountry(enums::Country),
    #[serde(rename = "customer_device_platform")]
    CustomerDevicePlatform(enums::CustomerDevicePlatform),
    #[serde(rename = "customer_device_type")]
    CustomerDeviceType(enums::CustomerDeviceType),
    #[serde(rename = "customer_device_display_size")]
    CustomerDeviceDisplaySize(enums::CustomerDeviceDisplaySize),
    #[serde(rename = "acquirer_country")]
    AcquirerCountry(enums::Country),
    #[serde(rename = "acquirer_fraud_rate")]
    AcquirerFraudRate(types::NumValue),
}

impl DirValue {
    pub fn get_key(&self) -> DirKey {
        let (kind, data) = match self {
            Self::PaymentMethod(_) => (DirKeyKind::PaymentMethod, None),
            Self::CardBin(_) => (DirKeyKind::CardBin, None),
            Self::ExtendedCardBin(_) => (DirKeyKind::ExtendedCardBin, None),
            Self::RewardType(_) => (DirKeyKind::RewardType, None),
            Self::BusinessCountry(_) => (DirKeyKind::BusinessCountry, None),
            Self::BillingCountry(_) => (DirKeyKind::BillingCountry, None),
            Self::BankTransferType(_) => (DirKeyKind::BankTransferType, None),
            Self::UpiType(_) => (DirKeyKind::UpiType, None),
            Self::CardType(_) => (DirKeyKind::CardType, None),
            Self::CardNetwork(_) => (DirKeyKind::CardNetwork, None),
            Self::MetaData(met) => (DirKeyKind::MetaData, Some(met.key.clone())),
            Self::PayLaterType(_) => (DirKeyKind::PayLaterType, None),
            Self::WalletType(_) => (DirKeyKind::WalletType, None),
            Self::BankRedirectType(_) => (DirKeyKind::BankRedirectType, None),
            Self::CryptoType(_) => (DirKeyKind::CryptoType, None),
            Self::AuthenticationType(_) => (DirKeyKind::AuthenticationType, None),
            Self::CaptureMethod(_) => (DirKeyKind::CaptureMethod, None),
            Self::PaymentAmount(_) => (DirKeyKind::PaymentAmount, None),
            Self::PaymentCurrency(_) => (DirKeyKind::PaymentCurrency, None),
            Self::Connector(_) => (DirKeyKind::Connector, None),
            Self::BankDebitType(_) => (DirKeyKind::BankDebitType, None),
            Self::MandateAcceptanceType(_) => (DirKeyKind::MandateAcceptanceType, None),
            Self::MandateType(_) => (DirKeyKind::MandateType, None),
            Self::PaymentType(_) => (DirKeyKind::PaymentType, None),
            Self::BusinessLabel(_) => (DirKeyKind::BusinessLabel, None),
            Self::SetupFutureUsage(_) => (DirKeyKind::SetupFutureUsage, None),
            Self::CardRedirectType(_) => (DirKeyKind::CardRedirectType, None),
            Self::VoucherType(_) => (DirKeyKind::VoucherType, None),
            Self::GiftCardType(_) => (DirKeyKind::GiftCardType, None),
            Self::RealTimePaymentType(_) => (DirKeyKind::RealTimePaymentType, None),
            Self::OpenBankingType(_) => (DirKeyKind::OpenBankingType, None),
            Self::MobilePaymentType(_) => (DirKeyKind::MobilePaymentType, None),
            Self::IssuerName(_) => (DirKeyKind::IssuerName, None),
            Self::IssuerCountry(_) => (DirKeyKind::IssuerCountry, None),
            Self::CustomerDevicePlatform(_) => (DirKeyKind::CustomerDevicePlatform, None),
            Self::CustomerDeviceType(_) => (DirKeyKind::CustomerDeviceType, None),
            Self::CustomerDeviceDisplaySize(_) => (DirKeyKind::CustomerDeviceDisplaySize, None),
            Self::AcquirerCountry(_) => (DirKeyKind::AcquirerCountry, None),
            Self::AcquirerFraudRate(_) => (DirKeyKind::AcquirerFraudRate, None),
        };

        DirKey::new(kind, data)
    }
    pub fn get_metadata_val(&self) -> Option<types::MetadataValue> {
        match self {
            Self::MetaData(val) => Some(val.clone()),
            Self::PaymentMethod(_) => None,
            Self::CardBin(_) => None,
            Self::ExtendedCardBin(_) => None,
            Self::CardType(_) => None,
            Self::CardNetwork(_) => None,
            Self::PayLaterType(_) => None,
            Self::WalletType(_) => None,
            Self::BankRedirectType(_) => None,
            Self::CryptoType(_) => None,
            Self::AuthenticationType(_) => None,
            Self::CaptureMethod(_) => None,
            Self::GiftCardType(_) => None,
            Self::PaymentAmount(_) => None,
            Self::PaymentCurrency(_) => None,
            Self::BusinessCountry(_) => None,
            Self::BillingCountry(_) => None,
            Self::Connector(_) => None,
            Self::BankTransferType(_) => None,
            Self::UpiType(_) => None,
            Self::BankDebitType(_) => None,
            Self::RewardType(_) => None,
            Self::VoucherType(_) => None,
            Self::MandateAcceptanceType(_) => None,
            Self::MandateType(_) => None,
            Self::PaymentType(_) => None,
            Self::BusinessLabel(_) => None,
            Self::SetupFutureUsage(_) => None,
            Self::CardRedirectType(_) => None,
            Self::RealTimePaymentType(_) => None,
            Self::OpenBankingType(_) => None,
            Self::MobilePaymentType(_) => None,
            Self::IssuerName(_) => None,
            Self::IssuerCountry(_) => None,
            Self::CustomerDevicePlatform(_) => None,
            Self::CustomerDeviceType(_) => None,
            Self::CustomerDeviceDisplaySize(_) => None,
            Self::AcquirerCountry(_) => None,
            Self::AcquirerFraudRate(_) => None,
        }
    }

    pub fn get_str_val(&self) -> Option<types::StrValue> {
        match self {
            Self::CardBin(val) => Some(val.clone()),
            Self::ExtendedCardBin(val) => Some(val.clone()),
            Self::IssuerName(val) => Some(val.clone()),
            _ => None,
        }
    }

    pub fn get_num_value(&self) -> Option<types::NumValue> {
        match self {
            Self::PaymentAmount(val) => Some(val.clone()),
            Self::AcquirerFraudRate(val) => Some(val.clone()),
            _ => None,
        }
    }

    pub fn check_equality(v1: &Self, v2: &Self) -> bool {
        match (v1, v2) {
            (Self::PaymentMethod(pm1), Self::PaymentMethod(pm2)) => pm1 == pm2,
            (Self::CardType(ct1), Self::CardType(ct2)) => ct1 == ct2,
            (Self::CardNetwork(cn1), Self::CardNetwork(cn2)) => cn1 == cn2,
            (Self::MetaData(md1), Self::MetaData(md2)) => md1 == md2,
            (Self::PayLaterType(plt1), Self::PayLaterType(plt2)) => plt1 == plt2,
            (Self::WalletType(wt1), Self::WalletType(wt2)) => wt1 == wt2,
            (Self::BankDebitType(bdt1), Self::BankDebitType(bdt2)) => bdt1 == bdt2,
            (Self::BankRedirectType(brt1), Self::BankRedirectType(brt2)) => brt1 == brt2,
            (Self::BankTransferType(btt1), Self::BankTransferType(btt2)) => btt1 == btt2,
            (Self::GiftCardType(gct1), Self::GiftCardType(gct2)) => gct1 == gct2,
            (Self::CryptoType(ct1), Self::CryptoType(ct2)) => ct1 == ct2,
            (Self::AuthenticationType(at1), Self::AuthenticationType(at2)) => at1 == at2,
            (Self::CaptureMethod(cm1), Self::CaptureMethod(cm2)) => cm1 == cm2,
            (Self::PaymentCurrency(pc1), Self::PaymentCurrency(pc2)) => pc1 == pc2,
            (Self::BusinessCountry(c1), Self::BusinessCountry(c2)) => c1 == c2,
            (Self::BillingCountry(c1), Self::BillingCountry(c2)) => c1 == c2,
            (Self::PaymentType(pt1), Self::PaymentType(pt2)) => pt1 == pt2,
            (Self::MandateType(mt1), Self::MandateType(mt2)) => mt1 == mt2,
            (Self::MandateAcceptanceType(mat1), Self::MandateAcceptanceType(mat2)) => mat1 == mat2,
            (Self::RewardType(rt1), Self::RewardType(rt2)) => rt1 == rt2,
            (Self::RealTimePaymentType(rtp1), Self::RealTimePaymentType(rtp2)) => rtp1 == rtp2,
            (Self::Connector(c1), Self::Connector(c2)) => c1 == c2,
            (Self::BusinessLabel(bl1), Self::BusinessLabel(bl2)) => bl1 == bl2,
            (Self::SetupFutureUsage(sfu1), Self::SetupFutureUsage(sfu2)) => sfu1 == sfu2,
            (Self::UpiType(ut1), Self::UpiType(ut2)) => ut1 == ut2,
            (Self::VoucherType(vt1), Self::VoucherType(vt2)) => vt1 == vt2,
            (Self::CardRedirectType(crt1), Self::CardRedirectType(crt2)) => crt1 == crt2,
            (Self::IssuerName(n1), Self::IssuerName(n2)) => n1 == n2,
            (Self::IssuerCountry(c1), Self::IssuerCountry(c2)) => c1 == c2,
            (Self::CustomerDevicePlatform(p1), Self::CustomerDevicePlatform(p2)) => p1 == p2,
            (Self::CustomerDeviceType(t1), Self::CustomerDeviceType(t2)) => t1 == t2,
            (Self::CustomerDeviceDisplaySize(s1), Self::CustomerDeviceDisplaySize(s2)) => s1 == s2,
            (Self::AcquirerCountry(c1), Self::AcquirerCountry(c2)) => c1 == c2,
            (Self::AcquirerFraudRate(r1), Self::AcquirerFraudRate(r2)) => r1 == r2,
            _ => false,
        }
    }
}

#[cfg(feature = "payouts")]
#[derive(
    Debug,
    Clone,
    Hash,
    PartialEq,
    Eq,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::VariantNames,
    strum::EnumString,
    strum::EnumMessage,
    strum::EnumProperty,
)]
pub enum PayoutDirKeyKind {
    #[strum(
        serialize = "country",
        serialize = "business_country",
        detailed_message = "Country of the business unit",
        props(Category = "Merchant")
    )]
    #[serde(rename = "business_country", alias = "country")]
    BusinessCountry,

    #[strum(
        serialize = "billing_country",
        detailed_message = "Country of the billing address of the customer",
        props(Category = "Customer")
    )]
    #[serde(rename = "billing_country")]
    BillingCountry,

    #[strum(
        serialize = "business_label",
        detailed_message = "Identifier for business unit",
        props(Category = "Merchant")
    )]
    #[serde(rename = "business_label")]
    BusinessLabel,

    #[strum(
        serialize = "amount",
        detailed_message = "Value of the transaction",
        props(Category = "Order details")
    )]
    #[serde(rename = "amount")]
    PayoutAmount,

    #[strum(
        serialize = "currency",
        detailed_message = "Currency used for the payout",
        props(Category = "Order details")
    )]
    #[serde(rename = "currency")]
    PayoutCurrency,

    #[strum(
        serialize = "payment_method",
        detailed_message = "Different modes of payout - eg. cards, wallets, banks",
        props(Category = "Payout Methods")
    )]
    #[serde(rename = "payment_method")]
    PayoutType,

    #[strum(
        serialize = "wallet",
        detailed_message = "Supported types of Wallets for payouts",
        props(Category = "Payout Methods Type")
    )]
    #[serde(rename = "wallet")]
    WalletType,

    #[strum(
        serialize = "bank_transfer",
        detailed_message = "Supported types of Bank transfer types for payouts",
        props(Category = "Payout Methods Type")
    )]
    #[serde(rename = "bank_transfer")]
    BankTransferType,
}

#[cfg(feature = "payouts")]
#[derive(
    Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, strum::Display, strum::VariantNames,
)]
pub enum PayoutDirValue {
    #[serde(rename = "business_country", alias = "country")]
    BusinessCountry(enums::Country),
    #[serde(rename = "billing_country")]
    BillingCountry(enums::Country),
    #[serde(rename = "business_label")]
    BusinessLabel(types::StrValue),
    #[serde(rename = "amount")]
    PayoutAmount(types::NumValue),
    #[serde(rename = "currency")]
    PayoutCurrency(enums::PaymentCurrency),
    #[serde(rename = "payment_method")]
    PayoutType(common_enums::PayoutType),
    #[serde(rename = "wallet")]
    WalletType(enums::PayoutWalletType),
    #[serde(rename = "bank_transfer")]
    BankTransferType(enums::PayoutBankTransferType),
}

#[derive(Debug, Clone)]
pub enum DirComparisonLogic {
    NegativeConjunction,
    PositiveDisjunction,
}

#[derive(Debug, Clone)]
pub struct DirComparison {
    pub values: Vec<DirValue>,
    pub logic: DirComparisonLogic,
    pub metadata: types::Metadata,
}

pub type DirIfCondition = Vec<DirComparison>;

#[derive(Debug, Clone)]
pub struct DirIfStatement {
    pub condition: DirIfCondition,
    pub nested: Option<Vec<DirIfStatement>>,
}

#[derive(Debug, Clone)]
pub struct DirRule<O> {
    pub name: String,
    pub connector_selection: O,
    pub statements: Vec<DirIfStatement>,
}

#[derive(Debug, Clone)]
pub struct DirProgram<O> {
    pub default_selection: O,
    pub rules: Vec<DirRule<O>>,
    pub metadata: types::Metadata,
}

#[cfg(test)]
mod test {
    use rustc_hash::FxHashMap;
    use strum::IntoEnumIterator;

    use super::*;

    #[test]
    fn test_consistent_dir_key_naming() {
        let mut key_names: FxHashMap<DirKeyKind, String> = FxHashMap::default();

        for key in DirKeyKind::iter() {
            if matches!(key, DirKeyKind::Connector) {
                continue;
            }

            let json_str = if let DirKeyKind::MetaData = key {
                r#""metadata""#.to_string()
            } else {
                serde_json::to_string(&key).expect("JSON Serialization")
            };
            let display_str = key.to_string();

            assert_eq!(
                json_str.get(1..json_str.len() - 1).expect("Value metadata"),
                display_str
            );
            key_names.insert(key, display_str);
        }

        let values = vec![
            dirval!(PaymentMethod = Card),
            dirval!(CardBin s= "123456"),
            dirval!(CardType = Credit),
            dirval!(CardNetwork = Visa),
            dirval!(PayLaterType = Klarna),
            dirval!(WalletType = Paypal),
            dirval!(BankRedirectType = Sofort),
            dirval!(BankDebitType = Bacs),
            dirval!(CryptoType = CryptoCurrency),
            dirval!("" = "metadata"),
            dirval!(PaymentAmount = 100),
            dirval!(PaymentCurrency = USD),
            dirval!(CardRedirectType = Benefit),
            dirval!(AuthenticationType = ThreeDs),
            dirval!(CaptureMethod = Manual),
            dirval!(BillingCountry = UnitedStatesOfAmerica),
            dirval!(BusinessCountry = France),
        ];

        for val in values {
            let json_val = serde_json::to_value(&val).expect("JSON Value Serialization");

            let json_key = json_val
                .as_object()
                .expect("Serialized Object")
                .get("key")
                .expect("Object Key");

            let value_str = json_key.as_str().expect("Value string");
            let dir_key = val.get_key();

            let key_name = key_names.get(&dir_key.kind).expect("Key name");

            assert_eq!(key_name, value_str);
        }
    }

    #[cfg(feature = "ast_parser")]
    #[test]
    fn test_allowed_dir_keys() {
        use crate::types::DummyOutput;

        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           payment_method = card
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");

        let out = ast::lowering::lower_program::<DummyOutput>(program);
        assert!(out.is_ok())
    }
    #[cfg(feature = "ast_parser")]
    #[test]
    fn test_not_allowed_dir_keys() {
        use crate::types::DummyOutput;

        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           bank_debit = ach
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");

        let out = ast::lowering::lower_program::<DummyOutput>(program);
        assert!(out.is_err())
    }
}


// File: crates/euclid/src/frontend/ast.rs

pub mod lowering;
#[cfg(feature = "ast_parser")]
pub mod parser;

use common_enums::RoutableConnectors;
use common_utils::types::MinorUnit;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use crate::types::{DataType, Metadata};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct ConnectorChoice {
    pub connector: RoutableConnectors,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
pub struct MetadataValue {
    pub key: String,
    pub value: String,
}

/// Represents a value in the DSL
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
#[serde(tag = "type", content = "value", rename_all = "snake_case")]
pub enum ValueType {
    /// Represents a number literal
    Number(MinorUnit),
    /// Represents an enum variant
    EnumVariant(String),
    /// Represents a Metadata variant
    MetadataVariant(MetadataValue),
    /// Represents a arbitrary String value
    StrValue(String),
    /// Represents an array of numbers. This is basically used for
    /// "one of the given numbers" operations
    /// eg: payment.method.amount = (1, 2, 3)
    NumberArray(Vec<MinorUnit>),
    /// Similar to NumberArray but for enum variants
    /// eg: payment.method.cardtype = (debit, credit)
    EnumVariantArray(Vec<String>),
    /// Like a number array but can include comparisons. Useful for
    /// conditions like "500 < amount < 1000"
    /// eg: payment.amount = (> 500, < 1000)
    NumberComparisonArray(Vec<NumberComparison>),
}

impl ValueType {
    pub fn get_type(&self) -> DataType {
        match self {
            Self::Number(_) => DataType::Number,
            Self::StrValue(_) => DataType::StrValue,
            Self::MetadataVariant(_) => DataType::MetadataValue,
            Self::EnumVariant(_) => DataType::EnumVariant,
            Self::NumberComparisonArray(_) => DataType::Number,
            Self::NumberArray(_) => DataType::Number,
            Self::EnumVariantArray(_) => DataType::EnumVariant,
        }
    }
}

/// Represents a number comparison for "NumberComparisonArrayValue"
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct NumberComparison {
    pub comparison_type: ComparisonType,
    pub number: MinorUnit,
}

/// Conditional comparison type
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ComparisonType {
    Equal,
    NotEqual,
    LessThan,
    LessThanEqual,
    GreaterThan,
    GreaterThanEqual,
}

/// Represents a single comparison condition.
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
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, ToSchema)]
#[serde(rename_all = "camelCase")]
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, ToSchema)]
#[serde(rename_all = "camelCase")]
#[aliases(RuleConnectorSelection = Rule<ConnectorSelection>)]
pub struct Rule<O> {
    pub name: String,
    #[serde(alias = "routingOutput")]
    pub connector_selection: O,
    pub statements: Vec<IfStatement>,
}

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

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RoutableConnectorChoice {
    #[serde(skip)]
    pub choice_kind: RoutableChoiceKind,
    pub connector: RoutableConnectors,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
}

#[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema)]
pub enum RoutableChoiceKind {
    OnlyConnector,
    #[default]
    FullStruct,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ConnectorVolumeSplit {
    pub connector: RoutableConnectorChoice,
    pub split: u8,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
pub enum ConnectorSelection {
    Priority(Vec<RoutableConnectorChoice>),
    VolumeSplit(Vec<ConnectorVolumeSplit>),
}


// File: crates/euclid/src/frontend/dir/lowering.rs

//! Analysis of the lowering logic for the DIR
//!
//! Consists of certain functions that supports the lowering logic from DIR to VIR.
//! These includes the lowering of the DIR program and vector of rules , and the lowering of ifstatements
//! ,and comparisonsLogic and also the lowering of the enums of value variants from DIR to VIR.
use super::enums;
use crate::{
    dssa::types::{AnalysisError, AnalysisErrorType},
    enums as global_enums,
    frontend::{dir, vir},
    types::EuclidValue,
};

impl From<enums::CardType> for global_enums::PaymentMethodType {
    fn from(value: enums::CardType) -> Self {
        match value {
            enums::CardType::Credit => Self::Credit,
            enums::CardType::Debit => Self::Debit,
            #[cfg(feature = "v2")]
            enums::CardType::Card => Self::Card,
        }
    }
}

impl From<enums::PayLaterType> for global_enums::PaymentMethodType {
    fn from(value: enums::PayLaterType) -> Self {
        match value {
            enums::PayLaterType::Affirm => Self::Affirm,
            enums::PayLaterType::AfterpayClearpay => Self::AfterpayClearpay,
            enums::PayLaterType::Alma => Self::Alma,
            enums::PayLaterType::Flexiti => Self::Flexiti,
            enums::PayLaterType::Klarna => Self::Klarna,
            enums::PayLaterType::PayBright => Self::PayBright,
            enums::PayLaterType::Walley => Self::Walley,
            enums::PayLaterType::Atome => Self::Atome,
            enums::PayLaterType::Breadpay => Self::Breadpay,
            enums::PayLaterType::Payjustnow => Self::Payjustnow,
        }
    }
}

impl From<enums::WalletType> for global_enums::PaymentMethodType {
    fn from(value: enums::WalletType) -> Self {
        match value {
            enums::WalletType::Bluecode => Self::Bluecode,
            enums::WalletType::GooglePay => Self::GooglePay,
            enums::WalletType::AmazonPay => Self::AmazonPay,
            enums::WalletType::Skrill => Self::Skrill,
            enums::WalletType::Paysera => Self::Paysera,
            enums::WalletType::ApplePay => Self::ApplePay,
            enums::WalletType::Paypal => Self::Paypal,
            enums::WalletType::AliPay => Self::AliPay,
            enums::WalletType::AliPayHk => Self::AliPayHk,
            enums::WalletType::MbWay => Self::MbWay,
            enums::WalletType::MobilePay => Self::MobilePay,
            enums::WalletType::WeChatPay => Self::WeChatPay,
            enums::WalletType::SamsungPay => Self::SamsungPay,
            enums::WalletType::GoPay => Self::GoPay,
            enums::WalletType::KakaoPay => Self::KakaoPay,
            enums::WalletType::Twint => Self::Twint,
            enums::WalletType::Gcash => Self::Gcash,
            enums::WalletType::Vipps => Self::Vipps,
            enums::WalletType::Momo => Self::Momo,
            enums::WalletType::Dana => Self::Dana,
            enums::WalletType::TouchNGo => Self::TouchNGo,
            enums::WalletType::Swish => Self::Swish,
            enums::WalletType::Cashapp => Self::Cashapp,
            enums::WalletType::Venmo => Self::Venmo,
            enums::WalletType::Mifinity => Self::Mifinity,
            enums::WalletType::Paze => Self::Paze,
            enums::WalletType::RevolutPay => Self::RevolutPay,
        }
    }
}

impl From<enums::BankDebitType> for global_enums::PaymentMethodType {
    fn from(value: enums::BankDebitType) -> Self {
        match value {
            enums::BankDebitType::Ach => Self::Ach,
            enums::BankDebitType::Sepa => Self::Sepa,
            enums::BankDebitType::SepaGuarenteedDebit => Self::SepaGuarenteedDebit,
            enums::BankDebitType::Bacs => Self::Bacs,
            enums::BankDebitType::Becs => Self::Becs,
        }
    }
}
impl From<enums::UpiType> for global_enums::PaymentMethodType {
    fn from(value: enums::UpiType) -> Self {
        match value {
            enums::UpiType::UpiCollect => Self::UpiCollect,
            enums::UpiType::UpiIntent => Self::UpiIntent,
            enums::UpiType::UpiQr => Self::UpiQr,
        }
    }
}

impl From<enums::VoucherType> for global_enums::PaymentMethodType {
    fn from(value: enums::VoucherType) -> Self {
        match value {
            enums::VoucherType::Boleto => Self::Boleto,
            enums::VoucherType::Efecty => Self::Efecty,
            enums::VoucherType::PagoEfectivo => Self::PagoEfectivo,
            enums::VoucherType::RedCompra => Self::RedCompra,
            enums::VoucherType::RedPagos => Self::RedPagos,
            enums::VoucherType::Alfamart => Self::Alfamart,
            enums::VoucherType::Indomaret => Self::Indomaret,
            enums::VoucherType::SevenEleven => Self::SevenEleven,
            enums::VoucherType::Lawson => Self::Lawson,
            enums::VoucherType::MiniStop => Self::MiniStop,
            enums::VoucherType::FamilyMart => Self::FamilyMart,
            enums::VoucherType::Seicomart => Self::Seicomart,
            enums::VoucherType::PayEasy => Self::PayEasy,
            enums::VoucherType::Oxxo => Self::Oxxo,
        }
    }
}

impl From<enums::BankTransferType> for global_enums::PaymentMethodType {
    fn from(value: enums::BankTransferType) -> Self {
        match value {
            enums::BankTransferType::Multibanco => Self::Multibanco,
            enums::BankTransferType::Pix => Self::Pix,
            enums::BankTransferType::Pse => Self::Pse,
            enums::BankTransferType::Ach => Self::Ach,
            enums::BankTransferType::SepaBankTransfer => Self::Sepa,
            enums::BankTransferType::Bacs => Self::Bacs,
            enums::BankTransferType::BcaBankTransfer => Self::BcaBankTransfer,
            enums::BankTransferType::BniVa => Self::BniVa,
            enums::BankTransferType::BriVa => Self::BriVa,
            enums::BankTransferType::CimbVa => Self::CimbVa,
            enums::BankTransferType::DanamonVa => Self::DanamonVa,
            enums::BankTransferType::MandiriVa => Self::MandiriVa,
            enums::BankTransferType::PermataBankTransfer => Self::PermataBankTransfer,
            enums::BankTransferType::LocalBankTransfer => Self::LocalBankTransfer,
            enums::BankTransferType::InstantBankTransfer => Self::InstantBankTransfer,
            enums::BankTransferType::InstantBankTransferFinland => Self::InstantBankTransferFinland,
            enums::BankTransferType::InstantBankTransferPoland => Self::InstantBankTransferPoland,
            enums::BankTransferType::IndonesianBankTransfer => Self::IndonesianBankTransfer,
        }
    }
}

impl From<enums::GiftCardType> for global_enums::PaymentMethodType {
    fn from(value: enums::GiftCardType) -> Self {
        match value {
            enums::GiftCardType::PaySafeCard => Self::PaySafeCard,
            enums::GiftCardType::Givex => Self::Givex,
            enums::GiftCardType::BhnCardNetwork => Self::BhnCardNetwork,
        }
    }
}

impl From<enums::CardRedirectType> for global_enums::PaymentMethodType {
    fn from(value: enums::CardRedirectType) -> Self {
        match value {
            enums::CardRedirectType::Benefit => Self::Benefit,
            enums::CardRedirectType::Knet => Self::Knet,
            enums::CardRedirectType::MomoAtm => Self::MomoAtm,
            enums::CardRedirectType::CardRedirect => Self::CardRedirect,
        }
    }
}

impl From<enums::MobilePaymentType> for global_enums::PaymentMethodType {
    fn from(value: enums::MobilePaymentType) -> Self {
        match value {
            enums::MobilePaymentType::DirectCarrierBilling => Self::DirectCarrierBilling,
        }
    }
}

impl From<enums::BankRedirectType> for global_enums::PaymentMethodType {
    fn from(value: enums::BankRedirectType) -> Self {
        match value {
            enums::BankRedirectType::Bizum => Self::Bizum,
            enums::BankRedirectType::Giropay => Self::Giropay,
            enums::BankRedirectType::Ideal => Self::Ideal,
            enums::BankRedirectType::Sofort => Self::Sofort,
            enums::BankRedirectType::Eft => Self::Eft,
            enums::BankRedirectType::Eps => Self::Eps,
            enums::BankRedirectType::BancontactCard => Self::BancontactCard,
            enums::BankRedirectType::Blik => Self::Blik,
            enums::BankRedirectType::Interac => Self::Interac,
            enums::BankRedirectType::LocalBankRedirect => Self::LocalBankRedirect,
            enums::BankRedirectType::OnlineBankingCzechRepublic => Self::OnlineBankingCzechRepublic,
            enums::BankRedirectType::OnlineBankingFinland => Self::OnlineBankingFinland,
            enums::BankRedirectType::OnlineBankingPoland => Self::OnlineBankingPoland,
            enums::BankRedirectType::OnlineBankingSlovakia => Self::OnlineBankingSlovakia,
            enums::BankRedirectType::OnlineBankingFpx => Self::OnlineBankingFpx,
            enums::BankRedirectType::OnlineBankingThailand => Self::OnlineBankingThailand,
            enums::BankRedirectType::OpenBankingUk => Self::OpenBankingUk,
            enums::BankRedirectType::Przelewy24 => Self::Przelewy24,
            enums::BankRedirectType::Trustly => Self::Trustly,
        }
    }
}

impl From<enums::OpenBankingType> for global_enums::PaymentMethodType {
    fn from(value: enums::OpenBankingType) -> Self {
        match value {
            enums::OpenBankingType::OpenBankingPIS => Self::OpenBankingPIS,
        }
    }
}

impl From<enums::CryptoType> for global_enums::PaymentMethodType {
    fn from(value: enums::CryptoType) -> Self {
        match value {
            enums::CryptoType::CryptoCurrency => Self::CryptoCurrency,
        }
    }
}

impl From<enums::RewardType> for global_enums::PaymentMethodType {
    fn from(value: enums::RewardType) -> Self {
        match value {
            enums::RewardType::ClassicReward => Self::ClassicReward,
            enums::RewardType::Evoucher => Self::Evoucher,
        }
    }
}

impl From<enums::RealTimePaymentType> for global_enums::PaymentMethodType {
    fn from(value: enums::RealTimePaymentType) -> Self {
        match value {
            enums::RealTimePaymentType::Fps => Self::Fps,
            enums::RealTimePaymentType::DuitNow => Self::DuitNow,
            enums::RealTimePaymentType::PromptPay => Self::PromptPay,
            enums::RealTimePaymentType::VietQr => Self::VietQr,
        }
    }
}

/// Analyses of the lowering of the DirValues to EuclidValues .
///
/// For example,
/// ```notrust
/// DirValue::PaymentMethod::Cards -> EuclidValue::PaymentMethod::Cards
/// ```notrust
/// This is a function that lowers the Values of the DIR variants into the Value of the VIR variants.
/// The function for each DirValue variant creates a corresponding EuclidValue variants and if there
/// lacks any direct mapping, it return an Error.
fn lower_value(dir_value: dir::DirValue) -> Result<EuclidValue, AnalysisErrorType> {
    Ok(match dir_value {
        dir::DirValue::PaymentMethod(pm) => EuclidValue::PaymentMethod(pm),
        dir::DirValue::CardBin(ci) => EuclidValue::CardBin(ci),
        dir::DirValue::ExtendedCardBin(ecb) => EuclidValue::ExtendedCardBin(ecb),
        dir::DirValue::CardType(ct) => EuclidValue::PaymentMethodType(ct.into()),
        dir::DirValue::CardNetwork(cn) => EuclidValue::CardNetwork(cn),
        dir::DirValue::MetaData(md) => EuclidValue::Metadata(md),
        dir::DirValue::PayLaterType(plt) => EuclidValue::PaymentMethodType(plt.into()),
        dir::DirValue::WalletType(wt) => EuclidValue::PaymentMethodType(wt.into()),
        dir::DirValue::UpiType(ut) => EuclidValue::PaymentMethodType(ut.into()),
        dir::DirValue::VoucherType(vt) => EuclidValue::PaymentMethodType(vt.into()),
        dir::DirValue::BankTransferType(btt) => EuclidValue::PaymentMethodType(btt.into()),
        dir::DirValue::GiftCardType(gct) => EuclidValue::PaymentMethodType(gct.into()),
        dir::DirValue::CardRedirectType(crt) => EuclidValue::PaymentMethodType(crt.into()),
        dir::DirValue::BankRedirectType(brt) => EuclidValue::PaymentMethodType(brt.into()),
        dir::DirValue::CryptoType(ct) => EuclidValue::PaymentMethodType(ct.into()),
        dir::DirValue::RealTimePaymentType(rtpt) => EuclidValue::PaymentMethodType(rtpt.into()),
        dir::DirValue::AuthenticationType(at) => EuclidValue::AuthenticationType(at),
        dir::DirValue::CaptureMethod(cm) => EuclidValue::CaptureMethod(cm),
        dir::DirValue::PaymentAmount(pa) => EuclidValue::PaymentAmount(pa),
        dir::DirValue::PaymentCurrency(pc) => EuclidValue::PaymentCurrency(pc),
        dir::DirValue::BusinessCountry(buc) => EuclidValue::BusinessCountry(buc),
        dir::DirValue::BillingCountry(bic) => EuclidValue::BillingCountry(bic),
        dir::DirValue::MandateAcceptanceType(mat) => EuclidValue::MandateAcceptanceType(mat),
        dir::DirValue::MandateType(mt) => EuclidValue::MandateType(mt),
        dir::DirValue::PaymentType(pt) => EuclidValue::PaymentType(pt),
        dir::DirValue::Connector(_) => Err(AnalysisErrorType::UnsupportedProgramKey(
            dir::DirKeyKind::Connector,
        ))?,
        dir::DirValue::BankDebitType(bdt) => EuclidValue::PaymentMethodType(bdt.into()),
        dir::DirValue::RewardType(rt) => EuclidValue::PaymentMethodType(rt.into()),
        dir::DirValue::BusinessLabel(bl) => EuclidValue::BusinessLabel(bl),
        dir::DirValue::SetupFutureUsage(sfu) => EuclidValue::SetupFutureUsage(sfu),
        dir::DirValue::OpenBankingType(ob) => EuclidValue::PaymentMethodType(ob.into()),
        dir::DirValue::MobilePaymentType(mp) => EuclidValue::PaymentMethodType(mp.into()),
        dir::DirValue::IssuerName(str_value) => EuclidValue::IssuerName(str_value),
        dir::DirValue::IssuerCountry(country) => EuclidValue::IssuerCountry(country),
        dir::DirValue::CustomerDevicePlatform(customer_device_platform) => {
            EuclidValue::CustomerDevicePlatform(customer_device_platform)
        }
        dir::DirValue::CustomerDeviceType(customer_device_type) => {
            EuclidValue::CustomerDeviceType(customer_device_type)
        }
        dir::DirValue::CustomerDeviceDisplaySize(customer_device_display_size) => {
            EuclidValue::CustomerDeviceDisplaySize(customer_device_display_size)
        }
        dir::DirValue::AcquirerCountry(country) => EuclidValue::AcquirerCountry(country),
        dir::DirValue::AcquirerFraudRate(num_value) => EuclidValue::AcquirerFraudRate(num_value),
    })
}

fn lower_comparison(
    dir_comparison: dir::DirComparison,
) -> Result<vir::ValuedComparison, AnalysisErrorType> {
    Ok(vir::ValuedComparison {
        values: dir_comparison
            .values
            .into_iter()
            .map(lower_value)
            .collect::<Result<_, _>>()?,
        logic: match dir_comparison.logic {
            dir::DirComparisonLogic::NegativeConjunction => {
                vir::ValuedComparisonLogic::NegativeConjunction
            }
            dir::DirComparisonLogic::PositiveDisjunction => {
                vir::ValuedComparisonLogic::PositiveDisjunction
            }
        },
        metadata: dir_comparison.metadata,
    })
}

fn lower_if_statement(
    dir_if_statement: dir::DirIfStatement,
) -> Result<vir::ValuedIfStatement, AnalysisErrorType> {
    Ok(vir::ValuedIfStatement {
        condition: dir_if_statement
            .condition
            .into_iter()
            .map(lower_comparison)
            .collect::<Result<_, _>>()?,
        nested: dir_if_statement
            .nested
            .map(|v| {
                v.into_iter()
                    .map(lower_if_statement)
                    .collect::<Result<_, _>>()
            })
            .transpose()?,
    })
}

fn lower_rule<O>(dir_rule: dir::DirRule<O>) -> Result<vir::ValuedRule<O>, AnalysisErrorType> {
    Ok(vir::ValuedRule {
        name: dir_rule.name,
        connector_selection: dir_rule.connector_selection,
        statements: dir_rule
            .statements
            .into_iter()
            .map(lower_if_statement)
            .collect::<Result<_, _>>()?,
    })
}

pub fn lower_program<O>(
    dir_program: dir::DirProgram<O>,
) -> Result<vir::ValuedProgram<O>, AnalysisError> {
    Ok(vir::ValuedProgram {
        default_selection: dir_program.default_selection,
        rules: dir_program
            .rules
            .into_iter()
            .map(lower_rule)
            .collect::<Result<_, _>>()
            .map_err(|e| AnalysisError {
                error_type: e,
                metadata: Default::default(),
            })?,
        metadata: dir_program.metadata,
    })
}


// File: crates/euclid/src/frontend/dir/transformers.rs

use crate::{dirval, dssa::types::AnalysisErrorType, enums as global_enums, frontend::dir};

pub trait IntoDirValue {
    fn into_dir_value(self) -> Result<dir::DirValue, AnalysisErrorType>;
}
impl IntoDirValue for (global_enums::PaymentMethodType, global_enums::PaymentMethod) {
    fn into_dir_value(self) -> Result<dir::DirValue, AnalysisErrorType> {
        match self.0 {
            global_enums::PaymentMethodType::Credit => Ok(dirval!(CardType = Credit)),
            global_enums::PaymentMethodType::Debit => Ok(dirval!(CardType = Debit)),
            #[cfg(feature = "v2")]
            global_enums::PaymentMethodType::Card => Ok(dirval!(CardType = Card)),
            global_enums::PaymentMethodType::Giropay => Ok(dirval!(BankRedirectType = Giropay)),
            global_enums::PaymentMethodType::Ideal => Ok(dirval!(BankRedirectType = Ideal)),
            global_enums::PaymentMethodType::Sofort => Ok(dirval!(BankRedirectType = Sofort)),
            global_enums::PaymentMethodType::DuitNow => Ok(dirval!(RealTimePaymentType = DuitNow)),
            global_enums::PaymentMethodType::Eps => Ok(dirval!(BankRedirectType = Eps)),
            global_enums::PaymentMethodType::Fps => Ok(dirval!(RealTimePaymentType = Fps)),
            global_enums::PaymentMethodType::Klarna => Ok(dirval!(PayLaterType = Klarna)),
            global_enums::PaymentMethodType::Affirm => Ok(dirval!(PayLaterType = Affirm)),
            global_enums::PaymentMethodType::Payjustnow => Ok(dirval!(PayLaterType = Payjustnow)),
            global_enums::PaymentMethodType::AfterpayClearpay => {
                Ok(dirval!(PayLaterType = AfterpayClearpay))
            }
            global_enums::PaymentMethodType::AmazonPay => Ok(dirval!(WalletType = AmazonPay)),
            global_enums::PaymentMethodType::Skrill => Ok(dirval!(WalletType = Skrill)),
            global_enums::PaymentMethodType::Paysera => Ok(dirval!(WalletType = Paysera)),
            global_enums::PaymentMethodType::GooglePay => Ok(dirval!(WalletType = GooglePay)),
            global_enums::PaymentMethodType::Bluecode => Ok(dirval!(WalletType = Bluecode)),
            global_enums::PaymentMethodType::ApplePay => Ok(dirval!(WalletType = ApplePay)),
            global_enums::PaymentMethodType::Paypal => Ok(dirval!(WalletType = Paypal)),
            global_enums::PaymentMethodType::RevolutPay => Ok(dirval!(WalletType = RevolutPay)),
            global_enums::PaymentMethodType::CryptoCurrency => {
                Ok(dirval!(CryptoType = CryptoCurrency))
            }
            global_enums::PaymentMethodType::Ach => match self.1 {
                global_enums::PaymentMethod::BankDebit => Ok(dirval!(BankDebitType = Ach)),
                global_enums::PaymentMethod::BankTransfer => Ok(dirval!(BankTransferType = Ach)),
                global_enums::PaymentMethod::PayLater
                | global_enums::PaymentMethod::Card
                | global_enums::PaymentMethod::CardRedirect
                | global_enums::PaymentMethod::Wallet
                | global_enums::PaymentMethod::BankRedirect
                | global_enums::PaymentMethod::Crypto
                | global_enums::PaymentMethod::Reward
                | global_enums::PaymentMethod::RealTimePayment
                | global_enums::PaymentMethod::Upi
                | global_enums::PaymentMethod::Voucher
                | global_enums::PaymentMethod::OpenBanking
                | global_enums::PaymentMethod::MobilePayment
                | global_enums::PaymentMethod::GiftCard => Err(AnalysisErrorType::NotSupported),
            },
            global_enums::PaymentMethodType::Bacs => match self.1 {
                global_enums::PaymentMethod::BankDebit => Ok(dirval!(BankDebitType = Bacs)),
                global_enums::PaymentMethod::BankTransfer => Ok(dirval!(BankTransferType = Bacs)),
                global_enums::PaymentMethod::PayLater
                | global_enums::PaymentMethod::Card
                | global_enums::PaymentMethod::CardRedirect
                | global_enums::PaymentMethod::Wallet
                | global_enums::PaymentMethod::BankRedirect
                | global_enums::PaymentMethod::Crypto
                | global_enums::PaymentMethod::Reward
                | global_enums::PaymentMethod::RealTimePayment
                | global_enums::PaymentMethod::Upi
                | global_enums::PaymentMethod::Voucher
                | global_enums::PaymentMethod::OpenBanking
                | global_enums::PaymentMethod::MobilePayment
                | global_enums::PaymentMethod::GiftCard => Err(AnalysisErrorType::NotSupported),
            },
            global_enums::PaymentMethodType::Becs => Ok(dirval!(BankDebitType = Becs)),
            global_enums::PaymentMethodType::Sepa => Ok(dirval!(BankDebitType = Sepa)),
            global_enums::PaymentMethodType::SepaGuarenteedDebit => {
                Ok(dirval!(BankDebitType = SepaGuarenteedDebit))
            }
            global_enums::PaymentMethodType::SepaBankTransfer => {
                Ok(dirval!(BankTransferType = SepaBankTransfer))
            }
            global_enums::PaymentMethodType::AliPay => Ok(dirval!(WalletType = AliPay)),
            global_enums::PaymentMethodType::AliPayHk => Ok(dirval!(WalletType = AliPayHk)),
            global_enums::PaymentMethodType::BancontactCard => {
                Ok(dirval!(BankRedirectType = BancontactCard))
            }
            global_enums::PaymentMethodType::Blik => Ok(dirval!(BankRedirectType = Blik)),
            global_enums::PaymentMethodType::MbWay => Ok(dirval!(WalletType = MbWay)),
            global_enums::PaymentMethodType::MobilePay => Ok(dirval!(WalletType = MobilePay)),
            global_enums::PaymentMethodType::Cashapp => Ok(dirval!(WalletType = Cashapp)),
            global_enums::PaymentMethodType::Multibanco => {
                Ok(dirval!(BankTransferType = Multibanco))
            }
            global_enums::PaymentMethodType::Pix => Ok(dirval!(BankTransferType = Pix)),
            global_enums::PaymentMethodType::Pse => Ok(dirval!(BankTransferType = Pse)),
            global_enums::PaymentMethodType::Interac => Ok(dirval!(BankRedirectType = Interac)),
            global_enums::PaymentMethodType::OnlineBankingCzechRepublic => {
                Ok(dirval!(BankRedirectType = OnlineBankingCzechRepublic))
            }
            global_enums::PaymentMethodType::OnlineBankingFinland => {
                Ok(dirval!(BankRedirectType = OnlineBankingFinland))
            }
            global_enums::PaymentMethodType::OnlineBankingPoland => {
                Ok(dirval!(BankRedirectType = OnlineBankingPoland))
            }
            global_enums::PaymentMethodType::OnlineBankingSlovakia => {
                Ok(dirval!(BankRedirectType = OnlineBankingSlovakia))
            }
            global_enums::PaymentMethodType::Swish => Ok(dirval!(WalletType = Swish)),
            global_enums::PaymentMethodType::Trustly => Ok(dirval!(BankRedirectType = Trustly)),
            global_enums::PaymentMethodType::Bizum => Ok(dirval!(BankRedirectType = Bizum)),

            global_enums::PaymentMethodType::PayBright => Ok(dirval!(PayLaterType = PayBright)),
            global_enums::PaymentMethodType::Flexiti => Ok(dirval!(PayLaterType = Flexiti)),
            global_enums::PaymentMethodType::Walley => Ok(dirval!(PayLaterType = Walley)),
            global_enums::PaymentMethodType::Breadpay => Ok(dirval!(PayLaterType = Breadpay)),
            global_enums::PaymentMethodType::Przelewy24 => {
                Ok(dirval!(BankRedirectType = Przelewy24))
            }
            global_enums::PaymentMethodType::PromptPay => {
                Ok(dirval!(RealTimePaymentType = PromptPay))
            }
            global_enums::PaymentMethodType::WeChatPay => Ok(dirval!(WalletType = WeChatPay)),

            global_enums::PaymentMethodType::ClassicReward => {
                Ok(dirval!(RewardType = ClassicReward))
            }
            global_enums::PaymentMethodType::Evoucher => Ok(dirval!(RewardType = Evoucher)),
            global_enums::PaymentMethodType::UpiCollect => Ok(dirval!(UpiType = UpiCollect)),
            global_enums::PaymentMethodType::UpiIntent => Ok(dirval!(UpiType = UpiIntent)),
            global_enums::PaymentMethodType::UpiQr => Ok(dirval!(UpiType = UpiQr)),
            global_enums::PaymentMethodType::SamsungPay => Ok(dirval!(WalletType = SamsungPay)),
            global_enums::PaymentMethodType::GoPay => Ok(dirval!(WalletType = GoPay)),
            global_enums::PaymentMethodType::KakaoPay => Ok(dirval!(WalletType = KakaoPay)),
            global_enums::PaymentMethodType::Twint => Ok(dirval!(WalletType = Twint)),
            global_enums::PaymentMethodType::Gcash => Ok(dirval!(WalletType = Gcash)),
            global_enums::PaymentMethodType::Vipps => Ok(dirval!(WalletType = Vipps)),
            global_enums::PaymentMethodType::VietQr => Ok(dirval!(RealTimePaymentType = VietQr)),
            global_enums::PaymentMethodType::Momo => Ok(dirval!(WalletType = Momo)),
            global_enums::PaymentMethodType::Alma => Ok(dirval!(PayLaterType = Alma)),
            global_enums::PaymentMethodType::Dana => Ok(dirval!(WalletType = Dana)),
            global_enums::PaymentMethodType::OnlineBankingFpx => {
                Ok(dirval!(BankRedirectType = OnlineBankingFpx))
            }
            global_enums::PaymentMethodType::LocalBankRedirect => {
                Ok(dirval!(BankRedirectType = LocalBankRedirect))
            }
            global_enums::PaymentMethodType::OnlineBankingThailand => {
                Ok(dirval!(BankRedirectType = OnlineBankingThailand))
            }
            global_enums::PaymentMethodType::TouchNGo => Ok(dirval!(WalletType = TouchNGo)),
            global_enums::PaymentMethodType::Atome => Ok(dirval!(PayLaterType = Atome)),
            global_enums::PaymentMethodType::Boleto => Ok(dirval!(VoucherType = Boleto)),
            global_enums::PaymentMethodType::Efecty => Ok(dirval!(VoucherType = Efecty)),
            global_enums::PaymentMethodType::PagoEfectivo => {
                Ok(dirval!(VoucherType = PagoEfectivo))
            }
            global_enums::PaymentMethodType::RedCompra => Ok(dirval!(VoucherType = RedCompra)),
            global_enums::PaymentMethodType::RedPagos => Ok(dirval!(VoucherType = RedPagos)),
            global_enums::PaymentMethodType::Alfamart => Ok(dirval!(VoucherType = Alfamart)),
            global_enums::PaymentMethodType::BcaBankTransfer => {
                Ok(dirval!(BankTransferType = BcaBankTransfer))
            }
            global_enums::PaymentMethodType::BniVa => Ok(dirval!(BankTransferType = BniVa)),
            global_enums::PaymentMethodType::BriVa => Ok(dirval!(BankTransferType = BriVa)),
            global_enums::PaymentMethodType::CimbVa => Ok(dirval!(BankTransferType = CimbVa)),
            global_enums::PaymentMethodType::DanamonVa => Ok(dirval!(BankTransferType = DanamonVa)),
            global_enums::PaymentMethodType::Indomaret => Ok(dirval!(VoucherType = Indomaret)),
            global_enums::PaymentMethodType::MandiriVa => Ok(dirval!(BankTransferType = MandiriVa)),
            global_enums::PaymentMethodType::LocalBankTransfer => {
                Ok(dirval!(BankTransferType = LocalBankTransfer))
            }
            global_enums::PaymentMethodType::InstantBankTransfer => {
                Ok(dirval!(BankTransferType = InstantBankTransfer))
            }
            global_enums::PaymentMethodType::InstantBankTransferFinland => {
                Ok(dirval!(BankTransferType = InstantBankTransferFinland))
            }
            global_enums::PaymentMethodType::InstantBankTransferPoland => {
                Ok(dirval!(BankTransferType = InstantBankTransferPoland))
            }
            global_enums::PaymentMethodType::PermataBankTransfer => {
                Ok(dirval!(BankTransferType = PermataBankTransfer))
            }
            global_enums::PaymentMethodType::PaySafeCard => Ok(dirval!(GiftCardType = PaySafeCard)),
            global_enums::PaymentMethodType::SevenEleven => Ok(dirval!(VoucherType = SevenEleven)),
            global_enums::PaymentMethodType::Lawson => Ok(dirval!(VoucherType = Lawson)),
            global_enums::PaymentMethodType::MiniStop => Ok(dirval!(VoucherType = MiniStop)),
            global_enums::PaymentMethodType::FamilyMart => Ok(dirval!(VoucherType = FamilyMart)),
            global_enums::PaymentMethodType::Seicomart => Ok(dirval!(VoucherType = Seicomart)),
            global_enums::PaymentMethodType::PayEasy => Ok(dirval!(VoucherType = PayEasy)),
            global_enums::PaymentMethodType::Givex => Ok(dirval!(GiftCardType = Givex)),
            global_enums::PaymentMethodType::Benefit => Ok(dirval!(CardRedirectType = Benefit)),
            global_enums::PaymentMethodType::Knet => Ok(dirval!(CardRedirectType = Knet)),
            global_enums::PaymentMethodType::OpenBankingUk => {
                Ok(dirval!(BankRedirectType = OpenBankingUk))
            }
            global_enums::PaymentMethodType::MomoAtm => Ok(dirval!(CardRedirectType = MomoAtm)),
            global_enums::PaymentMethodType::Oxxo => Ok(dirval!(VoucherType = Oxxo)),
            global_enums::PaymentMethodType::CardRedirect => {
                Ok(dirval!(CardRedirectType = CardRedirect))
            }
            global_enums::PaymentMethodType::Venmo => Ok(dirval!(WalletType = Venmo)),
            global_enums::PaymentMethodType::Mifinity => Ok(dirval!(WalletType = Mifinity)),
            global_enums::PaymentMethodType::OpenBankingPIS => {
                Ok(dirval!(OpenBankingType = OpenBankingPIS))
            }
            global_enums::PaymentMethodType::Paze => Ok(dirval!(WalletType = Paze)),
            global_enums::PaymentMethodType::DirectCarrierBilling => {
                Ok(dirval!(MobilePaymentType = DirectCarrierBilling))
            }
            global_enums::PaymentMethodType::Eft => Ok(dirval!(BankRedirectType = Eft)),
            global_enums::PaymentMethodType::IndonesianBankTransfer => {
                Ok(dirval!(BankTransferType = IndonesianBankTransfer))
            }
            global_enums::PaymentMethodType::BhnCardNetwork => {
                Ok(dirval!(GiftCardType = BhnCardNetwork))
            }
        }
    }
}


// File: crates/euclid/src/frontend/dir/enums.rs

use strum::VariantNames;
use utoipa::ToSchema;

use crate::enums::collect_variants;
pub use crate::enums::{
    AuthenticationType, CaptureMethod, CardNetwork, Country, Country as BusinessCountry,
    Country as BillingCountry, Country as IssuerCountry, Country as AcquirerCountry, CountryAlpha2,
    Currency as PaymentCurrency, MandateAcceptanceType, MandateType, PaymentMethod, PaymentType,
    RoutableConnectors, SetupFutureUsage,
};
#[cfg(feature = "payouts")]
pub use crate::enums::{PayoutBankTransferType, PayoutType, PayoutWalletType};

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CardType {
    Credit,
    Debit,
    #[cfg(feature = "v2")]
    Card,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PayLaterType {
    Affirm,
    AfterpayClearpay,
    Alma,
    Klarna,
    PayBright,
    Walley,
    Flexiti,
    Atome,
    Breadpay,
    Payjustnow,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum WalletType {
    Bluecode,
    GooglePay,
    AmazonPay,
    Skrill,
    Paysera,
    ApplePay,
    Paypal,
    AliPay,
    AliPayHk,
    MbWay,
    MobilePay,
    WeChatPay,
    SamsungPay,
    GoPay,
    KakaoPay,
    Twint,
    Gcash,
    Vipps,
    Momo,
    Dana,
    TouchNGo,
    Swish,
    Cashapp,
    Venmo,
    Mifinity,
    Paze,
    RevolutPay,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum VoucherType {
    Boleto,
    Efecty,
    PagoEfectivo,
    RedCompra,
    RedPagos,
    Alfamart,
    Indomaret,
    SevenEleven,
    Lawson,
    MiniStop,
    FamilyMart,
    Seicomart,
    PayEasy,
    Oxxo,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum BankRedirectType {
    Bizum,
    Giropay,
    Ideal,
    Sofort,
    Eft,
    Eps,
    BancontactCard,
    Blik,
    Interac,
    LocalBankRedirect,
    OnlineBankingCzechRepublic,
    OnlineBankingFinland,
    OnlineBankingPoland,
    OnlineBankingSlovakia,
    OnlineBankingFpx,
    OnlineBankingThailand,
    OpenBankingUk,
    Przelewy24,
    Trustly,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum OpenBankingType {
    OpenBankingPIS,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum BankTransferType {
    Multibanco,
    Ach,
    SepaBankTransfer,
    Bacs,
    BcaBankTransfer,
    BniVa,
    BriVa,
    CimbVa,
    DanamonVa,
    MandiriVa,
    PermataBankTransfer,
    Pix,
    Pse,
    LocalBankTransfer,
    InstantBankTransfer,
    InstantBankTransferFinland,
    InstantBankTransferPoland,
    IndonesianBankTransfer,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum GiftCardType {
    PaySafeCard,
    Givex,
    BhnCardNetwork,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CardRedirectType {
    Benefit,
    Knet,
    MomoAtm,
    CardRedirect,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MobilePaymentType {
    DirectCarrierBilling,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CryptoType {
    CryptoCurrency,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RealTimePaymentType {
    Fps,
    DuitNow,
    PromptPay,
    VietQr,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum UpiType {
    UpiCollect,
    UpiIntent,
    UpiQr,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum BankDebitType {
    Ach,
    Sepa,
    SepaGuarenteedDebit,
    Bacs,
    Becs,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RewardType {
    ClassicReward,
    Evoucher,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CustomerDevicePlatform {
    Web,
    Android,
    Ios,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CustomerDeviceType {
    Mobile,
    Tablet,
    Desktop,
    GamingConsole,
}

// Common display sizes for different device types
#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CustomerDeviceDisplaySize {
    // Mobile sizes
    Size320x568, // iPhone SE
    Size375x667, // iPhone 8
    Size390x844, // iPhone 12/13
    Size414x896, // iPhone XR/11
    Size428x926, // iPhone 12/13 Pro Max

    // Tablet sizes
    Size768x1024,  // iPad
    Size834x1112,  // iPad Pro 10.5
    Size834x1194,  // iPad Pro 11
    Size1024x1366, // iPad Pro 12.9

    // Desktop sizes
    Size1280x720,  // HD
    Size1366x768,  // Common laptop
    Size1440x900,  // MacBook Air
    Size1920x1080, // Full HD
    Size2560x1440, // QHD
    Size3840x2160, // 4K

    // Custom sizes
    Size500x600,
    Size600x400,

    // Other common sizes
    Size360x640,  // Common Android
    Size412x915,  // Pixel 6
    Size800x1280, // Common Android tablet
}

collect_variants!(CardType);
collect_variants!(PayLaterType);
collect_variants!(WalletType);
collect_variants!(BankRedirectType);
collect_variants!(BankDebitType);
collect_variants!(CryptoType);
collect_variants!(RewardType);
collect_variants!(RealTimePaymentType);
collect_variants!(UpiType);
collect_variants!(VoucherType);
collect_variants!(GiftCardType);
collect_variants!(BankTransferType);
collect_variants!(CardRedirectType);
collect_variants!(OpenBankingType);
collect_variants!(MobilePaymentType);
collect_variants!(CustomerDeviceType);
collect_variants!(CustomerDevicePlatform);
collect_variants!(CustomerDeviceDisplaySize);


// File: crates/euclid/src/frontend/ast/lowering.rs

//! Analysis for the Lowering logic in ast
//!
//!Certain functions that can be used to perform the complete lowering of ast to dir.
//!This includes lowering of enums, numbers, strings as well as Comparison logics.

use std::str::FromStr;

use crate::{
    dssa::types::{AnalysisError, AnalysisErrorType},
    enums::CollectVariants,
    frontend::{
        ast,
        dir::{self, enums as dir_enums, EuclidDirFilter},
    },
    types::{self, DataType},
};

/// lowers the provided key (enum variant) & value to the respective DirValue
///
/// For example
/// ```notrust
/// CardType = Visa
/// ```notrust
///
/// This serves for the purpose were we have the DirKey as an explicit Enum type and value as one
/// of the member of the same Enum.
/// So particularly it lowers a predefined Enum from DirKey to an Enum of DirValue.
macro_rules! lower_enum {
    ($key:ident, $value:ident) => {
        match $value {
            ast::ValueType::EnumVariant(ev) => Ok(vec![dir::DirValue::$key(
                dir_enums::$key::from_str(&ev).map_err(|_| AnalysisErrorType::InvalidVariant {
                    key: dir::DirKeyKind::$key.to_string(),
                    got: ev,
                    expected: dir_enums::$key::variants(),
                })?,
            )]),

            ast::ValueType::EnumVariantArray(eva) => eva
                .into_iter()
                .map(|ev| {
                    Ok(dir::DirValue::$key(
                        dir_enums::$key::from_str(&ev).map_err(|_| {
                            AnalysisErrorType::InvalidVariant {
                                key: dir::DirKeyKind::$key.to_string(),
                                got: ev,
                                expected: dir_enums::$key::variants(),
                            }
                        })?,
                    ))
                })
                .collect(),

            _ => Err(AnalysisErrorType::InvalidType {
                key: dir::DirKeyKind::$key.to_string(),
                expected: DataType::EnumVariant,
                got: $value.get_type(),
            }),
        }
    };
}

/// lowers the provided key for a numerical value
///
/// For example
/// ```notrust
/// payment_amount = 17052001
/// ```notrust
/// This is for the cases in which there are numerical values involved and they are lowered
/// accordingly on basis of the supplied key, currently payment_amount is the only key having this
/// use case
macro_rules! lower_number {
    ($key:ident, $value:ident, $comp:ident) => {
        match $value {
            ast::ValueType::Number(num) => Ok(vec![dir::DirValue::$key(types::NumValue {
                number: num,
                refinement: $comp.into(),
            })]),

            ast::ValueType::NumberArray(na) => na
                .into_iter()
                .map(|num| {
                    Ok(dir::DirValue::$key(types::NumValue {
                        number: num,
                        refinement: $comp.clone().into(),
                    }))
                })
                .collect(),

            ast::ValueType::NumberComparisonArray(nca) => nca
                .into_iter()
                .map(|nc| {
                    Ok(dir::DirValue::$key(types::NumValue {
                        number: nc.number,
                        refinement: nc.comparison_type.into(),
                    }))
                })
                .collect(),

            _ => Err(AnalysisErrorType::InvalidType {
                key: dir::DirKeyKind::$key.to_string(),
                expected: DataType::Number,
                got: $value.get_type(),
            }),
        }
    };
}

/// lowers the provided key & value to the respective DirValue
///
/// For example
/// ```notrust
/// card_bin = "123456"
/// ```notrust
///
/// This serves for the purpose were we have the DirKey as Card_bin and value as an arbitrary string
/// So particularly it lowers an arbitrary value to a predefined key.
macro_rules! lower_str {
    ($key:ident, $value:ident $(, $validation_closure:expr)?) => {
        match $value {
            ast::ValueType::StrValue(st) => {
                $($validation_closure(&st)?;)?
                Ok(vec![dir::DirValue::$key(types::StrValue { value: st })])
            }
            _ => Err(AnalysisErrorType::InvalidType {
                key: dir::DirKeyKind::$key.to_string(),
                expected: DataType::StrValue,
                got: $value.get_type(),
            }),
        }
    };
}

macro_rules! lower_metadata {
    ($key:ident, $value:ident) => {
        match $value {
            ast::ValueType::MetadataVariant(md) => {
                Ok(vec![dir::DirValue::$key(types::MetadataValue {
                    key: md.key,
                    value: md.value,
                })])
            }
            _ => Err(AnalysisErrorType::InvalidType {
                key: dir::DirKeyKind::$key.to_string(),
                expected: DataType::MetadataValue,
                got: $value.get_type(),
            }),
        }
    };
}
/// lowers the comparison operators for different subtle value types present
/// by throwing required errors for comparisons that can't be performed for a certain value type
/// for example
/// can't have greater/less than operations on enum types
fn lower_comparison_inner<O: EuclidDirFilter>(
    comp: ast::Comparison,
) -> Result<Vec<dir::DirValue>, AnalysisErrorType> {
    let key_enum = dir::DirKeyKind::from_str(comp.lhs.as_str())
        .map_err(|_| AnalysisErrorType::InvalidKey(comp.lhs.clone()))?;

    if !O::is_key_allowed(&key_enum) {
        return Err(AnalysisErrorType::InvalidKey(key_enum.to_string()));
    }

    match (&comp.comparison, &comp.value) {
        (
            ast::ComparisonType::LessThan
            | ast::ComparisonType::GreaterThan
            | ast::ComparisonType::GreaterThanEqual
            | ast::ComparisonType::LessThanEqual,
            ast::ValueType::EnumVariant(_),
        ) => {
            Err(AnalysisErrorType::InvalidComparison {
                operator: comp.comparison.clone(),
                value_type: DataType::EnumVariant,
            })?;
        }

        (
            ast::ComparisonType::LessThan
            | ast::ComparisonType::GreaterThan
            | ast::ComparisonType::GreaterThanEqual
            | ast::ComparisonType::LessThanEqual,
            ast::ValueType::NumberArray(_),
        ) => {
            Err(AnalysisErrorType::InvalidComparison {
                operator: comp.comparison.clone(),
                value_type: DataType::Number,
            })?;
        }

        (
            ast::ComparisonType::LessThan
            | ast::ComparisonType::GreaterThan
            | ast::ComparisonType::GreaterThanEqual
            | ast::ComparisonType::LessThanEqual,
            ast::ValueType::EnumVariantArray(_),
        ) => {
            Err(AnalysisErrorType::InvalidComparison {
                operator: comp.comparison.clone(),
                value_type: DataType::EnumVariant,
            })?;
        }

        (
            ast::ComparisonType::LessThan
            | ast::ComparisonType::GreaterThan
            | ast::ComparisonType::GreaterThanEqual
            | ast::ComparisonType::LessThanEqual,
            ast::ValueType::NumberComparisonArray(_),
        ) => {
            Err(AnalysisErrorType::InvalidComparison {
                operator: comp.comparison.clone(),
                value_type: DataType::Number,
            })?;
        }

        _ => {}
    }

    let value = comp.value;
    let comparison = comp.comparison;

    match key_enum {
        dir::DirKeyKind::PaymentMethod => lower_enum!(PaymentMethod, value),
        dir::DirKeyKind::CardType => lower_enum!(CardType, value),
        dir::DirKeyKind::CardNetwork => lower_enum!(CardNetwork, value),
        dir::DirKeyKind::PayLaterType => lower_enum!(PayLaterType, value),
        dir::DirKeyKind::WalletType => lower_enum!(WalletType, value),
        dir::DirKeyKind::BankDebitType => lower_enum!(BankDebitType, value),
        dir::DirKeyKind::BankRedirectType => lower_enum!(BankRedirectType, value),
        dir::DirKeyKind::CryptoType => lower_enum!(CryptoType, value),
        dir::DirKeyKind::PaymentType => lower_enum!(PaymentType, value),
        dir::DirKeyKind::MandateType => lower_enum!(MandateType, value),
        dir::DirKeyKind::MandateAcceptanceType => lower_enum!(MandateAcceptanceType, value),
        dir::DirKeyKind::RewardType => lower_enum!(RewardType, value),
        dir::DirKeyKind::PaymentCurrency => lower_enum!(PaymentCurrency, value),
        dir::DirKeyKind::AuthenticationType => lower_enum!(AuthenticationType, value),
        dir::DirKeyKind::CaptureMethod => lower_enum!(CaptureMethod, value),
        dir::DirKeyKind::BusinessCountry => lower_enum!(BusinessCountry, value),
        dir::DirKeyKind::BillingCountry => lower_enum!(BillingCountry, value),
        dir::DirKeyKind::SetupFutureUsage => lower_enum!(SetupFutureUsage, value),
        dir::DirKeyKind::UpiType => lower_enum!(UpiType, value),
        dir::DirKeyKind::OpenBankingType => lower_enum!(OpenBankingType, value),
        dir::DirKeyKind::VoucherType => lower_enum!(VoucherType, value),
        dir::DirKeyKind::GiftCardType => lower_enum!(GiftCardType, value),
        dir::DirKeyKind::BankTransferType => lower_enum!(BankTransferType, value),
        dir::DirKeyKind::CardRedirectType => lower_enum!(CardRedirectType, value),
        dir::DirKeyKind::MobilePaymentType => lower_enum!(MobilePaymentType, value),
        dir::DirKeyKind::RealTimePaymentType => lower_enum!(RealTimePaymentType, value),
        dir::DirKeyKind::CardBin => {
            let validation_closure = |st: &String| -> Result<(), AnalysisErrorType> {
                if st.len() == 6 && st.chars().all(|x| x.is_ascii_digit()) {
                    Ok(())
                } else {
                    Err(AnalysisErrorType::InvalidValue {
                        key: dir::DirKeyKind::CardBin,
                        value: st.clone(),
                        message: Some("Expected 6 digits".to_string()),
                    })
                }
            };
            lower_str!(CardBin, value, validation_closure)
        }
        dir::DirKeyKind::ExtendedCardBin => {
            let validation_closure = |st: &String| -> Result<(), AnalysisErrorType> {
                if st.len() == 8 && st.chars().all(|x| x.is_ascii_digit()) {
                    Ok(())
                } else {
                    Err(AnalysisErrorType::InvalidValue {
                        key: dir::DirKeyKind::ExtendedCardBin,
                        value: st.clone(),
                        message: Some("Expected 8 digits".to_string()),
                    })
                }
            };
            lower_str!(ExtendedCardBin, value, validation_closure)
        }
        dir::DirKeyKind::BusinessLabel => lower_str!(BusinessLabel, value),
        dir::DirKeyKind::MetaData => lower_metadata!(MetaData, value),
        dir::DirKeyKind::PaymentAmount => lower_number!(PaymentAmount, value, comparison),
        dir::DirKeyKind::Connector => Err(AnalysisErrorType::InvalidKey(
            dir::DirKeyKind::Connector.to_string(),
        )),
        dir::DirKeyKind::IssuerName => lower_str!(IssuerName, value),
        dir::DirKeyKind::IssuerCountry => lower_enum!(IssuerCountry, value),
        dir::DirKeyKind::CustomerDevicePlatform => lower_enum!(CustomerDevicePlatform, value),
        dir::DirKeyKind::CustomerDeviceType => lower_enum!(CustomerDeviceType, value),
        dir::DirKeyKind::CustomerDeviceDisplaySize => lower_enum!(CustomerDeviceDisplaySize, value),
        dir::DirKeyKind::AcquirerCountry => lower_enum!(AcquirerCountry, value),
        dir::DirKeyKind::AcquirerFraudRate => lower_number!(AcquirerFraudRate, value, comparison),
    }
}

/// returns all the comparison values by matching them appropriately to ComparisonTypes and in turn
/// calls the lower_comparison_inner function
fn lower_comparison<O: EuclidDirFilter>(
    comp: ast::Comparison,
) -> Result<dir::DirComparison, AnalysisError> {
    let metadata = comp.metadata.clone();
    let logic = match &comp.comparison {
        ast::ComparisonType::Equal => dir::DirComparisonLogic::PositiveDisjunction,
        ast::ComparisonType::NotEqual => dir::DirComparisonLogic::NegativeConjunction,
        ast::ComparisonType::LessThan => dir::DirComparisonLogic::PositiveDisjunction,
        ast::ComparisonType::LessThanEqual => dir::DirComparisonLogic::PositiveDisjunction,
        ast::ComparisonType::GreaterThanEqual => dir::DirComparisonLogic::PositiveDisjunction,
        ast::ComparisonType::GreaterThan => dir::DirComparisonLogic::PositiveDisjunction,
    };
    let values = lower_comparison_inner::<O>(comp).map_err(|etype| AnalysisError {
        error_type: etype,
        metadata: metadata.clone(),
    })?;

    Ok(dir::DirComparison {
        values,
        logic,
        metadata,
    })
}

/// lowers the if statement accordingly with a condition and following nested if statements (if
/// present)
fn lower_if_statement<O: EuclidDirFilter>(
    stmt: ast::IfStatement,
) -> Result<dir::DirIfStatement, AnalysisError> {
    Ok(dir::DirIfStatement {
        condition: stmt
            .condition
            .into_iter()
            .map(lower_comparison::<O>)
            .collect::<Result<_, _>>()?,
        nested: stmt
            .nested
            .map(|n| n.into_iter().map(lower_if_statement::<O>).collect())
            .transpose()?,
    })
}

/// lowers the rules supplied accordingly to DirRule struct by specifying the rule_name,
/// connector_selection and statements that are a bunch of if statements
pub fn lower_rule<O: EuclidDirFilter>(
    rule: ast::Rule<O>,
) -> Result<dir::DirRule<O>, AnalysisError> {
    Ok(dir::DirRule {
        name: rule.name,
        connector_selection: rule.connector_selection,
        statements: rule
            .statements
            .into_iter()
            .map(lower_if_statement::<O>)
            .collect::<Result<_, _>>()?,
    })
}

/// uses the above rules and lowers the whole ast Program into DirProgram by specifying
/// default_selection that is ast ConnectorSelection, a vector of DirRules and clones the metadata
/// whatever comes in the ast_program
pub fn lower_program<O: EuclidDirFilter>(
    program: ast::Program<O>,
) -> Result<dir::DirProgram<O>, AnalysisError> {
    Ok(dir::DirProgram {
        default_selection: program.default_selection,
        rules: program
            .rules
            .into_iter()
            .map(lower_rule)
            .collect::<Result<_, _>>()?,
        metadata: program.metadata,
    })
}