// File: crates/router/src/core/cards_info.rs

use actix_multipart::form::{bytes::Bytes, MultipartForm};
use api_models::cards_info as cards_info_api_types;
use common_utils::fp_utils::when;
use csv::Reader;
use diesel_models::cards_info as card_info_models;
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::cards_info;
use rdkafka::message::ToBytes;
use router_env::{instrument, tracing};

use crate::{
    core::{
        errors::{self, RouterResponse, RouterResult, StorageErrorExt},
        payments::helpers,
    },
    routes,
    services::ApplicationResponse,
    types::{
        domain,
        transformers::{ForeignFrom, ForeignInto},
    },
};

fn verify_iin_length(card_iin: &str) -> Result<(), errors::ApiErrorResponse> {
    let is_bin_length_in_range = card_iin.len() == 6 || card_iin.len() == 8;
    when(!is_bin_length_in_range, || {
        Err(errors::ApiErrorResponse::InvalidCardIinLength)
    })
}

#[instrument(skip_all)]
pub async fn retrieve_card_info(
    state: routes::SessionState,
    platform: domain::Platform,
    request: cards_info_api_types::CardsInfoRequest,
) -> RouterResponse<cards_info_api_types::CardInfoResponse> {
    let db = state.store.as_ref();

    verify_iin_length(&request.card_iin)?;
    helpers::verify_payment_intent_time_and_client_secret(&state, &platform, request.client_secret)
        .await?;

    let card_info = db
        .get_card_info(&request.card_iin)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to retrieve card information")?
        .ok_or(report!(errors::ApiErrorResponse::InvalidCardIin))?;

    Ok(ApplicationResponse::Json(
        cards_info_api_types::CardInfoResponse::foreign_from(card_info),
    ))
}

#[instrument(skip_all)]
pub async fn create_card_info(
    state: routes::SessionState,
    card_info_request: cards_info_api_types::CardInfoCreateRequest,
) -> RouterResponse<cards_info_api_types::CardInfoResponse> {
    let db = state.store.as_ref();
    cards_info::CardsInfoInterface::add_card_info(db, card_info_request.foreign_into())
        .await
        .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
            message: "CardInfo with given key already exists in our records".to_string(),
        })
        .map(|card_info| ApplicationResponse::Json(card_info.foreign_into()))
}

#[instrument(skip_all)]
pub async fn update_card_info(
    state: routes::SessionState,
    card_info_request: cards_info_api_types::CardInfoUpdateRequest,
) -> RouterResponse<cards_info_api_types::CardInfoResponse> {
    let db = state.store.as_ref();
    cards_info::CardsInfoInterface::update_card_info(
        db,
        card_info_request.card_iin,
        card_info_models::UpdateCardInfo {
            card_issuer: card_info_request.card_issuer,
            card_network: card_info_request.card_network,
            card_type: card_info_request.card_type,
            card_subtype: card_info_request.card_subtype,
            card_issuing_country: card_info_request.card_issuing_country,
            bank_code_id: card_info_request.bank_code_id,
            bank_code: card_info_request.bank_code,
            country_code: card_info_request.country_code,
            last_updated: Some(common_utils::date_time::now()),
            last_updated_provider: card_info_request.last_updated_provider,
        },
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
        message: "Card info with given key does not exist in our records".to_string(),
    })
    .attach_printable("Failed while updating card info")
    .map(|card_info| ApplicationResponse::Json(card_info.foreign_into()))
}

#[derive(Debug, MultipartForm)]
pub struct CardsInfoUpdateForm {
    #[multipart(limit = "1MB")]
    pub file: Bytes,
}

fn parse_cards_bin_csv(
    data: &[u8],
) -> csv::Result<Vec<cards_info_api_types::CardInfoUpdateRequest>> {
    let mut csv_reader = Reader::from_reader(data);
    let mut records = Vec::new();
    let mut id_counter = 0;
    for result in csv_reader.deserialize() {
        let mut record: cards_info_api_types::CardInfoUpdateRequest = result?;
        id_counter += 1;
        record.line_number = Some(id_counter);
        records.push(record);
    }
    Ok(records)
}

pub fn get_cards_bin_records(
    form: CardsInfoUpdateForm,
) -> Result<Vec<cards_info_api_types::CardInfoUpdateRequest>, errors::ApiErrorResponse> {
    match parse_cards_bin_csv(form.file.data.to_bytes()) {
        Ok(records) => Ok(records),
        Err(e) => Err(errors::ApiErrorResponse::PreconditionFailed {
            message: e.to_string(),
        }),
    }
}

#[instrument(skip_all)]
pub async fn migrate_cards_info(
    state: routes::SessionState,
    card_info_records: Vec<cards_info_api_types::CardInfoUpdateRequest>,
) -> RouterResponse<Vec<cards_info_api_types::CardInfoMigrationResponse>> {
    let mut result = Vec::new();
    for record in card_info_records {
        let res = card_info_flow(record.clone(), state.clone()).await;
        result.push(cards_info_api_types::CardInfoMigrationResponse::from((
            match res {
                Ok(ApplicationResponse::Json(response)) => Ok(response),
                Err(e) => Err(e.to_string()),
                _ => Err("Failed to migrate card info".to_string()),
            },
            record,
        )));
    }
    Ok(ApplicationResponse::Json(result))
}

pub trait State {}
pub trait TransitionTo<S: State> {}
// Available states for card info migration
pub struct CardInfoFetch;
pub struct CardInfoAdd;
pub struct CardInfoUpdate;
pub struct CardInfoResponse;

impl State for CardInfoFetch {}
impl State for CardInfoAdd {}
impl State for CardInfoUpdate {}
impl State for CardInfoResponse {}

// State transitions for card info migration
impl TransitionTo<CardInfoAdd> for CardInfoFetch {}
impl TransitionTo<CardInfoUpdate> for CardInfoFetch {}
impl TransitionTo<CardInfoResponse> for CardInfoAdd {}
impl TransitionTo<CardInfoResponse> for CardInfoUpdate {}

// Async executor
pub struct CardInfoMigrateExecutor<'a> {
    state: &'a routes::SessionState,
    record: &'a cards_info_api_types::CardInfoUpdateRequest,
}

impl<'a> CardInfoMigrateExecutor<'a> {
    fn new(
        state: &'a routes::SessionState,
        record: &'a cards_info_api_types::CardInfoUpdateRequest,
    ) -> Self {
        Self { state, record }
    }

    async fn fetch_card_info(&self) -> RouterResult<Option<card_info_models::CardInfo>> {
        let db = self.state.store.as_ref();
        let maybe_card_info = db
            .get_card_info(&self.record.card_iin)
            .await
            .change_context(errors::ApiErrorResponse::InvalidCardIin)?;
        Ok(maybe_card_info)
    }

    async fn add_card_info(&self) -> RouterResult<card_info_models::CardInfo> {
        let db = self.state.store.as_ref();
        let card_info =
            cards_info::CardsInfoInterface::add_card_info(db, self.record.clone().foreign_into())
                .await
                .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
                    message: "CardInfo with given key already exists in our records".to_string(),
                })?;
        Ok(card_info)
    }

    async fn update_card_info(&self) -> RouterResult<card_info_models::CardInfo> {
        let db = self.state.store.as_ref();
        let card_info = cards_info::CardsInfoInterface::update_card_info(
            db,
            self.record.card_iin.clone(),
            card_info_models::UpdateCardInfo {
                card_issuer: self.record.card_issuer.clone(),
                card_network: self.record.card_network.clone(),
                card_type: self.record.card_type.clone(),
                card_subtype: self.record.card_subtype.clone(),
                card_issuing_country: self.record.card_issuing_country.clone(),
                bank_code_id: self.record.bank_code_id.clone(),
                bank_code: self.record.bank_code.clone(),
                country_code: self.record.country_code.clone(),
                last_updated: Some(common_utils::date_time::now()),
                last_updated_provider: self.record.last_updated_provider.clone(),
            },
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Card info with given key does not exist in our records".to_string(),
        })
        .attach_printable("Failed while updating card info")?;
        Ok(card_info)
    }
}

// Builder
pub struct CardInfoBuilder<S: State> {
    state: std::marker::PhantomData<S>,
    pub card_info: Option<card_info_models::CardInfo>,
}

impl CardInfoBuilder<CardInfoFetch> {
    fn new() -> Self {
        Self {
            state: std::marker::PhantomData,
            card_info: None,
        }
    }
}

impl CardInfoBuilder<CardInfoFetch> {
    fn set_card_info(
        self,
        card_info: card_info_models::CardInfo,
    ) -> CardInfoBuilder<CardInfoUpdate> {
        CardInfoBuilder {
            state: std::marker::PhantomData,
            card_info: Some(card_info),
        }
    }

    fn transition(self) -> CardInfoBuilder<CardInfoAdd> {
        CardInfoBuilder {
            state: std::marker::PhantomData,
            card_info: None,
        }
    }
}

impl CardInfoBuilder<CardInfoUpdate> {
    fn set_updated_card_info(
        self,
        card_info: card_info_models::CardInfo,
    ) -> CardInfoBuilder<CardInfoResponse> {
        CardInfoBuilder {
            state: std::marker::PhantomData,
            card_info: Some(card_info),
        }
    }
}

impl CardInfoBuilder<CardInfoAdd> {
    fn set_added_card_info(
        self,
        card_info: card_info_models::CardInfo,
    ) -> CardInfoBuilder<CardInfoResponse> {
        CardInfoBuilder {
            state: std::marker::PhantomData,
            card_info: Some(card_info),
        }
    }
}

impl CardInfoBuilder<CardInfoResponse> {
    pub fn build(self) -> cards_info_api_types::CardInfoMigrateResponseRecord {
        match self.card_info {
            Some(card_info) => cards_info_api_types::CardInfoMigrateResponseRecord {
                card_iin: Some(card_info.card_iin),
                card_issuer: card_info.card_issuer,
                card_network: card_info.card_network.map(|cn| cn.to_string()),
                card_type: card_info.card_type,
                card_sub_type: card_info.card_subtype,
                card_issuing_country: card_info.card_issuing_country,
            },
            None => cards_info_api_types::CardInfoMigrateResponseRecord {
                card_iin: None,
                card_issuer: None,
                card_network: None,
                card_type: None,
                card_sub_type: None,
                card_issuing_country: None,
            },
        }
    }
}

async fn card_info_flow(
    record: cards_info_api_types::CardInfoUpdateRequest,
    state: routes::SessionState,
) -> RouterResponse<cards_info_api_types::CardInfoMigrateResponseRecord> {
    let builder = CardInfoBuilder::new();
    let executor = CardInfoMigrateExecutor::new(&state, &record);
    let fetched_card_info_details = executor.fetch_card_info().await?;

    let builder = match fetched_card_info_details {
        Some(card_info) => {
            let builder = builder.set_card_info(card_info);
            let updated_card_info = executor.update_card_info().await?;
            builder.set_updated_card_info(updated_card_info)
        }
        None => {
            let builder = builder.transition();
            let added_card_info = executor.add_card_info().await?;
            builder.set_added_card_info(added_card_info)
        }
    };

    Ok(ApplicationResponse::Json(builder.build()))
}


// File: crates/router/src/core/revenue_recovery_data_backfill.rs

use std::collections::HashMap;

use api_models::revenue_recovery_data_backfill::{
    BackfillError, ComprehensiveCardData, GetRedisDataQuery, RedisDataResponse, RedisKeyType,
    RevenueRecoveryBackfillRequest, RevenueRecoveryDataBackfillResponse, ScheduledAtUpdate,
    UnlockStatusResponse, UpdateTokenStatusRequest, UpdateTokenStatusResponse,
};
use common_enums::{CardNetwork, PaymentMethodType};
use common_utils::id_type;
use error_stack::ResultExt;
use hyperswitch_domain_models::api::ApplicationResponse;
use masking::ExposeInterface;
use router_env::{instrument, logger};
use time::{macros::format_description, Date};

use crate::{
    connection,
    core::errors::{self, RouterResult},
    routes::SessionState,
    types::{domain, storage},
};

pub async fn revenue_recovery_data_backfill(
    state: SessionState,
    records: Vec<RevenueRecoveryBackfillRequest>,
    cutoff_datetime: Option<time::PrimitiveDateTime>,
) -> RouterResult<ApplicationResponse<RevenueRecoveryDataBackfillResponse>> {
    let mut processed_records = 0;
    let mut failed_records = 0;

    // Process each record
    for record in records {
        match process_payment_method_record(&state, &record, cutoff_datetime).await {
            Ok(_) => {
                processed_records += 1;
                logger::info!(
                    "Successfully processed record with connector customer id: {}",
                    record.customer_id_resp
                );
            }
            Err(e) => {
                failed_records += 1;
                logger::error!(
                    "Payment method backfill failed: customer_id={}, error={}",
                    record.customer_id_resp,
                    e
                );
            }
        }
    }

    let response = RevenueRecoveryDataBackfillResponse {
        processed_records,
        failed_records,
    };

    logger::info!(
        "Revenue recovery data backfill completed - Processed: {}, Failed: {}",
        processed_records,
        failed_records
    );

    Ok(ApplicationResponse::Json(response))
}

pub async fn unlock_connector_customer_status_handler(
    state: SessionState,
    connector_customer_id: String,
    payment_id: id_type::GlobalPaymentId,
) -> RouterResult<ApplicationResponse<UnlockStatusResponse>> {
    let unlocked = storage::revenue_recovery_redis_operation::
        RedisTokenManager::unlock_connector_customer_status(&state, &connector_customer_id, &payment_id)
        .await
        .map_err(|e| {
            logger::error!(
                "Failed to unlock connector customer status for {}: {:?}",
                connector_customer_id,
                e
            );
            match e.current_context() {
                errors::StorageError::RedisError(redis_error) => {
                    match redis_error.current_context() {
                        storage_impl::errors::RedisError::DeleteFailed => {
                            // This indicates the payment_id doesn't own the lock
                            errors::ApiErrorResponse::InvalidPaymentIdProvided {
                                resource: format!("The Status token for connector costumer id:- {} is locked by different PaymentIntent ID", connector_customer_id)
                            }
                        }
                        _ => {
                            // Other Redis errors - infrastructure issue
                            errors::ApiErrorResponse::InternalServerError
                        }
                    }
                }
                errors::StorageError::ValueNotFound(_) => {
                    // Lock doesn't exist
                    errors::ApiErrorResponse::GenericNotFoundError {
                        message: format!("Lock not found for connector customer id: {}", connector_customer_id)
                    }
                }
                _ => {
                    // Fallback for other storage errors
                    errors::ApiErrorResponse::InternalServerError
                }
            }
        })?;

    let response = UnlockStatusResponse { unlocked };

    logger::info!(
        "Unlock operation completed for connector customer {}: {}",
        connector_customer_id,
        unlocked
    );

    Ok(ApplicationResponse::Json(response))
}
pub async fn get_redis_data(
    state: SessionState,
    connector_customer_id: &str,
    key_type: &RedisKeyType,
) -> RouterResult<ApplicationResponse<RedisDataResponse>> {
    match storage::revenue_recovery_redis_operation::RedisTokenManager::get_redis_key_data_raw(
        &state,
        connector_customer_id,
        key_type,
    )
    .await
    {
        Ok((exists, ttl_seconds, data)) => {
            let response = RedisDataResponse {
                exists,
                ttl_seconds,
                data,
            };

            logger::info!(
                "Retrieved Redis data for connector customer {}, exists={}, ttl={}",
                connector_customer_id,
                exists,
                ttl_seconds
            );

            Ok(ApplicationResponse::Json(response))
        }
        Err(error) => Err(
            error.change_context(errors::ApiErrorResponse::GenericNotFoundError {
                message: format!(
                    "Redis data not found for connector customer id:- '{}'",
                    connector_customer_id
                ),
            }),
        ),
    }
}

pub async fn redis_update_additional_details_for_revenue_recovery(
    state: SessionState,
    request: UpdateTokenStatusRequest,
) -> RouterResult<ApplicationResponse<UpdateTokenStatusResponse>> {
    // Get existing token
    let existing_token = storage::revenue_recovery_redis_operation::
        RedisTokenManager::get_payment_processor_token_using_token_id(
            &state,
            &request.connector_customer_id,
            &request.payment_processor_token.clone().expose(),
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to retrieve existing token data")?;

    // Check if token exists
    let mut token_status = existing_token.ok_or_else(|| {
        error_stack::Report::new(errors::ApiErrorResponse::GenericNotFoundError {
            message: format!(
                "Token '{:?}' not found for connector customer id:- '{}'",
                request.payment_processor_token, request.connector_customer_id
            ),
        })
    })?;

    let mut updated_fields = Vec::new();

    // Handle scheduled_at update
    match request.scheduled_at {
        Some(ScheduledAtUpdate::SetToDateTime(dt)) => {
            // Field provided with datetime - update schedule_at field with datetime
            token_status.scheduled_at = Some(dt);
            updated_fields.push(format!("scheduled_at: {}", dt));
            logger::info!(
                "Set scheduled_at to '{}' for token '{:?}'",
                dt,
                request.payment_processor_token
            );
        }
        Some(ScheduledAtUpdate::SetToNull) => {
            // Field provided with "null" variable - set schedule_at field to null
            token_status.scheduled_at = None;
            updated_fields.push("scheduled_at: set to null".to_string());
            logger::info!(
                "Set scheduled_at to null for token '{:?}'",
                request.payment_processor_token
            );
        }
        None => {
            // Field not provided - we don't update schedule_at field
            logger::debug!("scheduled_at not provided in request - leaving unchanged");
        }
    }

    // Update is_hard_decline field
    request.is_hard_decline.map(|is_hard_decline| {
        token_status.is_hard_decline = Some(is_hard_decline);
        updated_fields.push(format!("is_hard_decline: {}", is_hard_decline));
    });

    // Update error_code field
    request.error_code.as_ref().map(|error_code| {
        token_status.error_code = Some(error_code.clone());
        updated_fields.push(format!("error_code: {}", error_code));
    });

    // Update Redis with modified token
    let mut tokens_map = HashMap::new();
    tokens_map.insert(
        request.payment_processor_token.clone().expose(),
        token_status,
    );

    storage::revenue_recovery_redis_operation::
        RedisTokenManager::update_or_add_connector_customer_payment_processor_tokens(
            &state,
            &request.connector_customer_id,
            tokens_map,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update token status in Redis")?;

    let updated_fields_str = if updated_fields.is_empty() {
        "no fields were updated".to_string()
    } else {
        updated_fields.join(", ")
    };

    let response = UpdateTokenStatusResponse {
        updated: true,
        message: format!(
            "Successfully updated token '{:?}' for connector customer '{}'. Updated fields: {}",
            request.payment_processor_token, request.connector_customer_id, updated_fields_str
        ),
    };

    logger::info!(
        "Updated token status for connector customer {}, token: {:?}",
        request.connector_customer_id,
        request.payment_processor_token
    );

    Ok(ApplicationResponse::Json(response))
}

async fn process_payment_method_record(
    state: &SessionState,
    record: &RevenueRecoveryBackfillRequest,
    cutoff_datetime: Option<time::PrimitiveDateTime>,
) -> Result<(), BackfillError> {
    // Build comprehensive card data from CSV record
    let card_data = match build_comprehensive_card_data(record) {
        Ok(data) => data,
        Err(e) => {
            logger::warn!(
                "Failed to build card data for connector customer id: {}, error: {}.",
                record.customer_id_resp,
                e
            );
            ComprehensiveCardData {
                card_type: Some("card".to_string()),
                card_exp_month: None,
                card_exp_year: None,
                card_network: None,
                card_issuer: None,
                card_issuing_country: None,
                daily_retry_history: None,
                is_active: None,
                account_update_history: None,
            }
        }
    };
    logger::info!(
        "Built comprehensive card data - card_type: {:?}, exp_month: {}, exp_year: {}, network: {:?}, issuer: {:?}, country: {:?}, daily_retry_history: {:?}",
        card_data.card_type,
        card_data.card_exp_month.as_ref().map(|_| "**").unwrap_or("None"),
        card_data.card_exp_year.as_ref().map(|_| "**").unwrap_or("None"),
        card_data.card_network,
        card_data.card_issuer,
        card_data.card_issuing_country,
        card_data.daily_retry_history
    );

    // Update Redis if token exists and is valid
    match record.token.as_ref().map(|token| token.clone().expose()) {
        Some(token) if !token.is_empty() => {
            logger::info!("Updating Redis for customer: {}", record.customer_id_resp,);

            storage::revenue_recovery_redis_operation::
            RedisTokenManager::update_redis_token_with_comprehensive_card_data(
                state,
                &record.customer_id_resp,
                &token,
                &card_data,
                cutoff_datetime,
            )
            .await
            .map_err(|e| {
                logger::error!("Redis update failed: {}", e);
                BackfillError::RedisError(format!("Token not found in Redis: {}", e))
            })?;
        }
        _ => {
            logger::info!(
                "Skipping Redis update - token is missing, empty or 'nan': {:?}",
                record.token
            );
        }
    }

    logger::info!(
        "Successfully completed processing for connector customer id: {}",
        record.customer_id_resp
    );
    Ok(())
}

/// Parse daily retry history from CSV
fn parse_daily_retry_history(
    json_str: Option<&str>,
) -> Option<HashMap<time::PrimitiveDateTime, i32>> {
    match json_str {
        Some(json) if !json.is_empty() => {
            match serde_json::from_str::<HashMap<String, i32>>(json) {
                Ok(string_retry_history) => {
                    let date_format = format_description!("[year]-[month]-[day]");
                    let datetime_format = format_description!(
                        "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"
                    );

                    let mut hourly_retry_history = HashMap::new();

                    for (key, count) in string_retry_history {
                        // Try parsing full datetime first
                        let parsed_dt = time::PrimitiveDateTime::parse(&key, &datetime_format)
                            .or_else(|_| {
                                // Fallback to date only
                                Date::parse(&key, &date_format).map(|date| {
                                    time::PrimitiveDateTime::new(date, time::Time::MIDNIGHT)
                                })
                            });

                        match parsed_dt {
                            Ok(dt) => {
                                hourly_retry_history.insert(dt, count);
                            }
                            Err(_) => {
                                logger::error!("Error: failed to parse retry history key '{}'", key)
                            }
                        }
                    }

                    logger::debug!(
                        "Successfully parsed daily_retry_history with {} entries",
                        hourly_retry_history.len()
                    );

                    Some(hourly_retry_history)
                }
                Err(e) => {
                    logger::warn!("Failed to parse daily_retry_history JSON '{}': {}", json, e);
                    None
                }
            }
        }
        _ => {
            logger::debug!("Daily retry history not present or invalid, preserving existing data");
            None
        }
    }
}

/// Build comprehensive card data from CSV record
fn build_comprehensive_card_data(
    record: &RevenueRecoveryBackfillRequest,
) -> Result<ComprehensiveCardData, BackfillError> {
    // Extract card type from request, if not present then update it with 'card'
    let card_type = determine_card_type(record.payment_method_sub_type);

    // Parse expiration date
    let (exp_month, exp_year) = parse_expiration_date(
        record
            .exp_date
            .as_ref()
            .map(|date| date.clone().expose())
            .as_deref(),
    )?;

    let card_exp_month = exp_month.map(masking::Secret::new);
    let card_exp_year = exp_year.map(masking::Secret::new);

    // Extract card network
    let card_network = record.card_network.clone();

    // Extract card issuer and issuing country
    let card_issuer = record
        .clean_bank_name
        .as_ref()
        .filter(|value| !value.is_empty())
        .cloned();

    let card_issuing_country = record
        .country_name
        .as_ref()
        .filter(|value| !value.is_empty())
        .cloned();

    // Parse daily retry history
    let daily_retry_history = parse_daily_retry_history(record.daily_retry_history.as_deref());

    Ok(ComprehensiveCardData {
        card_type,
        card_exp_month,
        card_exp_year,
        card_network,
        card_issuer,
        card_issuing_country,
        daily_retry_history,
        is_active: record.is_active,
        account_update_history: record.account_update_history.clone(),
    })
}

/// Determine card type with fallback logic: payment_method_sub_type if not present -> "Card"
fn determine_card_type(payment_method_sub_type: Option<PaymentMethodType>) -> Option<String> {
    match payment_method_sub_type {
        Some(card_type_enum) => {
            let mapped_type = match card_type_enum {
                PaymentMethodType::Credit => "credit".to_string(),
                PaymentMethodType::Debit => "debit".to_string(),
                PaymentMethodType::Card => "card".to_string(),
                // For all other payment method types, default to "card"
                _ => "card".to_string(),
            };
            logger::debug!(
                "Using payment_method_sub_type enum '{:?}' -> '{}'",
                card_type_enum,
                mapped_type
            );
            Some(mapped_type)
        }
        None => {
            logger::info!("In CSV payment_method_sub_type not present...");
            None
        }
    }
}

/// Parse expiration date
fn parse_expiration_date(
    exp_date: Option<&str>,
) -> Result<(Option<String>, Option<String>), BackfillError> {
    exp_date
        .filter(|date| !date.is_empty())
        .map(|date| {
            date.split_once('/')
                .ok_or_else(|| {
                    logger::warn!("Unrecognized expiration date format (MM/YY expected)");
                    BackfillError::CsvParsingError(
                        "Invalid expiration date format: expected MM/YY".to_string(),
                    )
                })
                .and_then(|(month_part, year_part)| {
                    let month = month_part.trim();
                    let year = year_part.trim();

                    logger::debug!("Split expiration date - parsing month and year");

                    // Validate and parse month
                    let month_num = month.parse::<u8>().map_err(|_| {
                        logger::warn!("Failed to parse month component in expiration date");
                        BackfillError::CsvParsingError(
                            "Invalid month format in expiration date".to_string(),
                        )
                    })?;

                    if !(1..=12).contains(&month_num) {
                        logger::warn!("Invalid month value in expiration date (not in range 1-12)");
                        return Err(BackfillError::CsvParsingError(
                            "Invalid month value in expiration date".to_string(),
                        ));
                    }

                    // Handle year conversion
                    let final_year = match year.len() {
                        4 => &year[2..4], // Convert 4-digit to 2-digit
                        2 => year,        // Already 2-digit
                        _ => {
                            logger::warn!(
                                "Invalid year length in expiration date (expected 2 or 4 digits)"
                            );
                            return Err(BackfillError::CsvParsingError(
                                "Invalid year format in expiration date".to_string(),
                            ));
                        }
                    };

                    logger::debug!("Successfully parsed expiration date... ",);
                    Ok((Some(month.to_string()), Some(final_year.to_string())))
                })
        })
        .unwrap_or_else(|| {
            logger::debug!("Empty expiration date, returning None");
            Ok((None, None))
        })
}


// File: crates/router/src/core/configs.rs

use common_utils::errors::CustomResult;
use error_stack::ResultExt;
use external_services::superposition::ConfigContext;

use crate::{
    core::errors::{self, utils::StorageErrorExt, RouterResponse},
    routes::SessionState,
    services::ApplicationResponse,
    types::{api, transformers::ForeignInto},
};

pub async fn set_config(state: SessionState, config: api::Config) -> RouterResponse<api::Config> {
    let store = state.store.as_ref();
    let config = store
        .insert_config(diesel_models::configs::ConfigNew {
            key: config.key,
            config: config.value,
        })
        .await
        .to_duplicate_response(errors::ApiErrorResponse::DuplicateConfig)
        .attach_printable("Unknown error, while setting config key")?;

    Ok(ApplicationResponse::Json(config.foreign_into()))
}

pub async fn read_config(state: SessionState, key: &str) -> RouterResponse<api::Config> {
    let store = state.store.as_ref();
    let config = store
        .find_config_by_key(key)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)?;
    Ok(ApplicationResponse::Json(config.foreign_into()))
}

pub async fn update_config(
    state: SessionState,
    config_update: &api::ConfigUpdate,
) -> RouterResponse<api::Config> {
    let store = state.store.as_ref();
    let config = store
        .update_config_by_key(&config_update.key, config_update.foreign_into())
        .await
        .to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)?;
    Ok(ApplicationResponse::Json(config.foreign_into()))
}

pub async fn config_delete(state: SessionState, key: String) -> RouterResponse<api::Config> {
    let store = state.store.as_ref();
    let config = store
        .delete_config_by_key(&key)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)?;
    Ok(ApplicationResponse::Json(config.foreign_into()))
}

/// Get a boolean configuration value with superposition and database fallback
pub async fn get_config_bool(
    state: &SessionState,
    superposition_key: &str,
    db_key: &str,
    context: Option<ConfigContext>,
    default_value: bool,
) -> CustomResult<bool, errors::StorageError> {
    // Try superposition first if available
    let superposition_result = if let Some(ref superposition_client) = state.superposition_service {
        match superposition_client
            .get_bool_value(superposition_key, context.as_ref())
            .await
        {
            Ok(value) => Some(value),
            Err(err) => {
                router_env::logger::warn!(
                    "Failed to retrieve config from superposition, falling back to database: {:?}",
                    err
                );
                None
            }
        }
    } else {
        None
    };

    // Use superposition result or fall back to database
    if let Some(value) = superposition_result {
        Ok(value)
    } else {
        let config = state
            .store
            .find_config_by_key_unwrap_or(db_key, Some(default_value.to_string()))
            .await?;

        config
            .config
            .parse::<bool>()
            .change_context(errors::StorageError::DeserializationFailed)
    }
}

/// Get a string configuration value with superposition and database fallback
pub async fn get_config_string(
    state: &SessionState,
    superposition_key: &str,
    db_key: &str,
    context: Option<ConfigContext>,
    default_value: String,
) -> CustomResult<String, errors::StorageError> {
    // Try superposition first if available
    let superposition_result = if let Some(ref superposition_client) = state.superposition_service {
        match superposition_client
            .get_string_value(superposition_key, context.as_ref())
            .await
        {
            Ok(value) => Some(value),
            Err(err) => {
                router_env::logger::warn!(
                    "Failed to retrieve config from superposition, falling back to database: {:?}",
                    err
                );
                None
            }
        }
    } else {
        None
    };

    // Use superposition result or fall back to database
    if let Some(value) = superposition_result {
        Ok(value)
    } else {
        let config = state
            .store
            .find_config_by_key_unwrap_or(db_key, Some(default_value))
            .await?;

        Ok(config.config)
    }
}

/// Get an integer configuration value with superposition and database fallback
pub async fn get_config_int(
    state: &SessionState,
    superposition_key: &str,
    db_key: &str,
    context: Option<ConfigContext>,
    default_value: i64,
) -> CustomResult<i64, errors::StorageError> {
    // Try superposition first if available
    let superposition_result = if let Some(ref superposition_client) = state.superposition_service {
        match superposition_client
            .get_int_value(superposition_key, context.as_ref())
            .await
        {
            Ok(value) => Some(value),
            Err(err) => {
                router_env::logger::warn!(
                    "Failed to retrieve config from superposition, falling back to database: {:?}",
                    err
                );
                None
            }
        }
    } else {
        None
    };

    // Use superposition result or fall back to database
    if let Some(value) = superposition_result {
        Ok(value)
    } else {
        let config = state
            .store
            .find_config_by_key_unwrap_or(db_key, Some(default_value.to_string()))
            .await?;

        config
            .config
            .parse::<i64>()
            .change_context(errors::StorageError::DeserializationFailed)
    }
}

/// Get a float configuration value with superposition and database fallback
pub async fn get_config_float(
    state: &SessionState,
    superposition_key: &str,
    db_key: &str,
    context: Option<ConfigContext>,
    default_value: f64,
) -> CustomResult<f64, errors::StorageError> {
    // Try superposition first if available
    let superposition_result = if let Some(ref superposition_client) = state.superposition_service {
        match superposition_client
            .get_float_value(superposition_key, context.as_ref())
            .await
        {
            Ok(value) => Some(value),
            Err(err) => {
                router_env::logger::warn!(
                    "Failed to retrieve config from superposition, falling back to database: {:?}",
                    err
                );
                None
            }
        }
    } else {
        None
    };

    // Use superposition result or fall back to database
    if let Some(value) = superposition_result {
        Ok(value)
    } else {
        let config = state
            .store
            .find_config_by_key_unwrap_or(db_key, Some(default_value.to_string()))
            .await?;

        config
            .config
            .parse::<f64>()
            .change_context(errors::StorageError::DeserializationFailed)
    }
}

/// Get an object configuration value with superposition and database fallback
pub async fn get_config_object<T>(
    state: &SessionState,
    superposition_key: &str,
    db_key: &str,
    context: Option<ConfigContext>,
    default_value: T,
) -> CustomResult<T, errors::StorageError>
where
    T: serde::de::DeserializeOwned + serde::Serialize,
{
    // Try superposition first if available
    let superposition_result = if let Some(ref superposition_client) = state.superposition_service {
        match superposition_client
            .get_object_value(superposition_key, context.as_ref())
            .await
        {
            Ok(json_value) => Some(
                serde_json::from_value::<T>(json_value)
                    .change_context(errors::StorageError::DeserializationFailed),
            ),
            Err(err) => {
                router_env::logger::warn!(
                    "Failed to retrieve config from superposition, falling back to database: {:?}",
                    err
                );
                None
            }
        }
    } else {
        None
    };

    // Use superposition result or fall back to database
    if let Some(superposition_result) = superposition_result {
        superposition_result
    } else {
        let config = state
            .store
            .find_config_by_key_unwrap_or(
                db_key,
                Some(
                    serde_json::to_string(&default_value)
                        .change_context(errors::StorageError::SerializationFailed)?,
                ),
            )
            .await?;

        serde_json::from_str::<T>(&config.config)
            .change_context(errors::StorageError::DeserializationFailed)
    }
}


// File: crates/router/src/core/payout_link.rs

use std::{
    cmp::Ordering,
    collections::{HashMap, HashSet},
};

use actix_web::http::header;
use api_models::payouts;
use common_utils::{
    ext_traits::{AsyncExt, Encode, OptionExt},
    link_utils,
    types::{AmountConvertor, StringMajorUnitForConnector},
};
use diesel_models::PayoutLinkUpdate;
use error_stack::ResultExt;
use hyperswitch_domain_models::api::{GenericLinks, GenericLinksData};

use super::errors::{RouterResponse, StorageErrorExt};
use crate::{
    configs::settings::{PaymentMethodFilterKey, PaymentMethodFilters},
    core::payouts::{helpers as payout_helpers, validator},
    errors,
    routes::{app::StorageInterface, SessionState},
    services,
    types::{api, domain, transformers::ForeignFrom},
    utils::get_payout_attempt_id,
};

#[cfg(feature = "v2")]
pub async fn initiate_payout_link(
    _state: SessionState,
    _platform: domain::Platform,
    _req: payouts::PayoutLinkInitiateRequest,
    _request_headers: &header::HeaderMap,
    _locale: String,
) -> RouterResponse<services::GenericLinkFormData> {
    todo!()
}

#[cfg(feature = "v1")]
pub async fn initiate_payout_link(
    state: SessionState,
    platform: domain::Platform,
    req: payouts::PayoutLinkInitiateRequest,
    request_headers: &header::HeaderMap,
) -> RouterResponse<services::GenericLinkFormData> {
    let db: &dyn StorageInterface = &*state.store;
    let merchant_id = platform.get_processor().get_account().get_id();
    // Fetch payout
    let payout = db
        .find_payout_by_merchant_id_payout_id(
            merchant_id,
            &req.payout_id,
            platform.get_processor().get_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?;
    let payout_attempt = db
        .find_payout_attempt_by_merchant_id_payout_attempt_id(
            merchant_id,
            &get_payout_attempt_id(payout.payout_id.get_string_repr(), payout.attempt_count),
            platform.get_processor().get_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?;
    let payout_link_id = payout
        .payout_link_id
        .clone()
        .get_required_value("payout link id")
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payout link not found".to_string(),
        })?;
    // Fetch payout link
    let payout_link = db
        .find_payout_link_by_link_id(&payout_link_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payout link not found".to_string(),
        })?;

    let allowed_domains = validator::validate_payout_link_render_request_and_get_allowed_domains(
        request_headers,
        &payout_link,
    )?;

    // Check status and return form data accordingly
    let has_expired = common_utils::date_time::now() > payout_link.expiry;
    let status = payout_link.link_status.clone();
    let link_data = payout_link.link_data.clone();
    let default_config = &state.conf.generic_link.payout_link.clone();
    let default_ui_config = default_config.ui_config.clone();
    let ui_config_data = link_utils::GenericLinkUiConfigFormData {
        merchant_name: link_data
            .ui_config
            .merchant_name
            .unwrap_or(default_ui_config.merchant_name),
        logo: link_data.ui_config.logo.unwrap_or(default_ui_config.logo),
        theme: link_data
            .ui_config
            .theme
            .clone()
            .unwrap_or(default_ui_config.theme.clone()),
    };
    match (has_expired, &status) {
        // Send back generic expired page
        (true, _) | (_, &link_utils::PayoutLinkStatus::Invalidated) => {
            let expired_link_data = services::GenericExpiredLinkData {
                title: "Payout Expired".to_string(),
                message: "This payout link has expired.".to_string(),
                theme: link_data.ui_config.theme.unwrap_or(default_ui_config.theme),
            };

            if status != link_utils::PayoutLinkStatus::Invalidated {
                let payout_link_update = PayoutLinkUpdate::StatusUpdate {
                    link_status: link_utils::PayoutLinkStatus::Invalidated,
                };
                db.update_payout_link(payout_link, payout_link_update)
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Error updating payout links in db")?;
            }

            Ok(services::ApplicationResponse::GenericLinkForm(Box::new(
                GenericLinks {
                    allowed_domains,
                    data: GenericLinksData::ExpiredLink(expired_link_data),
                    locale: state.locale,
                },
            )))
        }

        // Initiate Payout link flow
        (_, link_utils::PayoutLinkStatus::Initiated) => {
            let customer_id = link_data.customer_id;
            let required_amount_type = StringMajorUnitForConnector;
            let amount = required_amount_type
                .convert(payout.amount, payout.destination_currency)
                .change_context(errors::ApiErrorResponse::CurrencyConversionFailed)?;
            // Fetch customer
            let customer = db
                .find_customer_by_customer_id_merchant_id(
                    &customer_id,
                    &req.merchant_id,
                    platform.get_processor().get_key_store(),
                    platform.get_processor().get_account().storage_scheme,
                )
                .await
                .change_context(errors::ApiErrorResponse::InvalidRequestData {
                    message: format!(
                        "Customer [{:?}] not found for link_id - {}",
                        customer_id, payout_link.link_id
                    ),
                })
                .attach_printable_lazy(|| format!("customer [{customer_id:?}] not found"))?;
            let address = payout
                .address_id
                .as_ref()
                .async_map(|address_id| async {
                    db.find_address_by_address_id(
                        address_id,
                        platform.get_processor().get_key_store(),
                    )
                    .await
                })
                .await
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable_lazy(|| {
                    format!(
                        "Failed while fetching address [id - {:?}] for payout [id - {:?}]",
                        payout.address_id, payout.payout_id
                    )
                })?;

            let enabled_payout_methods =
                filter_payout_methods(&state, &platform, &payout, address.as_ref()).await?;
            // Fetch default enabled_payout_methods
            let mut default_enabled_payout_methods: Vec<link_utils::EnabledPaymentMethod> = vec![];
            for (payment_method, payment_method_types) in
                default_config.enabled_payment_methods.clone().into_iter()
            {
                let enabled_payment_method = link_utils::EnabledPaymentMethod {
                    payment_method,
                    payment_method_types,
                };
                default_enabled_payout_methods.push(enabled_payment_method);
            }
            let fallback_enabled_payout_methods = if enabled_payout_methods.is_empty() {
                &default_enabled_payout_methods
            } else {
                &enabled_payout_methods
            };
            // Fetch enabled payout methods from the request. If not found, fetch the enabled payout methods from MCA,
            // If none are configured for merchant connector accounts, fetch them from the default enabled payout methods.
            let mut enabled_payment_methods = link_data
                .enabled_payment_methods
                .unwrap_or(fallback_enabled_payout_methods.to_vec());

            // Sort payment methods (cards first)
            enabled_payment_methods.sort_by(|a, b| match (a.payment_method, b.payment_method) {
                (_, common_enums::PaymentMethod::Card) => Ordering::Greater,
                (common_enums::PaymentMethod::Card, _) => Ordering::Less,
                _ => Ordering::Equal,
            });

            let required_field_override = api::RequiredFieldsOverrideRequest {
                billing: address
                    .as_ref()
                    .map(hyperswitch_domain_models::address::Address::from)
                    .map(From::from),
            };

            let enabled_payment_methods_with_required_fields = ForeignFrom::foreign_from((
                &state.conf.payouts.required_fields,
                enabled_payment_methods.clone(),
                required_field_override,
            ));

            let js_data = payouts::PayoutLinkDetails {
                publishable_key: masking::Secret::new(
                    platform
                        .get_processor()
                        .get_account()
                        .clone()
                        .publishable_key,
                ),
                client_secret: link_data.client_secret.clone(),
                payout_link_id: payout_link.link_id,
                payout_id: payout_link.primary_reference.clone(),
                customer_id: customer.customer_id,
                session_expiry: payout_link.expiry,
                return_url: payout_link
                    .return_url
                    .as_ref()
                    .map(|url| url::Url::parse(url))
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to parse payout status link's return URL")?,
                ui_config: ui_config_data,
                enabled_payment_methods,
                enabled_payment_methods_with_required_fields,
                amount,
                currency: payout.destination_currency,
                locale: state.locale.clone(),
                form_layout: link_data.form_layout,
                test_mode: link_data.test_mode.unwrap_or(false),
            };

            let serialized_css_content = String::new();

            let serialized_js_content = format!(
                "window.__PAYOUT_DETAILS = {}",
                js_data
                    .encode_to_string_of_json()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to serialize PaymentMethodCollectLinkDetails")?
            );

            let generic_form_data = services::GenericLinkFormData {
                js_data: serialized_js_content,
                css_data: serialized_css_content,
                sdk_url: default_config.sdk_url.clone(),
                html_meta_tags: String::new(),
            };
            Ok(services::ApplicationResponse::GenericLinkForm(Box::new(
                GenericLinks {
                    allowed_domains,
                    data: GenericLinksData::PayoutLink(generic_form_data),
                    locale: state.locale.clone(),
                },
            )))
        }

        // Send back status page
        (_, link_utils::PayoutLinkStatus::Submitted) => {
            let translated_unified_message =
                payout_helpers::get_translated_unified_code_and_message(
                    &state,
                    payout_attempt.unified_code.as_ref(),
                    payout_attempt.unified_message.as_ref(),
                    &state.locale.clone(),
                )
                .await?;
            let js_data = payouts::PayoutLinkStatusDetails {
                payout_link_id: payout_link.link_id,
                payout_id: payout_link.primary_reference.clone(),
                customer_id: link_data.customer_id,
                session_expiry: payout_link.expiry,
                return_url: payout_link
                    .return_url
                    .as_ref()
                    .map(|url| url::Url::parse(url))
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to parse payout status link's return URL")?,
                status: payout.status,
                error_code: payout_attempt.unified_code,
                error_message: translated_unified_message,
                ui_config: ui_config_data,
                test_mode: link_data.test_mode.unwrap_or(false),
            };

            let serialized_css_content = String::new();

            let serialized_js_content = format!(
                "window.__PAYOUT_DETAILS = {}",
                js_data
                    .encode_to_string_of_json()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to serialize PaymentMethodCollectLinkDetails")?
            );

            let generic_status_data = services::GenericLinkStatusData {
                js_data: serialized_js_content,
                css_data: serialized_css_content,
            };
            Ok(services::ApplicationResponse::GenericLinkForm(Box::new(
                GenericLinks {
                    allowed_domains,
                    data: GenericLinksData::PayoutLinkStatus(generic_status_data),
                    locale: state.locale.clone(),
                },
            )))
        }
    }
}

#[cfg(all(feature = "payouts", feature = "v1"))]
pub async fn filter_payout_methods(
    state: &SessionState,
    platform: &domain::Platform,
    payout: &hyperswitch_domain_models::payouts::payouts::Payouts,
    address: Option<&domain::Address>,
) -> errors::RouterResult<Vec<link_utils::EnabledPaymentMethod>> {
    use masking::ExposeInterface;

    let db = &*state.store;
    //Fetch all merchant connector accounts
    let all_mcas = db
        .find_merchant_connector_account_by_merchant_id_and_disabled_list(
            platform.get_processor().get_account().get_id(),
            false,
            platform.get_processor().get_key_store(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
    // Filter MCAs based on profile_id and connector_type
    let filtered_mcas = all_mcas.filter_based_on_profile_and_connector_type(
        &payout.profile_id,
        common_enums::ConnectorType::PayoutProcessor,
    );

    let mut response: Vec<link_utils::EnabledPaymentMethod> = vec![];
    let mut payment_method_list_hm: HashMap<
        common_enums::PaymentMethod,
        HashSet<common_enums::PaymentMethodType>,
    > = HashMap::new();
    let mut bank_transfer_hash_set: HashSet<common_enums::PaymentMethodType> = HashSet::new();
    let mut card_hash_set: HashSet<common_enums::PaymentMethodType> = HashSet::new();
    let mut wallet_hash_set: HashSet<common_enums::PaymentMethodType> = HashSet::new();
    let mut bank_redirect_hash_set: HashSet<common_enums::PaymentMethodType> = HashSet::new();
    let payout_filter_config = &state.conf.payout_method_filters.clone();
    for mca in &filtered_mcas {
        let payout_methods = match &mca.payment_methods_enabled {
            Some(pm) => pm,
            None => continue,
        };
        for payout_method in payout_methods.iter() {
            let parse_result = serde_json::from_value::<api_models::admin::PaymentMethodsEnabled>(
                payout_method.clone().expose(),
            );
            if let Ok(payment_methods_enabled) = parse_result {
                let payment_method = payment_methods_enabled.payment_method;
                let payment_method_types = match payment_methods_enabled.payment_method_types {
                    Some(payment_method_types) => payment_method_types,
                    None => continue,
                };
                let connector = mca.connector_name.clone();
                let payout_filter = payout_filter_config.0.get(&connector);
                for request_payout_method_type in &payment_method_types {
                    let currency_country_filter = check_currency_country_filters(
                        payout_filter,
                        request_payout_method_type,
                        payout.destination_currency,
                        address
                            .as_ref()
                            .and_then(|address| address.country)
                            .as_ref(),
                    )?;
                    if currency_country_filter.unwrap_or(true) {
                        match payment_method {
                            common_enums::PaymentMethod::Card => {
                                card_hash_set
                                    .insert(request_payout_method_type.payment_method_type);
                                payment_method_list_hm
                                    .insert(payment_method, card_hash_set.clone());
                            }
                            common_enums::PaymentMethod::Wallet => {
                                wallet_hash_set
                                    .insert(request_payout_method_type.payment_method_type);
                                payment_method_list_hm
                                    .insert(payment_method, wallet_hash_set.clone());
                            }
                            common_enums::PaymentMethod::BankTransfer => {
                                bank_transfer_hash_set
                                    .insert(request_payout_method_type.payment_method_type);
                                payment_method_list_hm
                                    .insert(payment_method, bank_transfer_hash_set.clone());
                            }
                            common_enums::PaymentMethod::BankRedirect => {
                                bank_redirect_hash_set
                                    .insert(request_payout_method_type.payment_method_type);
                                payment_method_list_hm
                                    .insert(payment_method, bank_redirect_hash_set.clone());
                            }
                            common_enums::PaymentMethod::CardRedirect
                            | common_enums::PaymentMethod::PayLater
                            | common_enums::PaymentMethod::Crypto
                            | common_enums::PaymentMethod::BankDebit
                            | common_enums::PaymentMethod::Reward
                            | common_enums::PaymentMethod::RealTimePayment
                            | common_enums::PaymentMethod::MobilePayment
                            | common_enums::PaymentMethod::Upi
                            | common_enums::PaymentMethod::Voucher
                            | common_enums::PaymentMethod::OpenBanking
                            | common_enums::PaymentMethod::GiftCard => continue,
                        }
                    }
                }
            }
        }
    }
    for (payment_method, payment_method_types) in payment_method_list_hm {
        if !payment_method_types.is_empty() {
            let enabled_payment_method = link_utils::EnabledPaymentMethod {
                payment_method,
                payment_method_types,
            };
            response.push(enabled_payment_method);
        }
    }
    Ok(response)
}

pub fn check_currency_country_filters(
    payout_method_filter: Option<&PaymentMethodFilters>,
    request_payout_method_type: &api_models::payment_methods::RequestPaymentMethodTypes,
    currency: common_enums::Currency,
    country: Option<&common_enums::CountryAlpha2>,
) -> errors::RouterResult<Option<bool>> {
    if matches!(
        request_payout_method_type.payment_method_type,
        common_enums::PaymentMethodType::Credit | common_enums::PaymentMethodType::Debit
    ) {
        Ok(Some(true))
    } else {
        let payout_method_type_filter =
            payout_method_filter.and_then(|payout_method_filter: &PaymentMethodFilters| {
                payout_method_filter
                    .0
                    .get(&PaymentMethodFilterKey::PaymentMethodType(
                        request_payout_method_type.payment_method_type,
                    ))
            });
        let country_filter = country.as_ref().and_then(|country| {
            payout_method_type_filter.and_then(|currency_country_filter| {
                currency_country_filter
                    .country
                    .as_ref()
                    .map(|country_hash_set| country_hash_set.contains(country))
            })
        });
        let currency_filter = payout_method_type_filter.and_then(|currency_country_filter| {
            currency_country_filter
                .currency
                .as_ref()
                .map(|currency_hash_set| currency_hash_set.contains(&currency))
        });
        Ok(currency_filter.or(country_filter))
    }
}


// File: crates/router/src/core/connector_validation.rs

use api_models::enums as api_enums;
use common_utils::pii;
use error_stack::ResultExt;
use external_services::http_client::client;
use masking::PeekInterface;
use pm_auth::connector::plaid::transformers::PlaidAuthType;

use crate::{core::errors, types, types::transformers::ForeignTryFrom};

pub struct ConnectorAuthTypeAndMetadataValidation<'a> {
    pub connector_name: &'a api_models::enums::Connector,
    pub auth_type: &'a types::ConnectorAuthType,
    pub connector_meta_data: &'a Option<pii::SecretSerdeValue>,
}

impl ConnectorAuthTypeAndMetadataValidation<'_> {
    pub fn validate_auth_and_metadata_type(
        &self,
    ) -> Result<(), error_stack::Report<errors::ApiErrorResponse>> {
        let connector_auth_type_validation = ConnectorAuthTypeValidation {
            auth_type: self.auth_type,
        };
        connector_auth_type_validation.validate_connector_auth_type()?;
        self.validate_auth_and_metadata_type_with_connector()
            .map_err(|err| match *err.current_context() {
                errors::ConnectorError::InvalidConnectorName => {
                    err.change_context(errors::ApiErrorResponse::InvalidRequestData {
                        message: "The connector name is invalid".to_string(),
                    })
                }
                errors::ConnectorError::InvalidConnectorConfig { config: field_name } => err
                    .change_context(errors::ApiErrorResponse::InvalidRequestData {
                        message: format!("The {field_name} is invalid"),
                    }),
                errors::ConnectorError::FailedToObtainAuthType => {
                    err.change_context(errors::ApiErrorResponse::InvalidRequestData {
                        message: "The auth type is invalid for the connector".to_string(),
                    })
                }
                _ => err.change_context(errors::ApiErrorResponse::InvalidRequestData {
                    message: "The request body is invalid".to_string(),
                }),
            })
    }

    fn validate_auth_and_metadata_type_with_connector(
        &self,
    ) -> Result<(), error_stack::Report<errors::ConnectorError>> {
        use crate::connector::*;

        match self.connector_name {
            api_enums::Connector::Vgs => {
                vgs::transformers::VgsAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Adyenplatform => {
                adyenplatform::transformers::AdyenplatformAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            #[cfg(feature = "dummy_connector")]
            api_enums::Connector::DummyBillingConnector
            | api_enums::Connector::DummyConnector1
            | api_enums::Connector::DummyConnector2
            | api_enums::Connector::DummyConnector3
            | api_enums::Connector::DummyConnector4
            | api_enums::Connector::DummyConnector5
            | api_enums::Connector::DummyConnector6
            | api_enums::Connector::DummyConnector7 => {
                hyperswitch_connectors::connectors::dummyconnector::transformers::DummyConnectorAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Aci => {
                aci::transformers::AciAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Adyen => {
                adyen::transformers::AdyenAuthType::try_from(self.auth_type)?;
                adyen::transformers::AdyenConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Affirm => {
                affirm::transformers::AffirmAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Airwallex => {
                airwallex::transformers::AirwallexAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Amazonpay => {
                amazonpay::transformers::AmazonpayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Archipel => {
                archipel::transformers::ArchipelAuthType::try_from(self.auth_type)?;
                archipel::transformers::ArchipelConfigData::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Authipay => {
                authipay::transformers::AuthipayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Authorizedotnet => {
                authorizedotnet::transformers::AuthorizedotnetAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Bankofamerica => {
                bankofamerica::transformers::BankOfAmericaAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Barclaycard => {
                barclaycard::transformers::BarclaycardAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Billwerk => {
                billwerk::transformers::BillwerkAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Bitpay => {
                bitpay::transformers::BitpayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_models::connector_enums::Connector::Zift => {
                zift::transformers::ZiftAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Bambora => {
                bambora::transformers::BamboraAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Bamboraapac => {
                bamboraapac::transformers::BamboraapacAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Boku => {
                boku::transformers::BokuAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Bluesnap => {
                bluesnap::transformers::BluesnapAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Blackhawknetwork => {
                blackhawknetwork::transformers::BlackhawknetworkAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Calida => {
                calida::transformers::CalidaAuthType::try_from(self.auth_type)?;
                calida::transformers::CalidaMetadataObject::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Braintree => {
                braintree::transformers::BraintreeAuthType::try_from(self.auth_type)?;
                braintree::transformers::BraintreeMeta::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Breadpay => {
                breadpay::transformers::BreadpayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Cardinal => Ok(()),
            api_enums::Connector::Cashtocode => {
                cashtocode::transformers::CashtocodeAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Chargebee => {
                chargebee::transformers::ChargebeeAuthType::try_from(self.auth_type)?;
                chargebee::transformers::ChargebeeMetadata::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Celero => {
                celero::transformers::CeleroAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Checkbook => {
                checkbook::transformers::CheckbookAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Checkout => {
                checkout::transformers::CheckoutAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Coinbase => {
                coinbase::transformers::CoinbaseAuthType::try_from(self.auth_type)?;
                coinbase::transformers::CoinbaseConnectorMeta::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Coingate => {
                coingate::transformers::CoingateAuthType::try_from(self.auth_type)?;
                coingate::transformers::CoingateConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Cryptopay => {
                cryptopay::transformers::CryptopayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::CtpMastercard => Ok(()),
            api_enums::Connector::Custombilling => Ok(()),
            api_enums::Connector::CtpVisa => Ok(()),
            api_enums::Connector::Cybersource => {
                cybersource::transformers::CybersourceAuthType::try_from(self.auth_type)?;
                cybersource::transformers::CybersourceConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Datatrans => {
                datatrans::transformers::DatatransAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Deutschebank => {
                deutschebank::transformers::DeutschebankAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Digitalvirgo => {
                digitalvirgo::transformers::DigitalvirgoAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Dlocal => {
                dlocal::transformers::DlocalAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Dwolla => {
                dwolla::transformers::DwollaAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Ebanx => {
                ebanx::transformers::EbanxAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Elavon => {
                elavon::transformers::ElavonAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Facilitapay => {
                facilitapay::transformers::FacilitapayAuthType::try_from(self.auth_type)?;
                facilitapay::transformers::FacilitapayConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Fiserv => {
                fiserv::transformers::FiservAuthType::try_from(self.auth_type)?;
                fiserv::transformers::FiservSessionObject::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Fiservemea => {
                fiservemea::transformers::FiservemeaAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Fiuu => {
                fiuu::transformers::FiuuAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Flexiti => {
                flexiti::transformers::FlexitiAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Forte => {
                forte::transformers::ForteAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Getnet => {
                getnet::transformers::GetnetAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Gigadat => {
                gigadat::transformers::GigadatAuthType::try_from(self.auth_type)?;
                gigadat::transformers::GigadatConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Globalpay => {
                globalpay::transformers::GlobalpayAuthType::try_from(self.auth_type)?;
                globalpay::transformers::GlobalPayMeta::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Globepay => {
                globepay::transformers::GlobepayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Gocardless => {
                gocardless::transformers::GocardlessAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Gpayments => {
                gpayments::transformers::GpaymentsAuthType::try_from(self.auth_type)?;
                gpayments::transformers::GpaymentsMetaData::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Hipay => {
                hipay::transformers::HipayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Helcim => {
                helcim::transformers::HelcimAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::HyperswitchVault => {
                hyperswitch_vault::transformers::HyperswitchVaultAuthType::try_from(
                    self.auth_type,
                )?;
                Ok(())
            }
            api_enums::Connector::Iatapay => {
                iatapay::transformers::IatapayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Inespay => {
                inespay::transformers::InespayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Itaubank => {
                itaubank::transformers::ItaubankAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Jpmorgan => {
                jpmorgan::transformers::JpmorganAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Juspaythreedsserver => Ok(()),
            api_enums::Connector::Klarna => {
                klarna::transformers::KlarnaAuthType::try_from(self.auth_type)?;
                klarna::transformers::KlarnaConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Loonio => {
                loonio::transformers::LoonioAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Mifinity => {
                mifinity::transformers::MifinityAuthType::try_from(self.auth_type)?;
                mifinity::transformers::MifinityConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Mollie => {
                mollie::transformers::MollieAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Moneris => {
                moneris::transformers::MonerisAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Multisafepay => {
                multisafepay::transformers::MultisafepayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Netcetera => {
                netcetera::transformers::NetceteraAuthType::try_from(self.auth_type)?;
                netcetera::transformers::NetceteraMetaData::try_from(self.connector_meta_data)?;
                Ok(())
            }
            api_enums::Connector::Nexinets => {
                nexinets::transformers::NexinetsAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Nexixpay => {
                nexixpay::transformers::NexixpayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Nmi => {
                nmi::transformers::NmiAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Nomupay => {
                nomupay::transformers::NomupayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Noon => {
                noon::transformers::NoonAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Nordea => {
                nordea::transformers::NordeaAuthType::try_from(self.auth_type)?;
                nordea::transformers::NordeaConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Novalnet => {
                novalnet::transformers::NovalnetAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Nuvei => {
                nuvei::transformers::NuveiAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Opennode => {
                opennode::transformers::OpennodeAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Paybox => {
                paybox::transformers::PayboxAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Payload => {
                payload::transformers::PayloadAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Payjustnow => {
                payjustnow::transformers::PayjustnowAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Payme => {
                payme::transformers::PaymeAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Paypal => {
                paypal::transformers::PaypalAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Paysafe => {
                paysafe::transformers::PaysafeAuthType::try_from(self.auth_type)?;
                paysafe::transformers::PaysafeConnectorMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Payone => {
                payone::transformers::PayoneAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Paystack => {
                paystack::transformers::PaystackAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Payu => {
                payu::transformers::PayuAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Peachpayments => {
                peachpayments::transformers::PeachpaymentsAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Placetopay => {
                placetopay::transformers::PlacetopayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Powertranz => {
                powertranz::transformers::PowertranzAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Prophetpay => {
                prophetpay::transformers::ProphetpayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Rapyd => {
                rapyd::transformers::RapydAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Razorpay => {
                razorpay::transformers::RazorpayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Recurly => {
                recurly::transformers::RecurlyAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Redsys => {
                redsys::transformers::RedsysAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Santander => {
                santander::transformers::SantanderAuthType::try_from(self.auth_type)?;
                santander::transformers::SantanderMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Shift4 => {
                shift4::transformers::Shift4AuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Silverflow => {
                silverflow::transformers::SilverflowAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Square => {
                square::transformers::SquareAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Stax => {
                stax::transformers::StaxAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Taxjar => {
                taxjar::transformers::TaxjarAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Stripe => {
                stripe::transformers::StripeAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Stripebilling => {
                stripebilling::transformers::StripebillingAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Tesouro => {
                tesouro::transformers::TesouroAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Trustpay => {
                trustpay::transformers::TrustpayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Trustpayments => {
                trustpayments::transformers::TrustpaymentsAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Tokenex => {
                tokenex::transformers::TokenexAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Tokenio => {
                tokenio::transformers::TokenioAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Tsys => {
                tsys::transformers::TsysAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Volt => {
                volt::transformers::VoltAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Wellsfargo => {
                wellsfargo::transformers::WellsfargoAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Wise => {
                wise::transformers::WiseAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Worldline => {
                worldline::transformers::WorldlineAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Worldpay => {
                worldpay::transformers::WorldpayAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Worldpayvantiv => {
                worldpayvantiv::transformers::WorldpayvantivAuthType::try_from(self.auth_type)?;
                worldpayvantiv::transformers::WorldpayvantivMetadataObject::try_from(
                    self.connector_meta_data,
                )?;
                Ok(())
            }
            api_enums::Connector::Worldpayxml => {
                worldpayxml::transformers::WorldpayxmlAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Xendit => {
                xendit::transformers::XenditAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Zen => {
                zen::transformers::ZenAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Zsl => {
                zsl::transformers::ZslAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Signifyd => {
                signifyd::transformers::SignifydAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Riskified => {
                riskified::transformers::RiskifiedAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Plaid => {
                PlaidAuthType::foreign_try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Threedsecureio => {
                threedsecureio::transformers::ThreedsecureioAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Phonepe => {
                phonepe::transformers::PhonepeAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Paytm => {
                paytm::transformers::PaytmAuthType::try_from(self.auth_type)?;
                Ok(())
            }
            api_enums::Connector::Finix => {
                finix::transformers::FinixAuthType::try_from(self.auth_type)?;
                Ok(())
            }
        }
    }
}

struct ConnectorAuthTypeValidation<'a> {
    auth_type: &'a types::ConnectorAuthType,
}

impl ConnectorAuthTypeValidation<'_> {
    fn validate_connector_auth_type(
        &self,
    ) -> Result<(), error_stack::Report<errors::ApiErrorResponse>> {
        let validate_non_empty_field = |field_value: &str, field_name: &str| {
            if field_value.trim().is_empty() {
                Err(errors::ApiErrorResponse::InvalidDataFormat {
                    field_name: format!("connector_account_details.{field_name}"),
                    expected_format: "a non empty String".to_string(),
                }
                .into())
            } else {
                Ok(())
            }
        };
        match self.auth_type {
            hyperswitch_domain_models::router_data::ConnectorAuthType::TemporaryAuth => Ok(()),
            hyperswitch_domain_models::router_data::ConnectorAuthType::HeaderKey { api_key } => {
                validate_non_empty_field(api_key.peek(), "api_key")
            }
            hyperswitch_domain_models::router_data::ConnectorAuthType::BodyKey {
                api_key,
                key1,
            } => {
                validate_non_empty_field(api_key.peek(), "api_key")?;
                validate_non_empty_field(key1.peek(), "key1")
            }
            hyperswitch_domain_models::router_data::ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => {
                validate_non_empty_field(api_key.peek(), "api_key")?;
                validate_non_empty_field(key1.peek(), "key1")?;
                validate_non_empty_field(api_secret.peek(), "api_secret")
            }
            hyperswitch_domain_models::router_data::ConnectorAuthType::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            } => {
                validate_non_empty_field(api_key.peek(), "api_key")?;
                validate_non_empty_field(key1.peek(), "key1")?;
                validate_non_empty_field(api_secret.peek(), "api_secret")?;
                validate_non_empty_field(key2.peek(), "key2")
            }
            hyperswitch_domain_models::router_data::ConnectorAuthType::CurrencyAuthKey {
                auth_key_map,
            } => {
                if auth_key_map.is_empty() {
                    Err(errors::ApiErrorResponse::InvalidDataFormat {
                        field_name: "connector_account_details.auth_key_map".to_string(),
                        expected_format: "a non empty map".to_string(),
                    }
                    .into())
                } else {
                    Ok(())
                }
            }
            hyperswitch_domain_models::router_data::ConnectorAuthType::CertificateAuth {
                certificate,
                private_key,
            } => {
                client::create_identity_from_certificate_and_key(
                    certificate.to_owned(),
                    private_key.to_owned(),
                )
                .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                    field_name:
                        "connector_account_details.certificate or connector_account_details.private_key"
                            .to_string(),
                    expected_format:
                        "a valid base64 encoded string of PEM encoded Certificate and Private Key"
                            .to_string(),
                })?;
                Ok(())
            }
            hyperswitch_domain_models::router_data::ConnectorAuthType::NoKey => Ok(()),
        }
    }
}


// File: crates/router/src/core/verification.rs

pub mod utils;
use api_models::verifications::{self, ApplepayMerchantResponse};
use common_utils::{errors::CustomResult, request::RequestContent};
use error_stack::ResultExt;
use masking::ExposeInterface;

use crate::{core::errors, headers, logger, routes::SessionState, services};

const APPLEPAY_INTERNAL_MERCHANT_NAME: &str = "Applepay_merchant";

pub async fn verify_merchant_creds_for_applepay(
    state: SessionState,
    body: verifications::ApplepayMerchantVerificationRequest,
    merchant_id: common_utils::id_type::MerchantId,
    profile_id: Option<common_utils::id_type::ProfileId>,
) -> CustomResult<services::ApplicationResponse<ApplepayMerchantResponse>, errors::ApiErrorResponse>
{
    let applepay_merchant_configs = state.conf.applepay_merchant_configs.get_inner();

    let applepay_internal_merchant_identifier = applepay_merchant_configs
        .common_merchant_identifier
        .clone()
        .expose();
    let cert_data = applepay_merchant_configs.merchant_cert.clone();
    let key_data = applepay_merchant_configs.merchant_cert_key.clone();
    let applepay_endpoint = &applepay_merchant_configs.applepay_endpoint;

    let request_body = verifications::ApplepayMerchantVerificationConfigs {
        domain_names: body.domain_names.clone(),
        encrypt_to: applepay_internal_merchant_identifier.clone(),
        partner_internal_merchant_identifier: applepay_internal_merchant_identifier,
        partner_merchant_name: APPLEPAY_INTERNAL_MERCHANT_NAME.to_string(),
    };

    let apple_pay_merch_verification_req = services::RequestBuilder::new()
        .method(services::Method::Post)
        .url(applepay_endpoint)
        .attach_default_headers()
        .headers(vec![(
            headers::CONTENT_TYPE.to_string(),
            "application/json".to_string().into(),
        )])
        .set_body(RequestContent::Json(Box::new(request_body)))
        .add_certificate(Some(cert_data))
        .add_certificate_key(Some(key_data))
        .build();

    let response = services::call_connector_api(
        &state,
        apple_pay_merch_verification_req,
        "verify_merchant_creds_for_applepay",
    )
    .await;
    utils::log_applepay_verification_response_if_error(&response);

    let applepay_response =
        response.change_context(errors::ApiErrorResponse::InternalServerError)?;

    // Error is already logged
    match applepay_response {
        Ok(_) => {
            utils::check_existence_and_add_domain_to_db(
                &state,
                merchant_id,
                profile_id,
                body.merchant_connector_account_id.clone(),
                body.domain_names.clone(),
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)?;
            Ok(services::api::ApplicationResponse::Json(
                ApplepayMerchantResponse {
                    status_message: "Applepay verification Completed".to_string(),
                },
            ))
        }
        Err(error) => {
            logger::error!(?error);
            Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "Applepay verification Failed".to_string(),
            }
            .into())
        }
    }
}

pub async fn get_verified_apple_domains_with_mid_mca_id(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId,
) -> CustomResult<
    services::ApplicationResponse<verifications::ApplepayVerifiedDomainsResponse>,
    errors::ApiErrorResponse,
> {
    let db = state.store.as_ref();
    let key_store = db
        .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into())
        .await
        .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    #[cfg(feature = "v1")]
    let verified_domains = db
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            &merchant_id,
            &merchant_connector_id,
            &key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?
        .applepay_verified_domains
        .unwrap_or_default();

    #[cfg(feature = "v2")]
    let verified_domains = db
        .find_merchant_connector_account_by_id(&merchant_connector_id, &key_store)
        .await
        .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?
        .applepay_verified_domains
        .unwrap_or_default();

    Ok(services::api::ApplicationResponse::Json(
        verifications::ApplepayVerifiedDomainsResponse { verified_domains },
    ))
}