// File: crates/router/src/compatibility/stripe/errors.rs
// Module: router

use common_utils::{errors::ErrorSwitch, id_type};
use hyperswitch_domain_models::errors::api_error_response as errors;

use crate::core::errors::CustomersErrorResponse;

#[derive(Debug, router_derive::ApiError, Clone)]
#[error(error_type_enum = StripeErrorType)]
pub enum StripeErrorCode {
    /*
    "error": {
        "message": "Invalid API Key provided: sk_jkjgs****nlgs",
        "type": "invalid_request_error"
    }
    */
    #[error(
        error_type = StripeErrorType::InvalidRequestError, code = "IR_01",
        message = "Invalid API Key provided"
    )]
    Unauthorized,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "IR_02", message = "Unrecognized request URL.")]
    InvalidRequestUrl,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "parameter_missing", message = "Missing required param: {field_name}.")]
    ParameterMissing { field_name: String, param: String },

    #[error(
        error_type = StripeErrorType::InvalidRequestError, code = "parameter_unknown",
        message = "{field_name} contains invalid data. Expected format is {expected_format}."
    )]
    ParameterUnknown {
        field_name: String,
        expected_format: String,
    },

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "IR_06",  message = "The refund amount exceeds the amount captured.")]
    RefundAmountExceedsPaymentAmount { param: String },

    #[error(error_type = StripeErrorType::ApiError, code = "payment_intent_authentication_failure", message = "Payment failed while processing with connector. Retry payment.")]
    PaymentIntentAuthenticationFailure { data: Option<serde_json::Value> },

    #[error(error_type = StripeErrorType::ApiError, code = "payment_intent_payment_attempt_failed", message = "Capture attempt failed while processing with connector.")]
    PaymentIntentPaymentAttemptFailed { data: Option<serde_json::Value> },

    #[error(error_type = StripeErrorType::ApiError, code = "dispute_failure", message = "Dispute failed while processing with connector. Retry operation.")]
    DisputeFailed { data: Option<serde_json::Value> },

    #[error(error_type = StripeErrorType::CardError, code = "expired_card", message = "Card Expired. Please use another card")]
    ExpiredCard,

    #[error(error_type = StripeErrorType::CardError, code = "invalid_card_type", message = "Card data is invalid")]
    InvalidCardType,

    #[error(
        error_type = StripeErrorType::ConnectorError, code = "invalid_wallet_token",
        message = "Invalid {wallet_name} wallet token"
    )]
    InvalidWalletToken { wallet_name: String },

    #[error(error_type = StripeErrorType::ApiError, code = "refund_failed", message = "refund has failed")]
    RefundFailed, // stripe error code

    #[error(error_type = StripeErrorType::ApiError, code = "payout_failed", message = "payout has failed")]
    PayoutFailed,

    #[error(error_type = StripeErrorType::ApiError, code = "external_vault_failed", message = "external vault has failed")]
    ExternalVaultFailed,

    #[error(error_type = StripeErrorType::ApiError, code = "internal_server_error", message = "Server is down")]
    InternalServerError,

    #[error(error_type = StripeErrorType::ApiError, code = "internal_server_error", message = "Server is down")]
    DuplicateRefundRequest,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "active_mandate", message = "Customer has active mandate")]
    MandateActive,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "customer_redacted", message = "Customer has redacted")]
    CustomerRedacted,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "customer_already_exists", message = "Customer with the given customer_id already exists")]
    DuplicateCustomer,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such refund")]
    RefundNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "client_secret_invalid", message = "Expected client secret to be included in the request")]
    ClientSecretNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such customer")]
    CustomerNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such config")]
    ConfigNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "duplicate_resource", message = "Duplicate config")]
    DuplicateConfig,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such payment")]
    PaymentNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such payment method")]
    PaymentMethodNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "{message}")]
    GenericNotFoundError { message: String },

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "duplicate_resource", message = "{message}")]
    GenericDuplicateError { message: String },

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such merchant account")]
    MerchantAccountNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such resource ID")]
    ResourceIdNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "Merchant connector account does not exist in our records")]
    MerchantConnectorAccountNotFound { id: String },

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "invalid_request", message = "The merchant connector account is disabled")]
    MerchantConnectorAccountDisabled,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such mandate")]
    MandateNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such API key")]
    ApiKeyNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such payout")]
    PayoutNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "resource_missing", message = "No such event")]
    EventNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "token_already_used", message = "Duplicate payout request")]
    DuplicatePayout { payout_id: id_type::PayoutId },

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "parameter_missing", message = "Return url is not available")]
    ReturnUrlUnavailable,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "token_already_used", message = "duplicate merchant account")]
    DuplicateMerchantAccount,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "token_already_used", message = "The merchant connector account with the specified profile_id '{profile_id}' and connector_label '{connector_label}' already exists in our records")]
    DuplicateMerchantConnectorAccount {
        profile_id: String,
        connector_label: String,
    },

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "token_already_used", message = "duplicate payment method")]
    DuplicatePaymentMethod,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "" , message = "deserialization failed: {error_message}")]
    SerdeQsError {
        error_message: String,
        param: Option<String>,
    },

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "payment_intent_invalid_parameter" , message = "The client_secret provided does not match the client_secret associated with the PaymentIntent.")]
    PaymentIntentInvalidParameter { param: String },

    #[error(
        error_type = StripeErrorType::InvalidRequestError, code = "IR_05",
        message = "{message}"
    )]
    InvalidRequestData { message: String },

    #[error(
        error_type = StripeErrorType::InvalidRequestError, code = "IR_10",
        message = "{message}"
    )]
    PreconditionFailed { message: String },

    #[error(
        error_type = StripeErrorType::InvalidRequestError, code = "",
        message = "The payment has not succeeded yet"
    )]
    PaymentFailed,

    #[error(
        error_type = StripeErrorType::InvalidRequestError, code = "",
        message = "The verification did not succeeded"
    )]
    VerificationFailed { data: Option<serde_json::Value> },

    #[error(
        error_type = StripeErrorType::InvalidRequestError, code = "",
        message = "Reached maximum refund attempts"
    )]
    MaximumRefundCount,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "Duplicate mandate request. Mandate already attempted with the Mandate ID.")]
    DuplicateMandate,

    #[error(error_type= StripeErrorType::InvalidRequestError, code = "", message = "Successful payment not found for the given payment id")]
    SuccessfulPaymentNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "Address does not exist in our records.")]
    AddressNotFound,

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "This PaymentIntent could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}.")]
    PaymentIntentUnexpectedState {
        current_flow: String,
        field_name: String,
        current_value: String,
        states: String,
    },
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "The mandate information is invalid. {message}")]
    PaymentIntentMandateInvalid { message: String },

    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "The payment with the specified payment_id already exists in our records.")]
    DuplicatePayment { payment_id: id_type::PaymentId },

    #[error(error_type = StripeErrorType::ConnectorError, code = "", message = "{code}: {message}")]
    ExternalConnectorError {
        code: String,
        message: String,
        connector: String,
        status_code: u16,
    },

    #[error(error_type = StripeErrorType::CardError, code = "", message = "{code}: {message}")]
    PaymentBlockedError {
        code: u16,
        message: String,
        status: String,
        reason: String,
    },

    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "The connector provided in the request is incorrect or not available")]
    IncorrectConnectorNameGiven,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "No such {object}: '{id}'")]
    ResourceMissing { object: String, id: String },
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "File validation failed")]
    FileValidationFailed,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "File not found in the request")]
    MissingFile,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "File puropse not found in the request")]
    MissingFilePurpose,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "File content type not found")]
    MissingFileContentType,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Dispute id not found in the request")]
    MissingDisputeId,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "File does not exists in our records")]
    FileNotFound,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "File not available")]
    FileNotAvailable,
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "Not Supported because provider is not Router")]
    FileProviderNotSupported,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "There was an issue with processing webhooks")]
    WebhookProcessingError,
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "payment_method_unactivated", message = "The operation cannot be performed as the payment method used has not been activated. Activate the payment method in the Dashboard, then try again.")]
    PaymentMethodUnactivated,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "{message}")]
    HyperswitchUnprocessableEntity { message: String },
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "{message}")]
    CurrencyNotSupported { message: String },
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Payment Link does not exist in our records")]
    PaymentLinkNotFound,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Resource Busy. Please try again later")]
    LockTimeout,
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "Merchant connector account is configured with invalid {config}")]
    InvalidConnectorConfiguration { config: String },
    #[error(error_type = StripeErrorType::HyperswitchError, code = "HE_01", message = "Failed to convert currency to minor unit")]
    CurrencyConversionFailed,
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "IR_25", message = "Cannot delete the default payment method")]
    PaymentMethodDeleteFailed,
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "Extended card info does not exist")]
    ExtendedCardInfoNotFound,
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "not_configured", message = "{message}")]
    LinkConfigurationError { message: String },
    #[error(error_type = StripeErrorType::ConnectorError, code = "CE", message = "{reason} as data mismatched for {field_names}")]
    IntegrityCheckFailed {
        reason: String,
        field_names: String,
        connector_transaction_id: Option<String>,
    },
    #[error(error_type = StripeErrorType::InvalidRequestError, code = "IR_28", message = "Invalid tenant")]
    InvalidTenant,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "HE_01", message = "Failed to convert amount to {amount_type} type")]
    AmountConversionFailed { amount_type: &'static str },
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Platform Bad Request")]
    PlatformBadRequest,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Platform Unauthorized Request")]
    PlatformUnauthorizedRequest,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Profile Acquirer not found")]
    ProfileAcquirerNotFound,
    #[error(error_type = StripeErrorType::HyperswitchError, code = "Subscription Error", message = "Subscription operation: {operation} failed with connector")]
    SubscriptionError { operation: String },
    // [#216]: https://github.com/juspay/hyperswitch/issues/216
    // Implement the remaining stripe error codes

    /*
        AccountCountryInvalidAddress,
        AccountErrorCountryChangeRequiresAdditionalSteps,
        AccountInformationMismatch,
        AccountInvalid,
        AccountNumberInvalid,
        AcssDebitSessionIncomplete,
        AlipayUpgradeRequired,
        AmountTooLarge,
        AmountTooSmall,
        ApiKeyExpired,
        AuthenticationRequired,
        BalanceInsufficient,
        BankAccountBadRoutingNumbers,
        BankAccountDeclined,
        BankAccountExists,
        BankAccountUnusable,
        BankAccountUnverified,
        BankAccountVerificationFailed,
        BillingInvalidMandate,
        BitcoinUpgradeRequired,
        CardDeclineRateLimitExceeded,
        CardDeclined,
        CardholderPhoneNumberRequired,
        ChargeAlreadyCaptured,
        ChargeAlreadyRefunded,
        ChargeDisputed,
        ChargeExceedsSourceLimit,
        ChargeExpiredForCapture,
        ChargeInvalidParameter,
        ClearingCodeUnsupported,
        CountryCodeInvalid,
        CountryUnsupported,
        CouponExpired,
        CustomerMaxPaymentMethods,
        CustomerMaxSubscriptions,
        DebitNotAuthorized,
        EmailInvalid,
        ExpiredCard,
        IdempotencyKeyInUse,
        IncorrectAddress,
        IncorrectCvc,
        IncorrectNumber,
        IncorrectZip,
        InstantPayoutsConfigDisabled,
        InstantPayoutsCurrencyDisabled,
        InstantPayoutsLimitExceeded,
        InstantPayoutsUnsupported,
        InsufficientFunds,
        IntentInvalidState,
        IntentVerificationMethodMissing,
        InvalidCardType,
        InvalidCharacters,
        InvalidChargeAmount,
        InvalidCvc,
        InvalidExpiryMonth,
        InvalidExpiryYear,
        InvalidNumber,
        InvalidSourceUsage,
        InvoiceNoCustomerLineItems,
        InvoiceNoPaymentMethodTypes,
        InvoiceNoSubscriptionLineItems,
        InvoiceNotEditable,
        InvoiceOnBehalfOfNotEditable,
        InvoicePaymentIntentRequiresAction,
        InvoiceUpcomingNone,
        LivemodeMismatch,
        LockTimeout,
        Missing,
        NoAccount,
        NotAllowedOnStandardAccount,
        OutOfInventory,
        ParameterInvalidEmpty,
        ParameterInvalidInteger,
        ParameterInvalidStringBlank,
        ParameterInvalidStringEmpty,
        ParametersExclusive,
        PaymentIntentActionRequired,
        PaymentIntentIncompatiblePaymentMethod,
        PaymentIntentInvalidParameter,
        PaymentIntentKonbiniRejectedConfirmationNumber,
        PaymentIntentPaymentAttemptExpired,
        PaymentIntentUnexpectedState,
        PaymentMethodBankAccountAlreadyVerified,
        PaymentMethodBankAccountBlocked,
        PaymentMethodBillingDetailsAddressMissing,
        PaymentMethodCurrencyMismatch,
        PaymentMethodInvalidParameter,
        PaymentMethodInvalidParameterTestmode,
        PaymentMethodMicrodepositFailed,
        PaymentMethodMicrodepositVerificationAmountsInvalid,
        PaymentMethodMicrodepositVerificationAmountsMismatch,
        PaymentMethodMicrodepositVerificationAttemptsExceeded,
        PaymentMethodMicrodepositVerificationDescriptorCodeMismatch,
        PaymentMethodMicrodepositVerificationTimeout,
        PaymentMethodProviderDecline,
        PaymentMethodProviderTimeout,
        PaymentMethodUnexpectedState,
        PaymentMethodUnsupportedType,
        PayoutsNotAllowed,
        PlatformAccountRequired,
        PlatformApiKeyExpired,
        PostalCodeInvalid,
        ProcessingError,
        ProductInactive,
        RateLimit,
        ReferToCustomer,
        RefundDisputedPayment,
        ResourceAlreadyExists,
        ResourceMissing,
        ReturnIntentAlreadyProcessed,
        RoutingNumberInvalid,
        SecretKeyRequired,
        SepaUnsupportedAccount,
        SetupAttemptFailed,
        SetupIntentAuthenticationFailure,
        SetupIntentInvalidParameter,
        SetupIntentSetupAttemptExpired,
        SetupIntentUnexpectedState,
        ShippingCalculationFailed,
        SkuInactive,
        StateUnsupported,
        StatusTransitionInvalid,
        TaxIdInvalid,
        TaxesCalculationFailed,
        TerminalLocationCountryUnsupported,
        TestmodeChargesOnly,
        TlsVersionUnsupported,
        TokenInUse,
        TransferSourceBalanceParametersMismatch,
        TransfersNotAllowed,
    */
}

impl ::core::fmt::Display for StripeErrorCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{{\"error\": {}}}",
            serde_json::to_string(self).unwrap_or_else(|_| "API error response".to_string())
        )
    }
}

#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[allow(clippy::enum_variant_names)]
pub enum StripeErrorType {
    ApiError,
    CardError,
    InvalidRequestError,
    ConnectorError,
    HyperswitchError,
}

impl From<errors::ApiErrorResponse> for StripeErrorCode {
    fn from(value: errors::ApiErrorResponse) -> Self {
        match value {
            errors::ApiErrorResponse::Unauthorized
            | errors::ApiErrorResponse::InvalidJwtToken
            | errors::ApiErrorResponse::GenericUnauthorized { .. }
            | errors::ApiErrorResponse::AccessForbidden { .. }
            | errors::ApiErrorResponse::InvalidCookie
            | errors::ApiErrorResponse::InvalidEphemeralKey
            | errors::ApiErrorResponse::CookieNotFound => Self::Unauthorized,
            errors::ApiErrorResponse::InvalidRequestUrl
            | errors::ApiErrorResponse::InvalidHttpMethod
            | errors::ApiErrorResponse::InvalidCardIin
            | errors::ApiErrorResponse::InvalidCardIinLength => Self::InvalidRequestUrl,
            errors::ApiErrorResponse::MissingRequiredField { field_name } => {
                Self::ParameterMissing {
                    field_name: field_name.to_string(),
                    param: field_name.to_string(),
                }
            }
            errors::ApiErrorResponse::UnprocessableEntity { message } => {
                Self::HyperswitchUnprocessableEntity { message }
            }
            errors::ApiErrorResponse::MissingRequiredFields { field_names } => {
                // Instead of creating a new error variant in StripeErrorCode for MissingRequiredFields, converted vec<&str> to String
                Self::ParameterMissing {
                    field_name: field_names.clone().join(", "),
                    param: field_names.clone().join(", "),
                }
            }
            errors::ApiErrorResponse::GenericNotFoundError { message } => {
                Self::GenericNotFoundError { message }
            }
            errors::ApiErrorResponse::GenericDuplicateError { message } => {
                Self::GenericDuplicateError { message }
            }
            // parameter unknown, invalid request error // actually if we type wrong values in address we get this error. Stripe throws parameter unknown. I don't know if stripe is validating email and stuff
            errors::ApiErrorResponse::InvalidDataFormat {
                field_name,
                expected_format,
            } => Self::ParameterUnknown {
                field_name,
                expected_format,
            },
            errors::ApiErrorResponse::RefundAmountExceedsPaymentAmount => {
                Self::RefundAmountExceedsPaymentAmount {
                    param: "amount".to_owned(),
                }
            }
            errors::ApiErrorResponse::PaymentAuthorizationFailed { data }
            | errors::ApiErrorResponse::PaymentAuthenticationFailed { data } => {
                Self::PaymentIntentAuthenticationFailure { data }
            }
            errors::ApiErrorResponse::VerificationFailed { data } => {
                Self::VerificationFailed { data }
            }
            errors::ApiErrorResponse::PaymentCaptureFailed { data } => {
                Self::PaymentIntentPaymentAttemptFailed { data }
            }
            errors::ApiErrorResponse::DisputeFailed { data } => Self::DisputeFailed { data },
            errors::ApiErrorResponse::InvalidCardData { data: _ } => Self::InvalidCardType, // Maybe it is better to de generalize this router error
            errors::ApiErrorResponse::CardExpired { data: _ } => Self::ExpiredCard,
            errors::ApiErrorResponse::RefundNotPossible { connector: _ } => Self::RefundFailed,
            errors::ApiErrorResponse::RefundFailed { data: _ } => Self::RefundFailed, // Nothing at stripe to map
            errors::ApiErrorResponse::PayoutFailed { data: _ } => Self::PayoutFailed,
            errors::ApiErrorResponse::ExternalVaultFailed => Self::ExternalVaultFailed,

            errors::ApiErrorResponse::MandateUpdateFailed
            | errors::ApiErrorResponse::MandateSerializationFailed
            | errors::ApiErrorResponse::MandateDeserializationFailed
            | errors::ApiErrorResponse::InternalServerError
            | errors::ApiErrorResponse::HealthCheckError { .. } => Self::InternalServerError, // not a stripe code
            errors::ApiErrorResponse::ExternalConnectorError {
                code,
                message,
                connector,
                status_code,
                ..
            } => Self::ExternalConnectorError {
                code,
                message,
                connector,
                status_code,
            },
            errors::ApiErrorResponse::IncorrectConnectorNameGiven => {
                Self::IncorrectConnectorNameGiven
            }
            errors::ApiErrorResponse::MandateActive => Self::MandateActive, //not a stripe code
            errors::ApiErrorResponse::CustomerRedacted => Self::CustomerRedacted, //not a stripe code
            errors::ApiErrorResponse::ConfigNotFound => Self::ConfigNotFound, // not a stripe code
            errors::ApiErrorResponse::DuplicateConfig => Self::DuplicateConfig, // not a stripe code
            errors::ApiErrorResponse::DuplicateRefundRequest => Self::DuplicateRefundRequest,
            errors::ApiErrorResponse::DuplicatePayout { payout_id } => {
                Self::DuplicatePayout { payout_id }
            }
            errors::ApiErrorResponse::RefundNotFound => Self::RefundNotFound,
            errors::ApiErrorResponse::CustomerNotFound => Self::CustomerNotFound,
            errors::ApiErrorResponse::PaymentNotFound => Self::PaymentNotFound,
            errors::ApiErrorResponse::PaymentMethodNotFound => Self::PaymentMethodNotFound,
            errors::ApiErrorResponse::ClientSecretNotGiven
            | errors::ApiErrorResponse::ClientSecretExpired => Self::ClientSecretNotFound,
            errors::ApiErrorResponse::MerchantAccountNotFound => Self::MerchantAccountNotFound,
            errors::ApiErrorResponse::PaymentLinkNotFound => Self::PaymentLinkNotFound,
            errors::ApiErrorResponse::ResourceIdNotFound => Self::ResourceIdNotFound,
            errors::ApiErrorResponse::MerchantConnectorAccountNotFound { id } => {
                Self::MerchantConnectorAccountNotFound { id }
            }
            errors::ApiErrorResponse::MandateNotFound => Self::MandateNotFound,
            errors::ApiErrorResponse::ApiKeyNotFound => Self::ApiKeyNotFound,
            errors::ApiErrorResponse::PayoutNotFound => Self::PayoutNotFound,
            errors::ApiErrorResponse::EventNotFound => Self::EventNotFound,
            errors::ApiErrorResponse::MandateValidationFailed { reason } => {
                Self::PaymentIntentMandateInvalid { message: reason }
            }
            errors::ApiErrorResponse::ReturnUrlUnavailable => Self::ReturnUrlUnavailable,
            errors::ApiErrorResponse::DuplicateMerchantAccount => Self::DuplicateMerchantAccount,
            errors::ApiErrorResponse::DuplicateMerchantConnectorAccount {
                profile_id,
                connector_label,
            } => Self::DuplicateMerchantConnectorAccount {
                profile_id,
                connector_label,
            },
            errors::ApiErrorResponse::DuplicatePaymentMethod => Self::DuplicatePaymentMethod,
            errors::ApiErrorResponse::PaymentBlockedError {
                code,
                message,
                status,
                reason,
            } => Self::PaymentBlockedError {
                code,
                message,
                status,
                reason,
            },
            errors::ApiErrorResponse::ClientSecretInvalid => Self::PaymentIntentInvalidParameter {
                param: "client_secret".to_owned(),
            },
            errors::ApiErrorResponse::InvalidRequestData { message } => {
                Self::InvalidRequestData { message }
            }
            errors::ApiErrorResponse::PreconditionFailed { message } => {
                Self::PreconditionFailed { message }
            }
            errors::ApiErrorResponse::InvalidDataValue { field_name } => Self::ParameterMissing {
                field_name: field_name.to_string(),
                param: field_name.to_string(),
            },
            errors::ApiErrorResponse::MaximumRefundCount => Self::MaximumRefundCount,
            errors::ApiErrorResponse::PaymentNotSucceeded => Self::PaymentFailed,
            errors::ApiErrorResponse::DuplicateMandate => Self::DuplicateMandate,
            errors::ApiErrorResponse::SuccessfulPaymentNotFound => Self::SuccessfulPaymentNotFound,
            errors::ApiErrorResponse::AddressNotFound => Self::AddressNotFound,
            errors::ApiErrorResponse::NotImplemented { .. } => Self::Unauthorized,
            errors::ApiErrorResponse::FlowNotSupported { .. } => Self::InternalServerError,
            errors::ApiErrorResponse::MandatePaymentDataMismatch { .. } => Self::PlatformBadRequest,
            errors::ApiErrorResponse::MaxFieldLengthViolated { .. } => Self::PlatformBadRequest,
            errors::ApiErrorResponse::PaymentUnexpectedState {
                current_flow,
                field_name,
                current_value,
                states,
            } => Self::PaymentIntentUnexpectedState {
                current_flow,
                field_name,
                current_value,
                states,
            },
            errors::ApiErrorResponse::DuplicatePayment { payment_id } => {
                Self::DuplicatePayment { payment_id }
            }
            errors::ApiErrorResponse::DisputeNotFound { dispute_id } => Self::ResourceMissing {
                object: "dispute".to_owned(),
                id: dispute_id,
            },
            errors::ApiErrorResponse::AuthenticationNotFound { id } => Self::ResourceMissing {
                object: "authentication".to_owned(),
                id,
            },
            errors::ApiErrorResponse::ProfileNotFound { id } => Self::ResourceMissing {
                object: "business_profile".to_owned(),
                id,
            },
            errors::ApiErrorResponse::PollNotFound { id } => Self::ResourceMissing {
                object: "poll".to_owned(),
                id,
            },
            errors::ApiErrorResponse::DisputeStatusValidationFailed { reason: _ } => {
                Self::InternalServerError
            }
            errors::ApiErrorResponse::FileValidationFailed { .. } => Self::FileValidationFailed,
            errors::ApiErrorResponse::MissingFile => Self::MissingFile,
            errors::ApiErrorResponse::MissingFilePurpose => Self::MissingFilePurpose,
            errors::ApiErrorResponse::MissingFileContentType => Self::MissingFileContentType,
            errors::ApiErrorResponse::MissingDisputeId => Self::MissingDisputeId,
            errors::ApiErrorResponse::FileNotFound => Self::FileNotFound,
            errors::ApiErrorResponse::FileNotAvailable => Self::FileNotAvailable,
            errors::ApiErrorResponse::MerchantConnectorAccountDisabled => {
                Self::MerchantConnectorAccountDisabled
            }
            errors::ApiErrorResponse::NotSupported { .. } => Self::InternalServerError,
            errors::ApiErrorResponse::CurrencyNotSupported { message } => {
                Self::CurrencyNotSupported { message }
            }
            errors::ApiErrorResponse::FileProviderNotSupported { .. } => {
                Self::FileProviderNotSupported
            }
            errors::ApiErrorResponse::WebhookBadRequest
            | errors::ApiErrorResponse::WebhookResourceNotFound
            | errors::ApiErrorResponse::WebhookProcessingFailure
            | errors::ApiErrorResponse::WebhookAuthenticationFailed
            | errors::ApiErrorResponse::WebhookUnprocessableEntity
            | errors::ApiErrorResponse::WebhookInvalidMerchantSecret => {
                Self::WebhookProcessingError
            }
            errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration => {
                Self::PaymentMethodUnactivated
            }
            errors::ApiErrorResponse::ResourceBusy => Self::PaymentMethodUnactivated,
            errors::ApiErrorResponse::InvalidConnectorConfiguration { config } => {
                Self::InvalidConnectorConfiguration { config }
            }
            errors::ApiErrorResponse::CurrencyConversionFailed => Self::CurrencyConversionFailed,
            errors::ApiErrorResponse::PaymentMethodDeleteFailed => Self::PaymentMethodDeleteFailed,
            errors::ApiErrorResponse::InvalidWalletToken { wallet_name } => {
                Self::InvalidWalletToken { wallet_name }
            }
            errors::ApiErrorResponse::ExtendedCardInfoNotFound => Self::ExtendedCardInfoNotFound,
            errors::ApiErrorResponse::LinkConfigurationError { message } => {
                Self::LinkConfigurationError { message }
            }
            errors::ApiErrorResponse::IntegrityCheckFailed {
                reason,
                field_names,
                connector_transaction_id,
            } => Self::IntegrityCheckFailed {
                reason,
                field_names,
                connector_transaction_id,
            },
            errors::ApiErrorResponse::InvalidTenant { tenant_id: _ }
            | errors::ApiErrorResponse::MissingTenantId => Self::InvalidTenant,
            errors::ApiErrorResponse::AmountConversionFailed { amount_type } => {
                Self::AmountConversionFailed { amount_type }
            }
            errors::ApiErrorResponse::PlatformAccountAuthNotSupported => Self::PlatformBadRequest,
            errors::ApiErrorResponse::InvalidPlatformOperation => Self::PlatformUnauthorizedRequest,
            errors::ApiErrorResponse::ProfileAcquirerNotFound { .. } => {
                Self::ProfileAcquirerNotFound
            }
            errors::ApiErrorResponse::TokenizationRecordNotFound { id } => Self::ResourceMissing {
                object: "tokenization record".to_owned(),
                id,
            },
            errors::ApiErrorResponse::SubscriptionError { operation } => {
                Self::SubscriptionError { operation }
            }
        }
    }
}

impl actix_web::ResponseError for StripeErrorCode {
    fn status_code(&self) -> reqwest::StatusCode {
        use reqwest::StatusCode;

        match self {
            Self::Unauthorized | Self::PlatformUnauthorizedRequest => StatusCode::UNAUTHORIZED,
            Self::InvalidRequestUrl | Self::GenericNotFoundError { .. } => StatusCode::NOT_FOUND,
            Self::ParameterUnknown { .. } | Self::HyperswitchUnprocessableEntity { .. } => {
                StatusCode::UNPROCESSABLE_ENTITY
            }
            Self::ParameterMissing { .. }
            | Self::RefundAmountExceedsPaymentAmount { .. }
            | Self::PaymentIntentAuthenticationFailure { .. }
            | Self::PaymentIntentPaymentAttemptFailed { .. }
            | Self::ExpiredCard
            | Self::InvalidCardType
            | Self::DuplicateRefundRequest
            | Self::DuplicatePayout { .. }
            | Self::RefundNotFound
            | Self::CustomerNotFound
            | Self::ConfigNotFound
            | Self::DuplicateConfig
            | Self::ClientSecretNotFound
            | Self::PaymentNotFound
            | Self::PaymentMethodNotFound
            | Self::MerchantAccountNotFound
            | Self::MerchantConnectorAccountNotFound { .. }
            | Self::MerchantConnectorAccountDisabled
            | Self::MandateNotFound
            | Self::ApiKeyNotFound
            | Self::PayoutNotFound
            | Self::EventNotFound
            | Self::DuplicateMerchantAccount
            | Self::DuplicateMerchantConnectorAccount { .. }
            | Self::DuplicatePaymentMethod
            | Self::PaymentFailed
            | Self::VerificationFailed { .. }
            | Self::DisputeFailed { .. }
            | Self::MaximumRefundCount
            | Self::PaymentIntentInvalidParameter { .. }
            | Self::SerdeQsError { .. }
            | Self::InvalidRequestData { .. }
            | Self::InvalidWalletToken { .. }
            | Self::PreconditionFailed { .. }
            | Self::DuplicateMandate
            | Self::SuccessfulPaymentNotFound
            | Self::AddressNotFound
            | Self::ResourceIdNotFound
            | Self::PaymentIntentMandateInvalid { .. }
            | Self::PaymentIntentUnexpectedState { .. }
            | Self::DuplicatePayment { .. }
            | Self::GenericDuplicateError { .. }
            | Self::IncorrectConnectorNameGiven
            | Self::ResourceMissing { .. }
            | Self::FileValidationFailed
            | Self::MissingFile
            | Self::MissingFileContentType
            | Self::MissingFilePurpose
            | Self::MissingDisputeId
            | Self::FileNotFound
            | Self::FileNotAvailable
            | Self::FileProviderNotSupported
            | Self::CurrencyNotSupported { .. }
            | Self::DuplicateCustomer
            | Self::PaymentMethodUnactivated
            | Self::InvalidConnectorConfiguration { .. }
            | Self::CurrencyConversionFailed
            | Self::PaymentMethodDeleteFailed
            | Self::ExtendedCardInfoNotFound
            | Self::PlatformBadRequest
            | Self::LinkConfigurationError { .. } => StatusCode::BAD_REQUEST,
            Self::RefundFailed
            | Self::PayoutFailed
            | Self::PaymentLinkNotFound
            | Self::InternalServerError
            | Self::MandateActive
            | Self::CustomerRedacted
            | Self::WebhookProcessingError
            | Self::InvalidTenant
            | Self::ExternalVaultFailed
            | Self::AmountConversionFailed { .. }
            | Self::SubscriptionError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            Self::ReturnUrlUnavailable => StatusCode::SERVICE_UNAVAILABLE,
            Self::ExternalConnectorError { status_code, .. } => {
                StatusCode::from_u16(*status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
            }
            Self::IntegrityCheckFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            Self::PaymentBlockedError { code, .. } => {
                StatusCode::from_u16(*code).unwrap_or(StatusCode::OK)
            }
            Self::LockTimeout => StatusCode::LOCKED,
            Self::ProfileAcquirerNotFound => StatusCode::NOT_FOUND,
        }
    }

    fn error_response(&self) -> actix_web::HttpResponse {
        use actix_web::http::header;

        actix_web::HttpResponseBuilder::new(self.status_code())
            .insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON))
            .body(self.to_string())
    }
}

impl From<serde_qs::Error> for StripeErrorCode {
    fn from(item: serde_qs::Error) -> Self {
        match item {
            serde_qs::Error::Custom(s) => Self::SerdeQsError {
                error_message: s,
                param: None,
            },
            serde_qs::Error::Parse(param, position) => Self::SerdeQsError {
                error_message: format!(
                    "parsing failed with error: '{param}' at position: {position}"
                ),
                param: Some(param),
            },
            serde_qs::Error::Unsupported => Self::SerdeQsError {
                error_message: "Given request format is not supported".to_owned(),
                param: None,
            },
            serde_qs::Error::FromUtf8(_) => Self::SerdeQsError {
                error_message: "Failed to parse request to from utf-8".to_owned(),
                param: None,
            },
            serde_qs::Error::Io(_) => Self::SerdeQsError {
                error_message: "Failed to parse request".to_owned(),
                param: None,
            },
            serde_qs::Error::ParseInt(_) => Self::SerdeQsError {
                error_message: "Failed to parse integer in request".to_owned(),
                param: None,
            },


// Struct: TriggeredAttemptsAccumulator
// File: crates/analytics/src/frm/accumulator.rs
// Module: analytics
// Implementations: 1
// Traits: FrmMetricAccumulator

pub struct TriggeredAttemptsAccumulator


// File: crates/euclid/src/dssa/graph.rs
// Module: euclid
// Public functions: 3
// Public structs: 1

use std::{fmt::Debug, sync::Weak};

use hyperswitch_constraint_graph as cgraph;
use rustc_hash::{FxHashMap, FxHashSet};

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

pub mod euclid_graph_prelude {
    pub use hyperswitch_constraint_graph as cgraph;
    pub use rustc_hash::{FxHashMap, FxHashSet};
    pub use strum::EnumIter;

    pub use crate::{
        dssa::graph::*,
        frontend::dir::{enums::*, DirKey, DirKeyKind, DirValue},
        types::*,
    };
}

impl cgraph::KeyNode for dir::DirKey {}

impl cgraph::NodeViz for dir::DirKey {
    fn viz(&self) -> String {
        self.kind.to_string()
    }
}

impl cgraph::ValueNode for dir::DirValue {
    type Key = dir::DirKey;

    fn get_key(&self) -> Self::Key {
        Self::get_key(self)
    }
}

impl cgraph::NodeViz for dir::DirValue {
    fn viz(&self) -> String {
        match self {
            Self::PaymentMethod(pm) => pm.to_string(),
            Self::CardBin(bin) => bin.value.clone(),
            Self::CardType(ct) => ct.to_string(),
            Self::CardNetwork(cn) => cn.to_string(),
            Self::PayLaterType(plt) => plt.to_string(),
            Self::WalletType(wt) => wt.to_string(),
            Self::UpiType(ut) => ut.to_string(),
            Self::BankTransferType(btt) => btt.to_string(),
            Self::BankRedirectType(brt) => brt.to_string(),
            Self::BankDebitType(bdt) => bdt.to_string(),
            Self::CryptoType(ct) => ct.to_string(),
            Self::RewardType(rt) => rt.to_string(),
            Self::PaymentAmount(amt) => amt.number.to_string(),
            Self::PaymentCurrency(curr) => curr.to_string(),
            Self::AuthenticationType(at) => at.to_string(),
            Self::CaptureMethod(cm) => cm.to_string(),
            Self::BusinessCountry(bc) => bc.to_string(),
            Self::BillingCountry(bc) => bc.to_string(),
            Self::Connector(conn) => conn.connector.to_string(),
            Self::MetaData(mv) => format!("[{} = {}]", mv.key, mv.value),
            Self::MandateAcceptanceType(mat) => mat.to_string(),
            Self::MandateType(mt) => mt.to_string(),
            Self::PaymentType(pt) => pt.to_string(),
            Self::VoucherType(vt) => vt.to_string(),
            Self::GiftCardType(gct) => gct.to_string(),
            Self::BusinessLabel(bl) => bl.value.to_string(),
            Self::SetupFutureUsage(sfu) => sfu.to_string(),
            Self::CardRedirectType(crt) => crt.to_string(),
            Self::RealTimePaymentType(rtpt) => rtpt.to_string(),
            Self::OpenBankingType(ob) => ob.to_string(),
            Self::MobilePaymentType(mpt) => mpt.to_string(),
            Self::IssuerName(issuer_name) => issuer_name.value.clone(),
            Self::IssuerCountry(issuer_country) => issuer_country.to_string(),
            Self::CustomerDevicePlatform(customer_device_platform) => {
                customer_device_platform.to_string()
            }
            Self::CustomerDeviceType(customer_device_type) => customer_device_type.to_string(),
            Self::CustomerDeviceDisplaySize(customer_device_display_size) => {
                customer_device_display_size.to_string()
            }
            Self::AcquirerCountry(acquirer_country) => acquirer_country.to_string(),
            Self::AcquirerFraudRate(acquirer_fraud_rate) => acquirer_fraud_rate.number.to_string(),
        }
    }
}

#[derive(Debug, Clone, serde::Serialize)]
#[serde(tag = "type", content = "details", rename_all = "snake_case")]
pub enum AnalysisError<V: cgraph::ValueNode> {
    Graph(cgraph::GraphError<V>),
    AssertionTrace {
        trace: Weak<cgraph::AnalysisTrace<V>>,
        metadata: Metadata,
    },
    NegationTrace {
        trace: Weak<cgraph::AnalysisTrace<V>>,
        metadata: Vec<Metadata>,
    },
}

impl<V: cgraph::ValueNode> AnalysisError<V> {
    fn assertion_from_graph_error(metadata: &Metadata, graph_error: cgraph::GraphError<V>) -> Self {
        match graph_error {
            cgraph::GraphError::AnalysisError(trace) => Self::AssertionTrace {
                trace,
                metadata: metadata.clone(),
            },

            other => Self::Graph(other),
        }
    }

    fn negation_from_graph_error(
        metadata: Vec<&Metadata>,
        graph_error: cgraph::GraphError<V>,
    ) -> Self {
        match graph_error {
            cgraph::GraphError::AnalysisError(trace) => Self::NegationTrace {
                trace,
                metadata: metadata.iter().map(|m| (*m).clone()).collect(),
            },

            other => Self::Graph(other),
        }
    }
}

#[derive(Debug)]
pub struct AnalysisContext {
    keywise_values: FxHashMap<dir::DirKey, FxHashSet<dir::DirValue>>,
}

impl AnalysisContext {
    pub fn from_dir_values(vals: impl IntoIterator<Item = dir::DirValue>) -> Self {
        let mut keywise_values: FxHashMap<dir::DirKey, FxHashSet<dir::DirValue>> =
            FxHashMap::default();

        for dir_val in vals {
            let key = dir_val.get_key();
            let set = keywise_values.entry(key).or_default();
            set.insert(dir_val);
        }

        Self { keywise_values }
    }

    pub fn insert(&mut self, value: dir::DirValue) {
        self.keywise_values
            .entry(value.get_key())
            .or_default()
            .insert(value);
    }

    pub fn remove(&mut self, value: dir::DirValue) {
        let set = self.keywise_values.entry(value.get_key()).or_default();

        set.remove(&value);

        if set.is_empty() {
            self.keywise_values.remove(&value.get_key());
        }
    }
}

impl cgraph::CheckingContext for AnalysisContext {
    type Value = dir::DirValue;

    fn from_node_values<L>(vals: impl IntoIterator<Item = L>) -> Self
    where
        L: Into<Self::Value>,
    {
        let mut keywise_values: FxHashMap<dir::DirKey, FxHashSet<dir::DirValue>> =
            FxHashMap::default();

        for dir_val in vals.into_iter().map(L::into) {
            let key = dir_val.get_key();
            let set = keywise_values.entry(key).or_default();
            set.insert(dir_val);
        }

        Self { keywise_values }
    }

    fn check_presence(
        &self,
        value: &cgraph::NodeValue<dir::DirValue>,
        strength: cgraph::Strength,
    ) -> bool {
        match value {
            cgraph::NodeValue::Key(k) => {
                self.keywise_values.contains_key(k) || matches!(strength, cgraph::Strength::Weak)
            }

            cgraph::NodeValue::Value(val) => {
                let key = val.get_key();
                let value_set = if let Some(set) = self.keywise_values.get(&key) {
                    set
                } else {
                    return matches!(strength, cgraph::Strength::Weak);
                };

                match key.kind.get_type() {
                    DataType::EnumVariant | DataType::StrValue | DataType::MetadataValue => {
                        value_set.contains(val)
                    }
                    DataType::Number => val.get_num_value().is_some_and(|num_val| {
                        value_set.iter().any(|ctx_val| {
                            ctx_val
                                .get_num_value()
                                .is_some_and(|ctx_num_val| num_val.fits(&ctx_num_val))
                        })
                    }),
                }
            }
        }
    }

    fn get_values_by_key(
        &self,
        key: &<Self::Value as cgraph::ValueNode>::Key,
    ) -> Option<Vec<Self::Value>> {
        self.keywise_values
            .get(key)
            .map(|set| set.iter().cloned().collect())
    }
}

pub trait CgraphExt {
    fn key_analysis(
        &self,
        key: dir::DirKey,
        ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), cgraph::GraphError<dir::DirValue>>;

    fn value_analysis(
        &self,
        val: dir::DirValue,
        ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), cgraph::GraphError<dir::DirValue>>;

    fn check_value_validity(
        &self,
        val: dir::DirValue,
        analysis_ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<bool, cgraph::GraphError<dir::DirValue>>;

    fn key_value_analysis(
        &self,
        val: dir::DirValue,
        ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), cgraph::GraphError<dir::DirValue>>;

    fn assertion_analysis(
        &self,
        positive_ctx: &[(&dir::DirValue, &Metadata)],
        analysis_ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), AnalysisError<dir::DirValue>>;

    fn negation_analysis(
        &self,
        negative_ctx: &[(&[dir::DirValue], &Metadata)],
        analysis_ctx: &mut AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), AnalysisError<dir::DirValue>>;

    fn perform_context_analysis(
        &self,
        ctx: &types::ConjunctiveContext<'_>,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        domains: Option<&[String]>,
    ) -> Result<(), AnalysisError<dir::DirValue>>;
}

impl CgraphExt for cgraph::ConstraintGraph<dir::DirValue> {
    fn key_analysis(
        &self,
        key: dir::DirKey,
        ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), cgraph::GraphError<dir::DirValue>> {
        self.value_map
            .get(&cgraph::NodeValue::Key(key))
            .map_or(Ok(()), |node_id| {
                self.check_node(
                    ctx,
                    *node_id,
                    cgraph::Relation::Positive,
                    cgraph::Strength::Strong,
                    memo,
                    cycle_map,
                    domains,
                )
            })
    }

    fn value_analysis(
        &self,
        val: dir::DirValue,
        ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), cgraph::GraphError<dir::DirValue>> {
        self.value_map
            .get(&cgraph::NodeValue::Value(val))
            .map_or(Ok(()), |node_id| {
                self.check_node(
                    ctx,
                    *node_id,
                    cgraph::Relation::Positive,
                    cgraph::Strength::Strong,
                    memo,
                    cycle_map,
                    domains,
                )
            })
    }

    fn check_value_validity(
        &self,
        val: dir::DirValue,
        analysis_ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<bool, cgraph::GraphError<dir::DirValue>> {
        let maybe_node_id = self.value_map.get(&cgraph::NodeValue::Value(val));

        let node_id = if let Some(nid) = maybe_node_id {
            nid
        } else {
            return Ok(false);
        };

        let result = self.check_node(
            analysis_ctx,
            *node_id,
            cgraph::Relation::Positive,
            cgraph::Strength::Weak,
            memo,
            cycle_map,
            domains,
        );

        match result {
            Ok(_) => Ok(true),
            Err(e) => {
                e.get_analysis_trace()?;
                Ok(false)
            }
        }
    }

    fn key_value_analysis(
        &self,
        val: dir::DirValue,
        ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), cgraph::GraphError<dir::DirValue>> {
        self.key_analysis(val.get_key(), ctx, memo, cycle_map, domains)
            .and_then(|_| self.value_analysis(val, ctx, memo, cycle_map, domains))
    }

    fn assertion_analysis(
        &self,
        positive_ctx: &[(&dir::DirValue, &Metadata)],
        analysis_ctx: &AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), AnalysisError<dir::DirValue>> {
        positive_ctx.iter().try_for_each(|(value, metadata)| {
            self.key_value_analysis((*value).clone(), analysis_ctx, memo, cycle_map, domains)
                .map_err(|e| AnalysisError::assertion_from_graph_error(metadata, e))
        })
    }

    fn negation_analysis(
        &self,
        negative_ctx: &[(&[dir::DirValue], &Metadata)],
        analysis_ctx: &mut AnalysisContext,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        cycle_map: &mut cgraph::CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), AnalysisError<dir::DirValue>> {
        let mut keywise_metadata: FxHashMap<dir::DirKey, Vec<&Metadata>> = FxHashMap::default();
        let mut keywise_negation: FxHashMap<dir::DirKey, FxHashSet<&dir::DirValue>> =
            FxHashMap::default();

        for (values, metadata) in negative_ctx {
            let mut metadata_added = false;

            for dir_value in *values {
                if !metadata_added {
                    keywise_metadata
                        .entry(dir_value.get_key())
                        .or_default()
                        .push(metadata);

                    metadata_added = true;
                }

                keywise_negation
                    .entry(dir_value.get_key())
                    .or_default()
                    .insert(dir_value);
            }
        }

        for (key, negation_set) in keywise_negation {
            let all_metadata = keywise_metadata.remove(&key).unwrap_or_default();
            let first_metadata = all_metadata.first().copied().cloned().unwrap_or_default();

            self.key_analysis(key.clone(), analysis_ctx, memo, cycle_map, domains)
                .map_err(|e| AnalysisError::assertion_from_graph_error(&first_metadata, e))?;

            let mut value_set = if let Some(set) = key.kind.get_value_set() {
                set
            } else {
                continue;
            };

            value_set.retain(|v| !negation_set.contains(v));

            for value in value_set {
                analysis_ctx.insert(value.clone());
                self.value_analysis(value.clone(), analysis_ctx, memo, cycle_map, domains)
                    .map_err(|e| {
                        AnalysisError::negation_from_graph_error(all_metadata.clone(), e)
                    })?;
                analysis_ctx.remove(value);
            }
        }

        Ok(())
    }

    fn perform_context_analysis(
        &self,
        ctx: &types::ConjunctiveContext<'_>,
        memo: &mut cgraph::Memoization<dir::DirValue>,
        domains: Option<&[String]>,
    ) -> Result<(), AnalysisError<dir::DirValue>> {
        let mut analysis_ctx = AnalysisContext::from_dir_values(
            ctx.iter()
                .filter_map(|ctx_val| ctx_val.value.get_assertion().cloned()),
        );

        let positive_ctx = ctx
            .iter()
            .filter_map(|ctx_val| {
                ctx_val
                    .value
                    .get_assertion()
                    .map(|val| (val, ctx_val.metadata))
            })
            .collect::<Vec<_>>();
        self.assertion_analysis(
            &positive_ctx,
            &analysis_ctx,
            memo,
            &mut cgraph::CycleCheck::new(),
            domains,
        )?;

        let negative_ctx = ctx
            .iter()
            .filter_map(|ctx_val| {
                ctx_val
                    .value
                    .get_negation()
                    .map(|vals| (vals, ctx_val.metadata))
            })
            .collect::<Vec<_>>();
        self.negation_analysis(
            &negative_ctx,
            &mut analysis_ctx,
            memo,
            &mut cgraph::CycleCheck::new(),
            domains,
        )?;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]

    use std::ops::Deref;

    use euclid_macros::knowledge;
    use hyperswitch_constraint_graph::CycleCheck;

    use super::*;
    use crate::{dirval, frontend::dir::enums};

    #[test]
    fn test_strong_positive_relation_success() {
        let graph = knowledge! {
            PaymentMethod(Card) ->> CaptureMethod(Automatic);
            PaymentMethod(not Wallet)
                & PaymentMethod(not PayLater) -> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = Card),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_ok());
    }

    #[test]
    fn test_strong_positive_relation_failure() {
        let graph = knowledge! {
            PaymentMethod(Card) ->> CaptureMethod(Automatic);
            PaymentMethod(not Wallet) -> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([dirval!(CaptureMethod = Automatic)]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_err());
    }

    #[test]
    fn test_strong_negative_relation_success() {
        let graph = knowledge! {
            PaymentMethod(Card) -> CaptureMethod(Automatic);
            PaymentMethod(not Wallet) ->> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = Card),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_ok());
    }

    #[test]
    fn test_strong_negative_relation_failure() {
        let graph = knowledge! {
            PaymentMethod(Card) -> CaptureMethod(Automatic);
            PaymentMethod(not Wallet) ->> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = Wallet),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_err());
    }

    #[test]
    fn test_normal_one_of_failure() {
        let graph = knowledge! {
            PaymentMethod(Card) -> CaptureMethod(Automatic);
            PaymentMethod(Wallet) -> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = PayLater),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );
        assert!(matches!(
            *Weak::upgrade(&result.unwrap_err().get_analysis_trace().unwrap())
                .expect("Expected Arc"),
            cgraph::AnalysisTrace::Value {
                predecessors: Some(cgraph::error::ValueTracePredecessor::OneOf(_)),
                ..
            }
        ));
    }

    #[test]
    fn test_all_aggregator_success() {
        let graph = knowledge! {
            PaymentMethod(Card) & PaymentMethod(not Wallet) -> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_ok());
    }

    #[test]
    fn test_all_aggregator_failure() {
        let graph = knowledge! {
            PaymentMethod(Card) & PaymentMethod(not Wallet) -> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = PayLater),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_err());
    }

    #[test]
    fn test_all_aggregator_mandatory_failure() {
        let graph = knowledge! {
            PaymentMethod(Card) & PaymentMethod(not Wallet) ->> CaptureMethod(Automatic);
        };
        let mut memo = cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = PayLater),
            ]),
            &mut memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(matches!(
            *Weak::upgrade(&result.unwrap_err().get_analysis_trace().unwrap())
                .expect("Expected Arc"),
            cgraph::AnalysisTrace::Value {
                predecessors: Some(cgraph::error::ValueTracePredecessor::Mandatory(_)),
                ..
            }
        ));
    }

    #[test]
    fn test_in_aggregator_success() {
        let graph = knowledge! {
            PaymentMethod(in [Card, Wallet]) -> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Wallet),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_ok());
    }

    #[test]
    fn test_in_aggregator_failure() {
        let graph = knowledge! {
            PaymentMethod(in [Card, Wallet]) -> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_err());
    }

    #[test]
    fn test_not_in_aggregator_success() {
        let graph = knowledge! {
            PaymentMethod(not in [Card, Wallet]) ->> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = PayLater),
                dirval!(PaymentMethod = BankRedirect),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_ok());
    }

    #[test]
    fn test_not_in_aggregator_failure() {
        let graph = knowledge! {
            PaymentMethod(not in [Card, Wallet]) ->> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = PayLater),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentMethod = Card),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        assert!(result.is_err());
    }

    #[test]
    fn test_in_aggregator_failure_trace() {
        let graph = knowledge! {
            PaymentMethod(in [Card, Wallet]) ->> CaptureMethod(Automatic);
        };
        let memo = &mut cgraph::Memoization::new();
        let result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
            ]),
            memo,
            &mut CycleCheck::new(),
            None,
        );

        if let cgraph::AnalysisTrace::Value {
            predecessors: Some(cgraph::error::ValueTracePredecessor::Mandatory(agg_error)),
            ..
        } = Weak::upgrade(&result.unwrap_err().get_analysis_trace().unwrap())
            .expect("Expected arc")
            .deref()
        {
            assert!(matches!(
                *Weak::upgrade(agg_error.deref()).expect("Expected Arc"),
                cgraph::AnalysisTrace::InAggregation {
                    found: Some(dir::DirValue::PaymentMethod(enums::PaymentMethod::PayLater)),
                    ..
                }
            ));
        } else {
            panic!("Failed unwrapping OnlyInAggregation trace from AnalysisTrace");
        }
    }

    #[test]
    fn test_memoization_in_kgraph() {
        let mut builder = cgraph::ConstraintGraphBuilder::new();
        let _node_1 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)),
            None,
            None::<()>,
        );
        let _node_2 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::BillingCountry(enums::BillingCountry::India)),
            None,
            None::<()>,
        );
        let _node_3 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::BusinessCountry(
                enums::BusinessCountry::UnitedStatesOfAmerica,
            )),
            None,
            None::<()>,
        );
        let mut memo = cgraph::Memoization::new();
        let mut cycle_map = CycleCheck::new();
        let _edge_1 = builder
            .make_edge(
                _node_1,
                _node_2,
                cgraph::Strength::Strong,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_2 = builder
            .make_edge(
                _node_2,
                _node_3,
                cgraph::Strength::Strong,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to an edge");
        let graph = builder.build();
        let _result = graph.key_value_analysis(
            dirval!(BusinessCountry = UnitedStatesOfAmerica),
            &AnalysisContext::from_dir_values([
                dirval!(PaymentMethod = Wallet),
                dirval!(BillingCountry = India),
                dirval!(BusinessCountry = UnitedStatesOfAmerica),
            ]),
            &mut memo,
            &mut cycle_map,
            None,
        );
        let _answer = memo
            .get(&(
                _node_3,
                cgraph::Relation::Positive,
                cgraph::Strength::Strong,
            ))
            .expect("Memoization not workng");
        matches!(_answer, Ok(()));
    }

    #[test]
    fn test_cycle_resolution_in_graph() {
        let mut builder = cgraph::ConstraintGraphBuilder::new();
        let _node_1 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)),
            None,
            None::<()>,
        );
        let _node_2 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Card)),
            None,
            None::<()>,
        );
        let mut memo = cgraph::Memoization::new();
        let mut cycle_map = CycleCheck::new();
        let _edge_1 = builder
            .make_edge(
                _node_1,
                _node_2,
                cgraph::Strength::Weak,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_2 = builder
            .make_edge(
                _node_2,
                _node_1,
                cgraph::Strength::Weak,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to an edge");
        let graph = builder.build();
        let _result = graph.key_value_analysis(
            dirval!(PaymentMethod = Wallet),
            &AnalysisContext::from_dir_values([
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
            ]),
            &mut memo,
            &mut cycle_map,
            None,
        );

        assert!(_result.is_ok());
    }

    #[test]
    fn test_cycle_resolution_in_graph1() {
        let mut builder = cgraph::ConstraintGraphBuilder::new();
        let _node_1 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::CaptureMethod(
                enums::CaptureMethod::Automatic,
            )),
            None,
            None::<()>,
        );

        let _node_2 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Card)),
            None,
            None::<()>,
        );
        let _node_3 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)),
            None,
            None::<()>,
        );
        let mut memo = cgraph::Memoization::new();
        let mut cycle_map = CycleCheck::new();

        let _edge_1 = builder
            .make_edge(
                _node_1,
                _node_2,
                cgraph::Strength::Weak,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_2 = builder
            .make_edge(
                _node_1,
                _node_3,
                cgraph::Strength::Weak,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_3 = builder
            .make_edge(
                _node_2,
                _node_1,
                cgraph::Strength::Weak,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_4 = builder
            .make_edge(
                _node_3,
                _node_1,
                cgraph::Strength::Strong,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");

        let graph = builder.build();
        let _result = graph.key_value_analysis(
            dirval!(CaptureMethod = Automatic),
            &AnalysisContext::from_dir_values([
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Wallet),
                dirval!(CaptureMethod = Automatic),
            ]),
            &mut memo,
            &mut cycle_map,
            None,
        );

        assert!(_result.is_ok());
    }

    #[test]
    fn test_cycle_resolution_in_graph2() {
        let mut builder = cgraph::ConstraintGraphBuilder::new();
        let _node_0 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::BillingCountry(
                enums::BillingCountry::Afghanistan,
            )),
            None,
            None::<()>,
        );

        let _node_1 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::CaptureMethod(
                enums::CaptureMethod::Automatic,
            )),
            None,
            None::<()>,
        );

        let _node_2 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Card)),
            None,
            None::<()>,
        );
        let _node_3 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Wallet)),
            None,
            None::<()>,
        );

        let _node_4 = builder.make_value_node(
            cgraph::NodeValue::Value(dir::DirValue::PaymentCurrency(enums::PaymentCurrency::USD)),
            None,
            None::<()>,
        );

        let mut memo = cgraph::Memoization::new();
        let mut cycle_map = CycleCheck::new();

        let _edge_1 = builder
            .make_edge(
                _node_0,
                _node_1,
                cgraph::Strength::Weak,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_2 = builder
            .make_edge(
                _node_1,
                _node_2,
                cgraph::Strength::Normal,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_3 = builder
            .make_edge(
                _node_1,
                _node_3,
                cgraph::Strength::Weak,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_4 = builder
            .make_edge(
                _node_3,
                _node_4,
                cgraph::Strength::Normal,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_5 = builder
            .make_edge(
                _node_2,
                _node_4,
                cgraph::Strength::Normal,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");

        let _edge_6 = builder
            .make_edge(
                _node_4,
                _node_1,
                cgraph::Strength::Normal,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");
        let _edge_7 = builder
            .make_edge(
                _node_4,
                _node_0,
                cgraph::Strength::Normal,
                cgraph::Relation::Positive,
                None::<cgraph::DomainId>,
            )
            .expect("Failed to make an edge");

        let graph = builder.build();
        let _result = graph.key_value_analysis(
            dirval!(BillingCountry = Afghanistan),
            &AnalysisContext::from_dir_values([
                dirval!(PaymentCurrency = USD),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Wallet),
                dirval!(CaptureMethod = Automatic),
                dirval!(BillingCountry = Afghanistan),
            ]),
            &mut memo,
            &mut cycle_map,
            None,
        );

        assert!(_result.is_ok());
    }
}


// Function: filter_payment_methods
// File: crates/router/src/core/payments/payment_methods.rs
// Module: router

pub fn filter_payment_methods(
    payment_method_type_details: hyperswitch_domain_models::merchant_connector_account::PaymentMethodsEnabledForConnector,
    req: &api_models::payments::ListMethodsForPaymentsRequest,
    resp: &mut Vec<
        hyperswitch_domain_models::merchant_connector_account::PaymentMethodsEnabledForConnector,
    >,
    payment_intent: Option<&storage::PaymentIntent>,
    address: Option<&hyperswitch_domain_models::address::AddressDetails>,
    configs: &settings::Settings<RawSecret>,
) -> errors::CustomResult<(), errors::ApiErrorResponse>


// File: crates/router/src/core/locker_migration.rs
// Module: router
// Public functions: 4

use ::payment_methods::controller::PaymentMethodsController;
#[cfg(feature = "v1")]
use api_models::enums as api_enums;
use api_models::locker_migration::MigrateCardResponse;
use common_utils::{errors::CustomResult, id_type};
#[cfg(feature = "v1")]
use diesel_models::enums as storage_enums;
#[cfg(feature = "v1")]
use error_stack::FutureExt;
use error_stack::ResultExt;
#[cfg(feature = "v1")]
use futures::TryFutureExt;

#[cfg(feature = "v1")]
use super::{errors::StorageErrorExt, payment_methods::cards};
use crate::{errors, routes::SessionState, services, types::domain};
#[cfg(feature = "v1")]
use crate::{services::logger, types::api};

#[cfg(feature = "v2")]
pub async fn rust_locker_migration(
    _state: SessionState,
    _merchant_id: &id_type::MerchantId,
) -> CustomResult<services::ApplicationResponse<MigrateCardResponse>, errors::ApiErrorResponse> {
    todo!()
}

#[cfg(feature = "v1")]
pub async fn rust_locker_migration(
    state: SessionState,
    merchant_id: &id_type::MerchantId,
) -> CustomResult<services::ApplicationResponse<MigrateCardResponse>, errors::ApiErrorResponse> {
    use crate::db::customers::CustomerListConstraints;

    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

    let merchant_account = db
        .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

    // Handle cases where the number of customers is greater than the limit
    let constraints = CustomerListConstraints {
        limit: u16::MAX,
        offset: None,
    };

    let domain_customers = db
        .list_customers_by_merchant_id(key_manager_state, merchant_id, &key_store, constraints)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

    let mut customers_moved = 0;
    let mut cards_moved = 0;

    let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
        merchant_account.clone(),
        key_store.clone(),
    )));
    for customer in domain_customers {
        let result = db
            .find_payment_method_by_customer_id_merchant_id_list(
                key_manager_state,
                &key_store,
                &customer.customer_id,
                merchant_id,
                None,
            )
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .and_then(|pm| {
                call_to_locker(
                    &state,
                    pm,
                    &customer.customer_id,
                    merchant_id,
                    &merchant_context,
                )
            })
            .await?;

        customers_moved += 1;
        cards_moved += result;
    }

    Ok(services::api::ApplicationResponse::Json(
        MigrateCardResponse {
            status_code: "200".to_string(),
            status_message: "Card migration completed".to_string(),
            customers_moved,
            cards_moved,
        },
    ))
}

#[cfg(feature = "v1")]
pub async fn call_to_locker(
    state: &SessionState,
    payment_methods: Vec<domain::PaymentMethod>,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    merchant_context: &domain::MerchantContext,
) -> CustomResult<usize, errors::ApiErrorResponse> {
    let mut cards_moved = 0;

    for pm in payment_methods.into_iter().filter(|pm| {
        matches!(
            pm.get_payment_method_type(),
            Some(storage_enums::PaymentMethod::Card)
        )
    }) {
        let card = cards::get_card_from_locker(
            state,
            customer_id,
            merchant_id,
            pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
        )
        .await;

        let card = match card {
            Ok(card) => card,
            Err(err) => {
                logger::error!("Failed to fetch card from Basilisk HS locker : {:?}", err);
                continue;
            }
        };

        let card_details = api::CardDetail {
            card_number: card.card_number,
            card_exp_month: card.card_exp_month,
            card_exp_year: card.card_exp_year,
            card_holder_name: card.name_on_card,
            nick_name: card.nick_name.map(masking::Secret::new),
            card_issuing_country: None,
            card_network: None,
            card_issuer: None,
            card_type: None,
        };

        let pm_create = api::PaymentMethodCreate {
            payment_method: pm.get_payment_method_type(),
            payment_method_type: pm.get_payment_method_subtype(),
            payment_method_issuer: pm.payment_method_issuer,
            payment_method_issuer_code: pm.payment_method_issuer_code,
            card: Some(card_details.clone()),
            #[cfg(feature = "payouts")]
            wallet: None,
            #[cfg(feature = "payouts")]
            bank_transfer: None,
            metadata: pm.metadata,
            customer_id: Some(pm.customer_id),
            card_network: card.card_brand,
            client_secret: None,
            payment_method_data: None,
            billing: None,
            connector_mandate_details: None,
            network_transaction_id: None,
        };

        let add_card_result = cards::PmCards{
            state,
            merchant_context,
        }.add_card_hs(
                pm_create,
                &card_details,
                customer_id,
                api_enums::LockerChoice::HyperswitchCardVault,
                Some(pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id)),

            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable(format!(
                "Card migration failed for merchant_id: {merchant_id:?}, customer_id: {customer_id:?}, payment_method_id: {} ",
                pm.payment_method_id
            ));

        let (_add_card_rs_resp, _is_duplicate) = match add_card_result {
            Ok(output) => output,
            Err(err) => {
                logger::error!("Failed to add card to Rust locker : {:?}", err);
                continue;
            }
        };

        cards_moved += 1;

        logger::info!(
                "Card migrated for merchant_id: {merchant_id:?}, customer_id: {customer_id:?}, payment_method_id: {} ",
                pm.payment_method_id
            );
    }

    Ok(cards_moved)
}

#[cfg(feature = "v2")]
pub async fn call_to_locker(
    _state: &SessionState,
    _payment_methods: Vec<domain::PaymentMethod>,
    _customer_id: &id_type::CustomerId,
    _merchant_id: &id_type::MerchantId,
    _merchant_context: &domain::MerchantContext,
) -> CustomResult<usize, errors::ApiErrorResponse> {
    todo!()
}


// Struct: BluesnapThreeDSecureInfo
// File: crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BluesnapThreeDSecureInfo


// Implementation: impl api::RefundExecute for for Coingate
// File: crates/hyperswitch_connectors/src/connectors/coingate.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Coingate


// Implementation: impl api::revenue_recovery::BillingConnectorPaymentsSyncIntegration for for Stripebilling
// File: crates/hyperswitch_connectors/src/connectors/stripebilling.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::revenue_recovery::BillingConnectorPaymentsSyncIntegration for for Stripebilling


// Function: switch_profile_for_user_in_org_and_merchant
// File: crates/router/src/core/user.rs
// Module: router

pub fn switch_profile_for_user_in_org_and_merchant(
    state: SessionState,
    request: user_api::SwitchProfileRequest,
    user_from_token: auth::UserFromToken,
) -> UserResponse<user_api::TokenResponse>


// Implementation: impl SubmitEvidence for for Worldpayvantiv
// File: crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl SubmitEvidence for for Worldpayvantiv


// Implementation: impl api::PaymentSession for for Digitalvirgo
// File: crates/hyperswitch_connectors/src/connectors/digitalvirgo.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSession for for Digitalvirgo


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

pub struct MifinityConnectorMetadataObject


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

pub struct GetPaymentIntentMetricRequest


// Implementation: impl TaxCalculationOverride
// File: crates/common_enums/src/enums.rs
// Module: common_enums
// Methods: 1 total (1 public)

impl TaxCalculationOverride


// Implementation: impl ExternalAuthentication for for Threedsecureio
// File: crates/hyperswitch_connectors/src/connectors/threedsecureio.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ExternalAuthentication for for Threedsecureio


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

pub struct WiseBankDetails


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

impl api::RefundSync for for Boku


// Struct: Authentication
// File: crates/hyperswitch_domain_models/src/authentication.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct Authentication


// Struct: AccessToken
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct AccessToken


// Function: routing_retrieve_config
// File: crates/router/src/routes/routing.rs
// Module: router

pub fn routing_retrieve_config(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::RoutingId>,
) -> impl Responder


// Function: server
// File: crates/router/src/routes/app.rs
// Module: router

pub fn server(state: AppState) -> Scope


// Struct: ContractBasedRoutingConfig
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 2
// Traits: Default

pub struct ContractBasedRoutingConfig


// Struct: CreateCustomerPaymentProfileRequest
// File: crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CreateCustomerPaymentProfileRequest


// Struct: Order
// File: crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Order


// Struct: FeesType
// File: crates/hyperswitch_connectors/src/connectors/nomupay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FeesType


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

pub fn log_payout(
        &self,
        payout: &KafkaPayout<'_>,
        old_payout: Option<KafkaPayout<'_>>,
        tenant_id: TenantID,
    ) -> MQResult<()>


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

impl api::PaymentAuthorize for for Razorpay