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

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

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

#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
#[diesel(table_name = user_authentication_methods, check_for_backend(diesel::pg::Pg))]
pub struct UserAuthenticationMethod {
    pub id: String,
    pub auth_id: String,
    pub owner_id: String,
    pub owner_type: enums::Owner,
    pub auth_type: enums::UserAuthType,
    pub private_config: Option<Encryption>,
    pub public_config: Option<serde_json::Value>,
    pub allow_signup: bool,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub email_domain: String,
}

#[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = user_authentication_methods)]
pub struct UserAuthenticationMethodNew {
    pub id: String,
    pub auth_id: String,
    pub owner_id: String,
    pub owner_type: enums::Owner,
    pub auth_type: enums::UserAuthType,
    pub private_config: Option<Encryption>,
    pub public_config: Option<serde_json::Value>,
    pub allow_signup: bool,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub email_domain: String,
}

#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = user_authentication_methods)]
pub struct OrgAuthenticationMethodUpdateInternal {
    pub private_config: Option<Encryption>,
    pub public_config: Option<serde_json::Value>,
    pub last_modified_at: PrimitiveDateTime,
    pub email_domain: Option<String>,
}

pub enum UserAuthenticationMethodUpdate {
    UpdateConfig {
        private_config: Option<Encryption>,
        public_config: Option<serde_json::Value>,
    },
    EmailDomain {
        email_domain: String,
    },
}

impl From<UserAuthenticationMethodUpdate> for OrgAuthenticationMethodUpdateInternal {
    fn from(value: UserAuthenticationMethodUpdate) -> Self {
        let last_modified_at = common_utils::date_time::now();
        match value {
            UserAuthenticationMethodUpdate::UpdateConfig {
                private_config,
                public_config,
            } => Self {
                private_config,
                public_config,
                last_modified_at,
                email_domain: None,
            },
            UserAuthenticationMethodUpdate::EmailDomain { email_domain } => Self {
                private_config: None,
                public_config: None,
                last_modified_at,
                email_domain: Some(email_domain),
            },
        }
    }
}


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

use common_utils::custom_serde;
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use masking::{Deserialize, Serialize};

use crate::schema::file_metadata;

#[derive(Clone, Debug, Deserialize, Insertable, Serialize, router_derive::DebugAsDisplay)]
#[diesel(table_name = file_metadata)]
#[serde(deny_unknown_fields)]
pub struct FileMetadataNew {
    pub file_id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub file_name: Option<String>,
    pub file_size: i32,
    pub file_type: String,
    pub provider_file_id: Option<String>,
    pub file_upload_provider: Option<common_enums::FileUploadProvider>,
    pub available: bool,
    pub connector_label: Option<String>,
    pub profile_id: Option<common_utils::id_type::ProfileId>,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
}

#[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable, Selectable)]
#[diesel(table_name = file_metadata, primary_key(file_id, merchant_id), check_for_backend(diesel::pg::Pg))]
pub struct FileMetadata {
    #[serde(skip_serializing)]
    pub file_id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub file_name: Option<String>,
    pub file_size: i32,
    pub file_type: String,
    pub provider_file_id: Option<String>,
    pub file_upload_provider: Option<common_enums::FileUploadProvider>,
    pub available: bool,
    #[serde(with = "custom_serde::iso8601")]
    pub created_at: time::PrimitiveDateTime,
    pub connector_label: Option<String>,
    pub profile_id: Option<common_utils::id_type::ProfileId>,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
}

#[derive(Debug)]
pub enum FileMetadataUpdate {
    Update {
        provider_file_id: Option<String>,
        file_upload_provider: Option<common_enums::FileUploadProvider>,
        available: bool,
        profile_id: Option<common_utils::id_type::ProfileId>,
        merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
    },
}

#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = file_metadata)]
pub struct FileMetadataUpdateInternal {
    provider_file_id: Option<String>,
    file_upload_provider: Option<common_enums::FileUploadProvider>,
    available: bool,
    profile_id: Option<common_utils::id_type::ProfileId>,
    merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
}

impl From<FileMetadataUpdate> for FileMetadataUpdateInternal {
    fn from(merchant_account_update: FileMetadataUpdate) -> Self {
        match merchant_account_update {
            FileMetadataUpdate::Update {
                provider_file_id,
                file_upload_provider,
                available,
                profile_id,
                merchant_connector_id,
            } => Self {
                provider_file_id,
                file_upload_provider,
                available,
                profile_id,
                merchant_connector_id,
            },
        }
    }
}


// File: crates/diesel_models/src/user/theme.rs

use common_enums::EntityType;
use common_utils::{
    date_time, id_type,
    types::user::{EmailThemeConfig, ThemeLineage},
};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use router_derive::DebugAsDisplay;
use time::PrimitiveDateTime;

use crate::schema::themes;

#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
#[diesel(table_name = themes, primary_key(theme_id), check_for_backend(diesel::pg::Pg))]
pub struct Theme {
    pub theme_id: String,
    pub tenant_id: id_type::TenantId,
    pub org_id: Option<id_type::OrganizationId>,
    pub merchant_id: Option<id_type::MerchantId>,
    pub profile_id: Option<id_type::ProfileId>,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub entity_type: EntityType,
    pub theme_name: String,
    pub email_primary_color: String,
    pub email_foreground_color: String,
    pub email_background_color: String,
    pub email_entity_name: String,
    pub email_entity_logo_url: String,
}

#[derive(Clone, Debug, Insertable, DebugAsDisplay)]
#[diesel(table_name = themes)]
pub struct ThemeNew {
    pub theme_id: String,
    pub tenant_id: id_type::TenantId,
    pub org_id: Option<id_type::OrganizationId>,
    pub merchant_id: Option<id_type::MerchantId>,
    pub profile_id: Option<id_type::ProfileId>,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub entity_type: EntityType,
    pub theme_name: String,
    pub email_primary_color: String,
    pub email_foreground_color: String,
    pub email_background_color: String,
    pub email_entity_name: String,
    pub email_entity_logo_url: String,
}

impl ThemeNew {
    pub fn new(
        theme_id: String,
        theme_name: String,
        lineage: ThemeLineage,
        email_config: EmailThemeConfig,
    ) -> Self {
        let now = date_time::now();

        Self {
            theme_id,
            theme_name,
            tenant_id: lineage.tenant_id().to_owned(),
            org_id: lineage.org_id().cloned(),
            merchant_id: lineage.merchant_id().cloned(),
            profile_id: lineage.profile_id().cloned(),
            entity_type: lineage.entity_type(),
            created_at: now,
            last_modified_at: now,
            email_primary_color: email_config.primary_color,
            email_foreground_color: email_config.foreground_color,
            email_background_color: email_config.background_color,
            email_entity_name: email_config.entity_name,
            email_entity_logo_url: email_config.entity_logo_url,
        }
    }
}

impl Theme {
    pub fn email_config(&self) -> EmailThemeConfig {
        EmailThemeConfig {
            primary_color: self.email_primary_color.clone(),
            foreground_color: self.email_foreground_color.clone(),
            background_color: self.email_background_color.clone(),
            entity_name: self.email_entity_name.clone(),
            entity_logo_url: self.email_entity_logo_url.clone(),
        }
    }
}

#[derive(Clone, Debug, Default, AsChangeset, DebugAsDisplay)]
#[diesel(table_name = themes)]
pub struct ThemeUpdateInternal {
    pub email_primary_color: Option<String>,
    pub email_foreground_color: Option<String>,
    pub email_background_color: Option<String>,
    pub email_entity_name: Option<String>,
    pub email_entity_logo_url: Option<String>,
}

#[derive(Clone)]
pub enum ThemeUpdate {
    EmailConfig { email_config: EmailThemeConfig },
}

impl From<ThemeUpdate> for ThemeUpdateInternal {
    fn from(value: ThemeUpdate) -> Self {
        match value {
            ThemeUpdate::EmailConfig { email_config } => Self {
                email_primary_color: Some(email_config.primary_color),
                email_foreground_color: Some(email_config.foreground_color),
                email_background_color: Some(email_config.background_color),
                email_entity_name: Some(email_config.entity_name),
                email_entity_logo_url: Some(email_config.entity_logo_url),
            },
        }
    }
}


// File: crates/diesel_models/src/user/dashboard_metadata.rs

use common_utils::id_type;
use diesel::{query_builder::AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use masking::Secret;
use time::PrimitiveDateTime;

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

#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
#[diesel(table_name = dashboard_metadata, check_for_backend(diesel::pg::Pg))]
pub struct DashboardMetadata {
    pub id: i32,
    pub user_id: Option<String>,
    pub merchant_id: id_type::MerchantId,
    pub org_id: id_type::OrganizationId,
    pub data_key: enums::DashboardMetadata,
    pub data_value: Secret<serde_json::Value>,
    pub created_by: String,
    pub created_at: PrimitiveDateTime,
    pub last_modified_by: String,
    pub last_modified_at: PrimitiveDateTime,
}

#[derive(
    router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay, AsChangeset,
)]
#[diesel(table_name = dashboard_metadata)]
pub struct DashboardMetadataNew {
    pub user_id: Option<String>,
    pub merchant_id: id_type::MerchantId,
    pub org_id: id_type::OrganizationId,
    pub data_key: enums::DashboardMetadata,
    pub data_value: Secret<serde_json::Value>,
    pub created_by: String,
    pub created_at: PrimitiveDateTime,
    pub last_modified_by: String,
    pub last_modified_at: PrimitiveDateTime,
}

#[derive(
    router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay, AsChangeset,
)]
#[diesel(table_name = dashboard_metadata)]
pub struct DashboardMetadataUpdateInternal {
    pub data_key: enums::DashboardMetadata,
    pub data_value: Secret<serde_json::Value>,
    pub last_modified_by: String,
    pub last_modified_at: PrimitiveDateTime,
}

#[derive(Debug)]
pub enum DashboardMetadataUpdate {
    UpdateData {
        data_key: enums::DashboardMetadata,
        data_value: Secret<serde_json::Value>,
        last_modified_by: String,
    },
}

impl From<DashboardMetadataUpdate> for DashboardMetadataUpdateInternal {
    fn from(metadata_update: DashboardMetadataUpdate) -> Self {
        let last_modified_at = common_utils::date_time::now();
        match metadata_update {
            DashboardMetadataUpdate::UpdateData {
                data_key,
                data_value,
                last_modified_by,
            } => Self {
                data_key,
                data_value,
                last_modified_by,
                last_modified_at,
            },
        }
    }
}


// File: crates/diesel_models/src/user/sample_data.rs

#[cfg(feature = "v1")]
use common_enums::{
    AttemptStatus, AuthenticationType, CaptureMethod, Currency, PaymentExperience, PaymentMethod,
    PaymentMethodType,
};
#[cfg(feature = "v1")]
use common_types::primitive_wrappers::{
    ExtendedAuthorizationAppliedBool, RequestExtendedAuthorizationBool,
};
#[cfg(feature = "v1")]
use common_utils::types::{ConnectorTransactionId, MinorUnit};
#[cfg(feature = "v1")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "v1")]
use time::PrimitiveDateTime;

#[cfg(feature = "v1")]
use crate::{
    enums::{MandateDataType, MandateDetails},
    schema::payment_attempt,
    ConnectorMandateReferenceId, NetworkDetails, PaymentAttemptNew,
};

// #[cfg(feature = "v2")]
// #[derive(
//     Clone, Debug, diesel::Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize,
// )]
// #[diesel(table_name = payment_attempt)]
// pub struct PaymentAttemptBatchNew {
//     pub payment_id: common_utils::id_type::PaymentId,
//     pub merchant_id: common_utils::id_type::MerchantId,
//     pub status: AttemptStatus,
//     pub error_message: Option<String>,
//     pub surcharge_amount: Option<i64>,
//     pub tax_on_surcharge: Option<i64>,
//     pub payment_method_id: Option<String>,
//     pub authentication_type: Option<AuthenticationType>,
//     #[serde(with = "common_utils::custom_serde::iso8601")]
//     pub created_at: PrimitiveDateTime,
//     #[serde(with = "common_utils::custom_serde::iso8601")]
//     pub modified_at: PrimitiveDateTime,
//     #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
//     pub last_synced: Option<PrimitiveDateTime>,
//     pub cancellation_reason: Option<String>,
//     pub browser_info: Option<serde_json::Value>,
//     pub payment_token: Option<String>,
//     pub error_code: Option<String>,
//     pub connector_metadata: Option<serde_json::Value>,
//     pub payment_experience: Option<PaymentExperience>,
//     pub payment_method_data: Option<serde_json::Value>,
//     pub preprocessing_step_id: Option<String>,
//     pub error_reason: Option<String>,
//     pub connector_response_reference_id: Option<String>,
//     pub multiple_capture_count: Option<i16>,
//     pub amount_capturable: i64,
//     pub updated_by: String,
//     pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
//     pub authentication_data: Option<serde_json::Value>,
//     pub encoded_data: Option<String>,
//     pub unified_code: Option<String>,
//     pub unified_message: Option<String>,
//     pub net_amount: Option<i64>,
//     pub external_three_ds_authentication_attempted: Option<bool>,
//     pub authentication_connector: Option<String>,
//     pub authentication_id: Option<String>,
//     pub fingerprint_id: Option<String>,
//     pub charge_id: Option<String>,
//     pub client_source: Option<String>,
//     pub client_version: Option<String>,
//     pub customer_acceptance: Option<common_utils::pii::SecretSerdeValue>,
//     pub profile_id: common_utils::id_type::ProfileId,
//     pub organization_id: common_utils::id_type::OrganizationId,
// }

// #[cfg(feature = "v2")]
// #[allow(dead_code)]
// impl PaymentAttemptBatchNew {
//     // Used to verify compatibility with PaymentAttemptTable
//     fn convert_into_normal_attempt_insert(self) -> PaymentAttemptNew {
//         // PaymentAttemptNew {
//         //     payment_id: self.payment_id,
//         //     merchant_id: self.merchant_id,
//         //     status: self.status,
//         //     error_message: self.error_message,
//         //     surcharge_amount: self.surcharge_amount,
//         //     tax_amount: self.tax_amount,
//         //     payment_method_id: self.payment_method_id,
//         //     confirm: self.confirm,
//         //     authentication_type: self.authentication_type,
//         //     created_at: self.created_at,
//         //     modified_at: self.modified_at,
//         //     last_synced: self.last_synced,
//         //     cancellation_reason: self.cancellation_reason,
//         //     browser_info: self.browser_info,
//         //     payment_token: self.payment_token,
//         //     error_code: self.error_code,
//         //     connector_metadata: self.connector_metadata,
//         //     payment_experience: self.payment_experience,
//         //     card_network: self
//         //         .payment_method_data
//         //         .as_ref()
//         //         .and_then(|data| data.as_object())
//         //         .and_then(|card| card.get("card"))
//         //         .and_then(|v| v.as_object())
//         //         .and_then(|v| v.get("card_network"))
//         //         .and_then(|network| network.as_str())
//         //         .map(|network| network.to_string()),
//         //     payment_method_data: self.payment_method_data,
//         //     straight_through_algorithm: self.straight_through_algorithm,
//         //     preprocessing_step_id: self.preprocessing_step_id,
//         //     error_reason: self.error_reason,
//         //     multiple_capture_count: self.multiple_capture_count,
//         //     connector_response_reference_id: self.connector_response_reference_id,
//         //     amount_capturable: self.amount_capturable,
//         //     updated_by: self.updated_by,
//         //     merchant_connector_id: self.merchant_connector_id,
//         //     authentication_data: self.authentication_data,
//         //     encoded_data: self.encoded_data,
//         //     unified_code: self.unified_code,
//         //     unified_message: self.unified_message,
//         //     net_amount: self.net_amount,
//         //     external_three_ds_authentication_attempted: self
//         //         .external_three_ds_authentication_attempted,
//         //     authentication_connector: self.authentication_connector,
//         //     authentication_id: self.authentication_id,
//         //     payment_method_billing_address_id: self.payment_method_billing_address_id,
//         //     fingerprint_id: self.fingerprint_id,
//         //     charge_id: self.charge_id,
//         //     client_source: self.client_source,
//         //     client_version: self.client_version,
//         //     customer_acceptance: self.customer_acceptance,
//         //     profile_id: self.profile_id,
//         //     organization_id: self.organization_id,
//         // }
//         todo!()
//     }
// }

#[cfg(feature = "v1")]
#[derive(
    Clone, Debug, diesel::Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize,
)]
#[diesel(table_name = payment_attempt)]
pub struct PaymentAttemptBatchNew {
    pub payment_id: common_utils::id_type::PaymentId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub attempt_id: String,
    pub status: AttemptStatus,
    pub amount: MinorUnit,
    pub currency: Option<Currency>,
    pub save_to_locker: Option<bool>,
    pub connector: Option<String>,
    pub error_message: Option<String>,
    pub offer_amount: Option<MinorUnit>,
    pub surcharge_amount: Option<MinorUnit>,
    pub tax_amount: Option<MinorUnit>,
    pub payment_method_id: Option<String>,
    pub payment_method: Option<PaymentMethod>,
    pub capture_method: Option<CaptureMethod>,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub capture_on: Option<PrimitiveDateTime>,
    pub confirm: bool,
    pub authentication_type: Option<AuthenticationType>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_synced: Option<PrimitiveDateTime>,
    pub cancellation_reason: Option<String>,
    pub amount_to_capture: Option<MinorUnit>,
    pub mandate_id: Option<String>,
    pub browser_info: Option<serde_json::Value>,
    pub payment_token: Option<String>,
    pub error_code: Option<String>,
    pub connector_metadata: Option<serde_json::Value>,
    pub payment_experience: Option<PaymentExperience>,
    pub payment_method_type: Option<PaymentMethodType>,
    pub payment_method_data: Option<serde_json::Value>,
    pub business_sub_label: Option<String>,
    pub straight_through_algorithm: Option<serde_json::Value>,
    pub preprocessing_step_id: Option<String>,
    pub mandate_details: Option<MandateDataType>,
    pub error_reason: Option<String>,
    pub connector_response_reference_id: Option<String>,
    pub connector_transaction_id: Option<ConnectorTransactionId>,
    pub multiple_capture_count: Option<i16>,
    pub amount_capturable: MinorUnit,
    pub updated_by: String,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub authentication_data: Option<serde_json::Value>,
    pub encoded_data: Option<String>,
    pub unified_code: Option<String>,
    pub unified_message: Option<String>,
    pub net_amount: Option<MinorUnit>,
    pub external_three_ds_authentication_attempted: Option<bool>,
    pub authentication_connector: Option<String>,
    pub authentication_id: Option<common_utils::id_type::AuthenticationId>,
    pub mandate_data: Option<MandateDetails>,
    pub payment_method_billing_address_id: Option<String>,
    pub fingerprint_id: Option<String>,
    pub charge_id: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub customer_acceptance: Option<common_utils::pii::SecretSerdeValue>,
    pub profile_id: common_utils::id_type::ProfileId,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub shipping_cost: Option<MinorUnit>,
    pub order_tax_amount: Option<MinorUnit>,
    pub processor_transaction_data: Option<String>,
    pub connector_mandate_detail: Option<ConnectorMandateReferenceId>,
    pub request_extended_authorization: Option<RequestExtendedAuthorizationBool>,
    pub extended_authorization_applied: Option<ExtendedAuthorizationAppliedBool>,
    pub tokenization: Option<common_enums::Tokenization>,
    pub extended_authorization_last_applied_at: Option<PrimitiveDateTime>,
    pub capture_before: Option<PrimitiveDateTime>,
    pub card_discovery: Option<common_enums::CardDiscovery>,
    pub processor_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub created_by: Option<String>,
    pub setup_future_usage_applied: Option<common_enums::FutureUsage>,
    pub routing_approach: Option<common_enums::RoutingApproach>,
    pub connector_request_reference_id: Option<String>,
    pub network_transaction_id: Option<String>,
    pub network_details: Option<NetworkDetails>,
    pub is_stored_credential: Option<bool>,
    pub authorized_amount: Option<MinorUnit>,
}

#[cfg(feature = "v1")]
#[allow(dead_code)]
impl PaymentAttemptBatchNew {
    // Used to verify compatibility with PaymentAttemptTable
    fn convert_into_normal_attempt_insert(self) -> PaymentAttemptNew {
        PaymentAttemptNew {
            payment_id: self.payment_id,
            merchant_id: self.merchant_id,
            attempt_id: self.attempt_id,
            status: self.status,
            amount: self.amount,
            currency: self.currency,
            save_to_locker: self.save_to_locker,
            connector: self.connector,
            error_message: self.error_message,
            offer_amount: self.offer_amount,
            surcharge_amount: self.surcharge_amount,
            tax_amount: self.tax_amount,
            payment_method_id: self.payment_method_id,
            payment_method: self.payment_method,
            capture_method: self.capture_method,
            capture_on: self.capture_on,
            confirm: self.confirm,
            authentication_type: self.authentication_type,
            created_at: self.created_at,
            modified_at: self.modified_at,
            last_synced: self.last_synced,
            cancellation_reason: self.cancellation_reason,
            amount_to_capture: self.amount_to_capture,
            mandate_id: self.mandate_id,
            browser_info: self.browser_info,
            payment_token: self.payment_token,
            error_code: self.error_code,
            connector_metadata: self.connector_metadata,
            payment_experience: self.payment_experience,
            payment_method_type: self.payment_method_type,
            card_network: self
                .payment_method_data
                .as_ref()
                .and_then(|data| data.as_object())
                .and_then(|card| card.get("card"))
                .and_then(|v| v.as_object())
                .and_then(|v| v.get("card_network"))
                .and_then(|network| network.as_str())
                .map(|network| network.to_string()),
            payment_method_data: self.payment_method_data,
            business_sub_label: self.business_sub_label,
            straight_through_algorithm: self.straight_through_algorithm,
            preprocessing_step_id: self.preprocessing_step_id,
            mandate_details: self.mandate_details,
            error_reason: self.error_reason,
            multiple_capture_count: self.multiple_capture_count,
            connector_response_reference_id: self.connector_response_reference_id,
            amount_capturable: self.amount_capturable,
            updated_by: self.updated_by,
            merchant_connector_id: self.merchant_connector_id,
            authentication_data: self.authentication_data,
            encoded_data: self.encoded_data,
            unified_code: self.unified_code,
            unified_message: self.unified_message,
            net_amount: self.net_amount,
            external_three_ds_authentication_attempted: self
                .external_three_ds_authentication_attempted,
            authentication_connector: self.authentication_connector,
            authentication_id: self.authentication_id,
            mandate_data: self.mandate_data,
            payment_method_billing_address_id: self.payment_method_billing_address_id,
            fingerprint_id: self.fingerprint_id,
            client_source: self.client_source,
            client_version: self.client_version,
            customer_acceptance: self.customer_acceptance,
            profile_id: self.profile_id,
            organization_id: self.organization_id,
            shipping_cost: self.shipping_cost,
            order_tax_amount: self.order_tax_amount,
            connector_mandate_detail: self.connector_mandate_detail,
            request_extended_authorization: self.request_extended_authorization,
            extended_authorization_applied: self.extended_authorization_applied,
            extended_authorization_last_applied_at: self.extended_authorization_last_applied_at,
            capture_before: self.capture_before,
            card_discovery: self.card_discovery,
            processor_merchant_id: self.processor_merchant_id,
            created_by: self.created_by,
            setup_future_usage_applied: self.setup_future_usage_applied,
            routing_approach: self.routing_approach,
            connector_request_reference_id: self.connector_request_reference_id,
            network_transaction_id: self.network_transaction_id,
            network_details: self.network_details,
            is_stored_credential: self.is_stored_credential,
            authorized_amount: self.authorized_amount,
            tokenization: self.tokenization,
        }
    }
}


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

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};
use error_stack::report;

use super::generics;
use crate::{
    dynamic_routing_stats::{
        DynamicRoutingStats, DynamicRoutingStatsNew, DynamicRoutingStatsUpdate,
    },
    errors,
    schema::dynamic_routing_stats::dsl,
    PgPooledConn, StorageResult,
};

impl DynamicRoutingStatsNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<DynamicRoutingStats> {
        generics::generic_insert(conn, self).await
    }
}

impl DynamicRoutingStats {
    pub async fn find_optional_by_attempt_id_merchant_id(
        conn: &PgPooledConn,
        attempt_id: String,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::attempt_id.eq(attempt_id.to_owned())),
        )
        .await
    }

    pub async fn update(
        conn: &PgPooledConn,
        attempt_id: String,
        merchant_id: &common_utils::id_type::MerchantId,
        dynamic_routing_stat: DynamicRoutingStatsUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<
            <Self as HasTable>::Table,
            DynamicRoutingStatsUpdate,
            _,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::attempt_id.eq(attempt_id.to_owned())),
            dynamic_routing_stat,
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(errors::DatabaseError::NotFound)
                .attach_printable("Error while updating dynamic_routing_stats entry")
        })
    }
}


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

#[cfg(feature = "v2")]
use diesel::associations::HasTable;
#[cfg(feature = "v2")]
use diesel::ExpressionMethods;

#[cfg(feature = "v2")]
use crate::{
    errors, query::generics, schema_v2::tokenization, tokenization as tokenization_diesel,
    PgPooledConn, StorageResult,
};

#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
impl tokenization_diesel::Tokenization {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Self> {
        generics::generic_insert(conn, self).await
    }

    pub async fn find_by_id(
        conn: &PgPooledConn,
        id: &common_utils::id_type::GlobalTokenId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            tokenization::dsl::id.eq(id.to_owned()),
        )
        .await
    }

    pub async fn update_with_id(
        self,
        conn: &PgPooledConn,
        tokenization_record: tokenization_diesel::TokenizationUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            tokenization::dsl::id.eq(self.id.to_owned()),
            tokenization_record,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }
}


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

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, Table};

use super::generics;
use crate::{
    dispute::{Dispute, DisputeNew, DisputeUpdate, DisputeUpdateInternal},
    errors,
    schema::dispute::dsl,
    PgPooledConn, StorageResult,
};

impl DisputeNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Dispute> {
        generics::generic_insert(conn, self).await
    }
}

impl Dispute {
    pub async fn find_by_merchant_id_payment_id_connector_dispute_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
        connector_dispute_id: &str,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned()))
                .and(dsl::connector_dispute_id.eq(connector_dispute_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_dispute_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::dispute_id.eq(dispute_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_payment_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned())),
            None,
            None,
            None,
        )
        .await
    }

    pub async fn update(self, conn: &PgPooledConn, dispute: DisputeUpdate) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::dispute_id.eq(self.dispute_id.to_owned()),
            DisputeUpdateInternal::from(dispute),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }
}


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

use diesel::{associations::HasTable, ExpressionMethods};

use super::generics;
use crate::{
    callback_mapper::CallbackMapper, schema::callback_mapper::dsl, PgPooledConn, StorageResult,
};

impl CallbackMapper {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Self> {
        generics::generic_insert(conn, self).await
    }

    pub async fn find_by_id(conn: &PgPooledConn, id: &str) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::id.eq(id.to_owned()),
        )
        .await
    }
}


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

use async_bb8_diesel::AsyncRunQueryDsl;
use common_enums::EntityType;
use common_utils::id_type;
use diesel::{
    associations::HasTable, debug_query, pg::Pg, result::Error as DieselError,
    BoolExpressionMethods, ExpressionMethods, QueryDsl,
};
use error_stack::{report, ResultExt};
use strum::IntoEnumIterator;

use crate::{
    enums::RoleScope, errors, query::generics, role::*, schema::roles::dsl, PgPooledConn,
    StorageResult,
};

impl RoleNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Role> {
        generics::generic_insert(conn, self).await
    }
}

impl Role {
    fn get_entity_list(
        current_entity: EntityType,
        is_lineage_data_required: bool,
    ) -> Vec<EntityType> {
        is_lineage_data_required
            .then(|| {
                EntityType::iter()
                    .filter(|variant| *variant <= current_entity)
                    .collect()
            })
            .unwrap_or(vec![current_entity])
    }

    pub async fn find_by_role_id(conn: &PgPooledConn, role_id: &str) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::role_id.eq(role_id.to_owned()),
        )
        .await
    }

    pub async fn find_by_role_id_in_lineage(
        conn: &PgPooledConn,
        role_id: &str,
        merchant_id: &id_type::MerchantId,
        org_id: &id_type::OrganizationId,
        profile_id: &id_type::ProfileId,
        tenant_id: &id_type::TenantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::role_id
                .eq(role_id.to_owned())
                .and(dsl::tenant_id.eq(tenant_id.to_owned()))
                .and(dsl::org_id.eq(org_id.to_owned()))
                .and(
                    dsl::scope
                        .eq(RoleScope::Organization)
                        .or(dsl::merchant_id
                            .eq(merchant_id.to_owned())
                            .and(dsl::scope.eq(RoleScope::Merchant)))
                        .or(dsl::profile_id
                            .eq(profile_id.to_owned())
                            .and(dsl::scope.eq(RoleScope::Profile))),
                ),
        )
        .await
    }

    pub async fn find_by_role_id_org_id_tenant_id(
        conn: &PgPooledConn,
        role_id: &str,
        org_id: &id_type::OrganizationId,
        tenant_id: &id_type::TenantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::role_id
                .eq(role_id.to_owned())
                .and(dsl::tenant_id.eq(tenant_id.to_owned()))
                .and(dsl::org_id.eq(org_id.to_owned())),
        )
        .await
    }

    pub async fn update_by_role_id(
        conn: &PgPooledConn,
        role_id: &str,
        role_update: RoleUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::role_id.eq(role_id.to_owned()),
            RoleUpdateInternal::from(role_update),
        )
        .await
    }

    pub async fn delete_by_role_id(conn: &PgPooledConn, role_id: &str) -> StorageResult<Self> {
        generics::generic_delete_one_with_result::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::role_id.eq(role_id.to_owned()),
        )
        .await
    }

    //TODO: Remove once generic_list_roles_by_entity_type is stable
    pub async fn generic_roles_list_for_org(
        conn: &PgPooledConn,
        tenant_id: id_type::TenantId,
        org_id: id_type::OrganizationId,
        merchant_id: Option<id_type::MerchantId>,
        entity_type: Option<EntityType>,
        limit: Option<u32>,
    ) -> StorageResult<Vec<Self>> {
        let mut query = <Self as HasTable>::table()
            .filter(dsl::tenant_id.eq(tenant_id).and(dsl::org_id.eq(org_id)))
            .into_boxed();

        if let Some(merchant_id) = merchant_id {
            query = query.filter(
                (dsl::merchant_id
                    .eq(merchant_id)
                    .and(dsl::scope.eq(RoleScope::Merchant)))
                .or(dsl::scope.eq(RoleScope::Organization)),
            );
        }

        if let Some(entity_type) = entity_type {
            query = query.filter(dsl::entity_type.eq(entity_type))
        }

        if let Some(limit) = limit {
            query = query.limit(limit.into());
        }

        router_env::logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());

        match generics::db_metrics::track_database_call::<Self, _, _>(
            query.get_results_async(conn),
            generics::db_metrics::DatabaseOperation::Filter,
        )
        .await
        {
            Ok(value) => Ok(value),
            Err(err) => match err {
                DieselError::NotFound => {
                    Err(report!(err)).change_context(errors::DatabaseError::NotFound)
                }
                _ => Err(report!(err)).change_context(errors::DatabaseError::Others),
            },
        }
    }

    pub async fn generic_list_roles_by_entity_type(
        conn: &PgPooledConn,
        payload: ListRolesByEntityPayload,
        is_lineage_data_required: bool,
        tenant_id: id_type::TenantId,
        org_id: id_type::OrganizationId,
    ) -> StorageResult<Vec<Self>> {
        let mut query = <Self as HasTable>::table()
            .into_boxed()
            .filter(dsl::tenant_id.eq(tenant_id))
            .filter(dsl::org_id.eq(org_id));

        match payload {
            ListRolesByEntityPayload::Organization => {
                let entity_in_vec =
                    Self::get_entity_list(EntityType::Organization, is_lineage_data_required);
                query = query.filter(dsl::entity_type.eq_any(entity_in_vec))
            }

            ListRolesByEntityPayload::Merchant(merchant_id) => {
                let entity_in_vec =
                    Self::get_entity_list(EntityType::Merchant, is_lineage_data_required);
                query = query
                    .filter(
                        dsl::scope
                            .eq(RoleScope::Organization)
                            .or(dsl::merchant_id.eq(merchant_id)),
                    )
                    .filter(dsl::entity_type.eq_any(entity_in_vec))
            }

            ListRolesByEntityPayload::Profile(merchant_id, profile_id) => {
                let entity_in_vec =
                    Self::get_entity_list(EntityType::Profile, is_lineage_data_required);
                query = query
                    .filter(
                        dsl::scope
                            .eq(RoleScope::Organization)
                            .or(dsl::scope
                                .eq(RoleScope::Merchant)
                                .and(dsl::merchant_id.eq(merchant_id.clone())))
                            .or(dsl::profile_id.eq(profile_id)),
                    )
                    .filter(dsl::entity_type.eq_any(entity_in_vec))
            }
        };

        router_env::logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());

        match generics::db_metrics::track_database_call::<Self, _, _>(
            query.get_results_async(conn),
            generics::db_metrics::DatabaseOperation::Filter,
        )
        .await
        {
            Ok(value) => Ok(value),
            Err(err) => Err(report!(err)).change_context(errors::DatabaseError::Others),
        }
    }
}


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

#[cfg(feature = "v1")]
use std::collections::HashSet;

use async_bb8_diesel::AsyncRunQueryDsl;
#[cfg(feature = "v1")]
use diesel::Table;
use diesel::{
    associations::HasTable, debug_query, pg::Pg, BoolExpressionMethods, ExpressionMethods, QueryDsl,
};
use error_stack::{report, ResultExt};

use super::generics;
#[cfg(feature = "v1")]
use crate::schema::payment_attempt::dsl;
#[cfg(feature = "v2")]
use crate::schema_v2::payment_attempt::dsl;
#[cfg(feature = "v1")]
use crate::{enums::IntentStatus, payment_attempt::PaymentAttemptUpdate, PaymentIntent};
use crate::{
    enums::{self},
    errors::DatabaseError,
    payment_attempt::{PaymentAttempt, PaymentAttemptNew, PaymentAttemptUpdateInternal},
    query::generics::db_metrics,
    PgPooledConn, StorageResult,
};

impl PaymentAttemptNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<PaymentAttempt> {
        generics::generic_insert(conn, self).await
    }
}

impl PaymentAttempt {
    #[cfg(feature = "v1")]
    pub async fn update_with_attempt_id(
        self,
        conn: &PgPooledConn,
        payment_attempt: PaymentAttemptUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::attempt_id
                .eq(self.attempt_id.to_owned())
                .and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
            PaymentAttemptUpdateInternal::from(payment_attempt).populate_derived_fields(&self),
        )
        .await
        {
            Err(error) => match error.current_context() {
                DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    #[cfg(feature = "v2")]
    pub async fn update_with_attempt_id(
        self,
        conn: &PgPooledConn,
        payment_attempt: PaymentAttemptUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(conn, dsl::id.eq(self.id.to_owned()), payment_attempt)
        .await
        {
            Err(error) => match error.current_context() {
                DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    #[cfg(feature = "v1")]
    pub async fn find_optional_by_payment_id_merchant_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_connector_transaction_id_payment_id_merchant_id(
        conn: &PgPooledConn,
        connector_transaction_id: &common_utils::types::ConnectorTransactionId,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::connector_transaction_id
                .eq(connector_transaction_id.get_id().to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned()))
                .and(dsl::merchant_id.eq(merchant_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_last_successful_attempt_by_payment_id_merchant_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        // perform ordering on the application level instead of database level
        generics::generic_filter::<<Self as HasTable>::Table, _, _, Self>(
            conn,
            dsl::payment_id
                .eq(payment_id.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned()))
                .and(dsl::status.eq(enums::AttemptStatus::Charged)),
            Some(1),
            None,
            Some(dsl::modified_at.desc()),
        )
        .await?
        .into_iter()
        .nth(0)
        .ok_or(report!(DatabaseError::NotFound))
    }

    #[cfg(feature = "v1")]
    pub async fn find_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        // perform ordering on the application level instead of database level
        generics::generic_filter::<<Self as HasTable>::Table, _, _, Self>(
            conn,
            dsl::payment_id
                .eq(payment_id.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned()))
                .and(
                    dsl::status
                        .eq(enums::AttemptStatus::Charged)
                        .or(dsl::status.eq(enums::AttemptStatus::PartialCharged)),
                ),
            Some(1),
            None,
            Some(dsl::modified_at.desc()),
        )
        .await?
        .into_iter()
        .nth(0)
        .ok_or(report!(DatabaseError::NotFound))
    }

    #[cfg(feature = "v2")]
    pub async fn find_last_successful_or_partially_captured_attempt_by_payment_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::GlobalPaymentId,
    ) -> StorageResult<Self> {
        // perform ordering on the application level instead of database level
        generics::generic_filter::<<Self as HasTable>::Table, _, _, Self>(
            conn,
            dsl::payment_id.eq(payment_id.to_owned()).and(
                dsl::status
                    .eq(enums::AttemptStatus::Charged)
                    .or(dsl::status.eq(enums::AttemptStatus::PartialCharged)),
            ),
            Some(1),
            None,
            Some(dsl::modified_at.desc()),
        )
        .await?
        .into_iter()
        .nth(0)
        .ok_or(report!(DatabaseError::NotFound))
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_merchant_id_connector_txn_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_txn_id: &str,
    ) -> StorageResult<Self> {
        let (txn_id, txn_data) = common_utils::types::ConnectorTransactionId::form_id_and_data(
            connector_txn_id.to_string(),
        );
        let connector_transaction_id = txn_id
            .get_txn_id(txn_data.as_ref())
            .change_context(DatabaseError::Others)
            .attach_printable_lazy(|| {
                format!("Failed to retrieve txn_id for ({txn_id:?}, {txn_data:?})")
            })?;
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::connector_transaction_id.eq(connector_transaction_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v2")]
    pub async fn find_by_profile_id_connector_transaction_id(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        connector_txn_id: &str,
    ) -> StorageResult<Self> {
        let (txn_id, txn_data) = common_utils::types::ConnectorTransactionId::form_id_and_data(
            connector_txn_id.to_string(),
        );
        let connector_transaction_id = txn_id
            .get_txn_id(txn_data.as_ref())
            .change_context(DatabaseError::Others)
            .attach_printable_lazy(|| {
                format!("Failed to retrieve txn_id for ({txn_id:?}, {txn_data:?})")
            })?;
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::profile_id
                .eq(profile_id.to_owned())
                .and(dsl::connector_payment_id.eq(connector_transaction_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_merchant_id_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        attempt_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::attempt_id.eq(attempt_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v2")]
    pub async fn find_by_id(
        conn: &PgPooledConn,
        id: &common_utils::id_type::GlobalAttemptId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::id.eq(id.to_owned()),
        )
        .await
    }

    #[cfg(feature = "v2")]
    pub async fn find_by_payment_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::GlobalPaymentId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::payment_id.eq(payment_id.to_owned()),
            None,
            None,
            Some(dsl::created_at.asc()),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_merchant_id_preprocessing_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        preprocessing_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::preprocessing_step_id.eq(preprocessing_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_payment_id_merchant_id_attempt_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        attempt_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::payment_id.eq(payment_id.to_owned()).and(
                dsl::merchant_id
                    .eq(merchant_id.to_owned())
                    .and(dsl::attempt_id.eq(attempt_id.to_owned())),
            ),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_merchant_id_payment_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned())),
            None,
            None,
            None,
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_filters_for_payments(
        conn: &PgPooledConn,
        pi: &[PaymentIntent],
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<(
        Vec<String>,
        Vec<enums::Currency>,
        Vec<IntentStatus>,
        Vec<enums::PaymentMethod>,
        Vec<enums::PaymentMethodType>,
        Vec<enums::AuthenticationType>,
    )> {
        let active_attempts: Vec<String> = pi
            .iter()
            .map(|payment_intent| payment_intent.clone().active_attempt_id)
            .collect();

        let filter = <Self as HasTable>::table()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(dsl::attempt_id.eq_any(active_attempts));

        let intent_status: Vec<IntentStatus> = pi
            .iter()
            .map(|payment_intent| payment_intent.status)
            .collect::<HashSet<IntentStatus>>()
            .into_iter()
            .collect();

        let filter_connector = filter
            .clone()
            .select(dsl::connector)
            .distinct()
            .get_results_async::<Option<String>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by connector")?
            .into_iter()
            .flatten()
            .collect::<Vec<String>>();

        let filter_currency = filter
            .clone()
            .select(dsl::currency)
            .distinct()
            .get_results_async::<Option<enums::Currency>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by currency")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::Currency>>();

        let filter_payment_method = filter
            .clone()
            .select(dsl::payment_method)
            .distinct()
            .get_results_async::<Option<enums::PaymentMethod>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by payment method")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::PaymentMethod>>();

        let filter_payment_method_type = filter
            .clone()
            .select(dsl::payment_method_type)
            .distinct()
            .get_results_async::<Option<enums::PaymentMethodType>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by payment method type")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::PaymentMethodType>>();

        let filter_authentication_type = filter
            .clone()
            .select(dsl::authentication_type)
            .distinct()
            .get_results_async::<Option<enums::AuthenticationType>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by authentication type")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::AuthenticationType>>();

        Ok((
            filter_connector,
            filter_currency,
            intent_status,
            filter_payment_method,
            filter_payment_method_type,
            filter_authentication_type,
        ))
    }

    #[cfg(feature = "v2")]
    #[allow(clippy::too_many_arguments)]
    pub async fn get_total_count_of_attempts(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<String>>,
        payment_method_type: Option<Vec<enums::PaymentMethod>>,
        payment_method_subtype: Option<Vec<enums::PaymentMethodType>>,
        authentication_type: Option<Vec<enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<enums::CardNetwork>>,
    ) -> StorageResult<i64> {
        let mut filter = <Self as HasTable>::table()
            .count()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(dsl::id.eq_any(active_attempt_ids.to_owned()))
            .into_boxed();

        if let Some(connectors) = connector {
            filter = filter.filter(dsl::connector.eq_any(connectors));
        }

        if let Some(payment_method_types) = payment_method_type {
            filter = filter.filter(dsl::payment_method_type_v2.eq_any(payment_method_types));
        }
        if let Some(payment_method_subtypes) = payment_method_subtype {
            filter = filter.filter(dsl::payment_method_subtype.eq_any(payment_method_subtypes));
        }
        if let Some(authentication_types) = authentication_type {
            filter = filter.filter(dsl::authentication_type.eq_any(authentication_types));
        }
        if let Some(merchant_connector_ids) = merchant_connector_id {
            filter = filter.filter(dsl::merchant_connector_id.eq_any(merchant_connector_ids));
        }
        if let Some(card_networks) = card_network {
            filter = filter.filter(dsl::card_network.eq_any(card_networks));
        }

        router_env::logger::debug!(query = %debug_query::<Pg, _>(&filter).to_string());

        // TODO: Remove these logs after debugging the issue for delay in count query
        let start_time = std::time::Instant::now();
        router_env::logger::debug!("Executing count query start_time: {:?}", start_time);
        let result = db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
            filter.get_result_async::<i64>(conn),
            db_metrics::DatabaseOperation::Filter,
        )
        .await
        .change_context(DatabaseError::Others)
        .attach_printable("Error filtering count of payments");

        let duration = start_time.elapsed();
        router_env::logger::debug!("Completed count query in {:?}", duration);

        result
    }

    #[cfg(feature = "v1")]
    #[allow(clippy::too_many_arguments)]
    pub async fn get_total_count_of_attempts(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<String>>,
        payment_method: Option<Vec<enums::PaymentMethod>>,
        payment_method_type: Option<Vec<enums::PaymentMethodType>>,
        authentication_type: Option<Vec<enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<enums::CardNetwork>>,
        card_discovery: Option<Vec<enums::CardDiscovery>>,
    ) -> StorageResult<i64> {
        let mut filter = <Self as HasTable>::table()
            .count()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(dsl::attempt_id.eq_any(active_attempt_ids.to_owned()))
            .into_boxed();

        if let Some(connector) = connector {
            filter = filter.filter(dsl::connector.eq_any(connector));
        }

        if let Some(payment_method) = payment_method {
            filter = filter.filter(dsl::payment_method.eq_any(payment_method));
        }
        if let Some(payment_method_type) = payment_method_type {
            filter = filter.filter(dsl::payment_method_type.eq_any(payment_method_type));
        }
        if let Some(authentication_type) = authentication_type {
            filter = filter.filter(dsl::authentication_type.eq_any(authentication_type));
        }
        if let Some(merchant_connector_id) = merchant_connector_id {
            filter = filter.filter(dsl::merchant_connector_id.eq_any(merchant_connector_id))
        }
        if let Some(card_network) = card_network {
            filter = filter.filter(dsl::card_network.eq_any(card_network))
        }
        if let Some(card_discovery) = card_discovery {
            filter = filter.filter(dsl::card_discovery.eq_any(card_discovery))
        }

        router_env::logger::debug!(query = %debug_query::<Pg, _>(&filter).to_string());

        // TODO: Remove these logs after debugging the issue for delay in count query
        let start_time = std::time::Instant::now();
        router_env::logger::debug!("Executing count query start_time: {:?}", start_time);
        let result = db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
            filter.get_result_async::<i64>(conn),
            db_metrics::DatabaseOperation::Filter,
        )
        .await
        .change_context(DatabaseError::Others)
        .attach_printable("Error filtering count of payments");

        let duration = start_time.elapsed();
        router_env::logger::debug!("Completed count query in {:?}", duration);

        result
    }
}


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

use std::collections::HashSet;

use diesel::{
    associations::HasTable, BoolExpressionMethods, ExpressionMethods, NullableExpressionMethods,
};

use super::generics;
use crate::{
    events::{Event, EventNew, EventUpdateInternal},
    schema::events::dsl,
    PgPooledConn, StorageResult,
};

impl EventNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Event> {
        generics::generic_insert(conn, self).await
    }
}

impl Event {
    pub async fn find_by_merchant_id_event_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        event_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::event_id.eq(event_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_idempotent_event_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        idempotent_event_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::idempotent_event_id.eq(idempotent_event_id.to_owned())),
        )
        .await
    }

    pub async fn list_initial_attempts_by_merchant_id_primary_object_id_or_initial_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        primary_object_id: &str,
        initial_attempt_id: &str,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::event_id
                .nullable()
                .eq(dsl::initial_attempt_id) // Filter initial attempts only
                .and(dsl::merchant_id.eq(merchant_id.to_owned()))
                .and(
                    dsl::primary_object_id
                        .eq(primary_object_id.to_owned())
                        .or(dsl::initial_attempt_id.eq(initial_attempt_id.to_owned())),
                ),
            None,
            None,
            Some(dsl::created_at.desc()),
        )
        .await
    }

    #[allow(clippy::too_many_arguments)]
    pub async fn list_initial_attempts_by_merchant_id_constraints(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        created_after: time::PrimitiveDateTime,
        created_before: time::PrimitiveDateTime,
        limit: Option<i64>,
        offset: Option<i64>,
        event_types: HashSet<common_enums::EventType>,
        is_delivered: Option<bool>,
    ) -> StorageResult<Vec<Self>> {
        use async_bb8_diesel::AsyncRunQueryDsl;
        use diesel::{debug_query, pg::Pg, QueryDsl};
        use error_stack::ResultExt;
        use router_env::logger;

        use super::generics::db_metrics::{track_database_call, DatabaseOperation};
        use crate::errors::DatabaseError;

        let mut query = Self::table()
            .filter(
                dsl::event_id
                    .nullable()
                    .eq(dsl::initial_attempt_id) // Filter initial attempts only
                    .and(dsl::merchant_id.eq(merchant_id.to_owned())),
            )
            .order(dsl::created_at.desc())
            .into_boxed();

        query = Self::apply_filters(
            query,
            None,
            (dsl::created_at, created_after, created_before),
            limit,
            offset,
            event_types,
            is_delivered,
        );

        logger::debug!(query = %debug_query::<Pg, _>(&query).to_string());

        track_database_call::<Self, _, _>(query.get_results_async(conn), DatabaseOperation::Filter)
            .await
            .change_context(DatabaseError::Others) // Query returns empty Vec when no records are found
            .attach_printable("Error filtering events by constraints")
    }

    pub async fn list_by_merchant_id_initial_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        initial_attempt_id: &str,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::initial_attempt_id.eq(initial_attempt_id.to_owned())),
            None,
            None,
            Some(dsl::created_at.desc()),
        )
        .await
    }

    pub async fn list_initial_attempts_by_profile_id_primary_object_id_or_initial_attempt_id(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        primary_object_id: &str,
        initial_attempt_id: &str,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::event_id
                .nullable()
                .eq(dsl::initial_attempt_id) // Filter initial attempts only
                .and(dsl::business_profile_id.eq(profile_id.to_owned()))
                .and(
                    dsl::primary_object_id
                        .eq(primary_object_id.to_owned())
                        .or(dsl::initial_attempt_id.eq(initial_attempt_id.to_owned())),
                ),
            None,
            None,
            Some(dsl::created_at.desc()),
        )
        .await
    }

    #[allow(clippy::too_many_arguments)]
    pub async fn list_initial_attempts_by_profile_id_constraints(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        created_after: time::PrimitiveDateTime,
        created_before: time::PrimitiveDateTime,
        limit: Option<i64>,
        offset: Option<i64>,
        event_types: HashSet<common_enums::EventType>,
        is_delivered: Option<bool>,
    ) -> StorageResult<Vec<Self>> {
        use async_bb8_diesel::AsyncRunQueryDsl;
        use diesel::{debug_query, pg::Pg, QueryDsl};
        use error_stack::ResultExt;
        use router_env::logger;

        use super::generics::db_metrics::{track_database_call, DatabaseOperation};
        use crate::errors::DatabaseError;

        let mut query = Self::table()
            .filter(
                dsl::event_id
                    .nullable()
                    .eq(dsl::initial_attempt_id) // Filter initial attempts only
                    .and(dsl::business_profile_id.eq(profile_id.to_owned())),
            )
            .order(dsl::created_at.desc())
            .into_boxed();

        query = Self::apply_filters(
            query,
            None,
            (dsl::created_at, created_after, created_before),
            limit,
            offset,
            event_types,
            is_delivered,
        );

        logger::debug!(query = %debug_query::<Pg, _>(&query).to_string());

        track_database_call::<Self, _, _>(query.get_results_async(conn), DatabaseOperation::Filter)
            .await
            .change_context(DatabaseError::Others) // Query returns empty Vec when no records are found
            .attach_printable("Error filtering events by constraints")
    }

    pub async fn list_by_profile_id_initial_attempt_id(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        initial_attempt_id: &str,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::business_profile_id
                .eq(profile_id.to_owned())
                .and(dsl::initial_attempt_id.eq(initial_attempt_id.to_owned())),
            None,
            None,
            Some(dsl::created_at.desc()),
        )
        .await
    }

    pub async fn update_by_merchant_id_event_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        event_id: &str,
        event: EventUpdateInternal,
    ) -> StorageResult<Self> {
        generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::event_id.eq(event_id.to_owned())),
            event,
        )
        .await
    }

    fn apply_filters<T>(
        mut query: T,
        profile_id: Option<common_utils::id_type::ProfileId>,
        (column, created_after, created_before): (
            dsl::created_at,
            time::PrimitiveDateTime,
            time::PrimitiveDateTime,
        ),
        limit: Option<i64>,
        offset: Option<i64>,
        event_types: HashSet<common_enums::EventType>,
        is_delivered: Option<bool>,
    ) -> T
    where
        T: diesel::query_dsl::methods::LimitDsl<Output = T>
            + diesel::query_dsl::methods::OffsetDsl<Output = T>,
        T: diesel::query_dsl::methods::FilterDsl<
            diesel::dsl::GtEq<dsl::created_at, time::PrimitiveDateTime>,
            Output = T,
        >,
        T: diesel::query_dsl::methods::FilterDsl<
            diesel::dsl::LtEq<dsl::created_at, time::PrimitiveDateTime>,
            Output = T,
        >,
        T: diesel::query_dsl::methods::FilterDsl<
            diesel::dsl::Eq<dsl::business_profile_id, common_utils::id_type::ProfileId>,
            Output = T,
        >,
        T: diesel::query_dsl::methods::FilterDsl<
            diesel::dsl::EqAny<dsl::event_type, HashSet<common_enums::EventType>>,
            Output = T,
        >,
        T: diesel::query_dsl::methods::FilterDsl<
            diesel::dsl::Eq<dsl::is_overall_delivery_successful, bool>,
            Output = T,
        >,
    {
        if let Some(profile_id) = profile_id {
            query = query.filter(dsl::business_profile_id.eq(profile_id));
        }

        query = query
            .filter(column.ge(created_after))
            .filter(column.le(created_before));

        if let Some(limit) = limit {
            query = query.limit(limit);
        }

        if let Some(offset) = offset {
            query = query.offset(offset);
        }

        if !event_types.is_empty() {
            query = query.filter(dsl::event_type.eq_any(event_types));
        }

        if let Some(is_delivered) = is_delivered {
            query = query.filter(dsl::is_overall_delivery_successful.eq(is_delivered));
        }

        query
    }

    pub async fn count_initial_attempts_by_constraints(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id: Option<common_utils::id_type::ProfileId>,
        created_after: time::PrimitiveDateTime,
        created_before: time::PrimitiveDateTime,
        event_types: HashSet<common_enums::EventType>,
        is_delivered: Option<bool>,
    ) -> StorageResult<i64> {
        use async_bb8_diesel::AsyncRunQueryDsl;
        use diesel::{debug_query, pg::Pg, QueryDsl};
        use error_stack::ResultExt;
        use router_env::logger;

        use super::generics::db_metrics::{track_database_call, DatabaseOperation};
        use crate::errors::DatabaseError;

        let mut query = Self::table()
            .count()
            .filter(
                dsl::event_id
                    .nullable()
                    .eq(dsl::initial_attempt_id) // Filter initial attempts only
                    .and(dsl::merchant_id.eq(merchant_id.to_owned())),
            )
            .into_boxed();

        query = Self::apply_filters(
            query,
            profile_id,
            (dsl::created_at, created_after, created_before),
            None,
            None,
            event_types,
            is_delivered,
        );

        logger::debug!(query = %debug_query::<Pg, _>(&query).to_string());

        track_database_call::<Self, _, _>(
            query.get_result_async::<i64>(conn),
            DatabaseOperation::Count,
        )
        .await
        .change_context(DatabaseError::Others)
        .attach_printable("Error counting events by constraints")
    }
}


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

use common_utils::id_type;
use diesel::{associations::HasTable, ExpressionMethods};

#[cfg(feature = "v1")]
use crate::schema::organization::dsl::org_id as dsl_identifier;
#[cfg(feature = "v2")]
use crate::schema_v2::organization::dsl::id as dsl_identifier;
use crate::{organization::*, query::generics, PgPooledConn, StorageResult};

impl OrganizationNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Organization> {
        generics::generic_insert(conn, self).await
    }
}

impl Organization {
    pub async fn find_by_org_id(
        conn: &PgPooledConn,
        org_id: id_type::OrganizationId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl_identifier.eq(org_id),
        )
        .await
    }

    pub async fn update_by_org_id(
        conn: &PgPooledConn,
        org_id: id_type::OrganizationId,
        update: OrganizationUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl_identifier.eq(org_id),
            OrganizationUpdateInternal::from(update),
        )
        .await
    }
}


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

#[cfg(feature = "v1")]
use common_types::consts::API_VERSION;
#[cfg(feature = "v1")]
use diesel::BoolExpressionMethods;
use diesel::{associations::HasTable, ExpressionMethods, Table};

use super::generics;
#[cfg(feature = "v1")]
use crate::schema::merchant_account::dsl;
#[cfg(feature = "v2")]
use crate::schema_v2::merchant_account::dsl;
use crate::{
    errors,
    merchant_account::{MerchantAccount, MerchantAccountNew, MerchantAccountUpdateInternal},
    PgPooledConn, StorageResult,
};

impl MerchantAccountNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<MerchantAccount> {
        generics::generic_insert(conn, self).await
    }
}

#[cfg(feature = "v1")]
impl MerchantAccount {
    pub async fn update(
        self,
        conn: &PgPooledConn,
        merchant_account: MerchantAccountUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            self.get_id().to_owned(),
            merchant_account,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn update_with_specific_fields(
        conn: &PgPooledConn,
        identifier: &common_utils::id_type::MerchantId,
        merchant_account: MerchantAccountUpdateInternal,
    ) -> StorageResult<Self> {
        generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::merchant_id.eq(identifier.to_owned()),
            merchant_account,
        )
        .await
    }

    pub async fn delete_by_merchant_id(
        conn: &PgPooledConn,
        identifier: &common_utils::id_type::MerchantId,
    ) -> StorageResult<bool> {
        generics::generic_delete::<<Self as HasTable>::Table, _>(
            conn,
            dsl::merchant_id.eq(identifier.to_owned()),
        )
        .await
    }

    pub async fn find_by_merchant_id(
        conn: &PgPooledConn,
        identifier: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id.eq(identifier.to_owned()),
        )
        .await
    }

    pub async fn find_by_publishable_key(
        conn: &PgPooledConn,
        publishable_key: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::publishable_key.eq(publishable_key.to_owned()),
        )
        .await
    }

    pub async fn list_by_organization_id(
        conn: &PgPooledConn,
        organization_id: &common_utils::id_type::OrganizationId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::organization_id
                .eq(organization_id.to_owned())
                .and(dsl::version.eq(API_VERSION)),
            None,
            None,
            None,
        )
        .await
    }

    pub async fn list_multiple_merchant_accounts(
        conn: &PgPooledConn,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::merchant_id.eq_any(merchant_ids.clone()),
            None,
            None,
            None,
        )
        .await
    }

    pub async fn list_all_merchant_accounts(
        conn: &PgPooledConn,
        limit: u32,
        offset: Option<u32>,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::merchant_id.ne_all(vec![""]),
            Some(i64::from(limit)),
            offset.map(i64::from),
            None,
        )
        .await
    }

    pub async fn update_all_merchant_accounts(
        conn: &PgPooledConn,
        merchant_account: MerchantAccountUpdateInternal,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id.ne_all(vec![""]),
            merchant_account,
        )
        .await
    }
}

#[cfg(feature = "v2")]
impl MerchantAccount {
    pub async fn update(
        self,
        conn: &PgPooledConn,
        merchant_account: MerchantAccountUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            self.get_id().to_owned(),
            merchant_account,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn update_with_specific_fields(
        conn: &PgPooledConn,
        identifier: &common_utils::id_type::MerchantId,
        merchant_account: MerchantAccountUpdateInternal,
    ) -> StorageResult<Self> {
        generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(conn, dsl::id.eq(identifier.to_owned()), merchant_account)
        .await
    }

    pub async fn delete_by_merchant_id(
        conn: &PgPooledConn,
        identifier: &common_utils::id_type::MerchantId,
    ) -> StorageResult<bool> {
        generics::generic_delete::<<Self as HasTable>::Table, _>(
            conn,
            dsl::id.eq(identifier.to_owned()),
        )
        .await
    }

    pub async fn find_by_merchant_id(
        conn: &PgPooledConn,
        identifier: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::id.eq(identifier.to_owned()),
        )
        .await
    }

    pub async fn find_by_publishable_key(
        conn: &PgPooledConn,
        publishable_key: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::publishable_key.eq(publishable_key.to_owned()),
        )
        .await
    }

    pub async fn list_by_organization_id(
        conn: &PgPooledConn,
        organization_id: &common_utils::id_type::OrganizationId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::organization_id.eq(organization_id.to_owned()),
            None,
            None,
            None,
        )
        .await
    }

    pub async fn list_multiple_merchant_accounts(
        conn: &PgPooledConn,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(conn, dsl::id.eq_any(merchant_ids), None, None, None)
        .await
    }

    pub async fn list_all_merchant_accounts(
        conn: &PgPooledConn,
        limit: u32,
        offset: Option<u32>,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::id.ne_all(vec![""]),
            Some(i64::from(limit)),
            offset.map(i64::from),
            None,
        )
        .await
    }

    pub async fn update_all_merchant_accounts(
        conn: &PgPooledConn,
        merchant_account: MerchantAccountUpdateInternal,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::id.ne_all(vec![""]),
            merchant_account,
        )
        .await
    }
}


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

use async_bb8_diesel::AsyncRunQueryDsl;
use diesel::{
    associations::HasTable, debug_query, pg::Pg, BoolExpressionMethods, ExpressionMethods,
    JoinOnDsl, QueryDsl,
};
use error_stack::{report, ResultExt};

use super::generics;
use crate::{
    enums, errors,
    payouts::{Payouts, PayoutsNew, PayoutsUpdate, PayoutsUpdateInternal},
    query::generics::db_metrics,
    schema::{payout_attempt, payouts::dsl},
    PgPooledConn, StorageResult,
};

impl PayoutsNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Payouts> {
        generics::generic_insert(conn, self).await
    }
}
impl Payouts {
    pub async fn update(
        self,
        conn: &PgPooledConn,
        payout_update: PayoutsUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::payout_id
                .eq(self.payout_id.to_owned())
                .and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
            PayoutsUpdateInternal::from(payout_update),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            Ok(mut payouts) => payouts
                .pop()
                .ok_or(error_stack::report!(errors::DatabaseError::NotFound)),
        }
    }

    pub async fn find_by_merchant_id_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_id: &common_utils::id_type::PayoutId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_id.eq(payout_id.to_owned())),
        )
        .await
    }

    pub async fn update_by_merchant_id_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_id: &common_utils::id_type::PayoutId,
        payout: PayoutsUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_id.eq(payout_id.to_owned())),
            PayoutsUpdateInternal::from(payout),
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(errors::DatabaseError::NotFound).attach_printable("Error while updating payout")
        })
    }

    pub async fn find_optional_by_merchant_id_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_id: &common_utils::id_type::PayoutId,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_id.eq(payout_id.to_owned())),
        )
        .await
    }

    pub async fn get_total_count_of_payouts(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        active_payout_ids: &[common_utils::id_type::PayoutId],
        connector: Option<Vec<String>>,
        currency: Option<Vec<enums::Currency>>,
        status: Option<Vec<enums::PayoutStatus>>,
        payout_type: Option<Vec<enums::PayoutType>>,
    ) -> StorageResult<i64> {
        let mut filter = <Self as HasTable>::table()
            .inner_join(payout_attempt::table.on(payout_attempt::dsl::payout_id.eq(dsl::payout_id)))
            .count()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(dsl::payout_id.eq_any(active_payout_ids.to_vec()))
            .into_boxed();

        if let Some(connector) = connector {
            filter = filter.filter(payout_attempt::dsl::connector.eq_any(connector));
        }
        if let Some(currency) = currency {
            filter = filter.filter(dsl::destination_currency.eq_any(currency));
        }
        if let Some(status) = status {
            filter = filter.filter(dsl::status.eq_any(status));
        }
        if let Some(payout_type) = payout_type {
            filter = filter.filter(dsl::payout_type.eq_any(payout_type));
        }
        router_env::logger::debug!(query = %debug_query::<Pg, _>(&filter).to_string());

        db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
            filter.get_result_async::<i64>(conn),
            db_metrics::DatabaseOperation::Filter,
        )
        .await
        .change_context(errors::DatabaseError::Others)
        .attach_printable("Error filtering count of payouts")
    }
}


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

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};

use crate::{
    errors, fraud_check::*, query::generics, schema::fraud_check::dsl, PgPooledConn, StorageResult,
};

impl FraudCheckNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<FraudCheck> {
        generics::generic_insert(conn, self).await
    }
}

impl FraudCheck {
    pub async fn update_with_attempt_id(
        self,
        conn: &PgPooledConn,
        fraud_check: FraudCheckUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::attempt_id
                .eq(self.attempt_id.to_owned())
                .and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
            FraudCheckUpdateInternal::from(fraud_check),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn get_with_payment_id(
        conn: &PgPooledConn,
        payment_id: common_utils::id_type::PaymentId,
        merchant_id: common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::payment_id
                .eq(payment_id)
                .and(dsl::merchant_id.eq(merchant_id)),
        )
        .await
    }

    pub async fn get_with_payment_id_if_present(
        conn: &PgPooledConn,
        payment_id: common_utils::id_type::PaymentId,
        merchant_id: common_utils::id_type::MerchantId,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::payment_id
                .eq(payment_id)
                .and(dsl::merchant_id.eq(merchant_id)),
        )
        .await
    }
}


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

use diesel::{associations::HasTable, ExpressionMethods};
use error_stack::report;

use crate::{
    cards_info::{CardInfo, UpdateCardInfo},
    errors,
    query::generics,
    schema::cards_info::dsl,
    PgPooledConn, StorageResult,
};

impl CardInfo {
    pub async fn find_by_iin(conn: &PgPooledConn, card_iin: &str) -> StorageResult<Option<Self>> {
        generics::generic_find_by_id_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            card_iin.to_owned(),
        )
        .await
    }
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Self> {
        generics::generic_insert(conn, self).await
    }
    pub async fn update(
        conn: &PgPooledConn,
        card_iin: String,
        data: UpdateCardInfo,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, UpdateCardInfo, _, _>(
            conn,
            dsl::card_iin.eq(card_iin),
            data,
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(errors::DatabaseError::NotFound)
                .attach_printable("Error while updating card_info entry")
        })
    }
}