// File: crates/hyperswitch_domain_models/src/merchant_account.rs

use common_utils::{
    crypto::{OptionalEncryptableName, OptionalEncryptableValue},
    date_time,
    encryption::Encryption,
    errors::{CustomResult, ValidationError},
    ext_traits::ValueExt,
    pii, type_name,
    types::keymanager::{self},
};
use diesel_models::{
    enums::MerchantStorageScheme, merchant_account::MerchantAccountUpdateInternal,
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use router_env::logger;

use crate::{
    behaviour::Conversion,
    merchant_key_store,
    type_encryption::{crypto_operation, AsyncLift, CryptoOperation},
};

#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Serialize)]
pub struct MerchantAccount {
    merchant_id: common_utils::id_type::MerchantId,
    pub return_url: Option<String>,
    pub enable_payment_response_hash: bool,
    pub payment_response_hash_key: Option<String>,
    pub redirect_to_merchant_with_http_post: bool,
    pub merchant_name: OptionalEncryptableName,
    pub merchant_details: OptionalEncryptableValue,
    pub webhook_details: Option<diesel_models::business_profile::WebhookDetails>,
    pub sub_merchants_enabled: Option<bool>,
    pub parent_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub publishable_key: String,
    pub storage_scheme: MerchantStorageScheme,
    pub locker_id: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub routing_algorithm: Option<serde_json::Value>,
    pub primary_business_details: serde_json::Value,
    pub frm_routing_algorithm: Option<serde_json::Value>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub intent_fulfillment_time: Option<i64>,
    pub payout_routing_algorithm: Option<serde_json::Value>,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub is_recon_enabled: bool,
    pub default_profile: Option<common_utils::id_type::ProfileId>,
    pub recon_status: diesel_models::enums::ReconStatus,
    pub payment_link_config: Option<serde_json::Value>,
    pub pm_collect_link_config: Option<serde_json::Value>,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

#[cfg(feature = "v1")]
#[derive(Clone)]
/// Set the private fields of merchant account
pub struct MerchantAccountSetter {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub return_url: Option<String>,
    pub enable_payment_response_hash: bool,
    pub payment_response_hash_key: Option<String>,
    pub redirect_to_merchant_with_http_post: bool,
    pub merchant_name: OptionalEncryptableName,
    pub merchant_details: OptionalEncryptableValue,
    pub webhook_details: Option<diesel_models::business_profile::WebhookDetails>,
    pub sub_merchants_enabled: Option<bool>,
    pub parent_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub publishable_key: String,
    pub storage_scheme: MerchantStorageScheme,
    pub locker_id: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub routing_algorithm: Option<serde_json::Value>,
    pub primary_business_details: serde_json::Value,
    pub frm_routing_algorithm: Option<serde_json::Value>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub intent_fulfillment_time: Option<i64>,
    pub payout_routing_algorithm: Option<serde_json::Value>,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub is_recon_enabled: bool,
    pub default_profile: Option<common_utils::id_type::ProfileId>,
    pub recon_status: diesel_models::enums::ReconStatus,
    pub payment_link_config: Option<serde_json::Value>,
    pub pm_collect_link_config: Option<serde_json::Value>,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

#[cfg(feature = "v1")]
impl From<MerchantAccountSetter> for MerchantAccount {
    fn from(item: MerchantAccountSetter) -> Self {
        Self {
            merchant_id: item.merchant_id,
            return_url: item.return_url,
            enable_payment_response_hash: item.enable_payment_response_hash,
            payment_response_hash_key: item.payment_response_hash_key,
            redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post,
            merchant_name: item.merchant_name,
            merchant_details: item.merchant_details,
            webhook_details: item.webhook_details,
            sub_merchants_enabled: item.sub_merchants_enabled,
            parent_merchant_id: item.parent_merchant_id,
            publishable_key: item.publishable_key,
            storage_scheme: item.storage_scheme,
            locker_id: item.locker_id,
            metadata: item.metadata,
            routing_algorithm: item.routing_algorithm,
            primary_business_details: item.primary_business_details,
            frm_routing_algorithm: item.frm_routing_algorithm,
            created_at: item.created_at,
            modified_at: item.modified_at,
            intent_fulfillment_time: item.intent_fulfillment_time,
            payout_routing_algorithm: item.payout_routing_algorithm,
            organization_id: item.organization_id,
            is_recon_enabled: item.is_recon_enabled,
            default_profile: item.default_profile,
            recon_status: item.recon_status,
            payment_link_config: item.payment_link_config,
            pm_collect_link_config: item.pm_collect_link_config,
            version: item.version,
            is_platform_account: item.is_platform_account,
            product_type: item.product_type,
            merchant_account_type: item.merchant_account_type,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone)]
/// Set the private fields of merchant account
pub struct MerchantAccountSetter {
    pub id: common_utils::id_type::MerchantId,
    pub merchant_name: OptionalEncryptableName,
    pub merchant_details: OptionalEncryptableValue,
    pub publishable_key: String,
    pub storage_scheme: MerchantStorageScheme,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub recon_status: diesel_models::enums::ReconStatus,
    pub is_platform_account: bool,
    pub version: common_enums::ApiVersion,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

#[cfg(feature = "v2")]
impl From<MerchantAccountSetter> for MerchantAccount {
    fn from(item: MerchantAccountSetter) -> Self {
        let MerchantAccountSetter {
            id,
            merchant_name,
            merchant_details,
            publishable_key,
            storage_scheme,
            metadata,
            created_at,
            modified_at,
            organization_id,
            recon_status,
            is_platform_account,
            version,
            product_type,
            merchant_account_type,
        } = item;
        Self {
            id,
            merchant_name,
            merchant_details,
            publishable_key,
            storage_scheme,
            metadata,
            created_at,
            modified_at,
            organization_id,
            recon_status,
            is_platform_account,
            version,
            product_type,
            merchant_account_type,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize)]
pub struct MerchantAccount {
    id: common_utils::id_type::MerchantId,
    pub merchant_name: OptionalEncryptableName,
    pub merchant_details: OptionalEncryptableValue,
    pub publishable_key: String,
    pub storage_scheme: MerchantStorageScheme,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub recon_status: diesel_models::enums::ReconStatus,
    pub is_platform_account: bool,
    pub version: common_enums::ApiVersion,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

impl MerchantAccount {
    #[cfg(feature = "v1")]
    /// Get the unique identifier of MerchantAccount
    pub fn get_id(&self) -> &common_utils::id_type::MerchantId {
        &self.merchant_id
    }

    #[cfg(feature = "v2")]
    /// Get the unique identifier of MerchantAccount
    pub fn get_id(&self) -> &common_utils::id_type::MerchantId {
        &self.id
    }

    /// Get the organization_id from MerchantAccount
    pub fn get_org_id(&self) -> &common_utils::id_type::OrganizationId {
        &self.organization_id
    }

    /// Get the merchant_details from MerchantAccount
    pub fn get_merchant_details(&self) -> &OptionalEncryptableValue {
        &self.merchant_details
    }

    /// Extract merchant_tax_registration_id from merchant_details
    pub fn get_merchant_tax_registration_id(&self) -> Option<Secret<String>> {
        self.merchant_details.as_ref().and_then(|details| {
            details
                .get_inner()
                .peek()
                .get("merchant_tax_registration_id")
                .and_then(|id| id.as_str().map(|s| Secret::new(s.to_string())))
        })
    }

    /// Check whether the merchant account is a platform account
    pub fn is_platform_account(&self) -> bool {
        matches!(
            self.merchant_account_type,
            common_enums::MerchantAccountType::Platform
        )
    }
}

#[cfg(feature = "v1")]
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
pub enum MerchantAccountUpdate {
    Update {
        merchant_name: OptionalEncryptableName,
        merchant_details: OptionalEncryptableValue,
        return_url: Option<String>,
        webhook_details: Option<diesel_models::business_profile::WebhookDetails>,
        sub_merchants_enabled: Option<bool>,
        parent_merchant_id: Option<common_utils::id_type::MerchantId>,
        enable_payment_response_hash: Option<bool>,
        payment_response_hash_key: Option<String>,
        redirect_to_merchant_with_http_post: Option<bool>,
        publishable_key: Option<String>,
        locker_id: Option<String>,
        metadata: Option<pii::SecretSerdeValue>,
        routing_algorithm: Option<serde_json::Value>,
        primary_business_details: Option<serde_json::Value>,
        intent_fulfillment_time: Option<i64>,
        frm_routing_algorithm: Option<serde_json::Value>,
        payout_routing_algorithm: Option<serde_json::Value>,
        default_profile: Option<Option<common_utils::id_type::ProfileId>>,
        payment_link_config: Option<serde_json::Value>,
        pm_collect_link_config: Option<serde_json::Value>,
    },
    StorageSchemeUpdate {
        storage_scheme: MerchantStorageScheme,
    },
    ReconUpdate {
        recon_status: diesel_models::enums::ReconStatus,
    },
    UnsetDefaultProfile,
    ModifiedAtUpdate,
    ToPlatformAccount,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone)]
pub enum MerchantAccountUpdate {
    Update {
        merchant_name: OptionalEncryptableName,
        merchant_details: OptionalEncryptableValue,
        publishable_key: Option<String>,
        metadata: Option<Box<pii::SecretSerdeValue>>,
    },
    StorageSchemeUpdate {
        storage_scheme: MerchantStorageScheme,
    },
    ReconUpdate {
        recon_status: diesel_models::enums::ReconStatus,
    },
    ModifiedAtUpdate,
    ToPlatformAccount,
}

#[cfg(feature = "v1")]
impl From<MerchantAccountUpdate> for MerchantAccountUpdateInternal {
    fn from(merchant_account_update: MerchantAccountUpdate) -> Self {
        let now = date_time::now();

        match merchant_account_update {
            MerchantAccountUpdate::Update {
                merchant_name,
                merchant_details,
                webhook_details,
                return_url,
                routing_algorithm,
                sub_merchants_enabled,
                parent_merchant_id,
                enable_payment_response_hash,
                payment_response_hash_key,
                redirect_to_merchant_with_http_post,
                publishable_key,
                locker_id,
                metadata,
                primary_business_details,
                intent_fulfillment_time,
                frm_routing_algorithm,
                payout_routing_algorithm,
                default_profile,
                payment_link_config,
                pm_collect_link_config,
            } => Self {
                merchant_name: merchant_name.map(Encryption::from),
                merchant_details: merchant_details.map(Encryption::from),
                frm_routing_algorithm,
                webhook_details,
                routing_algorithm,
                sub_merchants_enabled,
                parent_merchant_id,
                return_url,
                enable_payment_response_hash,
                payment_response_hash_key,
                redirect_to_merchant_with_http_post,
                publishable_key,
                locker_id,
                metadata,
                primary_business_details,
                modified_at: now,
                intent_fulfillment_time,
                payout_routing_algorithm,
                default_profile,
                payment_link_config,
                pm_collect_link_config,
                storage_scheme: None,
                organization_id: None,
                is_recon_enabled: None,
                recon_status: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self {
                storage_scheme: Some(storage_scheme),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                default_profile: None,
                recon_status: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ReconUpdate { recon_status } => Self {
                recon_status: Some(recon_status),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                storage_scheme: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                default_profile: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::UnsetDefaultProfile => Self {
                default_profile: Some(None),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                storage_scheme: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                recon_status: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ModifiedAtUpdate => Self {
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                storage_scheme: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                default_profile: None,
                recon_status: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ToPlatformAccount => Self {
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                storage_scheme: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                default_profile: None,
                recon_status: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: Some(true),
                product_type: None,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl From<MerchantAccountUpdate> for MerchantAccountUpdateInternal {
    fn from(merchant_account_update: MerchantAccountUpdate) -> Self {
        let now = date_time::now();

        match merchant_account_update {
            MerchantAccountUpdate::Update {
                merchant_name,
                merchant_details,
                publishable_key,
                metadata,
            } => Self {
                merchant_name: merchant_name.map(Encryption::from),
                merchant_details: merchant_details.map(Encryption::from),
                publishable_key,
                metadata: metadata.map(|metadata| *metadata),
                modified_at: now,
                storage_scheme: None,
                organization_id: None,
                recon_status: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self {
                storage_scheme: Some(storage_scheme),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                publishable_key: None,
                metadata: None,
                organization_id: None,
                recon_status: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ReconUpdate { recon_status } => Self {
                recon_status: Some(recon_status),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                publishable_key: None,
                storage_scheme: None,
                metadata: None,
                organization_id: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ModifiedAtUpdate => Self {
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                publishable_key: None,
                storage_scheme: None,
                metadata: None,
                organization_id: None,
                recon_status: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ToPlatformAccount => Self {
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                publishable_key: None,
                storage_scheme: None,
                metadata: None,
                organization_id: None,
                recon_status: None,
                is_platform_account: Some(true),
                product_type: None,
            },
        }
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl Conversion for MerchantAccount {
    type DstType = diesel_models::merchant_account::MerchantAccount;
    type NewDstType = diesel_models::merchant_account::MerchantAccountNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        let id = self.get_id().to_owned();

        let setter = diesel_models::merchant_account::MerchantAccountSetter {
            id,
            merchant_name: self.merchant_name.map(|name| name.into()),
            merchant_details: self.merchant_details.map(|details| details.into()),
            publishable_key: Some(self.publishable_key),
            storage_scheme: self.storage_scheme,
            metadata: self.metadata,
            created_at: self.created_at,
            modified_at: self.modified_at,
            organization_id: self.organization_id,
            recon_status: self.recon_status,
            version: common_types::consts::API_VERSION,
            is_platform_account: self.is_platform_account,
            product_type: self.product_type,
            merchant_account_type: self.merchant_account_type,
        };

        Ok(diesel_models::MerchantAccount::from(setter))
    }

    async fn convert_back(
        state: &keymanager::KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        let id = item.get_id().to_owned();
        let publishable_key =
            item.publishable_key
                .ok_or(ValidationError::MissingRequiredField {
                    field_name: "publishable_key".to_string(),
                })?;

        async {
            Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
                id,
                merchant_name: item
                    .merchant_name
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                merchant_details: item
                    .merchant_details
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                publishable_key,
                storage_scheme: item.storage_scheme,
                metadata: item.metadata,
                created_at: item.created_at,
                modified_at: item.modified_at,
                organization_id: item.organization_id,
                recon_status: item.recon_status,
                is_platform_account: item.is_platform_account,
                version: item.version,
                product_type: item.product_type,
                merchant_account_type: item.merchant_account_type.unwrap_or_default(),
            })
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting merchant data".to_string(),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        let now = date_time::now();
        Ok(diesel_models::merchant_account::MerchantAccountNew {
            id: self.id,
            merchant_name: self.merchant_name.map(Encryption::from),
            merchant_details: self.merchant_details.map(Encryption::from),
            publishable_key: Some(self.publishable_key),
            metadata: self.metadata,
            created_at: now,
            modified_at: now,
            organization_id: self.organization_id,
            recon_status: self.recon_status,
            version: common_types::consts::API_VERSION,
            is_platform_account: self.is_platform_account,
            product_type: self
                .product_type
                .or(Some(common_enums::MerchantProductType::Orchestration)),
            merchant_account_type: self.merchant_account_type,
        })
    }
}

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl Conversion for MerchantAccount {
    type DstType = diesel_models::merchant_account::MerchantAccount;
    type NewDstType = diesel_models::merchant_account::MerchantAccountNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        let setter = diesel_models::merchant_account::MerchantAccountSetter {
            merchant_id: self.merchant_id,
            return_url: self.return_url,
            enable_payment_response_hash: self.enable_payment_response_hash,
            payment_response_hash_key: self.payment_response_hash_key,
            redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
            merchant_name: self.merchant_name.map(|name| name.into()),
            merchant_details: self.merchant_details.map(|details| details.into()),
            webhook_details: self.webhook_details,
            sub_merchants_enabled: self.sub_merchants_enabled,
            parent_merchant_id: self.parent_merchant_id,
            publishable_key: Some(self.publishable_key),
            storage_scheme: self.storage_scheme,
            locker_id: self.locker_id,
            metadata: self.metadata,
            routing_algorithm: self.routing_algorithm,
            primary_business_details: self.primary_business_details,
            created_at: self.created_at,
            modified_at: self.modified_at,
            intent_fulfillment_time: self.intent_fulfillment_time,
            frm_routing_algorithm: self.frm_routing_algorithm,
            payout_routing_algorithm: self.payout_routing_algorithm,
            organization_id: self.organization_id,
            is_recon_enabled: self.is_recon_enabled,
            default_profile: self.default_profile,
            recon_status: self.recon_status,
            payment_link_config: self.payment_link_config,
            pm_collect_link_config: self.pm_collect_link_config,
            version: self.version,
            is_platform_account: self.is_platform_account,
            product_type: self.product_type,
            merchant_account_type: self.merchant_account_type,
        };

        Ok(diesel_models::MerchantAccount::from(setter))
    }

    async fn convert_back(
        state: &keymanager::KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        let merchant_id = item.get_id().to_owned();
        let publishable_key =
            item.publishable_key
                .ok_or(ValidationError::MissingRequiredField {
                    field_name: "publishable_key".to_string(),
                })?;

        async {
            Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
                merchant_id,
                return_url: item.return_url,
                enable_payment_response_hash: item.enable_payment_response_hash,
                payment_response_hash_key: item.payment_response_hash_key,
                redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post,
                merchant_name: item
                    .merchant_name
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                merchant_details: item
                    .merchant_details
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                webhook_details: item.webhook_details,
                sub_merchants_enabled: item.sub_merchants_enabled,
                parent_merchant_id: item.parent_merchant_id,
                publishable_key,
                storage_scheme: item.storage_scheme,
                locker_id: item.locker_id,
                metadata: item.metadata,
                routing_algorithm: item.routing_algorithm,
                frm_routing_algorithm: item.frm_routing_algorithm,
                primary_business_details: item.primary_business_details,
                created_at: item.created_at,
                modified_at: item.modified_at,
                intent_fulfillment_time: item.intent_fulfillment_time,
                payout_routing_algorithm: item.payout_routing_algorithm,
                organization_id: item.organization_id,
                is_recon_enabled: item.is_recon_enabled,
                default_profile: item.default_profile,
                recon_status: item.recon_status,
                payment_link_config: item.payment_link_config,
                pm_collect_link_config: item.pm_collect_link_config,
                version: item.version,
                is_platform_account: item.is_platform_account,
                product_type: item.product_type,
                merchant_account_type: item.merchant_account_type.unwrap_or_default(),
            })
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting merchant data".to_string(),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        let now = date_time::now();
        Ok(diesel_models::merchant_account::MerchantAccountNew {
            id: Some(self.merchant_id.clone()),
            merchant_id: self.merchant_id,
            merchant_name: self.merchant_name.map(Encryption::from),
            merchant_details: self.merchant_details.map(Encryption::from),
            return_url: self.return_url,
            webhook_details: self.webhook_details,
            sub_merchants_enabled: self.sub_merchants_enabled,
            parent_merchant_id: self.parent_merchant_id,
            enable_payment_response_hash: Some(self.enable_payment_response_hash),
            payment_response_hash_key: self.payment_response_hash_key,
            redirect_to_merchant_with_http_post: Some(self.redirect_to_merchant_with_http_post),
            publishable_key: Some(self.publishable_key),
            locker_id: self.locker_id,
            metadata: self.metadata,
            routing_algorithm: self.routing_algorithm,
            primary_business_details: self.primary_business_details,
            created_at: now,
            modified_at: now,
            intent_fulfillment_time: self.intent_fulfillment_time,
            frm_routing_algorithm: self.frm_routing_algorithm,
            payout_routing_algorithm: self.payout_routing_algorithm,
            organization_id: self.organization_id,
            is_recon_enabled: self.is_recon_enabled,
            default_profile: self.default_profile,
            recon_status: self.recon_status,
            payment_link_config: self.payment_link_config,
            pm_collect_link_config: self.pm_collect_link_config,
            version: common_types::consts::API_VERSION,
            is_platform_account: self.is_platform_account,
            product_type: self
                .product_type
                .or(Some(common_enums::MerchantProductType::Orchestration)),
            merchant_account_type: self.merchant_account_type,
        })
    }
}

impl MerchantAccount {
    pub fn get_compatible_connector(&self) -> Option<api_models::enums::Connector> {
        let metadata: Option<api_models::admin::MerchantAccountMetadata> =
            self.metadata.as_ref().and_then(|meta| {
                meta.clone()
                    .parse_value("MerchantAccountMetadata")
                    .map_err(|err| logger::error!("Failed to deserialize {:?}", err))
                    .ok()
            });
        metadata.and_then(|a| a.compatible_connector)
    }
}

#[async_trait::async_trait]
pub trait MerchantAccountInterface
where
    MerchantAccount: Conversion<
        DstType = diesel_models::merchant_account::MerchantAccount,
        NewDstType = diesel_models::merchant_account::MerchantAccountNew,
    >,
{
    type Error;
    async fn insert_merchant(
        &self,
        merchant_account: MerchantAccount,
        merchant_key_store: &merchant_key_store::MerchantKeyStore,
    ) -> CustomResult<MerchantAccount, Self::Error>;

    async fn find_merchant_account_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_key_store: &merchant_key_store::MerchantKeyStore,
    ) -> CustomResult<MerchantAccount, Self::Error>;

    async fn update_all_merchant_account(
        &self,
        merchant_account: MerchantAccountUpdate,
    ) -> CustomResult<usize, Self::Error>;

    async fn update_merchant(
        &self,
        this: MerchantAccount,
        merchant_account: MerchantAccountUpdate,
        merchant_key_store: &merchant_key_store::MerchantKeyStore,
    ) -> CustomResult<MerchantAccount, Self::Error>;

    async fn update_specific_fields_in_merchant(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_account: MerchantAccountUpdate,
        merchant_key_store: &merchant_key_store::MerchantKeyStore,
    ) -> CustomResult<MerchantAccount, Self::Error>;

    async fn find_merchant_account_by_publishable_key(
        &self,
        publishable_key: &str,
    ) -> CustomResult<(MerchantAccount, merchant_key_store::MerchantKeyStore), Self::Error>;

    #[cfg(feature = "olap")]
    async fn list_merchant_accounts_by_organization_id(
        &self,
        organization_id: &common_utils::id_type::OrganizationId,
    ) -> CustomResult<Vec<MerchantAccount>, Self::Error>;

    async fn delete_merchant_account_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, Self::Error>;

    #[cfg(feature = "olap")]
    async fn list_multiple_merchant_accounts(
        &self,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
    ) -> CustomResult<Vec<MerchantAccount>, Self::Error>;

    #[cfg(feature = "olap")]
    async fn list_merchant_and_org_ids(
        &self,
        limit: u32,
        offset: Option<u32>,
    ) -> CustomResult<
        Vec<(
            common_utils::id_type::MerchantId,
            common_utils::id_type::OrganizationId,
        )>,
        Self::Error,
    >;
}


// File: crates/hyperswitch_domain_models/src/router_data_v2.rs

pub mod flow_common_types;

use std::{marker::PhantomData, ops::Deref};

use common_utils::id_type;
#[cfg(feature = "frm")]
pub use flow_common_types::FrmFlowData;
#[cfg(feature = "payouts")]
pub use flow_common_types::PayoutFlowData;
pub use flow_common_types::{
    AccessTokenFlowData, AuthenticationTokenFlowData, DisputesFlowData,
    ExternalAuthenticationFlowData, ExternalVaultProxyFlowData, FilesFlowData,
    MandateRevokeFlowData, PaymentFlowData, RefundFlowData, UasFlowData, VaultConnectorFlowData,
    WebhookSourceVerifyData,
};

use crate::router_data::{ConnectorAuthType, ErrorResponse};

#[derive(Debug, Clone)]
pub struct RouterDataV2<Flow, ResourceCommonData, FlowSpecificRequest, FlowSpecificResponse> {
    pub flow: PhantomData<Flow>,
    pub tenant_id: id_type::TenantId,
    pub resource_common_data: ResourceCommonData,
    pub connector_auth_type: ConnectorAuthType,
    /// Contains flow-specific data required to construct a request and send it to the connector.
    pub request: FlowSpecificRequest,
    /// Contains flow-specific data that the connector responds with.
    pub response: Result<FlowSpecificResponse, ErrorResponse>,
}

impl<Flow, ResourceCommonData, FlowSpecificRequest, FlowSpecificResponse> Deref
    for RouterDataV2<Flow, ResourceCommonData, FlowSpecificRequest, FlowSpecificResponse>
{
    type Target = ResourceCommonData;
    fn deref(&self) -> &Self::Target {
        &self.resource_common_data
    }
}


// File: crates/hyperswitch_domain_models/src/payouts.rs

pub mod payout_attempt;
#[allow(clippy::module_inception)]
pub mod payouts;

use common_enums as storage_enums;
use common_utils::{consts, id_type};
use time::PrimitiveDateTime;

pub enum PayoutFetchConstraints {
    Single { payout_id: id_type::PayoutId },
    List(Box<PayoutListParams>),
}

pub struct PayoutListParams {
    pub offset: u32,
    pub starting_at: Option<PrimitiveDateTime>,
    pub ending_at: Option<PrimitiveDateTime>,
    pub connector: Option<Vec<api_models::enums::PayoutConnectors>>,
    pub currency: Option<Vec<storage_enums::Currency>>,
    pub status: Option<Vec<storage_enums::PayoutStatus>>,
    pub payout_method: Option<Vec<common_enums::PayoutType>>,
    pub profile_id: Option<id_type::ProfileId>,
    pub customer_id: Option<id_type::CustomerId>,
    pub starting_after_id: Option<id_type::PayoutId>,
    pub ending_before_id: Option<id_type::PayoutId>,
    pub entity_type: Option<common_enums::PayoutEntityType>,
    pub limit: Option<u32>,
    pub merchant_order_reference_id: Option<String>,
}

impl From<api_models::payouts::PayoutListConstraints> for PayoutFetchConstraints {
    fn from(value: api_models::payouts::PayoutListConstraints) -> Self {
        Self::List(Box::new(PayoutListParams {
            offset: 0,
            starting_at: value
                .time_range
                .map_or(value.created, |t| Some(t.start_time)),
            ending_at: value.time_range.and_then(|t| t.end_time),
            connector: None,
            currency: None,
            status: None,
            payout_method: None,
            profile_id: None,
            customer_id: value.customer_id,
            starting_after_id: value.starting_after,
            ending_before_id: value.ending_before,
            entity_type: None,
            merchant_order_reference_id: None,
            limit: Some(std::cmp::min(
                value.limit,
                consts::PAYOUTS_LIST_MAX_LIMIT_GET,
            )),
        }))
    }
}

impl From<common_utils::types::TimeRange> for PayoutFetchConstraints {
    fn from(value: common_utils::types::TimeRange) -> Self {
        Self::List(Box::new(PayoutListParams {
            offset: 0,
            starting_at: Some(value.start_time),
            ending_at: value.end_time,
            connector: None,
            currency: None,
            status: None,
            payout_method: None,
            profile_id: None,
            customer_id: None,
            starting_after_id: None,
            ending_before_id: None,
            entity_type: None,
            merchant_order_reference_id: None,
            limit: None,
        }))
    }
}

impl From<api_models::payouts::PayoutListFilterConstraints> for PayoutFetchConstraints {
    fn from(value: api_models::payouts::PayoutListFilterConstraints) -> Self {
        if let Some(payout_id) = value.payout_id {
            Self::Single { payout_id }
        } else {
            Self::List(Box::new(PayoutListParams {
                offset: value.offset.unwrap_or_default(),
                starting_at: value.time_range.map(|t| t.start_time),
                ending_at: value.time_range.and_then(|t| t.end_time),
                connector: value.connector,
                currency: value.currency,
                status: value.status,
                payout_method: value.payout_method,
                profile_id: value.profile_id,
                customer_id: value.customer_id,
                starting_after_id: None,
                ending_before_id: None,
                entity_type: value.entity_type,
                merchant_order_reference_id: value.merchant_order_reference_id,
                limit: Some(std::cmp::min(
                    value.limit,
                    consts::PAYOUTS_LIST_MAX_LIMIT_POST,
                )),
            }))
        }
    }
}


// File: crates/hyperswitch_domain_models/src/cards_info.rs

use common_utils::errors;
use diesel_models::cards_info;

#[async_trait::async_trait]
pub trait CardsInfoInterface {
    type Error;
    async fn get_card_info(
        &self,
        _card_iin: &str,
    ) -> errors::CustomResult<Option<cards_info::CardInfo>, Self::Error>;
    async fn add_card_info(
        &self,
        data: cards_info::CardInfo,
    ) -> errors::CustomResult<cards_info::CardInfo, Self::Error>;
    async fn update_card_info(
        &self,
        card_iin: String,
        data: cards_info::UpdateCardInfo,
    ) -> errors::CustomResult<cards_info::CardInfo, Self::Error>;
}


// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs

#[cfg(feature = "v2")]
use std::collections::HashMap;

use common_utils::{
    crypto::Encryptable,
    date_time,
    encryption::Encryption,
    errors::{CustomResult, ValidationError},
    ext_traits::{StringExt, ValueExt},
    id_type, pii, type_name,
    types::keymanager::{Identifier, KeyManagerState, ToEncryptable},
};
#[cfg(feature = "v2")]
use diesel_models::merchant_connector_account::{
    BillingAccountReference as DieselBillingAccountReference,
    MerchantConnectorAccountFeatureMetadata as DieselMerchantConnectorAccountFeatureMetadata,
    RevenueRecoveryMetadata as DieselRevenueRecoveryMetadata,
};
use diesel_models::{
    enums,
    merchant_connector_account::{self as storage, MerchantConnectorAccountUpdateInternal},
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use rustc_hash::FxHashMap;
use serde_json::Value;

use super::behaviour;
#[cfg(feature = "v2")]
use crate::errors::api_error_response;
use crate::{
    mandates::CommonMandateReference,
    merchant_key_store::MerchantKeyStore,
    router_data,
    type_encryption::{crypto_operation, CryptoOperation},
};

#[cfg(feature = "v1")]
#[derive(Clone, Debug, router_derive::ToEncryption)]
pub struct MerchantConnectorAccount {
    pub merchant_id: id_type::MerchantId,
    pub connector_name: String,
    #[encrypt]
    pub connector_account_details: Encryptable<Secret<Value>>,
    pub test_mode: Option<bool>,
    pub disabled: Option<bool>,
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
    pub payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
    pub connector_type: enums::ConnectorType,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub frm_configs: Option<Vec<pii::SecretSerdeValue>>,
    pub connector_label: Option<String>,
    pub business_country: Option<enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub business_sub_label: Option<String>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    pub profile_id: id_type::ProfileId,
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: enums::ConnectorStatus,
    #[encrypt]
    pub connector_wallets_details: Option<Encryptable<Secret<Value>>>,
    #[encrypt]
    pub additional_merchant_data: Option<Encryptable<Secret<Value>>>,
    pub version: common_enums::ApiVersion,
}

#[cfg(feature = "v1")]
impl MerchantConnectorAccount {
    pub fn get_id(&self) -> id_type::MerchantConnectorAccountId {
        self.merchant_connector_id.clone()
    }
    pub fn get_connector_account_details(
        &self,
    ) -> error_stack::Result<router_data::ConnectorAuthType, common_utils::errors::ParsingError>
    {
        self.connector_account_details
            .get_inner()
            .clone()
            .parse_value("ConnectorAuthType")
    }

    pub fn get_connector_wallets_details(&self) -> Option<Secret<Value>> {
        self.connector_wallets_details.as_deref().cloned()
    }

    pub fn get_connector_test_mode(&self) -> Option<bool> {
        self.test_mode
    }

    pub fn get_connector_name_as_string(&self) -> String {
        self.connector_name.clone()
    }

    pub fn get_metadata(&self) -> Option<Secret<Value>> {
        self.metadata.clone()
    }

    pub fn get_ctp_service_provider(
        &self,
    ) -> error_stack::Result<
        Option<common_enums::CtpServiceProvider>,
        common_utils::errors::ParsingError,
    > {
        let provider = self
            .connector_name
            .clone()
            .parse_enum("CtpServiceProvider")
            .attach_printable_lazy(|| {
                format!(
                    "Failed to parse ctp service provider from connector_name: {}",
                    self.connector_name
                )
            })?;

        Ok(Some(provider))
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug)]
pub enum MerchantConnectorAccountTypeDetails {
    MerchantConnectorAccount(Box<MerchantConnectorAccount>),
    MerchantConnectorDetails(common_types::domain::MerchantConnectorAuthDetails),
}

#[cfg(feature = "v2")]
impl MerchantConnectorAccountTypeDetails {
    pub fn get_connector_account_details(
        &self,
    ) -> error_stack::Result<router_data::ConnectorAuthType, common_utils::errors::ParsingError>
    {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                merchant_connector_account
                    .connector_account_details
                    .peek()
                    .clone()
                    .parse_value("ConnectorAuthType")
            }
            Self::MerchantConnectorDetails(merchant_connector_details) => {
                merchant_connector_details
                    .merchant_connector_creds
                    .peek()
                    .clone()
                    .parse_value("ConnectorAuthType")
            }
        }
    }

    pub fn is_disabled(&self) -> bool {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                merchant_connector_account.disabled.unwrap_or(false)
            }
            Self::MerchantConnectorDetails(_) => false,
        }
    }

    pub fn get_metadata(&self) -> Option<Secret<Value>> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                merchant_connector_account.metadata.to_owned()
            }
            Self::MerchantConnectorDetails(_) => None,
        }
    }

    pub fn get_id(&self) -> Option<id_type::MerchantConnectorAccountId> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account.id.clone())
            }
            Self::MerchantConnectorDetails(_) => None,
        }
    }

    pub fn get_mca_id(&self) -> Option<id_type::MerchantConnectorAccountId> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account.get_id())
            }
            Self::MerchantConnectorDetails(_) => None,
        }
    }

    pub fn get_connector_name(&self) -> Option<common_enums::connector_enums::Connector> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account.connector_name)
            }
            Self::MerchantConnectorDetails(merchant_connector_details) => {
                Some(merchant_connector_details.connector_name)
            }
        }
    }

    pub fn get_connector_name_as_string(&self) -> String {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                merchant_connector_account.connector_name.to_string()
            }
            Self::MerchantConnectorDetails(merchant_connector_details) => {
                merchant_connector_details.connector_name.to_string()
            }
        }
    }

    pub fn get_inner_db_merchant_connector_account(&self) -> Option<&MerchantConnectorAccount> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account)
            }
            Self::MerchantConnectorDetails(_) => None,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, router_derive::ToEncryption)]
pub struct MerchantConnectorAccount {
    pub id: id_type::MerchantConnectorAccountId,
    pub merchant_id: id_type::MerchantId,
    pub connector_name: common_enums::connector_enums::Connector,
    #[encrypt]
    pub connector_account_details: Encryptable<Secret<Value>>,
    pub disabled: Option<bool>,
    pub payment_methods_enabled: Option<Vec<common_types::payment_methods::PaymentMethodsEnabled>>,
    pub connector_type: enums::ConnectorType,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub frm_configs: Option<Vec<pii::SecretSerdeValue>>,
    pub connector_label: Option<String>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    pub profile_id: id_type::ProfileId,
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: enums::ConnectorStatus,
    #[encrypt]
    pub connector_wallets_details: Option<Encryptable<Secret<Value>>>,
    #[encrypt]
    pub additional_merchant_data: Option<Encryptable<Secret<Value>>>,
    pub version: common_enums::ApiVersion,
    pub feature_metadata: Option<MerchantConnectorAccountFeatureMetadata>,
}

#[cfg(feature = "v2")]
impl MerchantConnectorAccount {
    pub fn get_retry_threshold(&self) -> Option<u16> {
        self.feature_metadata
            .as_ref()
            .and_then(|metadata| metadata.revenue_recovery.as_ref())
            .map(|recovery| recovery.billing_connector_retry_threshold)
    }

    pub fn get_id(&self) -> id_type::MerchantConnectorAccountId {
        self.id.clone()
    }

    pub fn get_metadata(&self) -> Option<pii::SecretSerdeValue> {
        self.metadata.clone()
    }

    pub fn is_disabled(&self) -> bool {
        self.disabled.unwrap_or(false)
    }

    pub fn get_connector_account_details(
        &self,
    ) -> error_stack::Result<router_data::ConnectorAuthType, common_utils::errors::ParsingError>
    {
        use common_utils::ext_traits::ValueExt;

        self.connector_account_details
            .get_inner()
            .clone()
            .parse_value("ConnectorAuthType")
    }

    pub fn get_connector_wallets_details(&self) -> Option<Secret<Value>> {
        self.connector_wallets_details.as_deref().cloned()
    }

    pub fn get_connector_test_mode(&self) -> Option<bool> {
        todo!()
    }

    pub fn get_connector_name_as_string(&self) -> String {
        self.connector_name.clone().to_string()
    }

    #[cfg(feature = "v2")]
    pub fn get_connector_name(&self) -> common_enums::connector_enums::Connector {
        self.connector_name
    }

    pub fn get_payment_merchant_connector_account_id_using_account_reference_id(
        &self,
        account_reference_id: String,
    ) -> Option<id_type::MerchantConnectorAccountId> {
        self.feature_metadata.as_ref().and_then(|metadata| {
            metadata.revenue_recovery.as_ref().and_then(|recovery| {
                recovery
                    .mca_reference
                    .billing_to_recovery
                    .get(&account_reference_id)
                    .cloned()
            })
        })
    }
    pub fn get_account_reference_id_using_payment_merchant_connector_account_id(
        &self,
        payment_merchant_connector_account_id: id_type::MerchantConnectorAccountId,
    ) -> Option<String> {
        self.feature_metadata.as_ref().and_then(|metadata| {
            metadata.revenue_recovery.as_ref().and_then(|recovery| {
                recovery
                    .mca_reference
                    .recovery_to_billing
                    .get(&payment_merchant_connector_account_id)
                    .cloned()
            })
        })
    }
}

#[cfg(feature = "v2")]
/// Holds the payment methods enabled for a connector along with the connector name
/// This struct is a flattened representation of the payment methods enabled for a connector
#[derive(Debug)]
pub struct PaymentMethodsEnabledForConnector {
    pub payment_methods_enabled: common_types::payment_methods::RequestPaymentMethodTypes,
    pub payment_method: common_enums::PaymentMethod,
    pub connector: common_enums::connector_enums::Connector,
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone)]
pub struct MerchantConnectorAccountFeatureMetadata {
    pub revenue_recovery: Option<RevenueRecoveryMetadata>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone)]
pub struct RevenueRecoveryMetadata {
    pub max_retry_count: u16,
    pub billing_connector_retry_threshold: u16,
    pub mca_reference: AccountReferenceMap,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Deserialize)]
pub struct ExternalVaultConnectorMetadata {
    pub proxy_url: common_utils::types::Url,
    pub certificate: Secret<String>,
}
#[cfg(feature = "v2")]
#[derive(Debug, Clone)]
pub struct AccountReferenceMap {
    pub recovery_to_billing: HashMap<id_type::MerchantConnectorAccountId, String>,
    pub billing_to_recovery: HashMap<String, id_type::MerchantConnectorAccountId>,
}

#[cfg(feature = "v2")]
impl AccountReferenceMap {
    pub fn new(
        hash_map: HashMap<id_type::MerchantConnectorAccountId, String>,
    ) -> Result<Self, api_error_response::ApiErrorResponse> {
        Self::validate(&hash_map)?;

        let recovery_to_billing = hash_map.clone();
        let mut billing_to_recovery = HashMap::new();

        for (key, value) in &hash_map {
            billing_to_recovery.insert(value.clone(), key.clone());
        }

        Ok(Self {
            recovery_to_billing,
            billing_to_recovery,
        })
    }

    fn validate(
        hash_map: &HashMap<id_type::MerchantConnectorAccountId, String>,
    ) -> Result<(), api_error_response::ApiErrorResponse> {
        let mut seen_values = std::collections::HashSet::new(); // To check uniqueness of values

        for value in hash_map.values() {
            if !seen_values.insert(value.clone()) {
                return Err(api_error_response::ApiErrorResponse::InvalidRequestData {
                    message: "Duplicate account reference IDs found in Recovery feature metadata. Each account reference ID must be unique.".to_string(),
                });
            }
        }
        Ok(())
    }
}

#[cfg(feature = "v2")]
/// Holds the payment methods enabled for a connector
pub struct FlattenedPaymentMethodsEnabled {
    pub payment_methods_enabled: Vec<PaymentMethodsEnabledForConnector>,
}

#[cfg(feature = "v2")]
impl FlattenedPaymentMethodsEnabled {
    /// This functions flattens the payment methods enabled from the connector accounts
    /// Retains the connector name and payment method in every flattened element
    pub fn from_payment_connectors_list(payment_connectors: Vec<MerchantConnectorAccount>) -> Self {
        let payment_methods_enabled_flattened_with_connector = payment_connectors
            .into_iter()
            .map(|connector| {
                (
                    connector
                        .payment_methods_enabled
                        .clone()
                        .unwrap_or_default(),
                    connector.connector_name,
                    connector.get_id(),
                )
            })
            .flat_map(
                |(payment_method_enabled, connector, merchant_connector_id)| {
                    payment_method_enabled
                        .into_iter()
                        .flat_map(move |payment_method| {
                            let request_payment_methods_enabled =
                                payment_method.payment_method_subtypes.unwrap_or_default();
                            let length = request_payment_methods_enabled.len();
                            request_payment_methods_enabled
                                .into_iter()
                                .zip(std::iter::repeat_n(
                                    (
                                        connector,
                                        merchant_connector_id.clone(),
                                        payment_method.payment_method_type,
                                    ),
                                    length,
                                ))
                        })
                },
            )
            .map(
                |(request_payment_methods, (connector, merchant_connector_id, payment_method))| {
                    PaymentMethodsEnabledForConnector {
                        payment_methods_enabled: request_payment_methods,
                        connector,
                        payment_method,
                        merchant_connector_id,
                    }
                },
            )
            .collect();

        Self {
            payment_methods_enabled: payment_methods_enabled_flattened_with_connector,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug)]
pub enum MerchantConnectorAccountUpdate {
    Update {
        connector_type: Option<enums::ConnectorType>,
        connector_name: Option<String>,
        connector_account_details: Box<Option<Encryptable<pii::SecretSerdeValue>>>,
        test_mode: Option<bool>,
        disabled: Option<bool>,
        merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
        payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
        metadata: Option<pii::SecretSerdeValue>,
        frm_configs: Option<Vec<pii::SecretSerdeValue>>,
        connector_webhook_details: Box<Option<pii::SecretSerdeValue>>,
        applepay_verified_domains: Option<Vec<String>>,
        pm_auth_config: Box<Option<pii::SecretSerdeValue>>,
        connector_label: Option<String>,
        status: Option<enums::ConnectorStatus>,
        connector_wallets_details: Box<Option<Encryptable<pii::SecretSerdeValue>>>,
        additional_merchant_data: Box<Option<Encryptable<pii::SecretSerdeValue>>>,
    },
    ConnectorWalletDetailsUpdate {
        connector_wallets_details: Encryptable<pii::SecretSerdeValue>,
    },
}

#[cfg(feature = "v2")]
#[derive(Debug)]
pub enum MerchantConnectorAccountUpdate {
    Update {
        connector_type: Option<enums::ConnectorType>,
        connector_account_details: Box<Option<Encryptable<pii::SecretSerdeValue>>>,
        disabled: Option<bool>,
        payment_methods_enabled: Option<Vec<common_types::payment_methods::PaymentMethodsEnabled>>,
        metadata: Option<pii::SecretSerdeValue>,
        frm_configs: Option<Vec<pii::SecretSerdeValue>>,
        connector_webhook_details: Box<Option<pii::SecretSerdeValue>>,
        applepay_verified_domains: Option<Vec<String>>,
        pm_auth_config: Box<Option<pii::SecretSerdeValue>>,
        connector_label: Option<String>,
        status: Option<enums::ConnectorStatus>,
        connector_wallets_details: Box<Option<Encryptable<pii::SecretSerdeValue>>>,
        additional_merchant_data: Box<Option<Encryptable<pii::SecretSerdeValue>>>,
        feature_metadata: Box<Option<MerchantConnectorAccountFeatureMetadata>>,
    },
    ConnectorWalletDetailsUpdate {
        connector_wallets_details: Encryptable<pii::SecretSerdeValue>,
    },
}

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl behaviour::Conversion for MerchantConnectorAccount {
    type DstType = storage::MerchantConnectorAccount;
    type NewDstType = storage::MerchantConnectorAccountNew;

    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(storage::MerchantConnectorAccount {
            merchant_id: self.merchant_id,
            connector_name: self.connector_name,
            connector_account_details: self.connector_account_details.into(),
            test_mode: self.test_mode,
            disabled: self.disabled,
            merchant_connector_id: self.merchant_connector_id.clone(),
            id: Some(self.merchant_connector_id),
            payment_methods_enabled: self.payment_methods_enabled,
            connector_type: self.connector_type,
            metadata: self.metadata,
            frm_configs: None,
            frm_config: self.frm_configs,
            business_country: self.business_country,
            business_label: self.business_label,
            connector_label: self.connector_label,
            business_sub_label: self.business_sub_label,
            created_at: self.created_at,
            modified_at: self.modified_at,
            connector_webhook_details: self.connector_webhook_details,
            profile_id: Some(self.profile_id),
            applepay_verified_domains: self.applepay_verified_domains,
            pm_auth_config: self.pm_auth_config,
            status: self.status,
            connector_wallets_details: self.connector_wallets_details.map(Encryption::from),
            additional_merchant_data: self.additional_merchant_data.map(|data| data.into()),
            version: self.version,
        })
    }

    async fn convert_back(
        state: &KeyManagerState,
        other: Self::DstType,
        key: &Secret<Vec<u8>>,
        _key_manager_identifier: Identifier,
    ) -> CustomResult<Self, ValidationError> {
        let identifier = Identifier::Merchant(other.merchant_id.clone());
        let decrypted_data = crypto_operation(
            state,
            type_name!(Self::DstType),
            CryptoOperation::BatchDecrypt(EncryptedMerchantConnectorAccount::to_encryptable(
                EncryptedMerchantConnectorAccount {
                    connector_account_details: other.connector_account_details,
                    additional_merchant_data: other.additional_merchant_data,
                    connector_wallets_details: other.connector_wallets_details,
                },
            )),
            identifier.clone(),
            key.peek(),
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting connector account details".to_string(),
        })?;

        let decrypted_data = EncryptedMerchantConnectorAccount::from_encryptable(decrypted_data)
            .change_context(ValidationError::InvalidValue {
                message: "Failed while decrypting connector account details".to_string(),
            })?;

        Ok(Self {
            merchant_id: other.merchant_id,
            connector_name: other.connector_name,
            connector_account_details: decrypted_data.connector_account_details,
            test_mode: other.test_mode,
            disabled: other.disabled,
            merchant_connector_id: other.merchant_connector_id,
            payment_methods_enabled: other.payment_methods_enabled,
            connector_type: other.connector_type,
            metadata: other.metadata,

            frm_configs: other.frm_config,
            business_country: other.business_country,
            business_label: other.business_label,
            connector_label: other.connector_label,
            business_sub_label: other.business_sub_label,
            created_at: other.created_at,
            modified_at: other.modified_at,
            connector_webhook_details: other.connector_webhook_details,
            profile_id: other
                .profile_id
                .ok_or(ValidationError::MissingRequiredField {
                    field_name: "profile_id".to_string(),
                })?,
            applepay_verified_domains: other.applepay_verified_domains,
            pm_auth_config: other.pm_auth_config,
            status: other.status,
            connector_wallets_details: decrypted_data.connector_wallets_details,
            additional_merchant_data: decrypted_data.additional_merchant_data,
            version: other.version,
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        let now = date_time::now();
        Ok(Self::NewDstType {
            merchant_id: Some(self.merchant_id),
            connector_name: Some(self.connector_name),
            connector_account_details: Some(self.connector_account_details.into()),
            test_mode: self.test_mode,
            disabled: self.disabled,
            merchant_connector_id: self.merchant_connector_id.clone(),
            id: Some(self.merchant_connector_id),
            payment_methods_enabled: self.payment_methods_enabled,
            connector_type: Some(self.connector_type),
            metadata: self.metadata,
            frm_configs: None,
            frm_config: self.frm_configs,
            business_country: self.business_country,
            business_label: self.business_label,
            connector_label: self.connector_label,
            business_sub_label: self.business_sub_label,
            created_at: now,
            modified_at: now,
            connector_webhook_details: self.connector_webhook_details,
            profile_id: Some(self.profile_id),
            applepay_verified_domains: self.applepay_verified_domains,
            pm_auth_config: self.pm_auth_config,
            status: self.status,
            connector_wallets_details: self.connector_wallets_details.map(Encryption::from),
            additional_merchant_data: self.additional_merchant_data.map(|data| data.into()),
            version: self.version,
        })
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl behaviour::Conversion for MerchantConnectorAccount {
    type DstType = storage::MerchantConnectorAccount;
    type NewDstType = storage::MerchantConnectorAccountNew;

    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(storage::MerchantConnectorAccount {
            id: self.id,
            merchant_id: self.merchant_id,
            connector_name: self.connector_name,
            connector_account_details: self.connector_account_details.into(),
            disabled: self.disabled,
            payment_methods_enabled: self.payment_methods_enabled,
            connector_type: self.connector_type,
            metadata: self.metadata,
            frm_config: self.frm_configs,
            connector_label: self.connector_label,
            created_at: self.created_at,
            modified_at: self.modified_at,
            connector_webhook_details: self.connector_webhook_details,
            profile_id: self.profile_id,
            applepay_verified_domains: self.applepay_verified_domains,
            pm_auth_config: self.pm_auth_config,
            status: self.status,
            connector_wallets_details: self.connector_wallets_details.map(Encryption::from),
            additional_merchant_data: self.additional_merchant_data.map(|data| data.into()),
            version: self.version,
            feature_metadata: self.feature_metadata.map(From::from),
        })
    }

    async fn convert_back(
        state: &KeyManagerState,
        other: Self::DstType,
        key: &Secret<Vec<u8>>,
        _key_manager_identifier: Identifier,
    ) -> CustomResult<Self, ValidationError> {
        let identifier = Identifier::Merchant(other.merchant_id.clone());

        let decrypted_data = crypto_operation(
            state,
            type_name!(Self::DstType),
            CryptoOperation::BatchDecrypt(EncryptedMerchantConnectorAccount::to_encryptable(
                EncryptedMerchantConnectorAccount {
                    connector_account_details: other.connector_account_details,
                    additional_merchant_data: other.additional_merchant_data,
                    connector_wallets_details: other.connector_wallets_details,
                },
            )),
            identifier.clone(),
            key.peek(),
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting connector account details".to_string(),
        })?;

        let decrypted_data = EncryptedMerchantConnectorAccount::from_encryptable(decrypted_data)
            .change_context(ValidationError::InvalidValue {
                message: "Failed while decrypting connector account details".to_string(),
            })?;

        Ok(Self {
            id: other.id,
            merchant_id: other.merchant_id,
            connector_name: other.connector_name,
            connector_account_details: decrypted_data.connector_account_details,
            disabled: other.disabled,
            payment_methods_enabled: other.payment_methods_enabled,
            connector_type: other.connector_type,
            metadata: other.metadata,

            frm_configs: other.frm_config,
            connector_label: other.connector_label,
            created_at: other.created_at,
            modified_at: other.modified_at,
            connector_webhook_details: other.connector_webhook_details,
            profile_id: other.profile_id,
            applepay_verified_domains: other.applepay_verified_domains,
            pm_auth_config: other.pm_auth_config,
            status: other.status,
            connector_wallets_details: decrypted_data.connector_wallets_details,
            additional_merchant_data: decrypted_data.additional_merchant_data,
            version: other.version,
            feature_metadata: other.feature_metadata.map(From::from),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        let now = date_time::now();
        Ok(Self::NewDstType {
            id: self.id,
            merchant_id: Some(self.merchant_id),
            connector_name: Some(self.connector_name),
            connector_account_details: Some(self.connector_account_details.into()),
            disabled: self.disabled,
            payment_methods_enabled: self.payment_methods_enabled,
            connector_type: Some(self.connector_type),
            metadata: self.metadata,
            frm_config: self.frm_configs,
            connector_label: self.connector_label,
            created_at: now,
            modified_at: now,
            connector_webhook_details: self.connector_webhook_details,
            profile_id: self.profile_id,
            applepay_verified_domains: self.applepay_verified_domains,
            pm_auth_config: self.pm_auth_config,
            status: self.status,
            connector_wallets_details: self.connector_wallets_details.map(Encryption::from),
            additional_merchant_data: self.additional_merchant_data.map(|data| data.into()),
            version: self.version,
            feature_metadata: self.feature_metadata.map(From::from),
        })
    }
}

#[cfg(feature = "v1")]
impl From<MerchantConnectorAccountUpdate> for MerchantConnectorAccountUpdateInternal {
    fn from(merchant_connector_account_update: MerchantConnectorAccountUpdate) -> Self {
        match merchant_connector_account_update {
            MerchantConnectorAccountUpdate::Update {
                connector_type,
                connector_name,
                connector_account_details,
                test_mode,
                disabled,
                merchant_connector_id,
                payment_methods_enabled,
                metadata,
                frm_configs,
                connector_webhook_details,
                applepay_verified_domains,
                pm_auth_config,
                connector_label,
                status,
                connector_wallets_details,
                additional_merchant_data,
            } => Self {
                connector_type,
                connector_name,
                connector_account_details: connector_account_details.map(Encryption::from),
                test_mode,
                disabled,
                merchant_connector_id,
                payment_methods_enabled,
                metadata,
                frm_configs: None,
                frm_config: frm_configs,
                modified_at: Some(date_time::now()),
                connector_webhook_details: *connector_webhook_details,
                applepay_verified_domains,
                pm_auth_config: *pm_auth_config,
                connector_label,
                status,
                connector_wallets_details: connector_wallets_details.map(Encryption::from),
                additional_merchant_data: additional_merchant_data.map(Encryption::from),
            },
            MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate {
                connector_wallets_details,
            } => Self {
                connector_wallets_details: Some(Encryption::from(connector_wallets_details)),
                connector_type: None,
                connector_name: None,
                connector_account_details: None,
                connector_label: None,
                test_mode: None,
                disabled: None,
                merchant_connector_id: None,
                payment_methods_enabled: None,
                frm_configs: None,
                metadata: None,
                modified_at: None,
                connector_webhook_details: None,
                frm_config: None,
                applepay_verified_domains: None,
                pm_auth_config: None,
                status: None,
                additional_merchant_data: None,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl From<MerchantConnectorAccountUpdate> for MerchantConnectorAccountUpdateInternal {
    fn from(merchant_connector_account_update: MerchantConnectorAccountUpdate) -> Self {
        match merchant_connector_account_update {
            MerchantConnectorAccountUpdate::Update {
                connector_type,
                connector_account_details,
                disabled,
                payment_methods_enabled,
                metadata,
                frm_configs,
                connector_webhook_details,
                applepay_verified_domains,
                pm_auth_config,
                connector_label,
                status,
                connector_wallets_details,
                additional_merchant_data,
                feature_metadata,
            } => Self {
                connector_type,
                connector_account_details: connector_account_details.map(Encryption::from),
                disabled,
                payment_methods_enabled,
                metadata,
                frm_config: frm_configs,
                modified_at: Some(date_time::now()),
                connector_webhook_details: *connector_webhook_details,
                applepay_verified_domains,
                pm_auth_config: *pm_auth_config,
                connector_label,
                status,
                connector_wallets_details: connector_wallets_details.map(Encryption::from),
                additional_merchant_data: additional_merchant_data.map(Encryption::from),
                feature_metadata: feature_metadata.map(From::from),
            },
            MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate {
                connector_wallets_details,
            } => Self {
                connector_wallets_details: Some(Encryption::from(connector_wallets_details)),
                connector_type: None,
                connector_account_details: None,
                connector_label: None,
                disabled: None,
                payment_methods_enabled: None,
                metadata: None,
                modified_at: None,
                connector_webhook_details: None,
                frm_config: None,
                applepay_verified_domains: None,
                pm_auth_config: None,
                status: None,
                additional_merchant_data: None,
                feature_metadata: None,
            },
        }
    }
}

common_utils::create_list_wrapper!(
    MerchantConnectorAccounts,
    MerchantConnectorAccount,
    impl_functions: {
        fn filter_and_map<'a, T>(
            &'a self,
            filter: impl Fn(&'a MerchantConnectorAccount) -> bool,
            func: impl Fn(&'a MerchantConnectorAccount) -> T,
        ) -> rustc_hash::FxHashSet<T>
        where
            T: std::hash::Hash + Eq,
        {
            self.0
                .iter()
                .filter(|mca| filter(mca))
                .map(func)
                .collect::<rustc_hash::FxHashSet<_>>()
        }

        pub fn filter_by_profile<'a, T>(
            &'a self,
            profile_id: &'a id_type::ProfileId,
            func: impl Fn(&'a MerchantConnectorAccount) -> T,
        ) -> rustc_hash::FxHashSet<T>
        where
            T: std::hash::Hash + Eq,
        {
            self.filter_and_map(|mca| mca.profile_id == *profile_id, func)
        }
        #[cfg(feature = "v2")]
        pub fn get_connector_and_supporting_payment_method_type_for_session_call(
            &self,
        ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType, common_enums::PaymentMethod)> {
            // This vector is created to work around lifetimes
            let ref_vector = Vec::default();

            let connector_and_supporting_payment_method_type = self.iter().flat_map(|connector_account| {
                connector_account
                    .payment_methods_enabled.as_ref()
                    .unwrap_or(&Vec::default())
                    .iter()
                    .flat_map(|payment_method_types| payment_method_types.payment_method_subtypes.as_ref().unwrap_or(&ref_vector).iter().map(|payment_method_subtype| (payment_method_subtype, payment_method_types.payment_method_type)).collect::<Vec<_>>())
                    .filter(|(payment_method_types_enabled, _)| {
                        payment_method_types_enabled.payment_experience == Some(api_models::enums::PaymentExperience::InvokeSdkClient)
                    })
                    .map(|(payment_method_subtypes, payment_method_type)| {
                        (connector_account, payment_method_subtypes.payment_method_subtype, payment_method_type)
                    })
                    .collect::<Vec<_>>()
            }).collect();
            connector_and_supporting_payment_method_type
        }
        pub fn filter_based_on_profile_and_connector_type(
            self,
            profile_id: &id_type::ProfileId,
            connector_type: common_enums::ConnectorType,
        ) -> Self {
            self.into_iter()
                .filter(|mca| &mca.profile_id == profile_id && mca.connector_type == connector_type)
                .collect()
        }
        pub fn is_merchant_connector_account_id_in_connector_mandate_details(
            &self,
            profile_id: Option<&id_type::ProfileId>,
            connector_mandate_details: &CommonMandateReference,
        ) -> bool {
            let mca_ids = self
                .iter()
                .filter(|mca| {
                    mca.disabled.is_some_and(|disabled| !disabled)
                        && profile_id.is_some_and(|profile_id| *profile_id == mca.profile_id)
                })
                .map(|mca| mca.get_id())
                .collect::<std::collections::HashSet<_>>();

            connector_mandate_details
            .payments
            .as_ref()
            .as_ref().is_some_and(|payments| {
                payments.0.keys().any(|mca_id| mca_ids.contains(mca_id))
            })
        }
    }
);

#[cfg(feature = "v2")]
impl From<MerchantConnectorAccountFeatureMetadata>
    for DieselMerchantConnectorAccountFeatureMetadata
{
    fn from(feature_metadata: MerchantConnectorAccountFeatureMetadata) -> Self {
        let revenue_recovery = feature_metadata.revenue_recovery.map(|recovery_metadata| {
            DieselRevenueRecoveryMetadata {
                max_retry_count: recovery_metadata.max_retry_count,
                billing_connector_retry_threshold: recovery_metadata
                    .billing_connector_retry_threshold,
                billing_account_reference: DieselBillingAccountReference(
                    recovery_metadata.mca_reference.recovery_to_billing,
                ),
            }
        });
        Self { revenue_recovery }
    }
}

#[cfg(feature = "v2")]
impl From<DieselMerchantConnectorAccountFeatureMetadata>
    for MerchantConnectorAccountFeatureMetadata
{
    fn from(feature_metadata: DieselMerchantConnectorAccountFeatureMetadata) -> Self {
        let revenue_recovery = feature_metadata.revenue_recovery.map(|recovery_metadata| {
            let mut billing_to_recovery = HashMap::new();
            for (key, value) in &recovery_metadata.billing_account_reference.0 {
                billing_to_recovery.insert(value.to_string(), key.clone());
            }
            RevenueRecoveryMetadata {
                max_retry_count: recovery_metadata.max_retry_count,
                billing_connector_retry_threshold: recovery_metadata
                    .billing_connector_retry_threshold,
                mca_reference: AccountReferenceMap {
                    recovery_to_billing: recovery_metadata.billing_account_reference.0,
                    billing_to_recovery,
                },
            }
        });
        Self { revenue_recovery }
    }
}

#[async_trait::async_trait]
pub trait MerchantConnectorAccountInterface
where
    MerchantConnectorAccount: behaviour::Conversion<
        DstType = storage::MerchantConnectorAccount,
        NewDstType = storage::MerchantConnectorAccountNew,
    >,
{
    type Error;
    #[cfg(feature = "v1")]
    async fn find_merchant_connector_account_by_merchant_id_connector_label(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_label: &str,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<MerchantConnectorAccount, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_merchant_connector_account_by_profile_id_connector_name(
        &self,
        profile_id: &id_type::ProfileId,
        connector_name: &str,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<MerchantConnectorAccount, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_merchant_connector_account_by_merchant_id_connector_name(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_name: &str,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<Vec<MerchantConnectorAccount>, Self::Error>;

    async fn insert_merchant_connector_account(
        &self,
        t: MerchantConnectorAccount,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<MerchantConnectorAccount, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id(
        &self,
        merchant_id: &id_type::MerchantId,
        merchant_connector_id: &id_type::MerchantConnectorAccountId,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<MerchantConnectorAccount, Self::Error>;

    #[cfg(feature = "v2")]
    async fn find_merchant_connector_account_by_id(
        &self,
        id: &id_type::MerchantConnectorAccountId,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<MerchantConnectorAccount, Self::Error>;

    async fn find_merchant_connector_account_by_merchant_id_and_disabled_list(
        &self,
        merchant_id: &id_type::MerchantId,
        get_disabled: bool,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<MerchantConnectorAccounts, Self::Error>;

    #[cfg(all(feature = "olap", feature = "v2"))]
    async fn list_connector_account_by_profile_id(
        &self,
        profile_id: &id_type::ProfileId,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<Vec<MerchantConnectorAccount>, Self::Error>;

    async fn list_enabled_connector_accounts_by_profile_id(
        &self,
        profile_id: &id_type::ProfileId,
        key_store: &MerchantKeyStore,
        connector_type: common_enums::ConnectorType,
    ) -> CustomResult<Vec<MerchantConnectorAccount>, Self::Error>;

    async fn update_merchant_connector_account(
        &self,
        this: MerchantConnectorAccount,
        merchant_connector_account: MerchantConnectorAccountUpdateInternal,
        key_store: &MerchantKeyStore,
    ) -> CustomResult<MerchantConnectorAccount, Self::Error>;

    async fn update_multiple_merchant_connector_accounts(
        &self,
        this: Vec<(
            MerchantConnectorAccount,
            MerchantConnectorAccountUpdateInternal,
        )>,
    ) -> CustomResult<(), Self::Error>;

    #[cfg(feature = "v1")]
    async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id(
        &self,
        merchant_id: &id_type::MerchantId,
        merchant_connector_id: &id_type::MerchantConnectorAccountId,
    ) -> CustomResult<bool, Self::Error>;

    #[cfg(feature = "v2")]
    async fn delete_merchant_connector_account_by_id(
        &self,
        id: &id_type::MerchantConnectorAccountId,
    ) -> CustomResult<bool, Self::Error>;
}


// File: crates/hyperswitch_domain_models/src/configs.rs

use common_utils::errors::CustomResult;
use diesel_models::configs as storage;

#[async_trait::async_trait]
pub trait ConfigInterface {
    type Error;
    async fn insert_config(
        &self,
        config: storage::ConfigNew,
    ) -> CustomResult<storage::Config, Self::Error>;

    async fn find_config_by_key(&self, key: &str) -> CustomResult<storage::Config, Self::Error>;

    async fn find_config_by_key_unwrap_or(
        &self,
        key: &str,
        // If the config is not found it will be created with the default value.
        default_config: Option<String>,
    ) -> CustomResult<storage::Config, Self::Error>;

    async fn find_config_by_key_from_db(
        &self,
        key: &str,
    ) -> CustomResult<storage::Config, Self::Error>;

    async fn update_config_by_key(
        &self,
        key: &str,
        config_update: storage::ConfigUpdate,
    ) -> CustomResult<storage::Config, Self::Error>;

    async fn update_config_in_database(
        &self,
        key: &str,
        config_update: storage::ConfigUpdate,
    ) -> CustomResult<storage::Config, Self::Error>;

    async fn delete_config_by_key(&self, key: &str) -> CustomResult<storage::Config, Self::Error>;
}