// File: crates/router/src/core/debit_routing.rs
// Module: router
// Public functions: 4
// Public structs: 1

use std::{collections::HashSet, fmt::Debug};

use api_models::{enums as api_enums, open_router};
use common_enums::enums;
use common_utils::{
    errors::CustomResult, ext_traits::ValueExt, id_type, types::keymanager::KeyManagerState,
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};

use super::{
    payments::{OperationSessionGetters, OperationSessionSetters},
    routing::TransactionData,
};
use crate::{
    core::{
        errors,
        payments::{operations::BoxedOperation, routing},
    },
    logger,
    routes::SessionState,
    settings,
    types::{
        api::{self, ConnectorCallType},
        domain,
    },
    utils::id_type::MerchantConnectorAccountId,
};

pub struct DebitRoutingResult {
    pub debit_routing_connector_call_type: ConnectorCallType,
    pub debit_routing_output: open_router::DebitRoutingOutput,
}

pub async fn perform_debit_routing<F, Req, D>(
    operation: &BoxedOperation<'_, F, Req, D>,
    state: &SessionState,
    business_profile: &domain::Profile,
    payment_data: &mut D,
    connector: Option<ConnectorCallType>,
) -> (
    Option<ConnectorCallType>,
    Option<open_router::DebitRoutingOutput>,
)
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let mut debit_routing_output = None;

    if should_execute_debit_routing(state, business_profile, operation, payment_data).await {
        let debit_routing_config = state.conf.debit_routing_config.clone();
        let debit_routing_supported_connectors = debit_routing_config.supported_connectors.clone();

        // If the business profile does not have a country set, we cannot perform debit routing,
        // because the merchant_business_country will be treated as the acquirer_country,
        // which is used to determine whether a transaction is local or global in the open router.
        // For now, since debit routing is only implemented for USD, we can safely assume the
        // acquirer_country is US if not provided by the merchant.

        let acquirer_country = business_profile
            .merchant_business_country
            .unwrap_or_default();

        if let Some(call_connector_type) = connector.clone() {
            debit_routing_output = match call_connector_type {
                ConnectorCallType::PreDetermined(connector_data) => {
                    logger::info!("Performing debit routing for PreDetermined connector");
                    handle_pre_determined_connector(
                        state,
                        debit_routing_supported_connectors,
                        &connector_data,
                        payment_data,
                        acquirer_country,
                    )
                    .await
                }
                ConnectorCallType::Retryable(connector_data) => {
                    logger::info!("Performing debit routing for Retryable connector");
                    handle_retryable_connector(
                        state,
                        debit_routing_supported_connectors,
                        connector_data,
                        payment_data,
                        acquirer_country,
                    )
                    .await
                }
                ConnectorCallType::SessionMultiple(_) => {
                    logger::info!(
                        "SessionMultiple connector type is not supported for debit routing"
                    );
                    None
                }
                #[cfg(feature = "v2")]
                ConnectorCallType::Skip => {
                    logger::info!("Skip connector type is not supported for debit routing");
                    None
                }
            };
        }
    }

    if let Some(debit_routing_output) = debit_routing_output {
        (
            Some(debit_routing_output.debit_routing_connector_call_type),
            Some(debit_routing_output.debit_routing_output),
        )
    } else {
        // If debit_routing_output is None, return the static routing output (connector)
        logger::info!("Debit routing is not performed, returning static routing output");
        (connector, None)
    }
}

async fn should_execute_debit_routing<F, Req, D>(
    state: &SessionState,
    business_profile: &domain::Profile,
    operation: &BoxedOperation<'_, F, Req, D>,
    payment_data: &D,
) -> bool
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    if business_profile.is_debit_routing_enabled && state.conf.open_router.dynamic_routing_enabled {
        logger::info!("Debit routing is enabled for the profile");

        let debit_routing_config = &state.conf.debit_routing_config;

        if should_perform_debit_routing_for_the_flow(operation, payment_data, debit_routing_config)
        {
            let is_debit_routable_connector_present = check_for_debit_routing_connector_in_profile(
                state,
                business_profile.get_id(),
                payment_data,
            )
            .await;

            if is_debit_routable_connector_present {
                logger::debug!("Debit routable connector is configured for the profile");
                return true;
            }
        }
    }
    false
}

pub fn should_perform_debit_routing_for_the_flow<Op: Debug, F: Clone, D>(
    operation: &Op,
    payment_data: &D,
    debit_routing_config: &settings::DebitRoutingConfig,
) -> bool
where
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    match format!("{operation:?}").as_str() {
        "PaymentConfirm" => {
            logger::info!("Checking if debit routing is required");

            request_validation(payment_data, debit_routing_config)
        }
        _ => false,
    }
}

fn request_validation<F: Clone, D>(
    payment_data: &D,
    debit_routing_config: &settings::DebitRoutingConfig,
) -> bool
where
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    let payment_intent = payment_data.get_payment_intent();
    let payment_attempt = payment_data.get_payment_attempt();

    let is_currency_supported = is_currency_supported(payment_intent, debit_routing_config);

    let is_valid_payment_method = validate_payment_method_for_debit_routing(payment_data);

    payment_intent.setup_future_usage != Some(enums::FutureUsage::OffSession)
        && payment_intent.amount.is_greater_than(0)
        && is_currency_supported
        && payment_attempt.authentication_type == Some(enums::AuthenticationType::NoThreeDs)
        && is_valid_payment_method
}

fn is_currency_supported(
    payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
    debit_routing_config: &settings::DebitRoutingConfig,
) -> bool {
    payment_intent
        .currency
        .map(|currency| {
            debit_routing_config
                .supported_currencies
                .contains(&currency)
        })
        .unwrap_or(false)
}

fn validate_payment_method_for_debit_routing<F: Clone, D>(payment_data: &D) -> bool
where
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    let payment_attempt = payment_data.get_payment_attempt();
    match payment_attempt.payment_method {
        Some(enums::PaymentMethod::Card) => {
            payment_attempt.payment_method_type == Some(enums::PaymentMethodType::Debit)
        }
        Some(enums::PaymentMethod::Wallet) => {
            payment_attempt.payment_method_type == Some(enums::PaymentMethodType::ApplePay)
                && payment_data
                    .get_payment_method_data()
                    .and_then(|data| data.get_wallet_data())
                    .and_then(|data| data.get_apple_pay_wallet_data())
                    .and_then(|data| data.get_payment_method_type())
                    == Some(enums::PaymentMethodType::Debit)
                && matches!(
                    payment_data.get_payment_method_token().cloned(),
                    Some(
                        hyperswitch_domain_models::router_data::PaymentMethodToken::ApplePayDecrypt(
                            _
                        )
                    )
                )
        }
        _ => false,
    }
}

pub async fn check_for_debit_routing_connector_in_profile<
    F: Clone,
    D: OperationSessionGetters<F>,
>(
    state: &SessionState,
    business_profile_id: &id_type::ProfileId,
    payment_data: &D,
) -> bool {
    logger::debug!("Checking for debit routing connector in profile");
    let debit_routing_supported_connectors =
        state.conf.debit_routing_config.supported_connectors.clone();

    let transaction_data = super::routing::PaymentsDslInput::new(
        payment_data.get_setup_mandate(),
        payment_data.get_payment_attempt(),
        payment_data.get_payment_intent(),
        payment_data.get_payment_method_data(),
        payment_data.get_address(),
        payment_data.get_recurring_details(),
        payment_data.get_currency(),
    );

    let fallback_config_optional = super::routing::helpers::get_merchant_default_config(
        &*state.clone().store,
        business_profile_id.get_string_repr(),
        &enums::TransactionType::from(&TransactionData::Payment(transaction_data)),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .map_err(|error| {
        logger::warn!(?error, "Failed to fetch default connector for a profile");
    })
    .ok();

    let is_debit_routable_connector_present = fallback_config_optional
        .map(|fallback_config| {
            fallback_config.iter().any(|fallback_config_connector| {
                debit_routing_supported_connectors.contains(&api_enums::Connector::from(
                    fallback_config_connector.connector,
                ))
            })
        })
        .unwrap_or(false);

    is_debit_routable_connector_present
}

async fn handle_pre_determined_connector<F, D>(
    state: &SessionState,
    debit_routing_supported_connectors: HashSet<api_enums::Connector>,
    connector_data: &api::ConnectorRoutingData,
    payment_data: &mut D,
    acquirer_country: enums::CountryAlpha2,
) -> Option<DebitRoutingResult>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let db = state.store.as_ref();
    let key_manager_state = &(state).into();
    let merchant_id = payment_data.get_payment_attempt().merchant_id.clone();
    let profile_id = payment_data.get_payment_attempt().profile_id.clone();

    if debit_routing_supported_connectors.contains(&connector_data.connector_data.connector_name) {
        logger::debug!("Chosen connector is supported for debit routing");

        let debit_routing_output =
            get_debit_routing_output::<F, D>(state, payment_data, acquirer_country).await?;

        logger::debug!(
            "Sorted co-badged networks info: {:?}",
            debit_routing_output.co_badged_card_networks_info
        );

        let key_store = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &db.get_master_key().to_vec().into(),
            )
            .await
            .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)
            .map_err(|error| {
                logger::error!(
                    "Failed to get merchant key store by merchant_id  {:?}",
                    error
                )
            })
            .ok()?;

        let connector_routing_data = build_connector_routing_data(
            state,
            &profile_id,
            &key_store,
            vec![connector_data.clone()],
            debit_routing_output
                .co_badged_card_networks_info
                .clone()
                .get_card_networks(),
        )
        .await
        .map_err(|error| {
            logger::error!(
                "Failed to build connector routing data for debit routing {:?}",
                error
            )
        })
        .ok()?;

        if !connector_routing_data.is_empty() {
            return Some(DebitRoutingResult {
                debit_routing_connector_call_type: ConnectorCallType::Retryable(
                    connector_routing_data,
                ),
                debit_routing_output,
            });
        }
    }

    None
}

pub async fn get_debit_routing_output<
    F: Clone + Send,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
>(
    state: &SessionState,
    payment_data: &mut D,
    acquirer_country: enums::CountryAlpha2,
) -> Option<open_router::DebitRoutingOutput> {
    logger::debug!("Fetching sorted card networks");

    let card_info = extract_card_info(payment_data);

    let saved_co_badged_card_data = card_info.co_badged_card_data;
    let saved_card_type = card_info.card_type;
    let card_isin = card_info.card_isin;

    match (
        saved_co_badged_card_data
            .clone()
            .zip(saved_card_type.clone()),
        card_isin.clone(),
    ) {
        (None, None) => {
            logger::debug!("Neither co-badged data nor ISIN found; skipping routing");
            None
        }
        _ => {
            let co_badged_card_data = saved_co_badged_card_data
                .zip(saved_card_type)
                .and_then(|(co_badged, card_type)| {
                    open_router::DebitRoutingRequestData::try_from((co_badged, card_type))
                        .map(Some)
                        .map_err(|error| {
                            logger::warn!("Failed to convert co-badged card data: {:?}", error);
                        })
                        .ok()
                })
                .flatten();

            if co_badged_card_data.is_none() && card_isin.is_none() {
                logger::debug!("Neither co-badged data nor ISIN found; skipping routing");
                return None;
            }

            let co_badged_card_request = open_router::CoBadgedCardRequest {
                merchant_category_code: enums::DecisionEngineMerchantCategoryCode::Mcc0001,
                acquirer_country,
                co_badged_card_data,
            };

            routing::perform_open_routing_for_debit_routing(
                state,
                co_badged_card_request,
                card_isin,
                payment_data,
            )
            .await
            .map_err(|error| {
                logger::warn!(?error, "Debit routing call to open router failed");
            })
            .ok()
        }
    }
}

#[derive(Debug, Clone)]
struct ExtractedCardInfo {
    co_badged_card_data: Option<api_models::payment_methods::CoBadgedCardData>,
    card_type: Option<String>,
    card_isin: Option<Secret<String>>,
}

impl ExtractedCardInfo {
    fn new(
        co_badged_card_data: Option<api_models::payment_methods::CoBadgedCardData>,
        card_type: Option<String>,
        card_isin: Option<Secret<String>>,
    ) -> Self {
        Self {
            co_badged_card_data,
            card_type,
            card_isin,
        }
    }

    fn empty() -> Self {
        Self::new(None, None, None)
    }
}

fn extract_card_info<F, D>(payment_data: &D) -> ExtractedCardInfo
where
    D: OperationSessionGetters<F>,
{
    extract_from_saved_payment_method(payment_data)
        .unwrap_or_else(|| extract_from_payment_method_data(payment_data))
}

fn extract_from_saved_payment_method<F, D>(payment_data: &D) -> Option<ExtractedCardInfo>
where
    D: OperationSessionGetters<F>,
{
    let payment_methods_data = payment_data
        .get_payment_method_info()?
        .get_payment_methods_data()?;

    if let hyperswitch_domain_models::payment_method_data::PaymentMethodsData::Card(card) =
        payment_methods_data
    {
        return Some(extract_card_info_from_saved_card(&card));
    }

    None
}

fn extract_card_info_from_saved_card(
    card: &hyperswitch_domain_models::payment_method_data::CardDetailsPaymentMethod,
) -> ExtractedCardInfo {
    match (&card.co_badged_card_data, &card.card_isin) {
        (Some(co_badged), _) => {
            logger::debug!("Co-badged card data found in saved payment method");
            ExtractedCardInfo::new(Some(co_badged.clone()), card.card_type.clone(), None)
        }
        (None, Some(card_isin)) => {
            logger::debug!("No co-badged data; using saved card ISIN");
            ExtractedCardInfo::new(None, None, Some(Secret::new(card_isin.clone())))
        }
        _ => ExtractedCardInfo::empty(),
    }
}

fn extract_from_payment_method_data<F, D>(payment_data: &D) -> ExtractedCardInfo
where
    D: OperationSessionGetters<F>,
{
    match payment_data.get_payment_method_data() {
        Some(hyperswitch_domain_models::payment_method_data::PaymentMethodData::Card(card)) => {
            logger::debug!("Using card data from payment request");
            ExtractedCardInfo::new(
                None,
                None,
                Some(Secret::new(card.card_number.get_extended_card_bin())),
            )
        }
        Some(hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(
            wallet_data,
        )) => extract_from_wallet_data(wallet_data, payment_data),
        _ => ExtractedCardInfo::empty(),
    }
}

fn extract_from_wallet_data<F, D>(
    wallet_data: &hyperswitch_domain_models::payment_method_data::WalletData,
    payment_data: &D,
) -> ExtractedCardInfo
where
    D: OperationSessionGetters<F>,
{
    match wallet_data {
        hyperswitch_domain_models::payment_method_data::WalletData::ApplePay(_) => {
            logger::debug!("Using Apple Pay data from payment request");
            let apple_pay_isin = extract_apple_pay_isin(payment_data);
            ExtractedCardInfo::new(None, None, apple_pay_isin)
        }
        _ => ExtractedCardInfo::empty(),
    }
}

fn extract_apple_pay_isin<F, D>(payment_data: &D) -> Option<Secret<String>>
where
    D: OperationSessionGetters<F>,
{
    payment_data.get_payment_method_token().and_then(|token| {
        if let hyperswitch_domain_models::router_data::PaymentMethodToken::ApplePayDecrypt(
            apple_pay_decrypt_data,
        ) = token
        {
            logger::debug!("Using Apple Pay decrypt data from payment method token");
            Some(Secret::new(
                apple_pay_decrypt_data
                    .application_primary_account_number
                    .peek()
                    .chars()
                    .take(8)
                    .collect::<String>(),
            ))
        } else {
            None
        }
    })
}

async fn handle_retryable_connector<F, D>(
    state: &SessionState,
    debit_routing_supported_connectors: HashSet<api_enums::Connector>,
    connector_data_list: Vec<api::ConnectorRoutingData>,
    payment_data: &mut D,
    acquirer_country: enums::CountryAlpha2,
) -> Option<DebitRoutingResult>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let key_manager_state = &(state).into();
    let db = state.store.as_ref();
    let profile_id = payment_data.get_payment_attempt().profile_id.clone();
    let merchant_id = payment_data.get_payment_attempt().merchant_id.clone();
    let is_any_debit_routing_connector_supported =
        connector_data_list.iter().any(|connector_data| {
            debit_routing_supported_connectors
                .contains(&connector_data.connector_data.connector_name)
        });

    if is_any_debit_routing_connector_supported {
        let debit_routing_output =
            get_debit_routing_output::<F, D>(state, payment_data, acquirer_country).await?;
        let key_store = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &db.get_master_key().to_vec().into(),
            )
            .await
            .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)
            .map_err(|error| {
                logger::error!(
                    "Failed to get merchant key store by merchant_id  {:?}",
                    error
                )
            })
            .ok()?;

        let connector_routing_data = build_connector_routing_data(
            state,
            &profile_id,
            &key_store,
            connector_data_list.clone(),
            debit_routing_output
                .co_badged_card_networks_info
                .clone()
                .get_card_networks(),
        )
        .await
        .map_err(|error| {
            logger::error!(
                "Failed to build connector routing data for debit routing {:?}",
                error
            )
        })
        .ok()?;

        if !connector_routing_data.is_empty() {
            return Some(DebitRoutingResult {
                debit_routing_connector_call_type: ConnectorCallType::Retryable(
                    connector_routing_data,
                ),
                debit_routing_output,
            });
        };
    }

    None
}

async fn build_connector_routing_data(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    key_store: &domain::MerchantKeyStore,
    eligible_connector_data_list: Vec<api::ConnectorRoutingData>,
    fee_sorted_debit_networks: Vec<common_enums::CardNetwork>,
) -> CustomResult<Vec<api::ConnectorRoutingData>, errors::ApiErrorResponse> {
    let key_manager_state = &state.into();
    let debit_routing_config = &state.conf.debit_routing_config;

    let mcas_for_profile =
        fetch_merchant_connector_accounts(state, key_manager_state, profile_id, key_store).await?;

    let mut connector_routing_data = Vec::new();
    let mut has_us_local_network = false;

    for connector_data in eligible_connector_data_list {
        if let Some(routing_data) = process_connector_for_networks(
            &connector_data,
            &mcas_for_profile,
            &fee_sorted_debit_networks,
            debit_routing_config,
            &mut has_us_local_network,
        )? {
            connector_routing_data.extend(routing_data);
        }
    }

    validate_us_local_network_requirement(has_us_local_network)?;
    Ok(connector_routing_data)
}

/// Fetches merchant connector accounts for the given profile
async fn fetch_merchant_connector_accounts(
    state: &SessionState,
    key_manager_state: &KeyManagerState,
    profile_id: &id_type::ProfileId,
    key_store: &domain::MerchantKeyStore,
) -> CustomResult<Vec<domain::MerchantConnectorAccount>, errors::ApiErrorResponse> {
    state
        .store
        .list_enabled_connector_accounts_by_profile_id(
            key_manager_state,
            profile_id,
            key_store,
            common_enums::ConnectorType::PaymentProcessor,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to fetch merchant connector accounts")
}

/// Processes a single connector to find matching networks
fn process_connector_for_networks(
    connector_data: &api::ConnectorRoutingData,
    mcas_for_profile: &[domain::MerchantConnectorAccount],
    fee_sorted_debit_networks: &[common_enums::CardNetwork],
    debit_routing_config: &settings::DebitRoutingConfig,
    has_us_local_network: &mut bool,
) -> CustomResult<Option<Vec<api::ConnectorRoutingData>>, errors::ApiErrorResponse> {
    let Some(merchant_connector_id) = &connector_data.connector_data.merchant_connector_id else {
        logger::warn!("Skipping connector with missing merchant_connector_id");
        return Ok(None);
    };

    let Some(account) = find_merchant_connector_account(mcas_for_profile, merchant_connector_id)
    else {
        logger::warn!(
            "No MCA found for merchant_connector_id: {:?}",
            merchant_connector_id
        );
        return Ok(None);
    };

    let merchant_debit_networks = extract_debit_networks(&account)?;
    let matching_networks = find_matching_networks(
        &merchant_debit_networks,
        fee_sorted_debit_networks,
        connector_data,
        debit_routing_config,
        has_us_local_network,
    );

    Ok(Some(matching_networks))
}

/// Finds a merchant connector account by ID
fn find_merchant_connector_account(
    mcas: &[domain::MerchantConnectorAccount],
    merchant_connector_id: &MerchantConnectorAccountId,
) -> Option<domain::MerchantConnectorAccount> {
    mcas.iter()
        .find(|mca| mca.merchant_connector_id == *merchant_connector_id)
        .cloned()
}

/// Finds networks that match between merchant and fee-sorted networks
fn find_matching_networks(
    merchant_debit_networks: &HashSet<common_enums::CardNetwork>,
    fee_sorted_debit_networks: &[common_enums::CardNetwork],
    connector_routing_data: &api::ConnectorRoutingData,
    debit_routing_config: &settings::DebitRoutingConfig,
    has_us_local_network: &mut bool,
) -> Vec<api::ConnectorRoutingData> {
    let is_routing_enabled = debit_routing_config
        .supported_connectors
        .contains(&connector_routing_data.connector_data.connector_name.clone());

    fee_sorted_debit_networks
        .iter()
        .filter(|network| merchant_debit_networks.contains(network))
        .filter(|network| is_routing_enabled || network.is_signature_network())
        .map(|network| {
            if network.is_us_local_network() {
                *has_us_local_network = true;
            }

            api::ConnectorRoutingData {
                connector_data: connector_routing_data.connector_data.clone(),
                network: Some(network.clone()),
                action_type: connector_routing_data.action_type.clone(),
            }
        })
        .collect()
}

/// Validates that at least one US local network is present
fn validate_us_local_network_requirement(
    has_us_local_network: bool,
) -> CustomResult<(), errors::ApiErrorResponse> {
    if !has_us_local_network {
        return Err(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("At least one US local network is required in routing");
    }
    Ok(())
}

fn extract_debit_networks(
    account: &domain::MerchantConnectorAccount,
) -> CustomResult<HashSet<common_enums::CardNetwork>, errors::ApiErrorResponse> {
    let mut networks = HashSet::new();

    if let Some(values) = &account.payment_methods_enabled {
        for val in values {
            let payment_methods_enabled: api_models::admin::PaymentMethodsEnabled =
                val.to_owned().parse_value("PaymentMethodsEnabled")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to parse enabled payment methods for a merchant connector account in debit routing flow")?;

            if let Some(types) = payment_methods_enabled.payment_method_types {
                for method_type in types {
                    if method_type.payment_method_type
                        == api_models::enums::PaymentMethodType::Debit
                    {
                        if let Some(card_networks) = method_type.card_networks {
                            networks.extend(card_networks);
                        }
                    }
                }
            }
        }
    }

    Ok(networks)
}


// Function: check_refund_integrity
// File: crates/router/src/core/refunds_v2.rs
// Module: router

pub fn check_refund_integrity<T, Request>(
    request: &Request,
    refund_response_data: &Result<types::RefundsResponseData, ErrorResponse>,
) -> Result<(), common_utils::errors::IntegrityCheckError>
where
    T: FlowIntegrity,
    Request: GetIntegrityObject<T> + CheckIntegrity<Request, T>,


// Function: update_with_id
// File: crates/diesel_models/src/query/refund.rs
// Module: diesel_models

pub fn update_with_id(
        self,
        conn: &PgPooledConn,
        refund: RefundUpdate,
    ) -> StorageResult<Self>


// Struct: GpaymentsPreAuthenticationRequest
// File: crates/hyperswitch_connectors/src/connectors/gpayments/gpayments_types.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GpaymentsPreAuthenticationRequest


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

pub struct PaymentIntentFiltersResponse


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

// Documentation: Construct config builder extending it by fall-back defaults and setting config file to load.

pub fn builder(
        environment: &str,
    ) -> Result<config::ConfigBuilder<config::builder::DefaultState>, config::ConfigError>


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

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


// Implementation: impl Theme
// File: crates/diesel_models/src/user/theme.rs
// Module: diesel_models
// Methods: 1 total (1 public)

impl Theme


// Function: get_acs_signed_content
// File: crates/hyperswitch_domain_models/src/router_request_types/authentication.rs
// Module: hyperswitch_domain_models

pub fn get_acs_signed_content(&self) -> Option<String>


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

impl ConnectorCommon for for Tokenex


// Function: get_profile_payment_intent_metrics
// File: crates/router/src/analytics.rs
// Module: router

// Documentation: Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element.

pub fn get_profile_payment_intent_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentIntentMetricRequest; 1]>,
    ) -> impl Responder


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

pub struct ZenItemObject


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/wise.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Struct: ApplePayDecryptedData
// File: crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ApplePayDecryptedData


// Implementation: impl Handler
// File: crates/drainer/src/handler.rs
// Module: drainer
// Methods: 5 total (3 public)

impl Handler


// Function: from_origin
// File: crates/router/src/types/domain/user/decision_manager.rs
// Module: router

pub fn from_origin(
        origin: Origin,
        user: UserFromStorage,
        state: &SessionState,
    ) -> UserResult<Self>


// Function: find_optional_resource_by_id
// File: crates/storage_impl/src/kv_router_store.rs
// Module: storage_impl

pub fn find_optional_resource_by_id<D, R, M>(
        &self,
        state: &KeyManagerState,
        key_store: &MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
        find_resource_db_fut: R,
        find_by: FindResourceBy<'_>,
    ) -> error_stack::Result<Option<D>, errors::StorageError>
    where
        D: DomainType,
        M: StorageModel<D>,
        R: futures::Future<Output = error_stack::Result<Option<M>, DatabaseError>> + Send,


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

pub struct CancelOrCaptureTransactionRequest


// Struct: RoleNew
// File: crates/diesel_models/src/role.rs
// Module: diesel_models
// Implementations: 0

pub struct RoleNew


// Function: get_payment_method
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models

pub fn get_payment_method(&self) -> Option<common_enums::PaymentMethod>


// Function: validate
// File: crates/hyperswitch_domain_models/src/connector_endpoints.rs
// Module: hyperswitch_domain_models

// Documentation: function to satisfy connector param validation macro

pub fn validate(&self, _parent_field: &str) -> Result<(), ApplicationError>


// Function: payment_sync_call
// File: crates/router/src/core/revenue_recovery/types.rs
// Module: router

pub fn payment_sync_call(
        state: &SessionState,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        payment_intent: &PaymentIntent,
        process: &storage::ProcessTracker,
        profile: &domain::Profile,
        merchant_context: domain::MerchantContext,
        payment_attempt: PaymentAttempt,
    ) -> RecoveryResult<Self>


// Implementation: impl HeaderMapStruct
// File: crates/router/src/services/authentication.rs
// Module: router
// Methods: 7 total (6 public)

impl HeaderMapStruct


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

pub mod transformers;

use std::sync::LazyLock;

use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use masking::PeekInterface;
use transformers as flexiti;
use uuid::Uuid;

use crate::{
    capture_method_not_supported,
    constants::headers,
    types::{RefreshTokenRouterData, ResponseRouterData},
    utils,
};

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

impl Flexiti {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &FloatMajorUnitForConnector,
        }
    }
}

impl api::Payment for Flexiti {}
impl api::PaymentSession for Flexiti {}
impl api::ConnectorAccessToken for Flexiti {}
impl api::MandateSetup for Flexiti {}
impl api::PaymentAuthorize for Flexiti {}
impl api::PaymentSync for Flexiti {}
impl api::PaymentCapture for Flexiti {}
impl api::PaymentVoid for Flexiti {}
impl api::Refund for Flexiti {}
impl api::RefundExecute for Flexiti {}
impl api::RefundSync for Flexiti {}
impl api::PaymentToken for Flexiti {}

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

impl Flexiti {
    fn get_default_header() -> (String, masking::Maskable<String>) {
        (
            "x-reference-id".to_string(),
            Uuid::new_v4().to_string().into(),
        )
    }
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Flexiti
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let access_token = req
            .access_token
            .clone()
            .ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
        let mut header = vec![(
            headers::AUTHORIZATION.to_string(),
            format!("Bearer {}", access_token.token.peek()).into(),
        )];
        header.push(Self::get_default_header());
        Ok(header)
    }
}

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

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

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

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

    fn get_auth_header(
        &self,
        _auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        Ok(vec![])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: flexiti::FlexitiErrorResponse = res
            .response
            .parse_struct("FlexitiErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error.to_owned(),
            message: response.message.to_owned(),
            reason: Some(response.message.to_owned()),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Flexiti {
    fn validate_mandate_payment(
        &self,
        _pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match pm_data {
            PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
                "validate_mandate_payment does not support cards".to_string(),
            )
            .into()),
            _ => Ok(()),
        }
    }

    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<common_enums::CaptureMethod>,
        _payment_method: common_enums::PaymentMethod,
        pmt: Option<common_enums::PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();

        match capture_method {
            enums::CaptureMethod::Manual => Ok(()),
            enums::CaptureMethod::Automatic
            | enums::CaptureMethod::SequentialAutomatic
            | enums::CaptureMethod::ManualMultiple
            | enums::CaptureMethod::Scheduled => {
                let connector = self.id();
                match pmt {
                    Some(payment_method_type) => {
                        capture_method_not_supported!(
                            connector,
                            capture_method,
                            payment_method_type
                        )
                    }
                    None => capture_method_not_supported!(connector, capture_method),
                }
            }
        }
    }

    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        Ok(())
    }
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Flexiti {
    fn get_url(
        &self,
        _req: &RefreshTokenRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}oauth/token", self.base_url(connectors)))
    }
    fn get_content_type(&self) -> &'static str {
        "application/x-www-form-urlencoded"
    }
    fn get_headers(
        &self,
        _req: &RefreshTokenRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        Ok(vec![(Self::get_default_header())])
    }
    fn get_request_body(
        &self,
        req: &RefreshTokenRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = flexiti::FlexitiAccessTokenRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

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

        Ok(req)
    }

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

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

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

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

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Flexiti {
    //TODO: implement sessions flow
}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Flexiti {}

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

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

    fn get_url(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let auth_details = flexiti::FlexitiAuthType::try_from(&req.connector_auth_type)?;
        Ok(format!(
            "{}online/v2/client-id/{:?}/systems/init",
            self.base_url(connectors),
            auth_details.client_id.peek()
        ))
    }

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

        let connector_router_data = flexiti::FlexitiRouterData::from((amount, req));
        let connector_req = flexiti::FlexitiPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: flexiti::FlexitiPaymentsResponse = res
            .response
            .parse_struct("Flexiti PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

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

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

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

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let auth_details = flexiti::FlexitiAuthType::try_from(&req.connector_auth_type.to_owned())?;
        let order_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;
        Ok(format!(
            "{}online/client-id/{}/notifications/order-id/{}",
            self.base_url(connectors),
            auth_details.client_id.peek(),
            order_id
        ))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: flexiti::FlexitiSyncResponse = res
            .response
            .parse_struct("flexiti FlexitiSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

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

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Flexiti {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

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

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        let response: flexiti::FlexitiPaymentsResponse = res
            .response
            .parse_struct("Flexiti PaymentsCaptureResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

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

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

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

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

    fn get_url(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

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

        let connector_router_data = flexiti::FlexitiRouterData::from((refund_amount, req));
        let connector_req = flexiti::FlexitiRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: flexiti::RefundResponse = res
            .response
            .parse_struct("flexiti RefundResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

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

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

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

    fn get_url(
        &self,
        _req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

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

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: flexiti::RefundResponse = res
            .response
            .parse_struct("flexiti RefundSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

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

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

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

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

static FLEXITI_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> = LazyLock::new(|| {
    let supported_capture_methods = vec![enums::CaptureMethod::Manual];
    let mut flexiti_supported_payment_methods = SupportedPaymentMethods::new();

    flexiti_supported_payment_methods.add(
        enums::PaymentMethod::PayLater,
        enums::PaymentMethodType::Klarna,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::NotSupported,
            supported_capture_methods,
            specific_features: None,
        },
    );

    flexiti_supported_payment_methods
});

static FLEXITI_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Flexiti",
    description: "Flexiti connector",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Alpha,
};

static FLEXITI_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];

impl ConnectorSpecifications for Flexiti {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&FLEXITI_CONNECTOR_INFO)
    }

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

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


// Struct: CardReqBody
// File: crates/router/src/core/payment_methods/transformers.rs
// Module: router
// Implementations: 0

pub struct CardReqBody


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

impl Responder


// Struct: ForteErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/forte/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ForteErrorResponse


// Function: generate_redis_key
// File: crates/common_utils/src/id_type/client_secret.rs
// Module: common_utils

// Documentation: Generate a key for redis

pub fn generate_redis_key(&self) -> String


// File: crates/analytics/src/payment_intents/metrics/payment_processed_amount.rs
// Module: analytics

use std::collections::HashSet;

use api_models::analytics::{
    payment_intents::{
        PaymentIntentDimensions, PaymentIntentFilters, PaymentIntentMetricsBucketIdentifier,
    },
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use diesel_models::enums as storage_enums;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::PaymentIntentMetricRow;
use crate::{
    enums::AuthInfo,
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};

#[derive(Default)]
pub(super) struct PaymentProcessedAmount;

#[async_trait::async_trait]
impl<T> super::PaymentIntentMetric<T> for PaymentProcessedAmount
where
    T: AnalyticsDataSource + super::PaymentIntentMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentIntentDimensions],
        auth: &AuthInfo,
        filters: &PaymentIntentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>>
    {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::PaymentIntent);

        let mut dimensions = dimensions.to_vec();

        dimensions.push(PaymentIntentDimensions::PaymentIntentStatus);

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

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

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

        query_builder
            .add_select_column(Aggregate::Sum {
                field: "amount",
                alias: Some("total"),
            })
            .switch()?;

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

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

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

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

        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        query_builder
            .add_group_by_clause("currency")
            .attach_printable("Error grouping by currency")
            .switch()?;

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

        query_builder
            .add_filter_clause(
                PaymentIntentDimensions::PaymentIntentStatus,
                storage_enums::IntentStatus::Succeeded,
            )
            .switch()?;

        query_builder
            .execute_query::<PaymentIntentMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    PaymentIntentMetricsBucketIdentifier::new(
                        None,
                        i.currency.as_ref().map(|i| i.0),
                        i.profile_id.clone(),
                        i.connector.clone(),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.payment_method.clone(),
                        i.payment_method_type.clone(),
                        i.card_network.clone(),
                        i.merchant_id.clone(),
                        i.card_last_4.clone(),
                        i.card_issuer.clone(),
                        i.error_reason.clone(),
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


// Struct: VoltRefundWebhookObjectResource
// File: crates/hyperswitch_connectors/src/connectors/volt/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct VoltRefundWebhookObjectResource


// Struct: FiuuApplePayData
// File: crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FiuuApplePayData


// Struct: CoinbasePaymentResponseData
// File: crates/hyperswitch_connectors/src/connectors/coinbase/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CoinbasePaymentResponseData


// Struct: CustomerPaymentMethodResponseItem
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 0

pub struct CustomerPaymentMethodResponseItem