// File: crates/diesel_models/src/payment_method.rs

use std::collections::HashMap;

use common_enums::MerchantStorageScheme;
use common_utils::{
    encryption::Encryption,
    errors::{CustomResult, ParsingError},
    pii,
};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use error_stack::ResultExt;
#[cfg(feature = "v1")]
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

#[cfg(feature = "v1")]
use crate::{enums as storage_enums, schema::payment_methods};
#[cfg(feature = "v2")]
use crate::{enums as storage_enums, schema_v2::payment_methods};

#[cfg(feature = "v1")]
#[derive(
    Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize,
)]
#[diesel(table_name = payment_methods, primary_key(payment_method_id), check_for_backend(diesel::pg::Pg))]
pub struct PaymentMethod {
    pub customer_id: common_utils::id_type::CustomerId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_method_id: String,
    #[diesel(deserialize_as = super::OptionalDieselArray<storage_enums::Currency>)]
    pub accepted_currency: Option<Vec<storage_enums::Currency>>,
    pub scheme: Option<String>,
    pub token: Option<String>,
    pub cardholder_name: Option<Secret<String>>,
    pub issuer_name: Option<String>,
    pub issuer_country: Option<String>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub payer_country: Option<Vec<String>>,
    pub is_stored: Option<bool>,
    pub swift_code: Option<String>,
    pub direct_debit_token: Option<String>,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub payment_method: Option<storage_enums::PaymentMethod>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub payment_method_issuer: Option<String>,
    pub payment_method_issuer_code: Option<storage_enums::PaymentMethodIssuerCode>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub payment_method_data: Option<Encryption>,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<serde_json::Value>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: Option<Encryption>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: Option<Encryption>,
    pub external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub vault_type: Option<storage_enums::VaultType>,
    pub created_by: Option<String>,
    pub last_modified_by: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Identifiable, Queryable, Selectable, Serialize, Deserialize)]
#[diesel(table_name = payment_methods, primary_key(id), check_for_backend(diesel::pg::Pg))]
pub struct PaymentMethod {
    pub customer_id: common_utils::id_type::GlobalCustomerId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub payment_method_data: Option<Encryption>,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<CommonMandateReference>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: Option<Encryption>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: Option<Encryption>,
    pub external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub vault_type: Option<storage_enums::VaultType>,
    pub created_by: Option<String>,
    pub last_modified_by: Option<String>,
    pub locker_fingerprint_id: Option<String>,
    pub payment_method_type_v2: Option<storage_enums::PaymentMethod>,
    pub payment_method_subtype: Option<storage_enums::PaymentMethodType>,
    pub id: common_utils::id_type::GlobalPaymentMethodId,
    pub external_vault_token_data: Option<Encryption>,
}

impl PaymentMethod {
    #[cfg(feature = "v1")]
    pub fn get_id(&self) -> &String {
        &self.payment_method_id
    }

    #[cfg(feature = "v2")]
    pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId {
        &self.id
    }
}

#[cfg(feature = "v1")]
#[derive(
    Clone, Debug, Eq, PartialEq, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize,
)]
#[diesel(table_name = payment_methods)]
pub struct PaymentMethodNew {
    pub customer_id: common_utils::id_type::CustomerId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_method_id: String,
    pub payment_method: Option<storage_enums::PaymentMethod>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub payment_method_issuer: Option<String>,
    pub payment_method_issuer_code: Option<storage_enums::PaymentMethodIssuerCode>,
    pub accepted_currency: Option<Vec<storage_enums::Currency>>,
    pub scheme: Option<String>,
    pub token: Option<String>,
    pub cardholder_name: Option<Secret<String>>,
    pub issuer_name: Option<String>,
    pub issuer_country: Option<String>,
    pub payer_country: Option<Vec<String>>,
    pub is_stored: Option<bool>,
    pub swift_code: Option<String>,
    pub direct_debit_token: Option<String>,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub payment_method_data: Option<Encryption>,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<serde_json::Value>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: Option<Encryption>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: Option<Encryption>,
    pub external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub vault_type: Option<storage_enums::VaultType>,
    pub created_by: Option<String>,
    pub last_modified_by: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_methods)]
pub struct PaymentMethodNew {
    pub customer_id: common_utils::id_type::GlobalCustomerId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub payment_method_data: Option<Encryption>,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<CommonMandateReference>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: Option<Encryption>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: Option<Encryption>,
    pub external_vault_token_data: Option<Encryption>,
    pub locker_fingerprint_id: Option<String>,
    pub payment_method_type_v2: Option<storage_enums::PaymentMethod>,
    pub payment_method_subtype: Option<storage_enums::PaymentMethodType>,
    pub id: common_utils::id_type::GlobalPaymentMethodId,
    pub vault_type: Option<storage_enums::VaultType>,
    pub created_by: Option<String>,
    pub last_modified_by: Option<String>,
}

impl PaymentMethodNew {
    pub fn update_storage_scheme(&mut self, storage_scheme: MerchantStorageScheme) {
        self.updated_by = Some(storage_scheme.to_string());
    }

    #[cfg(feature = "v1")]
    pub fn get_id(&self) -> &String {
        &self.payment_method_id
    }

    #[cfg(feature = "v2")]
    pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId {
        &self.id
    }
}

#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct TokenizeCoreWorkflow {
    pub lookup_key: String,
    pub pm: storage_enums::PaymentMethod,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize, Deserialize)]
pub enum PaymentMethodUpdate {
    MetadataUpdateAndLastUsed {
        metadata: Option<serde_json::Value>,
        last_used_at: PrimitiveDateTime,
        last_modified_by: Option<String>,
    },
    UpdatePaymentMethodDataAndLastUsed {
        payment_method_data: Option<Encryption>,
        scheme: Option<String>,
        last_used_at: PrimitiveDateTime,
        last_modified_by: Option<String>,
    },
    PaymentMethodDataUpdate {
        payment_method_data: Option<Encryption>,
        last_modified_by: Option<String>,
    },
    LastUsedUpdate {
        last_used_at: PrimitiveDateTime,
    },
    NetworkTransactionIdAndStatusUpdate {
        network_transaction_id: Option<String>,
        status: Option<storage_enums::PaymentMethodStatus>,
        last_modified_by: Option<String>,
    },
    StatusUpdate {
        status: Option<storage_enums::PaymentMethodStatus>,
        last_modified_by: Option<String>,
    },
    AdditionalDataUpdate {
        payment_method_data: Option<Encryption>,
        status: Option<storage_enums::PaymentMethodStatus>,
        locker_id: Option<String>,
        payment_method: Option<storage_enums::PaymentMethod>,
        payment_method_type: Option<storage_enums::PaymentMethodType>,
        payment_method_issuer: Option<String>,
        network_token_requestor_reference_id: Option<String>,
        network_token_locker_id: Option<String>,
        network_token_payment_method_data: Option<Encryption>,
        last_modified_by: Option<String>,
    },
    ConnectorMandateDetailsUpdate {
        connector_mandate_details: Option<serde_json::Value>,
        last_modified_by: Option<String>,
    },
    NetworkTokenDataUpdate {
        network_token_requestor_reference_id: Option<String>,
        network_token_locker_id: Option<String>,
        network_token_payment_method_data: Option<Encryption>,
        last_modified_by: Option<String>,
    },
    ConnectorNetworkTransactionIdAndMandateDetailsUpdate {
        connector_mandate_details: Option<pii::SecretSerdeValue>,
        network_transaction_id: Option<Secret<String>>,
        last_modified_by: Option<String>,
    },
    PaymentMethodBatchUpdate {
        connector_mandate_details: Option<pii::SecretSerdeValue>,
        network_transaction_id: Option<String>,
        status: Option<storage_enums::PaymentMethodStatus>,
        payment_method_data: Option<Encryption>,
        last_modified_by: Option<String>,
    },
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize, Deserialize)]
pub enum PaymentMethodUpdate {
    UpdatePaymentMethodDataAndLastUsed {
        payment_method_data: Option<Encryption>,
        scheme: Option<String>,
        last_used_at: PrimitiveDateTime,
        last_modified_by: Option<String>,
    },
    PaymentMethodDataUpdate {
        payment_method_data: Option<Encryption>,
        last_modified_by: Option<String>,
    },
    LastUsedUpdate {
        last_used_at: PrimitiveDateTime,
    },
    NetworkTransactionIdAndStatusUpdate {
        network_transaction_id: Option<String>,
        status: Option<storage_enums::PaymentMethodStatus>,
        last_modified_by: Option<String>,
    },
    StatusUpdate {
        status: Option<storage_enums::PaymentMethodStatus>,
        last_modified_by: Option<String>,
    },
    GenericUpdate {
        payment_method_data: Option<Encryption>,
        status: Option<storage_enums::PaymentMethodStatus>,
        locker_id: Option<String>,
        payment_method_type_v2: Option<storage_enums::PaymentMethod>,
        payment_method_subtype: Option<storage_enums::PaymentMethodType>,
        network_token_requestor_reference_id: Option<String>,
        network_token_locker_id: Option<String>,
        network_token_payment_method_data: Option<Encryption>,
        locker_fingerprint_id: Option<String>,
        connector_mandate_details: Option<CommonMandateReference>,
        external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
        last_modified_by: Option<String>,
    },
    ConnectorMandateDetailsUpdate {
        connector_mandate_details: Option<CommonMandateReference>,
        last_modified_by: Option<String>,
    },
}

impl PaymentMethodUpdate {
    pub fn convert_to_payment_method_update(
        self,
        storage_scheme: MerchantStorageScheme,
    ) -> PaymentMethodUpdateInternal {
        let mut update_internal: PaymentMethodUpdateInternal = self.into();
        update_internal.updated_by = Some(storage_scheme.to_string());
        update_internal
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_methods)]
pub struct PaymentMethodUpdateInternal {
    payment_method_data: Option<Encryption>,
    last_used_at: Option<PrimitiveDateTime>,
    network_transaction_id: Option<String>,
    status: Option<storage_enums::PaymentMethodStatus>,
    locker_id: Option<String>,
    payment_method_type_v2: Option<storage_enums::PaymentMethod>,
    connector_mandate_details: Option<CommonMandateReference>,
    updated_by: Option<String>,
    payment_method_subtype: Option<storage_enums::PaymentMethodType>,
    last_modified: PrimitiveDateTime,
    network_token_requestor_reference_id: Option<String>,
    network_token_locker_id: Option<String>,
    network_token_payment_method_data: Option<Encryption>,
    locker_fingerprint_id: Option<String>,
    external_vault_source: Option<common_utils::id_type::MerchantConnectorAccountId>,
    last_modified_by: Option<String>,
}

#[cfg(feature = "v2")]
impl PaymentMethodUpdateInternal {
    pub fn apply_changeset(self, source: PaymentMethod) -> PaymentMethod {
        let Self {
            payment_method_data,
            last_used_at,
            network_transaction_id,
            status,
            locker_id,
            payment_method_type_v2,
            connector_mandate_details,
            updated_by,
            payment_method_subtype,
            last_modified,
            network_token_requestor_reference_id,
            network_token_locker_id,
            network_token_payment_method_data,
            locker_fingerprint_id,
            external_vault_source,
            last_modified_by,
        } = self;

        PaymentMethod {
            customer_id: source.customer_id,
            merchant_id: source.merchant_id,
            created_at: source.created_at,
            last_modified,
            payment_method_data: payment_method_data.or(source.payment_method_data),
            locker_id: locker_id.or(source.locker_id),
            last_used_at: last_used_at.unwrap_or(source.last_used_at),
            connector_mandate_details: connector_mandate_details
                .or(source.connector_mandate_details),
            customer_acceptance: source.customer_acceptance,
            status: status.unwrap_or(source.status),
            network_transaction_id: network_transaction_id.or(source.network_transaction_id),
            client_secret: source.client_secret,
            payment_method_billing_address: source.payment_method_billing_address,
            updated_by: updated_by.or(source.updated_by),
            locker_fingerprint_id: locker_fingerprint_id.or(source.locker_fingerprint_id),
            payment_method_type_v2: payment_method_type_v2.or(source.payment_method_type_v2),
            payment_method_subtype: payment_method_subtype.or(source.payment_method_subtype),
            id: source.id,
            version: source.version,
            network_token_requestor_reference_id: network_token_requestor_reference_id
                .or(source.network_token_requestor_reference_id),
            network_token_locker_id: network_token_locker_id.or(source.network_token_locker_id),
            network_token_payment_method_data: network_token_payment_method_data
                .or(source.network_token_payment_method_data),
            external_vault_source: external_vault_source.or(source.external_vault_source),
            external_vault_token_data: source.external_vault_token_data,
            vault_type: source.vault_type,
            created_by: source.created_by,
            last_modified_by: last_modified_by.or(source.last_modified_by),
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_methods)]
pub struct PaymentMethodUpdateInternal {
    metadata: Option<serde_json::Value>,
    payment_method_data: Option<Encryption>,
    last_used_at: Option<PrimitiveDateTime>,
    network_transaction_id: Option<String>,
    status: Option<storage_enums::PaymentMethodStatus>,
    locker_id: Option<String>,
    network_token_requestor_reference_id: Option<String>,
    payment_method: Option<storage_enums::PaymentMethod>,
    connector_mandate_details: Option<serde_json::Value>,
    updated_by: Option<String>,
    payment_method_type: Option<storage_enums::PaymentMethodType>,
    payment_method_issuer: Option<String>,
    last_modified: PrimitiveDateTime,
    network_token_locker_id: Option<String>,
    network_token_payment_method_data: Option<Encryption>,
    scheme: Option<String>,
    last_modified_by: Option<String>,
}

#[cfg(feature = "v1")]
impl PaymentMethodUpdateInternal {
    pub fn apply_changeset(self, source: PaymentMethod) -> PaymentMethod {
        let Self {
            metadata,
            payment_method_data,
            last_used_at,
            network_transaction_id,
            status,
            locker_id,
            network_token_requestor_reference_id,
            payment_method,
            connector_mandate_details,
            updated_by,
            payment_method_type,
            payment_method_issuer,
            last_modified,
            network_token_locker_id,
            network_token_payment_method_data,
            scheme,
            last_modified_by,
        } = self;

        PaymentMethod {
            customer_id: source.customer_id,
            merchant_id: source.merchant_id,
            payment_method_id: source.payment_method_id,
            accepted_currency: source.accepted_currency,
            scheme: scheme.or(source.scheme),
            token: source.token,
            cardholder_name: source.cardholder_name,
            issuer_name: source.issuer_name,
            issuer_country: source.issuer_country,
            payer_country: source.payer_country,
            is_stored: source.is_stored,
            swift_code: source.swift_code,
            direct_debit_token: source.direct_debit_token,
            created_at: source.created_at,
            last_modified,
            payment_method: payment_method.or(source.payment_method),
            payment_method_type: payment_method_type.or(source.payment_method_type),
            payment_method_issuer: payment_method_issuer.or(source.payment_method_issuer),
            payment_method_issuer_code: source.payment_method_issuer_code,
            metadata: metadata.map_or(source.metadata, |v| Some(v.into())),
            payment_method_data: payment_method_data.or(source.payment_method_data),
            locker_id: locker_id.or(source.locker_id),
            last_used_at: last_used_at.unwrap_or(source.last_used_at),
            connector_mandate_details: connector_mandate_details
                .or(source.connector_mandate_details),
            customer_acceptance: source.customer_acceptance,
            status: status.unwrap_or(source.status),
            network_transaction_id: network_transaction_id.or(source.network_transaction_id),
            client_secret: source.client_secret,
            payment_method_billing_address: source.payment_method_billing_address,
            updated_by: updated_by.or(source.updated_by),
            version: source.version,
            network_token_requestor_reference_id: network_token_requestor_reference_id
                .or(source.network_token_requestor_reference_id),
            network_token_locker_id: network_token_locker_id.or(source.network_token_locker_id),
            network_token_payment_method_data: network_token_payment_method_data
                .or(source.network_token_payment_method_data),
            external_vault_source: source.external_vault_source,
            vault_type: source.vault_type,
            created_by: source.created_by,
            last_modified_by: last_modified_by.or(source.last_modified_by),
        }
    }
}

#[cfg(feature = "v1")]
impl From<PaymentMethodUpdate> for PaymentMethodUpdateInternal {
    fn from(payment_method_update: PaymentMethodUpdate) -> Self {
        match payment_method_update {
            PaymentMethodUpdate::MetadataUpdateAndLastUsed {
                metadata,
                last_used_at,
                last_modified_by,
            } => Self {
                metadata,
                payment_method_data: None,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
                last_modified_by,
            },
            PaymentMethodUpdate::PaymentMethodDataUpdate {
                payment_method_data,
                last_modified_by,
            } => Self {
                metadata: None,
                payment_method_data,
                last_used_at: None,
                network_transaction_id: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
                last_modified_by,
            },
            PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
                last_modified_by: None,
            },
            PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed {
                payment_method_data,
                scheme,
                last_used_at,
                last_modified_by,
            } => Self {
                metadata: None,
                payment_method_data,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme,
                last_modified_by,
            },
            PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate {
                network_transaction_id,
                status,
                last_modified_by,
            } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                network_transaction_id,
                status,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
                last_modified_by,
            },
            PaymentMethodUpdate::StatusUpdate {
                status,
                last_modified_by,
            } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                network_transaction_id: None,
                status,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
                last_modified_by,
            },
            PaymentMethodUpdate::AdditionalDataUpdate {
                payment_method_data,
                status,
                locker_id,
                network_token_requestor_reference_id,
                payment_method,
                payment_method_type,
                payment_method_issuer,
                network_token_locker_id,
                network_token_payment_method_data,
                last_modified_by,
            } => Self {
                metadata: None,
                payment_method_data,
                last_used_at: None,
                network_transaction_id: None,
                status,
                locker_id,
                network_token_requestor_reference_id,
                payment_method,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer,
                payment_method_type,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id,
                network_token_payment_method_data,
                scheme: None,
                last_modified_by,
            },
            PaymentMethodUpdate::ConnectorMandateDetailsUpdate {
                connector_mandate_details,
                last_modified_by,
            } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                status: None,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details,
                network_transaction_id: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
                last_modified_by,
            },
            PaymentMethodUpdate::NetworkTokenDataUpdate {
                network_token_requestor_reference_id,
                network_token_locker_id,
                network_token_payment_method_data,
                last_modified_by,
            } => Self {
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                status: None,
                locker_id: None,
                payment_method: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_transaction_id: None,
                network_token_requestor_reference_id,
                network_token_locker_id,
                network_token_payment_method_data,
                scheme: None,
                last_modified_by,
            },
            PaymentMethodUpdate::ConnectorNetworkTransactionIdAndMandateDetailsUpdate {
                connector_mandate_details,
                network_transaction_id,
                last_modified_by,
            } => Self {
                connector_mandate_details: connector_mandate_details
                    .map(|mandate_details| mandate_details.expose()),
                network_transaction_id: network_transaction_id.map(|txn_id| txn_id.expose()),
                last_modified: common_utils::date_time::now(),
                status: None,
                metadata: None,
                payment_method_data: None,
                last_used_at: None,
                locker_id: None,
                payment_method: None,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                network_token_requestor_reference_id: None,
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
                last_modified_by,
            },
            PaymentMethodUpdate::PaymentMethodBatchUpdate {
                connector_mandate_details,
                network_transaction_id,
                status,
                payment_method_data,
                last_modified_by,
            } => Self {
                metadata: None,
                last_used_at: None,
                status,
                locker_id: None,
                network_token_requestor_reference_id: None,
                payment_method: None,
                connector_mandate_details: connector_mandate_details
                    .map(|mandate_details| mandate_details.expose()),
                network_transaction_id,
                updated_by: None,
                payment_method_issuer: None,
                payment_method_type: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                scheme: None,
                payment_method_data,
                last_modified_by,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl From<PaymentMethodUpdate> for PaymentMethodUpdateInternal {
    fn from(payment_method_update: PaymentMethodUpdate) -> Self {
        match payment_method_update {
            PaymentMethodUpdate::PaymentMethodDataUpdate {
                payment_method_data,
                last_modified_by,
            } => Self {
                payment_method_data,
                last_used_at: None,
                network_transaction_id: None,
                status: None,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
                last_modified_by,
            },
            PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self {
                payment_method_data: None,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
                last_modified_by: None,
            },
            PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed {
                payment_method_data,
                last_used_at,
                last_modified_by,
                ..
            } => Self {
                payment_method_data,
                last_used_at: Some(last_used_at),
                network_transaction_id: None,
                status: None,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
                last_modified_by,
            },
            PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate {
                network_transaction_id,
                status,
                last_modified_by,
            } => Self {
                payment_method_data: None,
                last_used_at: None,
                network_transaction_id,
                status,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
                last_modified_by,
            },
            PaymentMethodUpdate::StatusUpdate {
                status,
                last_modified_by,
            } => Self {
                payment_method_data: None,
                last_used_at: None,
                network_transaction_id: None,
                status,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
                last_modified_by,
            },
            PaymentMethodUpdate::GenericUpdate {
                payment_method_data,
                status,
                locker_id,
                payment_method_type_v2,
                payment_method_subtype,
                network_token_requestor_reference_id,
                network_token_locker_id,
                network_token_payment_method_data,
                locker_fingerprint_id,
                connector_mandate_details,
                external_vault_source,
                last_modified_by,
            } => Self {
                payment_method_data,
                last_used_at: None,
                network_transaction_id: None,
                status,
                locker_id,
                payment_method_type_v2,
                connector_mandate_details,
                updated_by: None,
                payment_method_subtype,
                last_modified: common_utils::date_time::now(),
                network_token_requestor_reference_id,
                network_token_locker_id,
                network_token_payment_method_data,
                locker_fingerprint_id,
                external_vault_source,
                last_modified_by,
            },
            PaymentMethodUpdate::ConnectorMandateDetailsUpdate {
                connector_mandate_details,
                last_modified_by,
            } => Self {
                payment_method_data: None,
                last_used_at: None,
                status: None,
                locker_id: None,
                payment_method_type_v2: None,
                connector_mandate_details,
                network_transaction_id: None,
                updated_by: None,
                payment_method_subtype: None,
                last_modified: common_utils::date_time::now(),
                network_token_locker_id: None,
                network_token_requestor_reference_id: None,
                network_token_payment_method_data: None,
                locker_fingerprint_id: None,
                external_vault_source: None,
                last_modified_by,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl From<&PaymentMethodNew> for PaymentMethod {
    fn from(payment_method_new: &PaymentMethodNew) -> Self {
        Self {
            customer_id: payment_method_new.customer_id.clone(),
            merchant_id: payment_method_new.merchant_id.clone(),
            payment_method_id: payment_method_new.payment_method_id.clone(),
            locker_id: payment_method_new.locker_id.clone(),
            network_token_requestor_reference_id: payment_method_new
                .network_token_requestor_reference_id
                .clone(),
            accepted_currency: payment_method_new.accepted_currency.clone(),
            scheme: payment_method_new.scheme.clone(),
            token: payment_method_new.token.clone(),
            cardholder_name: payment_method_new.cardholder_name.clone(),
            issuer_name: payment_method_new.issuer_name.clone(),
            issuer_country: payment_method_new.issuer_country.clone(),
            payer_country: payment_method_new.payer_country.clone(),
            is_stored: payment_method_new.is_stored,
            swift_code: payment_method_new.swift_code.clone(),
            direct_debit_token: payment_method_new.direct_debit_token.clone(),
            created_at: payment_method_new.created_at,
            last_modified: payment_method_new.last_modified,
            payment_method: payment_method_new.payment_method,
            payment_method_type: payment_method_new.payment_method_type,
            payment_method_issuer: payment_method_new.payment_method_issuer.clone(),
            payment_method_issuer_code: payment_method_new.payment_method_issuer_code,
            metadata: payment_method_new.metadata.clone(),
            payment_method_data: payment_method_new.payment_method_data.clone(),
            last_used_at: payment_method_new.last_used_at,
            connector_mandate_details: payment_method_new.connector_mandate_details.clone(),
            customer_acceptance: payment_method_new.customer_acceptance.clone(),
            status: payment_method_new.status,
            network_transaction_id: payment_method_new.network_transaction_id.clone(),
            client_secret: payment_method_new.client_secret.clone(),
            updated_by: payment_method_new.updated_by.clone(),
            payment_method_billing_address: payment_method_new
                .payment_method_billing_address
                .clone(),
            version: payment_method_new.version,
            network_token_locker_id: payment_method_new.network_token_locker_id.clone(),
            network_token_payment_method_data: payment_method_new
                .network_token_payment_method_data
                .clone(),
            external_vault_source: payment_method_new.external_vault_source.clone(),
            vault_type: payment_method_new.vault_type,
            created_by: payment_method_new.created_by.clone(),
            last_modified_by: payment_method_new.last_modified_by.clone(),
        }
    }
}

#[cfg(feature = "v2")]
impl From<&PaymentMethodNew> for PaymentMethod {
    fn from(payment_method_new: &PaymentMethodNew) -> Self {
        Self {
            customer_id: payment_method_new.customer_id.clone(),
            merchant_id: payment_method_new.merchant_id.clone(),
            locker_id: payment_method_new.locker_id.clone(),
            created_at: payment_method_new.created_at,
            last_modified: payment_method_new.last_modified,
            payment_method_data: payment_method_new.payment_method_data.clone(),
            last_used_at: payment_method_new.last_used_at,
            connector_mandate_details: payment_method_new.connector_mandate_details.clone(),
            customer_acceptance: payment_method_new.customer_acceptance.clone(),
            status: payment_method_new.status,
            network_transaction_id: payment_method_new.network_transaction_id.clone(),
            client_secret: payment_method_new.client_secret.clone(),
            updated_by: payment_method_new.updated_by.clone(),
            payment_method_billing_address: payment_method_new
                .payment_method_billing_address
                .clone(),
            locker_fingerprint_id: payment_method_new.locker_fingerprint_id.clone(),
            payment_method_type_v2: payment_method_new.payment_method_type_v2,
            payment_method_subtype: payment_method_new.payment_method_subtype,
            id: payment_method_new.id.clone(),
            version: payment_method_new.version,
            network_token_requestor_reference_id: payment_method_new
                .network_token_requestor_reference_id
                .clone(),
            network_token_locker_id: payment_method_new.network_token_locker_id.clone(),
            network_token_payment_method_data: payment_method_new
                .network_token_payment_method_data
                .clone(),
            external_vault_source: None,
            external_vault_token_data: payment_method_new.external_vault_token_data.clone(),
            vault_type: payment_method_new.vault_type,
            created_by: payment_method_new.created_by.clone(),
            last_modified_by: payment_method_new.last_modified_by.clone(),
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsMandateReferenceRecord {
    pub connector_mandate_id: String,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<i64>,
    pub original_payment_authorized_currency: Option<common_enums::Currency>,
    pub mandate_metadata: Option<pii::SecretSerdeValue>,
    pub connector_mandate_status: Option<common_enums::ConnectorMandateStatus>,
    pub connector_mandate_request_reference_id: Option<String>,
    pub connector_customer_id: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConnectorTokenReferenceRecord {
    pub connector_token: String,
    pub payment_method_subtype: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<common_utils::types::MinorUnit>,
    pub original_payment_authorized_currency: Option<common_enums::Currency>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_token_status: common_enums::ConnectorTokenStatus,
    pub connector_token_request_reference_id: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct PaymentsMandateReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>,
);

#[cfg(feature = "v1")]
impl std::ops::Deref for PaymentsMandateReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "v1")]
impl std::ops::DerefMut for PaymentsMandateReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct PaymentsTokenReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>,
);

#[cfg(feature = "v2")]
impl std::ops::Deref for PaymentsTokenReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "v2")]
impl std::ops::DerefMut for PaymentsTokenReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v1")]
common_utils::impl_to_sql_from_sql_json!(PaymentsMandateReference);

#[cfg(feature = "v2")]
common_utils::impl_to_sql_from_sql_json!(PaymentsTokenReference);

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PayoutsMandateReferenceRecord {
    pub transfer_method_id: Option<String>,
    pub connector_customer_id: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct PayoutsMandateReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>,
);

impl std::ops::Deref for PayoutsMandateReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl std::ops::DerefMut for PayoutsMandateReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsMandateReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsTokenReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

impl CommonMandateReference {
    pub fn get_mandate_details_value(&self) -> CustomResult<serde_json::Value, ParsingError> {
        let mut payments = self
            .payments
            .as_ref()
            .map_or_else(|| Ok(serde_json::json!({})), serde_json::to_value)
            .change_context(ParsingError::StructParseFailure("payment mandate details"))?;

        self.payouts
            .as_ref()
            .map(|payouts_mandate| {
                serde_json::to_value(payouts_mandate).map(|payouts_mandate_value| {
                    payments.as_object_mut().map(|payments_object| {
                        payments_object.insert("payouts".to_string(), payouts_mandate_value);
                    })
                })
            })
            .transpose()
            .change_context(ParsingError::StructParseFailure("payout mandate details"))?;

        Ok(payments)
    }

    #[cfg(feature = "v2")]
    /// Insert a new payment token reference for the given connector_id
    pub fn insert_payment_token_reference_record(
        &mut self,
        connector_id: &common_utils::id_type::MerchantConnectorAccountId,
        record: ConnectorTokenReferenceRecord,
    ) {
        match self.payments {
            Some(ref mut payments_reference) => {
                payments_reference.insert(connector_id.clone(), record);
            }
            None => {
                let mut payments_reference = HashMap::new();
                payments_reference.insert(connector_id.clone(), record);
                self.payments = Some(PaymentsTokenReference(payments_reference));
            }
        }
    }
}

impl diesel::serialize::ToSql<diesel::sql_types::Jsonb, diesel::pg::Pg> for CommonMandateReference {
    fn to_sql<'b>(
        &'b self,
        out: &mut diesel::serialize::Output<'b, '_, diesel::pg::Pg>,
    ) -> diesel::serialize::Result {
        let payments = self.get_mandate_details_value()?;

        <serde_json::Value as diesel::serialize::ToSql<
            diesel::sql_types::Jsonb,
            diesel::pg::Pg,
        >>::to_sql(&payments, &mut out.reborrow())
    }
}

#[cfg(feature = "v1")]
impl<DB: diesel::backend::Backend> diesel::deserialize::FromSql<diesel::sql_types::Jsonb, DB>
    for CommonMandateReference
where
    serde_json::Value: diesel::deserialize::FromSql<diesel::sql_types::Jsonb, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
        let value = <serde_json::Value as diesel::deserialize::FromSql<
            diesel::sql_types::Jsonb,
            DB,
        >>::from_sql(bytes)?;

        let payments_data = value
            .clone()
            .as_object_mut()
            .map(|obj| {
                obj.remove("payouts");

                serde_json::from_value::<PaymentsMandateReference>(serde_json::Value::Object(
                    obj.clone(),
                ))
                .inspect_err(|err| {
                    router_env::logger::error!("Failed to parse payments data: {}", err);
                })
                .change_context(ParsingError::StructParseFailure(
                    "Failed to parse payments data",
                ))
            })
            .transpose()?;

        let payouts_data = serde_json::from_value::<Option<Self>>(value)
            .inspect_err(|err| {
                router_env::logger::error!("Failed to parse payouts data: {}", err);
            })
            .change_context(ParsingError::StructParseFailure(
                "Failed to parse payouts data",
            ))
            .map(|optional_common_mandate_details| {
                optional_common_mandate_details
                    .and_then(|common_mandate_details| common_mandate_details.payouts)
            })?;

        Ok(Self {
            payments: payments_data,
            payouts: payouts_data,
        })
    }
}

#[cfg(feature = "v2")]
impl<DB: diesel::backend::Backend> diesel::deserialize::FromSql<diesel::sql_types::Jsonb, DB>
    for CommonMandateReference
where
    serde_json::Value: diesel::deserialize::FromSql<diesel::sql_types::Jsonb, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
        let value = <serde_json::Value as diesel::deserialize::FromSql<
            diesel::sql_types::Jsonb,
            DB,
        >>::from_sql(bytes)?;

        let payments_data = value
            .clone()
            .as_object_mut()
            .map(|obj| {
                obj.remove("payouts");

                serde_json::from_value::<PaymentsTokenReference>(serde_json::Value::Object(
                    obj.clone(),
                ))
                .inspect_err(|err| {
                    router_env::logger::error!("Failed to parse payments data: {}", err);
                })
                .change_context(ParsingError::StructParseFailure(
                    "Failed to parse payments data",
                ))
            })
            .transpose()?;

        let payouts_data = serde_json::from_value::<Option<Self>>(value)
            .inspect_err(|err| {
                router_env::logger::error!("Failed to parse payouts data: {}", err);
            })
            .change_context(ParsingError::StructParseFailure(
                "Failed to parse payouts data",
            ))
            .map(|optional_common_mandate_details| {
                optional_common_mandate_details
                    .and_then(|common_mandate_details| common_mandate_details.payouts)
            })?;

        Ok(Self {
            payments: payments_data,
            payouts: payouts_data,
        })
    }
}

#[cfg(feature = "v1")]
impl From<PaymentsMandateReference> for CommonMandateReference {
    fn from(payment_reference: PaymentsMandateReference) -> Self {
        Self {
            payments: Some(payment_reference),
            payouts: None,
        }
    }
}


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

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

use common_utils::{encryption::Encryption, id_type, pii};
#[cfg(feature = "v2")]
use diesel::{sql_types::Jsonb, AsExpression};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};

use crate::enums as storage_enums;
#[cfg(feature = "v1")]
use crate::schema::merchant_connector_account;
#[cfg(feature = "v2")]
use crate::schema_v2::merchant_connector_account;

#[cfg(feature = "v1")]
#[derive(
    Clone,
    Debug,
    serde::Serialize,
    serde::Deserialize,
    Identifiable,
    Queryable,
    Selectable,
    router_derive::DebugAsDisplay,
)]
#[diesel(table_name = merchant_connector_account, primary_key(merchant_connector_id), check_for_backend(diesel::pg::Pg))]
pub struct MerchantConnectorAccount {
    pub merchant_id: id_type::MerchantId,
    pub connector_name: String,
    pub connector_account_details: Encryption,
    pub test_mode: Option<bool>,
    pub disabled: Option<bool>,
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
    pub connector_type: storage_enums::ConnectorType,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub business_sub_label: Option<String>,
    pub frm_configs: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    pub profile_id: Option<id_type::ProfileId>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: Option<id_type::MerchantConnectorAccountId>,
}

#[cfg(feature = "v1")]
impl MerchantConnectorAccount {
    pub fn get_id(&self) -> id_type::MerchantConnectorAccountId {
        self.merchant_connector_id.clone()
    }
}

#[cfg(feature = "v2")]
use crate::RequiredFromNullable;

#[cfg(feature = "v2")]
#[derive(
    Clone,
    Debug,
    serde::Serialize,
    serde::Deserialize,
    Identifiable,
    Queryable,
    Selectable,
    router_derive::DebugAsDisplay,
)]
#[diesel(table_name = merchant_connector_account, check_for_backend(diesel::pg::Pg))]
pub struct MerchantConnectorAccount {
    pub merchant_id: id_type::MerchantId,
    pub connector_name: common_enums::connector_enums::Connector,
    pub connector_account_details: Encryption,
    pub disabled: Option<bool>,
    #[diesel(deserialize_as = super::OptionalDieselArray<common_types::payment_methods::PaymentMethodsEnabled>)]
    pub payment_methods_enabled: Option<Vec<common_types::payment_methods::PaymentMethodsEnabled>>,
    pub connector_type: storage_enums::ConnectorType,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    #[diesel(deserialize_as = RequiredFromNullable<id_type::ProfileId>)]
    pub profile_id: id_type::ProfileId,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: id_type::MerchantConnectorAccountId,
    pub feature_metadata: Option<MerchantConnectorAccountFeatureMetadata>,
}

#[cfg(feature = "v2")]
impl MerchantConnectorAccount {
    pub fn get_id(&self) -> id_type::MerchantConnectorAccountId {
        self.id.clone()
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_connector_account)]
pub struct MerchantConnectorAccountNew {
    pub merchant_id: Option<id_type::MerchantId>,
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_name: Option<String>,
    pub connector_account_details: Option<Encryption>,
    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 metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub business_sub_label: Option<String>,
    pub frm_configs: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    pub profile_id: Option<id_type::ProfileId>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: Option<id_type::MerchantConnectorAccountId>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_connector_account)]
pub struct MerchantConnectorAccountNew {
    pub merchant_id: Option<id_type::MerchantId>,
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_name: Option<common_enums::connector_enums::Connector>,
    pub connector_account_details: Option<Encryption>,
    pub disabled: Option<bool>,
    #[diesel(deserialize_as = super::OptionalDieselArray<common_types::payment_methods::PaymentMethodsEnabled>)]
    pub payment_methods_enabled: Option<Vec<common_types::payment_methods::PaymentMethodsEnabled>>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    pub profile_id: id_type::ProfileId,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: id_type::MerchantConnectorAccountId,
    pub feature_metadata: Option<MerchantConnectorAccountFeatureMetadata>,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_connector_account)]
pub struct MerchantConnectorAccountUpdateInternal {
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_name: Option<String>,
    pub connector_account_details: Option<Encryption>,
    pub connector_label: Option<String>,
    pub test_mode: Option<bool>,
    pub disabled: Option<bool>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
    pub frm_configs: Option<pii::SecretSerdeValue>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: Option<time::PrimitiveDateTime>,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: Option<storage_enums::ConnectorStatus>,
    pub connector_wallets_details: Option<Encryption>,
    pub additional_merchant_data: Option<Encryption>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_connector_account)]
pub struct MerchantConnectorAccountUpdateInternal {
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_account_details: Option<Encryption>,
    pub connector_label: Option<String>,
    pub disabled: Option<bool>,
    #[diesel(deserialize_as = super::OptionalDieselArray<common_types::payment_methods::PaymentMethodsEnabled>)]
    pub payment_methods_enabled: Option<Vec<common_types::payment_methods::PaymentMethodsEnabled>>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: Option<time::PrimitiveDateTime>,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: Option<storage_enums::ConnectorStatus>,
    pub connector_wallets_details: Option<Encryption>,
    pub additional_merchant_data: Option<Encryption>,
    pub feature_metadata: Option<MerchantConnectorAccountFeatureMetadata>,
}

#[cfg(feature = "v1")]
impl MerchantConnectorAccountUpdateInternal {
    pub fn create_merchant_connector_account(
        self,
        source: MerchantConnectorAccount,
    ) -> MerchantConnectorAccount {
        MerchantConnectorAccount {
            merchant_id: source.merchant_id,
            connector_type: self.connector_type.unwrap_or(source.connector_type),
            connector_account_details: self
                .connector_account_details
                .unwrap_or(source.connector_account_details),
            test_mode: self.test_mode,
            disabled: self.disabled,
            merchant_connector_id: self
                .merchant_connector_id
                .unwrap_or(source.merchant_connector_id),
            payment_methods_enabled: self.payment_methods_enabled,
            frm_config: self.frm_config,
            modified_at: self.modified_at.unwrap_or(source.modified_at),
            pm_auth_config: self.pm_auth_config,
            status: self.status.unwrap_or(source.status),

            ..source
        }
    }
}

#[cfg(feature = "v2")]
impl MerchantConnectorAccountUpdateInternal {
    pub fn create_merchant_connector_account(
        self,
        source: MerchantConnectorAccount,
    ) -> MerchantConnectorAccount {
        MerchantConnectorAccount {
            connector_type: self.connector_type.unwrap_or(source.connector_type),
            connector_account_details: self
                .connector_account_details
                .unwrap_or(source.connector_account_details),
            disabled: self.disabled,
            payment_methods_enabled: self.payment_methods_enabled,
            frm_config: self.frm_config,
            modified_at: self.modified_at.unwrap_or(source.modified_at),
            pm_auth_config: self.pm_auth_config,
            status: self.status.unwrap_or(source.status),
            feature_metadata: self.feature_metadata,

            ..source
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, AsExpression)]
#[diesel(sql_type = Jsonb)]
pub struct MerchantConnectorAccountFeatureMetadata {
    pub revenue_recovery: Option<RevenueRecoveryMetadata>,
}

#[cfg(feature = "v2")]
common_utils::impl_to_sql_from_sql_json!(MerchantConnectorAccountFeatureMetadata);

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct RevenueRecoveryMetadata {
    /// The maximum number of retries allowed for an invoice. This limit is set by the merchant for each `billing connector`.Once this limit is reached, no further retries will be attempted.
    pub max_retry_count: u16,
    /// Maximum number of `billing connector` retries before revenue recovery can start executing retries.
    pub billing_connector_retry_threshold: u16,
    /// Billing account reference id is payment gateway id at billing connector end.
    /// Merchants need to provide a mapping between these merchant connector account and the corresponding
    /// account reference IDs for each `billing connector`.
    pub billing_account_reference: BillingAccountReference,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct BillingAccountReference(pub HashMap<id_type::MerchantConnectorAccountId, String>);


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

use std::convert::From;

use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};

use crate::schema::configs;

#[derive(Default, Clone, Debug, Insertable, Serialize, Deserialize)]
#[diesel(table_name = configs)]
pub struct ConfigNew {
    pub key: String,
    pub config: String,
}

#[derive(Default, Clone, Debug, Identifiable, Queryable, Selectable, Deserialize, Serialize)]
#[diesel(table_name = configs, primary_key(key), check_for_backend(diesel::pg::Pg))]
pub struct Config {
    pub key: String,
    pub config: String,
}

#[derive(Debug)]
pub enum ConfigUpdate {
    Update { config: Option<String> },
}

#[derive(Clone, Debug, AsChangeset, Default)]
#[diesel(table_name = configs)]
pub struct ConfigUpdateInternal {
    config: Option<String>,
}

impl ConfigUpdateInternal {
    pub fn create_config(self, source: Config) -> Config {
        Config { ..source }
    }
}

impl From<ConfigUpdate> for ConfigUpdateInternal {
    fn from(config_update: ConfigUpdate) -> Self {
        match config_update {
            ConfigUpdate::Update { config } => Self { config },
        }
    }
}

impl From<ConfigNew> for Config {
    fn from(config_new: ConfigNew) -> Self {
        Self {
            key: config_new.key,
            config: config_new.config,
        }
    }
}


// File: crates/diesel_models/src/blocklist_fingerprint.rs

use diesel::{Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};

use crate::schema::blocklist_fingerprint;

#[derive(Clone, Debug, Eq, Insertable, PartialEq, Serialize, Deserialize)]
#[diesel(table_name = blocklist_fingerprint)]
pub struct BlocklistFingerprintNew {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub fingerprint_id: String,
    pub data_kind: common_enums::BlocklistDataKind,
    pub encrypted_fingerprint: String,
    pub created_at: time::PrimitiveDateTime,
}

#[derive(
    Clone, Debug, Eq, PartialEq, Queryable, Identifiable, Selectable, Deserialize, Serialize,
)]
#[diesel(table_name = blocklist_fingerprint, primary_key(merchant_id, fingerprint_id), check_for_backend(diesel::pg::Pg))]
pub struct BlocklistFingerprint {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub fingerprint_id: String,
    pub data_kind: common_enums::BlocklistDataKind,
    pub encrypted_fingerprint: String,
    pub created_at: time::PrimitiveDateTime,
}


// File: crates/diesel_models/src/lib.rs

pub mod address;
pub mod api_keys;
pub mod blocklist_lookup;
pub mod business_profile;
pub mod capture;
pub mod cards_info;
pub mod configs;

pub mod authentication;
pub mod authorization;
pub mod blocklist;
pub mod blocklist_fingerprint;
pub mod callback_mapper;
pub mod customers;
pub mod dispute;
pub mod dynamic_routing_stats;
pub mod enums;
pub mod ephemeral_key;
pub mod errors;
pub mod events;
pub mod file;
#[allow(unused)]
pub mod fraud_check;
pub mod generic_link;
pub mod gsm;
pub mod hyperswitch_ai_interaction;
pub mod invoice;
#[cfg(feature = "kv_store")]
pub mod kv;
pub mod locker_mock_up;
pub mod mandate;
pub mod merchant_account;
pub mod merchant_connector_account;
pub mod merchant_key_store;
pub mod organization;
pub mod payment_attempt;
pub mod payment_intent;
pub mod payment_link;
pub mod payment_method;
pub mod payout_attempt;
pub mod payouts;
pub mod process_tracker;
pub mod query;
pub mod refund;
pub mod relay;
pub mod reverse_lookup;
pub mod role;
pub mod routing_algorithm;
pub mod subscription;
pub mod types;
pub mod unified_translations;

#[cfg(feature = "v2")]
pub mod payment_methods_session;

#[allow(unused_qualifications)]
pub mod schema;
#[allow(unused_qualifications)]
pub mod schema_v2;

pub mod user;
pub mod user_authentication_method;
pub mod user_key_store;
pub mod user_role;

use diesel_impl::{DieselArray, OptionalDieselArray};
#[cfg(feature = "v2")]
use diesel_impl::{RequiredFromNullable, RequiredFromNullableWithDefault};

pub type StorageResult<T> = error_stack::Result<T, errors::DatabaseError>;
pub type PgPooledConn = async_bb8_diesel::Connection<diesel::PgConnection>;
pub use self::{
    address::*, api_keys::*, callback_mapper::*, cards_info::*, configs::*, customers::*,
    dispute::*, ephemeral_key::*, events::*, file::*, generic_link::*,
    hyperswitch_ai_interaction::*, locker_mock_up::*, mandate::*, merchant_account::*,
    merchant_connector_account::*, payment_attempt::*, payment_intent::*, payment_method::*,
    payout_attempt::*, payouts::*, process_tracker::*, refund::*, reverse_lookup::*,
    user_authentication_method::*,
};
/// The types and implementations provided by this module are required for the schema generated by
/// `diesel_cli` 2.0 to work with the types defined in Rust code. This is because
/// [`diesel`][diesel] 2.0 [changed the nullability of array elements][diesel-2.0-array-nullability],
/// which causes [`diesel`][diesel] to deserialize arrays as `Vec<Option<T>>`. To prevent declaring
/// array elements as `Option<T>`, this module provides types and implementations to deserialize
/// arrays as `Vec<T>`, considering only non-null values (`Some(T)`) among the deserialized
/// `Option<T>` values.
///
/// [diesel-2.0-array-nullability]: https://diesel.rs/guides/migration_guide.html#2-0-0-nullability-of-array-elements
#[doc(hidden)]
pub(crate) mod diesel_impl {
    #[cfg(feature = "v2")]
    use common_utils::{id_type, types};
    use diesel::{
        deserialize::FromSql,
        pg::Pg,
        sql_types::{Array, Nullable},
        Queryable,
    };

    #[cfg(feature = "v2")]
    use crate::enums;

    pub struct DieselArray<T>(Vec<Option<T>>);

    impl<T> From<DieselArray<T>> for Vec<T> {
        fn from(array: DieselArray<T>) -> Self {
            array.0.into_iter().flatten().collect()
        }
    }

    impl<T, U> Queryable<Array<Nullable<U>>, Pg> for DieselArray<T>
    where
        T: FromSql<U, Pg>,
        Vec<Option<T>>: FromSql<Array<Nullable<U>>, Pg>,
    {
        type Row = Vec<Option<T>>;

        fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
            Ok(Self(row))
        }
    }

    pub struct OptionalDieselArray<T>(Option<Vec<Option<T>>>);

    impl<T> From<OptionalDieselArray<T>> for Option<Vec<T>> {
        fn from(option_array: OptionalDieselArray<T>) -> Self {
            option_array
                .0
                .map(|array| array.into_iter().flatten().collect())
        }
    }

    impl<T, U> Queryable<Nullable<Array<Nullable<U>>>, Pg> for OptionalDieselArray<T>
    where
        Option<Vec<Option<T>>>: FromSql<Nullable<Array<Nullable<U>>>, Pg>,
    {
        type Row = Option<Vec<Option<T>>>;

        fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
            Ok(Self(row))
        }
    }
    #[cfg(feature = "v2")]
    /// If the DB value is null, this wrapper will return an error when deserializing.
    ///
    /// This is useful when you want to ensure that a field is always present, even if the database
    /// value is NULL. If the database column contains a NULL value, an error will be returned.
    pub struct RequiredFromNullable<T>(T);

    #[cfg(feature = "v2")]
    impl<T> RequiredFromNullable<T> {
        /// Extracts the inner value from the wrapper
        pub fn into_inner(self) -> T {
            self.0
        }
    }

    #[cfg(feature = "v2")]
    impl<T, ST, DB> Queryable<Nullable<ST>, DB> for RequiredFromNullable<T>
    where
        DB: diesel::backend::Backend,
        T: Queryable<ST, DB>,
        Option<T::Row>: FromSql<Nullable<ST>, DB>,
        ST: diesel::sql_types::SingleValue,
    {
        type Row = Option<T::Row>;

        fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
            match row {
                Some(inner_row) => {
                    let value = T::build(inner_row)?;
                    Ok(Self(value))
                }
                None => Err("Cannot deserialize NULL value for required field. Check if the database column that should not be NULL contains a NULL value.".into()),
            }
        }
    }

    #[cfg(feature = "v2")]
    /// If the DB value is null, this wrapper will provide a default value for the type `T`.
    ///
    /// This is useful when you want to ensure that a field is always present, even if the database
    /// value is NULL. The default value is provided by the `Default` trait implementation of `T`.
    pub struct RequiredFromNullableWithDefault<T>(T);
    #[cfg(feature = "v2")]
    impl<T> RequiredFromNullableWithDefault<T> {
        /// Extracts the inner value from the wrapper
        pub fn into_inner(self) -> T {
            self.0
        }
    }
    #[cfg(feature = "v2")]
    impl<T, ST, DB> Queryable<Nullable<ST>, DB> for RequiredFromNullableWithDefault<T>
    where
        DB: diesel::backend::Backend,
        T: Queryable<ST, DB>,
        T: Default,
        Option<T::Row>: FromSql<Nullable<ST>, DB>,
        ST: diesel::sql_types::SingleValue,
    {
        type Row = Option<T::Row>;

        fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
            match row {
                Some(inner_row) => {
                    let value = T::build(inner_row)?;
                    Ok(Self(value))
                }
                None => Ok(Self(T::default())),
            }
        }
    }

    #[cfg(feature = "v2")]
    /// Macro to implement From trait for types wrapped in RequiredFromNullable
    #[macro_export]
    macro_rules! impl_from_required_from_nullable {
        ($($type:ty),* $(,)?) => {
            $(
                impl From<$crate::RequiredFromNullable<$type>> for $type {
                    fn from(wrapper: $crate::RequiredFromNullable<$type>) -> Self {
                        wrapper.into_inner()
                    }
                }
            )*
        };
    }

    #[cfg(feature = "v2")]
    /// Macro to implement From trait for types wrapped in RequiredFromNullableWithDefault
    #[macro_export]
    macro_rules! impl_from_required_from_nullable_with_default {
        ($($type:ty),* $(,)?) => {
            $(
                impl From<$crate::RequiredFromNullableWithDefault<$type>> for $type {
                    fn from(wrapper: $crate::RequiredFromNullableWithDefault<$type>) -> Self {
                        wrapper.into_inner()
                    }
                }
            )*
        };
    }
    #[cfg(feature = "v2")]
    crate::impl_from_required_from_nullable_with_default!(enums::DeleteStatus);

    #[cfg(feature = "v2")]
    crate::impl_from_required_from_nullable!(
        enums::AuthenticationType,
        types::MinorUnit,
        enums::PaymentMethod,
        enums::Currency,
        id_type::ProfileId,
        time::PrimitiveDateTime,
        id_type::RefundReferenceId,
    );
}

pub(crate) mod metrics {
    use router_env::{counter_metric, global_meter, histogram_metric_f64};

    global_meter!(GLOBAL_METER, "ROUTER_API");

    counter_metric!(DATABASE_CALLS_COUNT, GLOBAL_METER);
    histogram_metric_f64!(DATABASE_CALL_TIME, GLOBAL_METER);
}

#[cfg(feature = "tokenization_v2")]
pub mod tokenization;


// File: crates/diesel_models/src/routing_algorithm.rs

use common_utils::id_type;
use diesel::{Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};

use crate::{enums, schema::routing_algorithm};

#[derive(Clone, Debug, Identifiable, Insertable, Queryable, Selectable, Serialize, Deserialize)]
#[diesel(table_name = routing_algorithm, primary_key(algorithm_id), check_for_backend(diesel::pg::Pg))]
pub struct RoutingAlgorithm {
    pub algorithm_id: id_type::RoutingId,
    pub profile_id: id_type::ProfileId,
    pub merchant_id: id_type::MerchantId,
    pub name: String,
    pub description: Option<String>,
    pub kind: enums::RoutingAlgorithmKind,
    pub algorithm_data: serde_json::Value,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub algorithm_for: enums::TransactionType,
    pub decision_engine_routing_id: Option<String>,
}

pub struct RoutingAlgorithmMetadata {
    pub algorithm_id: id_type::RoutingId,
    pub name: String,
    pub description: Option<String>,
    pub kind: enums::RoutingAlgorithmKind,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub algorithm_for: enums::TransactionType,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RoutingProfileMetadata {
    pub profile_id: id_type::ProfileId,
    pub algorithm_id: id_type::RoutingId,
    pub name: String,
    pub description: Option<String>,
    pub kind: enums::RoutingAlgorithmKind,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub algorithm_for: enums::TransactionType,
}

impl RoutingProfileMetadata {
    pub fn metadata_is_advanced_rule_for_payments(&self) -> bool {
        matches!(self.kind, enums::RoutingAlgorithmKind::Advanced)
            && matches!(self.algorithm_for, enums::TransactionType::Payment)
    }
}


// File: crates/diesel_models/src/query.rs

pub mod address;
pub mod api_keys;
pub mod blocklist_lookup;
pub mod business_profile;
mod capture;
pub mod cards_info;
pub mod configs;

pub mod authentication;
pub mod authorization;
pub mod blocklist;
pub mod blocklist_fingerprint;
pub mod callback_mapper;
pub mod customers;
pub mod dashboard_metadata;
pub mod dispute;
pub mod dynamic_routing_stats;
pub mod events;
pub mod file;
pub mod fraud_check;
pub mod generic_link;
pub mod generics;
pub mod gsm;
pub mod hyperswitch_ai_interaction;
pub mod invoice;
pub mod locker_mock_up;
pub mod mandate;
pub mod merchant_account;
pub mod merchant_connector_account;
pub mod merchant_key_store;
pub mod organization;
pub mod payment_attempt;
pub mod payment_intent;
pub mod payment_link;
pub mod payment_method;
pub mod payout_attempt;
pub mod payouts;
pub mod process_tracker;
pub mod refund;
pub mod relay;
pub mod reverse_lookup;
pub mod role;
pub mod routing_algorithm;
pub mod subscription;
#[cfg(feature = "tokenization_v2")]
pub mod tokenization;
pub mod unified_translations;
pub mod user;
pub mod user_authentication_method;
pub mod user_key_store;
pub mod user_role;
mod utils;


// File: crates/diesel_models/src/subscription.rs

use common_utils::{generate_id_with_default_len, pii::SecretSerdeValue};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use masking::{PeekInterface, Secret};
use serde::{Deserialize, Serialize};

use crate::schema::subscription;

#[derive(Clone, Debug, Eq, Insertable, PartialEq, Serialize, Deserialize)]
#[diesel(table_name = subscription)]
pub struct SubscriptionNew {
    id: common_utils::id_type::SubscriptionId,
    status: String,
    billing_processor: Option<String>,
    payment_method_id: Option<String>,
    merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
    client_secret: Option<String>,
    connector_subscription_id: Option<String>,
    merchant_id: common_utils::id_type::MerchantId,
    customer_id: common_utils::id_type::CustomerId,
    metadata: Option<SecretSerdeValue>,
    created_at: time::PrimitiveDateTime,
    modified_at: time::PrimitiveDateTime,
    profile_id: common_utils::id_type::ProfileId,
    merchant_reference_id: Option<String>,
    plan_id: Option<String>,
    item_price_id: Option<String>,
}

#[derive(
    Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Deserialize, Serialize,
)]
#[diesel(table_name = subscription, primary_key(id), check_for_backend(diesel::pg::Pg))]
pub struct Subscription {
    pub id: common_utils::id_type::SubscriptionId,
    pub status: String,
    pub billing_processor: Option<String>,
    pub payment_method_id: Option<String>,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub client_secret: Option<String>,
    pub connector_subscription_id: Option<String>,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: common_utils::id_type::CustomerId,
    pub metadata: Option<serde_json::Value>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub profile_id: common_utils::id_type::ProfileId,
    pub merchant_reference_id: Option<String>,
    pub plan_id: Option<String>,
    pub item_price_id: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, AsChangeset, router_derive::DebugAsDisplay, Deserialize)]
#[diesel(table_name = subscription)]
pub struct SubscriptionUpdate {
    pub connector_subscription_id: Option<String>,
    pub payment_method_id: Option<String>,
    pub status: Option<String>,
    pub modified_at: time::PrimitiveDateTime,
    pub plan_id: Option<String>,
    pub item_price_id: Option<String>,
}

impl SubscriptionNew {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        id: common_utils::id_type::SubscriptionId,
        status: String,
        billing_processor: Option<String>,
        payment_method_id: Option<String>,
        merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
        client_secret: Option<String>,
        connector_subscription_id: Option<String>,
        merchant_id: common_utils::id_type::MerchantId,
        customer_id: common_utils::id_type::CustomerId,
        metadata: Option<SecretSerdeValue>,
        profile_id: common_utils::id_type::ProfileId,
        merchant_reference_id: Option<String>,
        plan_id: Option<String>,
        item_price_id: Option<String>,
    ) -> Self {
        let now = common_utils::date_time::now();
        Self {
            id,
            status,
            billing_processor,
            payment_method_id,
            merchant_connector_id,
            client_secret,
            connector_subscription_id,
            merchant_id,
            customer_id,
            metadata,
            created_at: now,
            modified_at: now,
            profile_id,
            merchant_reference_id,
            plan_id,
            item_price_id,
        }
    }

    pub fn generate_and_set_client_secret(&mut self) -> Secret<String> {
        let client_secret =
            generate_id_with_default_len(&format!("{}_secret", self.id.get_string_repr()));
        self.client_secret = Some(client_secret.clone());
        Secret::new(client_secret)
    }
}

impl SubscriptionUpdate {
    pub fn new(
        connector_subscription_id: Option<String>,
        payment_method_id: Option<Secret<String>>,
        status: Option<String>,
        plan_id: Option<String>,
        item_price_id: Option<String>,
    ) -> Self {
        Self {
            connector_subscription_id,
            payment_method_id: payment_method_id.map(|pmid| pmid.peek().clone()),
            status,
            modified_at: common_utils::date_time::now(),
            plan_id,
            item_price_id,
        }
    }
}


// File: crates/diesel_models/src/address.rs

use common_utils::{crypto, encryption::Encryption};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{enums, schema::address};

#[derive(Clone, Debug, Insertable, Serialize, Deserialize, router_derive::DebugAsDisplay)]
#[diesel(table_name = address)]
pub struct AddressNew {
    pub address_id: String,
    pub city: Option<String>,
    pub country: Option<enums::CountryAlpha2>,
    pub line1: Option<Encryption>,
    pub line2: Option<Encryption>,
    pub line3: Option<Encryption>,
    pub state: Option<Encryption>,
    pub zip: Option<Encryption>,
    pub first_name: Option<Encryption>,
    pub last_name: Option<Encryption>,
    pub phone_number: Option<Encryption>,
    pub country_code: Option<String>,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: Option<common_utils::id_type::PaymentId>,
    pub created_at: PrimitiveDateTime,
    pub modified_at: PrimitiveDateTime,
    pub updated_by: String,
    pub email: Option<Encryption>,
    pub origin_zip: Option<Encryption>,
}

#[derive(Clone, Debug, Queryable, Identifiable, Selectable, Serialize, Deserialize)]
#[diesel(table_name = address, primary_key(address_id), check_for_backend(diesel::pg::Pg))]
pub struct Address {
    pub address_id: String,
    pub city: Option<String>,
    pub country: Option<enums::CountryAlpha2>,
    pub line1: Option<Encryption>,
    pub line2: Option<Encryption>,
    pub line3: Option<Encryption>,
    pub state: Option<Encryption>,
    pub zip: Option<Encryption>,
    pub first_name: Option<Encryption>,
    pub last_name: Option<Encryption>,
    pub phone_number: Option<Encryption>,
    pub country_code: Option<String>,
    pub created_at: PrimitiveDateTime,
    pub modified_at: PrimitiveDateTime,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: Option<common_utils::id_type::PaymentId>,
    pub updated_by: String,
    pub email: Option<Encryption>,
    pub origin_zip: Option<Encryption>,
}

#[derive(Clone)]
// Intermediate struct to convert HashMap to Address
pub struct EncryptableAddress {
    pub line1: crypto::OptionalEncryptableSecretString,
    pub line2: crypto::OptionalEncryptableSecretString,
    pub line3: crypto::OptionalEncryptableSecretString,
    pub state: crypto::OptionalEncryptableSecretString,
    pub zip: crypto::OptionalEncryptableSecretString,
    pub first_name: crypto::OptionalEncryptableSecretString,
    pub last_name: crypto::OptionalEncryptableSecretString,
    pub phone_number: crypto::OptionalEncryptableSecretString,
    pub email: crypto::OptionalEncryptableEmail,
    pub origin_zip: crypto::OptionalEncryptableSecretString,
}

#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = address)]
pub struct AddressUpdateInternal {
    pub city: Option<String>,
    pub country: Option<enums::CountryAlpha2>,
    pub line1: Option<Encryption>,
    pub line2: Option<Encryption>,
    pub line3: Option<Encryption>,
    pub state: Option<Encryption>,
    pub zip: Option<Encryption>,
    pub first_name: Option<Encryption>,
    pub last_name: Option<Encryption>,
    pub phone_number: Option<Encryption>,
    pub country_code: Option<String>,
    pub modified_at: PrimitiveDateTime,
    pub updated_by: String,
    pub email: Option<Encryption>,
    pub origin_zip: Option<Encryption>,
}

impl AddressUpdateInternal {
    pub fn create_address(self, source: Address) -> Address {
        Address {
            city: self.city,
            country: self.country,
            line1: self.line1,
            line2: self.line2,
            line3: self.line3,
            state: self.state,
            zip: self.zip,
            first_name: self.first_name,
            last_name: self.last_name,
            phone_number: self.phone_number,
            country_code: self.country_code,
            modified_at: self.modified_at,
            updated_by: self.updated_by,
            origin_zip: self.origin_zip,
            ..source
        }
    }
}


// File: crates/diesel_models/src/blocklist_lookup.rs

use diesel::{Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};

use crate::schema::blocklist_lookup;

#[derive(Default, Clone, Debug, Eq, Insertable, PartialEq, Serialize, Deserialize)]
#[diesel(table_name = blocklist_lookup)]
pub struct BlocklistLookupNew {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub fingerprint: String,
}

#[derive(
    Default,
    Clone,
    Debug,
    Eq,
    PartialEq,
    Identifiable,
    Queryable,
    Selectable,
    Deserialize,
    Serialize,
)]
#[diesel(table_name = blocklist_lookup, primary_key(merchant_id, fingerprint), check_for_backend(diesel::pg::Pg))]
pub struct BlocklistLookup {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub fingerprint: String,
}


// File: crates/diesel_models/src/enums.rs

#[doc(hidden)]
pub mod diesel_exports {
    pub use super::{
        DbApiVersion as ApiVersion, DbAttemptStatus as AttemptStatus,
        DbAuthenticationType as AuthenticationType, DbBlocklistDataKind as BlocklistDataKind,
        DbCaptureMethod as CaptureMethod, DbCaptureStatus as CaptureStatus,
        DbCardDiscovery as CardDiscovery, DbConnectorStatus as ConnectorStatus,
        DbConnectorType as ConnectorType, DbCountryAlpha2 as CountryAlpha2, DbCurrency as Currency,
        DbDashboardMetadata as DashboardMetadata, DbDeleteStatus as DeleteStatus,
        DbDisputeStage as DisputeStage, DbDisputeStatus as DisputeStatus,
        DbEventClass as EventClass, DbEventObjectType as EventObjectType, DbEventType as EventType,
        DbFraudCheckStatus as FraudCheckStatus, DbFraudCheckType as FraudCheckType,
        DbFutureUsage as FutureUsage, DbGenericLinkType as GenericLinkType,
        DbIntentStatus as IntentStatus, DbMandateStatus as MandateStatus,
        DbMandateType as MandateType, DbMerchantStorageScheme as MerchantStorageScheme,
        DbOrderFulfillmentTimeOrigin as OrderFulfillmentTimeOrigin,
        DbPaymentMethodIssuerCode as PaymentMethodIssuerCode, DbPaymentSource as PaymentSource,
        DbPaymentType as PaymentType, DbPayoutStatus as PayoutStatus, DbPayoutType as PayoutType,
        DbProcessTrackerStatus as ProcessTrackerStatus, DbReconStatus as ReconStatus,
        DbRefundStatus as RefundStatus, DbRefundType as RefundType, DbRelayStatus as RelayStatus,
        DbRelayType as RelayType,
        DbRequestIncrementalAuthorization as RequestIncrementalAuthorization,
        DbRevenueRecoveryAlgorithmType as RevenueRecoveryAlgorithmType, DbRoleScope as RoleScope,
        DbRoutingAlgorithmKind as RoutingAlgorithmKind, DbRoutingApproach as RoutingApproach,
        DbScaExemptionType as ScaExemptionType,
        DbSuccessBasedRoutingConclusiveState as SuccessBasedRoutingConclusiveState,
        DbTokenizationFlag as TokenizationFlag, DbTotpStatus as TotpStatus,
        DbTransactionType as TransactionType, DbUserRoleVersion as UserRoleVersion,
        DbUserStatus as UserStatus, DbWebhookDeliveryAttempt as WebhookDeliveryAttempt,
    };
}

pub use common_enums::*;
use common_utils::pii;
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
pub use common_utils::tokenization;
use diesel::{deserialize::FromSqlRow, expression::AsExpression, sql_types::Jsonb};
use router_derive::diesel_enum;
use time::PrimitiveDateTime;

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RoutingAlgorithmKind {
    Single,
    Priority,
    VolumeSplit,
    Advanced,
    Dynamic,
    ThreeDsDecisionRule,
}

// Refund
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum RefundType {
    InstantRefund,
    RegularRefund,
    RetryRefund,
}

// Mandate
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    Default,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MandateType {
    SingleUse,
    #[default]
    MultiUse,
}

#[derive(
    serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression,
)]
#[diesel(sql_type = Jsonb)]
#[serde(rename_all = "snake_case")]
pub struct MandateDetails {
    pub update_mandate_id: Option<String>,
}

common_utils::impl_to_sql_from_sql_json!(MandateDetails);

#[derive(
    serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression,
)]
#[diesel(sql_type = Jsonb)]
#[serde(rename_all = "snake_case")]
pub enum MandateDataType {
    SingleUse(MandateAmountData),
    MultiUse(Option<MandateAmountData>),
}

common_utils::impl_to_sql_from_sql_json!(MandateDataType);

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct MandateAmountData {
    pub amount: common_utils::types::MinorUnit,
    pub currency: Currency,
    pub start_date: Option<PrimitiveDateTime>,
    pub end_date: Option<PrimitiveDateTime>,
    pub metadata: Option<pii::SecretSerdeValue>,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum FraudCheckType {
    PreFrm,
    PostFrm,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "text")]
#[strum(serialize_all = "snake_case")]
pub enum FraudCheckLastStep {
    #[default]
    Processing,
    CheckoutOrSale,
    TransactionOrRecordRefund,
    Fulfillment,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum UserStatus {
    Active,
    #[default]
    InvitationSent,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum DashboardMetadata {
    ProductionAgreement,
    SetupProcessor,
    ConfigureEndpoint,
    SetupComplete,
    FirstProcessorConnected,
    SecondProcessorConnected,
    ConfiguredRouting,
    TestPayment,
    IntegrationMethod,
    ConfigurationType,
    IntegrationCompleted,
    StripeConnected,
    PaypalConnected,
    SpRoutingConfigured,
    Feedback,
    ProdIntent,
    SpTestPayment,
    DownloadWoocom,
    ConfigureWoocom,
    SetupWoocomWebhook,
    IsMultipleConfiguration,
    IsChangePasswordRequired,
    OnboardingSurvey,
    ReconStatus,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum TotpStatus {
    Set,
    InProgress,
    #[default]
    NotSet,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::EnumString,
    strum::Display,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum UserRoleVersion {
    #[default]
    V1,
    V2,
}