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

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

use crate::{enums as storage_enums, schema::events};

#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = events)]
pub struct EventNew {
    pub event_id: String,
    pub event_type: storage_enums::EventType,
    pub event_class: storage_enums::EventClass,
    pub is_webhook_notified: bool,
    pub primary_object_id: String,
    pub primary_object_type: storage_enums::EventObjectType,
    pub created_at: PrimitiveDateTime,
    pub merchant_id: Option<common_utils::id_type::MerchantId>,
    pub business_profile_id: Option<common_utils::id_type::ProfileId>,
    pub primary_object_created_at: Option<PrimitiveDateTime>,
    pub idempotent_event_id: Option<String>,
    pub initial_attempt_id: Option<String>,
    pub request: Option<Encryption>,
    pub response: Option<Encryption>,
    pub delivery_attempt: Option<storage_enums::WebhookDeliveryAttempt>,
    pub metadata: Option<EventMetadata>,
    pub is_overall_delivery_successful: Option<bool>,
}

#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = events)]
pub struct EventUpdateInternal {
    pub is_webhook_notified: Option<bool>,
    pub response: Option<Encryption>,
    pub is_overall_delivery_successful: Option<bool>,
}

#[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable, Selectable)]
#[diesel(table_name = events, primary_key(event_id), check_for_backend(diesel::pg::Pg))]
pub struct Event {
    pub event_id: String,
    pub event_type: storage_enums::EventType,
    pub event_class: storage_enums::EventClass,
    pub is_webhook_notified: bool,
    pub primary_object_id: String,
    pub primary_object_type: storage_enums::EventObjectType,
    #[serde(with = "custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    pub merchant_id: Option<common_utils::id_type::MerchantId>,
    pub business_profile_id: Option<common_utils::id_type::ProfileId>,
    // This column can be used to partition the database table, so that all events related to a
    // single object would reside in the same partition
    pub primary_object_created_at: Option<PrimitiveDateTime>,
    pub idempotent_event_id: Option<String>,
    pub initial_attempt_id: Option<String>,
    pub request: Option<Encryption>,
    pub response: Option<Encryption>,
    pub delivery_attempt: Option<storage_enums::WebhookDeliveryAttempt>,
    pub metadata: Option<EventMetadata>,
    pub is_overall_delivery_successful: Option<bool>,
}

#[derive(Clone, Debug, Deserialize, Serialize, AsExpression, diesel::FromSqlRow)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub enum EventMetadata {
    #[cfg(feature = "v1")]
    Payment {
        payment_id: common_utils::id_type::PaymentId,
    },
    #[cfg(feature = "v2")]
    Payment {
        payment_id: common_utils::id_type::GlobalPaymentId,
    },
    Payout {
        payout_id: common_utils::id_type::PayoutId,
    },
    #[cfg(feature = "v1")]
    Refund {
        payment_id: common_utils::id_type::PaymentId,
        refund_id: String,
    },
    #[cfg(feature = "v2")]
    Refund {
        payment_id: common_utils::id_type::GlobalPaymentId,
        refund_id: common_utils::id_type::GlobalRefundId,
    },
    #[cfg(feature = "v1")]
    Dispute {
        payment_id: common_utils::id_type::PaymentId,
        attempt_id: String,
        dispute_id: String,
    },
    #[cfg(feature = "v2")]
    Dispute {
        payment_id: common_utils::id_type::GlobalPaymentId,
        attempt_id: String,
        dispute_id: String,
    },
    Mandate {
        payment_method_id: String,
        mandate_id: String,
    },
    Subscription {
        subscription_id: common_utils::id_type::SubscriptionId,
        invoice_id: Option<common_utils::id_type::InvoiceId>,
        payment_id: Option<common_utils::id_type::PaymentId>,
    },
}

common_utils::impl_to_sql_from_sql_json!(EventMetadata);


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

use common_utils::{id_type, pii};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};

#[cfg(feature = "v1")]
use crate::schema::organization;
#[cfg(feature = "v2")]
use crate::schema_v2::organization;
pub trait OrganizationBridge {
    fn get_organization_id(&self) -> id_type::OrganizationId;
    fn get_organization_name(&self) -> Option<String>;
    fn set_organization_name(&mut self, organization_name: String);
}
#[cfg(feature = "v1")]
#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
#[diesel(
    table_name = organization,
    primary_key(org_id),
    check_for_backend(diesel::pg::Pg)
)]
pub struct Organization {
    org_id: id_type::OrganizationId,
    org_name: Option<String>,
    pub organization_details: Option<pii::SecretSerdeValue>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    #[allow(dead_code)]
    id: Option<id_type::OrganizationId>,
    #[allow(dead_code)]
    organization_name: Option<String>,
    pub version: common_enums::ApiVersion,
    pub organization_type: Option<common_enums::OrganizationType>,
    pub platform_merchant_id: Option<id_type::MerchantId>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
#[diesel(
    table_name = organization,
    primary_key(id),
    check_for_backend(diesel::pg::Pg)
)]
pub struct Organization {
    pub organization_details: Option<pii::SecretSerdeValue>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    id: id_type::OrganizationId,
    organization_name: Option<String>,
    pub version: common_enums::ApiVersion,
    pub organization_type: Option<common_enums::OrganizationType>,
    pub platform_merchant_id: Option<id_type::MerchantId>,
}

#[cfg(feature = "v1")]
impl Organization {
    pub fn new(org_new: OrganizationNew) -> Self {
        let OrganizationNew {
            org_id,
            org_name,
            organization_details,
            metadata,
            created_at,
            modified_at,
            id: _,
            organization_name: _,
            version,
            organization_type,
            platform_merchant_id,
        } = org_new;
        Self {
            id: Some(org_id.clone()),
            organization_name: org_name.clone(),
            org_id,
            org_name,
            organization_details,
            metadata,
            created_at,
            modified_at,
            version,
            organization_type: Some(organization_type),
            platform_merchant_id,
        }
    }

    pub fn get_organization_type(&self) -> common_enums::OrganizationType {
        self.organization_type.unwrap_or_default()
    }
}

#[cfg(feature = "v2")]
impl Organization {
    pub fn new(org_new: OrganizationNew) -> Self {
        let OrganizationNew {
            id,
            organization_name,
            organization_details,
            metadata,
            created_at,
            modified_at,
            version,
            organization_type,
            platform_merchant_id,
        } = org_new;
        Self {
            id,
            organization_name,
            organization_details,
            metadata,
            created_at,
            modified_at,
            version,
            organization_type: Some(organization_type),
            platform_merchant_id,
        }
    }

    pub fn get_organization_type(&self) -> common_enums::OrganizationType {
        self.organization_type.unwrap_or_default()
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, Insertable)]
#[diesel(table_name = organization, primary_key(org_id))]
pub struct OrganizationNew {
    org_id: id_type::OrganizationId,
    org_name: Option<String>,
    id: id_type::OrganizationId,
    organization_name: Option<String>,
    pub organization_details: Option<pii::SecretSerdeValue>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub version: common_enums::ApiVersion,
    pub organization_type: common_enums::OrganizationType,
    pub platform_merchant_id: Option<id_type::MerchantId>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Insertable)]
#[diesel(table_name = organization, primary_key(id))]
pub struct OrganizationNew {
    id: id_type::OrganizationId,
    organization_name: Option<String>,
    pub organization_details: Option<pii::SecretSerdeValue>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub version: common_enums::ApiVersion,
    pub organization_type: common_enums::OrganizationType,
    pub platform_merchant_id: Option<id_type::MerchantId>,
}

#[cfg(feature = "v1")]
impl OrganizationNew {
    pub fn new(
        id: id_type::OrganizationId,
        organization_type: common_enums::OrganizationType,
        organization_name: Option<String>,
    ) -> Self {
        Self {
            org_id: id.clone(),
            org_name: organization_name.clone(),
            id,
            organization_name,
            organization_details: None,
            metadata: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            version: common_types::consts::API_VERSION,
            organization_type,
            platform_merchant_id: None,
        }
    }
}

#[cfg(feature = "v2")]
impl OrganizationNew {
    pub fn new(
        id: id_type::OrganizationId,
        organization_type: common_enums::OrganizationType,
        organization_name: Option<String>,
    ) -> Self {
        Self {
            id,
            organization_name,
            organization_details: None,
            metadata: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            version: common_types::consts::API_VERSION,
            organization_type,
            platform_merchant_id: None,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, AsChangeset)]
#[diesel(table_name = organization)]
pub struct OrganizationUpdateInternal {
    org_name: Option<String>,
    organization_name: Option<String>,
    organization_details: Option<pii::SecretSerdeValue>,
    metadata: Option<pii::SecretSerdeValue>,
    modified_at: time::PrimitiveDateTime,
    platform_merchant_id: Option<id_type::MerchantId>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, AsChangeset)]
#[diesel(table_name = organization)]
pub struct OrganizationUpdateInternal {
    organization_name: Option<String>,
    organization_details: Option<pii::SecretSerdeValue>,
    metadata: Option<pii::SecretSerdeValue>,
    modified_at: time::PrimitiveDateTime,
    platform_merchant_id: Option<id_type::MerchantId>,
}

pub enum OrganizationUpdate {
    Update {
        organization_name: Option<String>,
        organization_details: Option<pii::SecretSerdeValue>,
        metadata: Option<pii::SecretSerdeValue>,
        platform_merchant_id: Option<id_type::MerchantId>,
    },
}

#[cfg(feature = "v1")]
impl From<OrganizationUpdate> for OrganizationUpdateInternal {
    fn from(value: OrganizationUpdate) -> Self {
        match value {
            OrganizationUpdate::Update {
                organization_name,
                organization_details,
                metadata,
                platform_merchant_id,
            } => Self {
                org_name: organization_name.clone(),
                organization_name,
                organization_details,
                metadata,
                modified_at: common_utils::date_time::now(),
                platform_merchant_id,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl From<OrganizationUpdate> for OrganizationUpdateInternal {
    fn from(value: OrganizationUpdate) -> Self {
        match value {
            OrganizationUpdate::Update {
                organization_name,
                organization_details,
                metadata,
                platform_merchant_id,
            } => Self {
                organization_name,
                organization_details,
                metadata,
                modified_at: common_utils::date_time::now(),
                platform_merchant_id,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl OrganizationBridge for Organization {
    fn get_organization_id(&self) -> id_type::OrganizationId {
        self.org_id.clone()
    }
    fn get_organization_name(&self) -> Option<String> {
        self.org_name.clone()
    }
    fn set_organization_name(&mut self, organization_name: String) {
        self.org_name = Some(organization_name);
    }
}

#[cfg(feature = "v1")]
impl OrganizationBridge for OrganizationNew {
    fn get_organization_id(&self) -> id_type::OrganizationId {
        self.org_id.clone()
    }
    fn get_organization_name(&self) -> Option<String> {
        self.org_name.clone()
    }
    fn set_organization_name(&mut self, organization_name: String) {
        self.org_name = Some(organization_name);
    }
}

#[cfg(feature = "v2")]
impl OrganizationBridge for Organization {
    fn get_organization_id(&self) -> id_type::OrganizationId {
        self.id.clone()
    }
    fn get_organization_name(&self) -> Option<String> {
        self.organization_name.clone()
    }
    fn set_organization_name(&mut self, organization_name: String) {
        self.organization_name = Some(organization_name);
    }
}

#[cfg(feature = "v2")]
impl OrganizationBridge for OrganizationNew {
    fn get_organization_id(&self) -> id_type::OrganizationId {
        self.id.clone()
    }
    fn get_organization_name(&self) -> Option<String> {
        self.organization_name.clone()
    }
    fn set_organization_name(&mut self, organization_name: String) {
        self.organization_name = Some(organization_name);
    }
}


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

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

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

/// Note: The order of fields in the struct is important.
/// This should be in the same order as the fields in the schema.rs file, otherwise the code will not compile
/// If two adjacent columns have the same type, then the compiler will not throw any error, but the fields read / written will be interchanged
#[cfg(feature = "v1")]
#[derive(
    Clone,
    Debug,
    serde::Deserialize,
    Identifiable,
    serde::Serialize,
    Queryable,
    Selectable,
    router_derive::DebugAsDisplay,
)]
#[diesel(table_name = merchant_account, primary_key(merchant_id), check_for_backend(diesel::pg::Pg))]
pub struct MerchantAccount {
    merchant_id: common_utils::id_type::MerchantId,
    pub return_url: Option<String>,
    pub enable_payment_response_hash: bool,
    pub payment_response_hash_key: Option<String>,
    pub redirect_to_merchant_with_http_post: bool,
    pub merchant_name: Option<Encryption>,
    pub merchant_details: Option<Encryption>,
    pub webhook_details: Option<crate::business_profile::WebhookDetails>,
    pub sub_merchants_enabled: Option<bool>,
    pub parent_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub publishable_key: Option<String>,
    pub storage_scheme: storage_enums::MerchantStorageScheme,
    pub locker_id: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub routing_algorithm: Option<serde_json::Value>,
    pub primary_business_details: serde_json::Value,
    pub intent_fulfillment_time: Option<i64>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub frm_routing_algorithm: Option<serde_json::Value>,
    pub payout_routing_algorithm: Option<serde_json::Value>,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub is_recon_enabled: bool,
    pub default_profile: Option<common_utils::id_type::ProfileId>,
    pub recon_status: storage_enums::ReconStatus,
    pub payment_link_config: Option<serde_json::Value>,
    pub pm_collect_link_config: Option<serde_json::Value>,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub id: Option<common_utils::id_type::MerchantId>,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: Option<common_enums::MerchantAccountType>,
}

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

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

/// Note: The order of fields in the struct is important.
/// This should be in the same order as the fields in the schema.rs file, otherwise the code will not compile
/// If two adjacent columns have the same type, then the compiler will not throw any error, but the fields read / written will be interchanged
#[cfg(feature = "v2")]
#[derive(
    Clone,
    Debug,
    serde::Deserialize,
    Identifiable,
    serde::Serialize,
    Queryable,
    router_derive::DebugAsDisplay,
    Selectable,
)]
#[diesel(table_name = merchant_account, primary_key(id), check_for_backend(diesel::pg::Pg))]
pub struct MerchantAccount {
    pub merchant_name: Option<Encryption>,
    pub merchant_details: Option<Encryption>,
    pub publishable_key: Option<String>,
    pub storage_scheme: storage_enums::MerchantStorageScheme,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub recon_status: storage_enums::ReconStatus,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub id: common_utils::id_type::MerchantId,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: Option<common_enums::MerchantAccountType>,
}

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

#[cfg(feature = "v2")]
pub struct MerchantAccountSetter {
    pub id: common_utils::id_type::MerchantId,
    pub merchant_name: Option<Encryption>,
    pub merchant_details: Option<Encryption>,
    pub publishable_key: Option<String>,
    pub storage_scheme: storage_enums::MerchantStorageScheme,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub recon_status: storage_enums::ReconStatus,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

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

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

#[cfg(feature = "v1")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_account)]
pub struct MerchantAccountNew {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub merchant_name: Option<Encryption>,
    pub merchant_details: Option<Encryption>,
    pub return_url: Option<String>,
    pub webhook_details: Option<crate::business_profile::WebhookDetails>,
    pub sub_merchants_enabled: Option<bool>,
    pub parent_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub enable_payment_response_hash: Option<bool>,
    pub payment_response_hash_key: Option<String>,
    pub redirect_to_merchant_with_http_post: Option<bool>,
    pub publishable_key: Option<String>,
    pub locker_id: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub routing_algorithm: Option<serde_json::Value>,
    pub primary_business_details: serde_json::Value,
    pub intent_fulfillment_time: Option<i64>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub frm_routing_algorithm: Option<serde_json::Value>,
    pub payout_routing_algorithm: Option<serde_json::Value>,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub is_recon_enabled: bool,
    pub default_profile: Option<common_utils::id_type::ProfileId>,
    pub recon_status: storage_enums::ReconStatus,
    pub payment_link_config: Option<serde_json::Value>,
    pub pm_collect_link_config: Option<serde_json::Value>,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub id: Option<common_utils::id_type::MerchantId>,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_account)]
pub struct MerchantAccountNew {
    pub merchant_name: Option<Encryption>,
    pub merchant_details: Option<Encryption>,
    pub publishable_key: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub recon_status: storage_enums::ReconStatus,
    pub id: common_utils::id_type::MerchantId,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_account)]
pub struct MerchantAccountUpdateInternal {
    pub merchant_name: Option<Encryption>,
    pub merchant_details: Option<Encryption>,
    pub publishable_key: Option<String>,
    pub storage_scheme: Option<storage_enums::MerchantStorageScheme>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: time::PrimitiveDateTime,
    pub organization_id: Option<common_utils::id_type::OrganizationId>,
    pub recon_status: Option<storage_enums::ReconStatus>,
    pub is_platform_account: Option<bool>,
    pub product_type: Option<common_enums::MerchantProductType>,
}

#[cfg(feature = "v2")]
impl MerchantAccountUpdateInternal {
    pub fn apply_changeset(self, source: MerchantAccount) -> MerchantAccount {
        let Self {
            merchant_name,
            merchant_details,
            publishable_key,
            storage_scheme,
            metadata,
            modified_at,
            organization_id,
            recon_status,
            is_platform_account,
            product_type,
        } = self;

        MerchantAccount {
            merchant_name: merchant_name.or(source.merchant_name),
            merchant_details: merchant_details.or(source.merchant_details),
            publishable_key: publishable_key.or(source.publishable_key),
            storage_scheme: storage_scheme.unwrap_or(source.storage_scheme),
            metadata: metadata.or(source.metadata),
            created_at: source.created_at,
            modified_at,
            organization_id: organization_id.unwrap_or(source.organization_id),
            recon_status: recon_status.unwrap_or(source.recon_status),
            version: source.version,
            id: source.id,
            is_platform_account: is_platform_account.unwrap_or(source.is_platform_account),
            product_type: product_type.or(source.product_type),
            merchant_account_type: source.merchant_account_type,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_account)]
pub struct MerchantAccountUpdateInternal {
    pub merchant_name: Option<Encryption>,
    pub merchant_details: Option<Encryption>,
    pub return_url: Option<String>,
    pub webhook_details: Option<crate::business_profile::WebhookDetails>,
    pub sub_merchants_enabled: Option<bool>,
    pub parent_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub enable_payment_response_hash: Option<bool>,
    pub payment_response_hash_key: Option<String>,
    pub redirect_to_merchant_with_http_post: Option<bool>,
    pub publishable_key: Option<String>,
    pub storage_scheme: Option<storage_enums::MerchantStorageScheme>,
    pub locker_id: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub routing_algorithm: Option<serde_json::Value>,
    pub primary_business_details: Option<serde_json::Value>,
    pub modified_at: time::PrimitiveDateTime,
    pub intent_fulfillment_time: Option<i64>,
    pub frm_routing_algorithm: Option<serde_json::Value>,
    pub payout_routing_algorithm: Option<serde_json::Value>,
    pub organization_id: Option<common_utils::id_type::OrganizationId>,
    pub is_recon_enabled: Option<bool>,
    pub default_profile: Option<Option<common_utils::id_type::ProfileId>>,
    pub recon_status: Option<storage_enums::ReconStatus>,
    pub payment_link_config: Option<serde_json::Value>,
    pub pm_collect_link_config: Option<serde_json::Value>,
    pub is_platform_account: Option<bool>,
    pub product_type: Option<common_enums::MerchantProductType>,
}

#[cfg(feature = "v1")]
impl MerchantAccountUpdateInternal {
    pub fn apply_changeset(self, source: MerchantAccount) -> MerchantAccount {
        let Self {
            merchant_name,
            merchant_details,
            return_url,
            webhook_details,
            sub_merchants_enabled,
            parent_merchant_id,
            enable_payment_response_hash,
            payment_response_hash_key,
            redirect_to_merchant_with_http_post,
            publishable_key,
            storage_scheme,
            locker_id,
            metadata,
            routing_algorithm,
            primary_business_details,
            modified_at,
            intent_fulfillment_time,
            frm_routing_algorithm,
            payout_routing_algorithm,
            organization_id,
            is_recon_enabled,
            default_profile,
            recon_status,
            payment_link_config,
            pm_collect_link_config,
            is_platform_account,
            product_type,
        } = self;

        MerchantAccount {
            merchant_id: source.merchant_id,
            return_url: return_url.or(source.return_url),
            enable_payment_response_hash: enable_payment_response_hash
                .unwrap_or(source.enable_payment_response_hash),
            payment_response_hash_key: payment_response_hash_key
                .or(source.payment_response_hash_key),
            redirect_to_merchant_with_http_post: redirect_to_merchant_with_http_post
                .unwrap_or(source.redirect_to_merchant_with_http_post),
            merchant_name: merchant_name.or(source.merchant_name),
            merchant_details: merchant_details.or(source.merchant_details),
            webhook_details: webhook_details.or(source.webhook_details),
            sub_merchants_enabled: sub_merchants_enabled.or(source.sub_merchants_enabled),
            parent_merchant_id: parent_merchant_id.or(source.parent_merchant_id),
            publishable_key: publishable_key.or(source.publishable_key),
            storage_scheme: storage_scheme.unwrap_or(source.storage_scheme),
            locker_id: locker_id.or(source.locker_id),
            metadata: metadata.or(source.metadata),
            routing_algorithm: routing_algorithm.or(source.routing_algorithm),
            primary_business_details: primary_business_details
                .unwrap_or(source.primary_business_details),
            intent_fulfillment_time: intent_fulfillment_time.or(source.intent_fulfillment_time),
            created_at: source.created_at,
            modified_at,
            frm_routing_algorithm: frm_routing_algorithm.or(source.frm_routing_algorithm),
            payout_routing_algorithm: payout_routing_algorithm.or(source.payout_routing_algorithm),
            organization_id: organization_id.unwrap_or(source.organization_id),
            is_recon_enabled: is_recon_enabled.unwrap_or(source.is_recon_enabled),
            default_profile: default_profile.unwrap_or(source.default_profile),
            recon_status: recon_status.unwrap_or(source.recon_status),
            payment_link_config: payment_link_config.or(source.payment_link_config),
            pm_collect_link_config: pm_collect_link_config.or(source.pm_collect_link_config),
            version: source.version,
            is_platform_account: is_platform_account.unwrap_or(source.is_platform_account),
            id: source.id,
            product_type: product_type.or(source.product_type),
            merchant_account_type: source.merchant_account_type,
        }
    }
}


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

use common_utils::{pii, types::MinorUnit};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use serde::{self, Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{enums as storage_enums, schema::payouts};

// Payouts
#[derive(
    Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize,
)]
#[diesel(table_name = payouts, primary_key(payout_id), check_for_backend(diesel::pg::Pg))]
pub struct Payouts {
    pub payout_id: common_utils::id_type::PayoutId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub address_id: Option<String>,
    pub payout_type: Option<storage_enums::PayoutType>,
    pub payout_method_id: Option<String>,
    pub amount: MinorUnit,
    pub destination_currency: storage_enums::Currency,
    pub source_currency: storage_enums::Currency,
    pub description: Option<String>,
    pub recurring: bool,
    pub auto_fulfill: bool,
    pub return_url: Option<String>,
    pub entity_type: storage_enums::PayoutEntityType,
    pub metadata: Option<pii::SecretSerdeValue>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_modified_at: PrimitiveDateTime,
    pub attempt_count: i16,
    pub profile_id: common_utils::id_type::ProfileId,
    pub status: storage_enums::PayoutStatus,
    pub confirm: Option<bool>,
    pub payout_link_id: Option<String>,
    pub client_secret: Option<String>,
    pub priority: Option<storage_enums::PayoutSendPriority>,
    pub organization_id: Option<common_utils::id_type::OrganizationId>,
}

#[derive(
    Clone,
    Debug,
    Eq,
    PartialEq,
    Insertable,
    serde::Serialize,
    serde::Deserialize,
    router_derive::DebugAsDisplay,
    router_derive::Setter,
)]
#[diesel(table_name = payouts)]
pub struct PayoutsNew {
    pub payout_id: common_utils::id_type::PayoutId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub address_id: Option<String>,
    pub payout_type: Option<storage_enums::PayoutType>,
    pub payout_method_id: Option<String>,
    pub amount: MinorUnit,
    pub destination_currency: storage_enums::Currency,
    pub source_currency: storage_enums::Currency,
    pub description: Option<String>,
    pub recurring: bool,
    pub auto_fulfill: bool,
    pub return_url: Option<String>,
    pub entity_type: storage_enums::PayoutEntityType,
    pub metadata: Option<pii::SecretSerdeValue>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_modified_at: PrimitiveDateTime,
    pub attempt_count: i16,
    pub profile_id: common_utils::id_type::ProfileId,
    pub status: storage_enums::PayoutStatus,
    pub confirm: Option<bool>,
    pub payout_link_id: Option<String>,
    pub client_secret: Option<String>,
    pub priority: Option<storage_enums::PayoutSendPriority>,
    pub organization_id: Option<common_utils::id_type::OrganizationId>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PayoutsUpdate {
    Update {
        amount: MinorUnit,
        destination_currency: storage_enums::Currency,
        source_currency: storage_enums::Currency,
        description: Option<String>,
        recurring: bool,
        auto_fulfill: bool,
        return_url: Option<String>,
        entity_type: storage_enums::PayoutEntityType,
        metadata: Option<pii::SecretSerdeValue>,
        profile_id: Option<common_utils::id_type::ProfileId>,
        status: Option<storage_enums::PayoutStatus>,
        confirm: Option<bool>,
        payout_type: Option<storage_enums::PayoutType>,
        address_id: Option<String>,
        customer_id: Option<common_utils::id_type::CustomerId>,
    },
    PayoutMethodIdUpdate {
        payout_method_id: String,
    },
    RecurringUpdate {
        recurring: bool,
    },
    AttemptCountUpdate {
        attempt_count: i16,
    },
    StatusUpdate {
        status: storage_enums::PayoutStatus,
    },
}

#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = payouts)]
pub struct PayoutsUpdateInternal {
    pub amount: Option<MinorUnit>,
    pub destination_currency: Option<storage_enums::Currency>,
    pub source_currency: Option<storage_enums::Currency>,
    pub description: Option<String>,
    pub recurring: Option<bool>,
    pub auto_fulfill: Option<bool>,
    pub return_url: Option<String>,
    pub entity_type: Option<storage_enums::PayoutEntityType>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub payout_method_id: Option<String>,
    pub profile_id: Option<common_utils::id_type::ProfileId>,
    pub status: Option<storage_enums::PayoutStatus>,
    pub last_modified_at: PrimitiveDateTime,
    pub attempt_count: Option<i16>,
    pub confirm: Option<bool>,
    pub payout_type: Option<common_enums::PayoutType>,
    pub address_id: Option<String>,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
}

impl Default for PayoutsUpdateInternal {
    fn default() -> Self {
        Self {
            amount: None,
            destination_currency: None,
            source_currency: None,
            description: None,
            recurring: None,
            auto_fulfill: None,
            return_url: None,
            entity_type: None,
            metadata: None,
            payout_method_id: None,
            profile_id: None,
            status: None,
            last_modified_at: common_utils::date_time::now(),
            attempt_count: None,
            confirm: None,
            payout_type: None,
            address_id: None,
            customer_id: None,
        }
    }
}

impl From<PayoutsUpdate> for PayoutsUpdateInternal {
    fn from(payout_update: PayoutsUpdate) -> Self {
        match payout_update {
            PayoutsUpdate::Update {
                amount,
                destination_currency,
                source_currency,
                description,
                recurring,
                auto_fulfill,
                return_url,
                entity_type,
                metadata,
                profile_id,
                status,
                confirm,
                payout_type,
                address_id,
                customer_id,
            } => Self {
                amount: Some(amount),
                destination_currency: Some(destination_currency),
                source_currency: Some(source_currency),
                description,
                recurring: Some(recurring),
                auto_fulfill: Some(auto_fulfill),
                return_url,
                entity_type: Some(entity_type),
                metadata,
                profile_id,
                status,
                confirm,
                payout_type,
                address_id,
                customer_id,
                ..Default::default()
            },
            PayoutsUpdate::PayoutMethodIdUpdate { payout_method_id } => Self {
                payout_method_id: Some(payout_method_id),
                ..Default::default()
            },
            PayoutsUpdate::RecurringUpdate { recurring } => Self {
                recurring: Some(recurring),
                ..Default::default()
            },
            PayoutsUpdate::AttemptCountUpdate { attempt_count } => Self {
                attempt_count: Some(attempt_count),
                ..Default::default()
            },
            PayoutsUpdate::StatusUpdate { status } => Self {
                status: Some(status),
                ..Default::default()
            },
        }
    }
}

impl PayoutsUpdate {
    pub fn apply_changeset(self, source: Payouts) -> Payouts {
        let PayoutsUpdateInternal {
            amount,
            destination_currency,
            source_currency,
            description,
            recurring,
            auto_fulfill,
            return_url,
            entity_type,
            metadata,
            payout_method_id,
            profile_id,
            status,
            last_modified_at,
            attempt_count,
            confirm,
            payout_type,
            address_id,
            customer_id,
        } = self.into();
        Payouts {
            amount: amount.unwrap_or(source.amount),
            destination_currency: destination_currency.unwrap_or(source.destination_currency),
            source_currency: source_currency.unwrap_or(source.source_currency),
            description: description.or(source.description),
            recurring: recurring.unwrap_or(source.recurring),
            auto_fulfill: auto_fulfill.unwrap_or(source.auto_fulfill),
            return_url: return_url.or(source.return_url),
            entity_type: entity_type.unwrap_or(source.entity_type),
            metadata: metadata.or(source.metadata),
            payout_method_id: payout_method_id.or(source.payout_method_id),
            profile_id: profile_id.unwrap_or(source.profile_id),
            status: status.unwrap_or(source.status),
            last_modified_at,
            attempt_count: attempt_count.unwrap_or(source.attempt_count),
            confirm: confirm.or(source.confirm),
            payout_type: payout_type.or(source.payout_type),
            address_id: address_id.or(source.address_id),
            customer_id: customer_id.or(source.customer_id),
            ..source
        }
    }
}


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

use common_enums as storage_enums;
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use masking::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{
    enums::{FraudCheckLastStep, FraudCheckStatus, FraudCheckType},
    schema::fraud_check,
};
#[derive(Clone, Debug, Identifiable, Queryable, Selectable, Serialize, Deserialize)]
#[diesel(table_name = fraud_check,  primary_key(payment_id, merchant_id), check_for_backend(diesel::pg::Pg))]
pub struct FraudCheck {
    pub frm_id: String,
    pub payment_id: common_utils::id_type::PaymentId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub attempt_id: String,
    pub created_at: PrimitiveDateTime,
    pub frm_name: String,
    pub frm_transaction_id: Option<String>,
    pub frm_transaction_type: FraudCheckType,
    pub frm_status: FraudCheckStatus,
    pub frm_score: Option<i32>,
    pub frm_reason: Option<serde_json::Value>,
    pub frm_error: Option<String>,
    pub payment_details: Option<serde_json::Value>,
    pub metadata: Option<serde_json::Value>,
    pub modified_at: PrimitiveDateTime,
    pub last_step: FraudCheckLastStep,
    pub payment_capture_method: Option<storage_enums::CaptureMethod>, // In postFrm, we are updating capture method from automatic to manual. To store the merchant actual capture method, we are storing the actual capture method in payment_capture_method. It will be useful while approving the FRM decision.
}

#[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = fraud_check)]
pub struct FraudCheckNew {
    pub frm_id: String,
    pub payment_id: common_utils::id_type::PaymentId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub attempt_id: String,
    pub created_at: PrimitiveDateTime,
    pub frm_name: String,
    pub frm_transaction_id: Option<String>,
    pub frm_transaction_type: FraudCheckType,
    pub frm_status: FraudCheckStatus,
    pub frm_score: Option<i32>,
    pub frm_reason: Option<serde_json::Value>,
    pub frm_error: Option<String>,
    pub payment_details: Option<serde_json::Value>,
    pub metadata: Option<serde_json::Value>,
    pub modified_at: PrimitiveDateTime,
    pub last_step: FraudCheckLastStep,
    pub payment_capture_method: Option<storage_enums::CaptureMethod>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FraudCheckUpdate {
    //Refer PaymentAttemptUpdate for other variants implementations
    ResponseUpdate {
        frm_status: FraudCheckStatus,
        frm_transaction_id: Option<String>,
        frm_reason: Option<serde_json::Value>,
        frm_score: Option<i32>,
        metadata: Option<serde_json::Value>,
        modified_at: PrimitiveDateTime,
        last_step: FraudCheckLastStep,
        payment_capture_method: Option<storage_enums::CaptureMethod>,
    },
    ErrorUpdate {
        status: FraudCheckStatus,
        error_message: Option<Option<String>>,
    },
}

#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = fraud_check)]
pub struct FraudCheckUpdateInternal {
    frm_status: Option<FraudCheckStatus>,
    frm_transaction_id: Option<String>,
    frm_reason: Option<serde_json::Value>,
    frm_score: Option<i32>,
    frm_error: Option<Option<String>>,
    metadata: Option<serde_json::Value>,
    last_step: FraudCheckLastStep,
    payment_capture_method: Option<storage_enums::CaptureMethod>,
}

impl From<FraudCheckUpdate> for FraudCheckUpdateInternal {
    fn from(fraud_check_update: FraudCheckUpdate) -> Self {
        match fraud_check_update {
            FraudCheckUpdate::ResponseUpdate {
                frm_status,
                frm_transaction_id,
                frm_reason,
                frm_score,
                metadata,
                modified_at: _,
                last_step,
                payment_capture_method,
            } => Self {
                frm_status: Some(frm_status),
                frm_transaction_id,
                frm_reason,
                frm_score,
                metadata,
                last_step,
                payment_capture_method,
                ..Default::default()
            },
            FraudCheckUpdate::ErrorUpdate {
                status,
                error_message,
            } => Self {
                frm_status: Some(status),
                frm_error: error_message,
                ..Default::default()
            },
        }
    }
}


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

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

use crate::{enums as storage_enums, schema::cards_info};

#[derive(
    Clone,
    Debug,
    Queryable,
    Identifiable,
    Selectable,
    serde::Deserialize,
    serde::Serialize,
    Insertable,
)]
#[diesel(table_name = cards_info, primary_key(card_iin), check_for_backend(diesel::pg::Pg))]
pub struct CardInfo {
    pub card_iin: String,
    pub card_issuer: Option<String>,
    pub card_network: Option<storage_enums::CardNetwork>,
    pub card_type: Option<String>,
    pub card_subtype: Option<String>,
    pub card_issuing_country: Option<String>,
    pub bank_code_id: Option<String>,
    pub bank_code: Option<String>,
    pub country_code: Option<String>,
    pub date_created: PrimitiveDateTime,
    pub last_updated: Option<PrimitiveDateTime>,
    pub last_updated_provider: Option<String>,
}

#[derive(
    Clone, Debug, PartialEq, Eq, AsChangeset, router_derive::DebugAsDisplay, serde::Deserialize,
)]
#[diesel(table_name = cards_info)]
pub struct UpdateCardInfo {
    pub card_issuer: Option<String>,
    pub card_network: Option<storage_enums::CardNetwork>,
    pub card_type: Option<String>,
    pub card_subtype: Option<String>,
    pub card_issuing_country: Option<String>,
    pub bank_code_id: Option<String>,
    pub bank_code: Option<String>,
    pub country_code: Option<String>,
    pub last_updated: Option<PrimitiveDateTime>,
    pub last_updated_provider: Option<String>,
}