// File: crates/router/src/db/unified_translations.rs

use diesel_models::unified_translations as storage;
use error_stack::report;

use super::MockDb;
use crate::{
    connection,
    core::errors::{self, CustomResult},
    services::Store,
};

#[async_trait::async_trait]
pub trait UnifiedTranslationsInterface {
    async fn add_unfied_translation(
        &self,
        translation: storage::UnifiedTranslationsNew,
    ) -> CustomResult<storage::UnifiedTranslations, errors::StorageError>;

    async fn update_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
        data: storage::UnifiedTranslationsUpdate,
    ) -> CustomResult<storage::UnifiedTranslations, errors::StorageError>;

    async fn find_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
    ) -> CustomResult<String, errors::StorageError>;

    async fn delete_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
    ) -> CustomResult<bool, errors::StorageError>;
}

#[async_trait::async_trait]
impl UnifiedTranslationsInterface for Store {
    async fn add_unfied_translation(
        &self,
        translation: storage::UnifiedTranslationsNew,
    ) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        translation
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    async fn update_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
        data: storage::UnifiedTranslationsUpdate,
    ) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        storage::UnifiedTranslations::update_by_unified_code_unified_message_locale(
            &conn,
            unified_code,
            unified_message,
            locale,
            data,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    async fn find_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
    ) -> CustomResult<String, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        let translations =
            storage::UnifiedTranslations::find_by_unified_code_unified_message_locale(
                &conn,
                unified_code,
                unified_message,
                locale,
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?;
        Ok(translations.translation)
    }

    async fn delete_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
    ) -> CustomResult<bool, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        storage::UnifiedTranslations::delete_by_unified_code_unified_message_locale(
            &conn,
            unified_code,
            unified_message,
            locale,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl UnifiedTranslationsInterface for MockDb {
    async fn add_unfied_translation(
        &self,
        _translation: storage::UnifiedTranslationsNew,
    ) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn find_translation(
        &self,
        _unified_code: String,
        _unified_message: String,
        _locale: String,
    ) -> CustomResult<String, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn update_translation(
        &self,
        _unified_code: String,
        _unified_message: String,
        _locale: String,
        _data: storage::UnifiedTranslationsUpdate,
    ) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn delete_translation(
        &self,
        _unified_code: String,
        _unified_message: String,
        _locale: String,
    ) -> CustomResult<bool, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }
}


// File: crates/router/src/db/api_keys.rs

use error_stack::report;
use router_env::{instrument, tracing};
#[cfg(feature = "accounts_cache")]
use storage_impl::redis::cache::{self, CacheKind, ACCOUNTS_CACHE};

use super::{MockDb, Store};
use crate::{
    connection,
    core::errors::{self, CustomResult},
    types::storage,
};

#[async_trait::async_trait]
pub trait ApiKeyInterface {
    async fn insert_api_key(
        &self,
        api_key: storage::ApiKeyNew,
    ) -> CustomResult<storage::ApiKey, errors::StorageError>;

    async fn update_api_key(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        key_id: common_utils::id_type::ApiKeyId,
        api_key: storage::ApiKeyUpdate,
    ) -> CustomResult<storage::ApiKey, errors::StorageError>;

    async fn revoke_api_key(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<bool, errors::StorageError>;

    async fn find_api_key_by_merchant_id_key_id_optional(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError>;

    async fn find_api_key_by_hash_optional(
        &self,
        hashed_api_key: storage::HashedApiKey,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError>;

    async fn list_api_keys_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        limit: Option<i64>,
        offset: Option<i64>,
    ) -> CustomResult<Vec<storage::ApiKey>, errors::StorageError>;
}

#[async_trait::async_trait]
impl ApiKeyInterface for Store {
    #[instrument(skip_all)]
    async fn insert_api_key(
        &self,
        api_key: storage::ApiKeyNew,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        api_key
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_api_key(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        key_id: common_utils::id_type::ApiKeyId,
        api_key: storage::ApiKeyUpdate,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        let _merchant_id = merchant_id.clone();
        let _key_id = key_id.clone();
        let update_call = || async {
            storage::ApiKey::update_by_merchant_id_key_id(&conn, merchant_id, key_id, api_key)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };

        #[cfg(not(feature = "accounts_cache"))]
        {
            update_call().await
        }

        #[cfg(feature = "accounts_cache")]
        {
            use error_stack::report;

            // We need to fetch api_key here because the key that's saved in cache in HashedApiKey.
            // Used function from storage model to reuse the connection that made here instead of
            // creating new.
            let api_key = storage::ApiKey::find_optional_by_merchant_id_key_id(
                &conn,
                &_merchant_id,
                &_key_id,
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .ok_or(report!(errors::StorageError::ValueNotFound(format!(
                "ApiKey of {} not found",
                _key_id.get_string_repr()
            ))))?;

            cache::publish_and_redact(
                self,
                CacheKind::Accounts(api_key.hashed_api_key.into_inner().into()),
                update_call,
            )
            .await
        }
    }

    #[instrument(skip_all)]
    async fn revoke_api_key(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<bool, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        let delete_call = || async {
            storage::ApiKey::revoke_by_merchant_id_key_id(&conn, merchant_id, key_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };
        #[cfg(not(feature = "accounts_cache"))]
        {
            delete_call().await
        }

        #[cfg(feature = "accounts_cache")]
        {
            use error_stack::report;

            // We need to fetch api_key here because the key that's saved in cache in HashedApiKey.
            // Used function from storage model to reuse the connection that made here instead of
            // creating new.

            let api_key =
                storage::ApiKey::find_optional_by_merchant_id_key_id(&conn, merchant_id, key_id)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))?
                    .ok_or(report!(errors::StorageError::ValueNotFound(format!(
                        "ApiKey of {} not found",
                        key_id.get_string_repr()
                    ))))?;

            cache::publish_and_redact(
                self,
                CacheKind::Accounts(api_key.hashed_api_key.into_inner().into()),
                delete_call,
            )
            .await
        }
    }

    #[instrument(skip_all)]
    async fn find_api_key_by_merchant_id_key_id_optional(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::ApiKey::find_optional_by_merchant_id_key_id(&conn, merchant_id, key_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_api_key_by_hash_optional(
        &self,
        hashed_api_key: storage::HashedApiKey,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        let _hashed_api_key = hashed_api_key.clone();
        let find_call = || async {
            let conn = connection::pg_connection_read(self).await?;
            storage::ApiKey::find_optional_by_hashed_api_key(&conn, hashed_api_key)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };

        #[cfg(not(feature = "accounts_cache"))]
        {
            find_call().await
        }

        #[cfg(feature = "accounts_cache")]
        {
            cache::get_or_populate_in_memory(
                self,
                &_hashed_api_key.into_inner(),
                find_call,
                &ACCOUNTS_CACHE,
            )
            .await
        }
    }

    #[instrument(skip_all)]
    async fn list_api_keys_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        limit: Option<i64>,
        offset: Option<i64>,
    ) -> CustomResult<Vec<storage::ApiKey>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::ApiKey::find_by_merchant_id(&conn, merchant_id, limit, offset)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl ApiKeyInterface for MockDb {
    async fn insert_api_key(
        &self,
        api_key: storage::ApiKeyNew,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        let mut locked_api_keys = self.api_keys.lock().await;
        // don't allow duplicate key_ids, a those would be a unique constraint violation in the
        // real db as it is used as the primary key
        if locked_api_keys.iter().any(|k| k.key_id == api_key.key_id) {
            Err(errors::StorageError::MockDbError)?;
        }
        let stored_key = storage::ApiKey {
            key_id: api_key.key_id,
            merchant_id: api_key.merchant_id,
            name: api_key.name,
            description: api_key.description,
            hashed_api_key: api_key.hashed_api_key,
            prefix: api_key.prefix,
            created_at: api_key.created_at,
            expires_at: api_key.expires_at,
            last_used: api_key.last_used,
        };
        locked_api_keys.push(stored_key.clone());

        Ok(stored_key)
    }

    async fn update_api_key(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        key_id: common_utils::id_type::ApiKeyId,
        api_key: storage::ApiKeyUpdate,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        let mut locked_api_keys = self.api_keys.lock().await;
        // find a key with the given merchant_id and key_id and update, otherwise return an error
        let key_to_update = locked_api_keys
            .iter_mut()
            .find(|k| k.merchant_id == merchant_id && k.key_id == key_id)
            .ok_or(errors::StorageError::MockDbError)?;

        match api_key {
            storage::ApiKeyUpdate::Update {
                name,
                description,
                expires_at,
                last_used,
            } => {
                if let Some(name) = name {
                    key_to_update.name = name;
                }
                // only update these fields if the value was Some(_)
                if description.is_some() {
                    key_to_update.description = description;
                }
                if let Some(expires_at) = expires_at {
                    key_to_update.expires_at = expires_at;
                }
                if last_used.is_some() {
                    key_to_update.last_used = last_used
                }
            }
            storage::ApiKeyUpdate::LastUsedUpdate { last_used } => {
                key_to_update.last_used = Some(last_used);
            }
        }

        Ok(key_to_update.clone())
    }

    async fn revoke_api_key(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut locked_api_keys = self.api_keys.lock().await;
        // find the key to remove, if it exists
        if let Some(pos) = locked_api_keys
            .iter()
            .position(|k| k.merchant_id == *merchant_id && k.key_id == *key_id)
        {
            // use `remove` instead of `swap_remove` so we have a consistent order, which might
            // matter to someone using limit/offset in `list_api_keys_by_merchant_id`
            locked_api_keys.remove(pos);
            Ok(true)
        } else {
            Ok(false)
        }
    }

    async fn find_api_key_by_merchant_id_key_id_optional(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        Ok(self
            .api_keys
            .lock()
            .await
            .iter()
            .find(|k| k.merchant_id == *merchant_id && k.key_id == *key_id)
            .cloned())
    }

    async fn find_api_key_by_hash_optional(
        &self,
        hashed_api_key: storage::HashedApiKey,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        Ok(self
            .api_keys
            .lock()
            .await
            .iter()
            .find(|k| k.hashed_api_key == hashed_api_key)
            .cloned())
    }

    async fn list_api_keys_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        limit: Option<i64>,
        offset: Option<i64>,
    ) -> CustomResult<Vec<storage::ApiKey>, errors::StorageError> {
        // mimic the SQL limit/offset behavior
        let offset: usize = if let Some(offset) = offset {
            if offset < 0 {
                Err(errors::StorageError::MockDbError)?;
            }
            offset
                .try_into()
                .map_err(|_| errors::StorageError::MockDbError)?
        } else {
            0
        };

        let limit: usize = if let Some(limit) = limit {
            if limit < 0 {
                Err(errors::StorageError::MockDbError)?;
            }
            limit
                .try_into()
                .map_err(|_| errors::StorageError::MockDbError)?
        } else {
            usize::MAX
        };

        let keys_for_merchant_id: Vec<storage::ApiKey> = self
            .api_keys
            .lock()
            .await
            .iter()
            .filter(|k| k.merchant_id == *merchant_id)
            .skip(offset)
            .take(limit)
            .cloned()
            .collect();

        Ok(keys_for_merchant_id)
    }
}

#[cfg(test)]
mod tests {
    use std::borrow::Cow;

    use common_utils::types::keymanager::KeyManagerState;
    use storage_impl::redis::{
        cache::{self, CacheKey, CacheKind, ACCOUNTS_CACHE},
        kv_store::RedisConnInterface,
        pub_sub::PubSubInterface,
    };
    use time::macros::datetime;

    use crate::{
        db::{api_keys::ApiKeyInterface, MockDb},
        types::storage,
    };

    #[tokio::test]
    async fn test_mockdb_api_key_interface() {
        #[allow(clippy::expect_used)]
        let mockdb = MockDb::new(
            &redis_interface::RedisSettings::default(),
            KeyManagerState::new(),
        )
        .await
        .expect("Failed to create Mock store");

        let merchant_id =
            common_utils::id_type::MerchantId::try_from(Cow::from("merchant1")).unwrap();

        let key_id1 = common_utils::id_type::ApiKeyId::try_from(Cow::from("key_id1")).unwrap();

        let key_id2 = common_utils::id_type::ApiKeyId::try_from(Cow::from("key_id2")).unwrap();

        let non_existent_key_id =
            common_utils::id_type::ApiKeyId::try_from(Cow::from("does_not_exist")).unwrap();

        let key1 = mockdb
            .insert_api_key(storage::ApiKeyNew {
                key_id: key_id1.clone(),
                merchant_id: merchant_id.clone(),
                name: "Key 1".into(),
                description: None,
                hashed_api_key: "hashed_key1".to_string().into(),
                prefix: "abc".into(),
                created_at: datetime!(2023-02-01 0:00),
                expires_at: Some(datetime!(2023-03-01 0:00)),
                last_used: None,
            })
            .await
            .unwrap();

        mockdb
            .insert_api_key(storage::ApiKeyNew {
                key_id: key_id2.clone(),
                merchant_id: merchant_id.clone(),
                name: "Key 2".into(),
                description: None,
                hashed_api_key: "hashed_key2".to_string().into(),
                prefix: "abc".into(),
                created_at: datetime!(2023-03-01 0:00),
                expires_at: None,
                last_used: None,
            })
            .await
            .unwrap();

        let found_key1 = mockdb
            .find_api_key_by_merchant_id_key_id_optional(&merchant_id, &key_id1)
            .await
            .unwrap()
            .unwrap();
        assert_eq!(found_key1.key_id, key1.key_id);
        assert!(mockdb
            .find_api_key_by_merchant_id_key_id_optional(&merchant_id, &non_existent_key_id)
            .await
            .unwrap()
            .is_none());

        mockdb
            .update_api_key(
                merchant_id.clone(),
                key_id1.clone(),
                storage::ApiKeyUpdate::LastUsedUpdate {
                    last_used: datetime!(2023-02-04 1:11),
                },
            )
            .await
            .unwrap();
        let updated_key1 = mockdb
            .find_api_key_by_merchant_id_key_id_optional(&merchant_id, &key_id1)
            .await
            .unwrap()
            .unwrap();
        assert_eq!(updated_key1.last_used, Some(datetime!(2023-02-04 1:11)));

        assert_eq!(
            mockdb
                .list_api_keys_by_merchant_id(&merchant_id, None, None)
                .await
                .unwrap()
                .len(),
            2
        );
        mockdb.revoke_api_key(&merchant_id, &key_id1).await.unwrap();
        assert_eq!(
            mockdb
                .list_api_keys_by_merchant_id(&merchant_id, None, None)
                .await
                .unwrap()
                .len(),
            1
        );
    }

    #[tokio::test]
    async fn test_api_keys_cache() {
        let merchant_id =
            common_utils::id_type::MerchantId::try_from(Cow::from("test_merchant")).unwrap();

        let db = MockDb::new(
            &redis_interface::RedisSettings::default(),
            KeyManagerState::new(),
        )
        .await
        .expect("Failed to create Mock store");

        let redis_conn = db.get_redis_conn().unwrap();
        redis_conn
            .subscribe("hyperswitch_invalidate")
            .await
            .unwrap();

        let test_key = common_utils::id_type::ApiKeyId::try_from(Cow::from("test_ey")).unwrap();

        let api = storage::ApiKeyNew {
            key_id: test_key.clone(),
            merchant_id: merchant_id.clone(),
            name: "My test key".into(),
            description: None,
            hashed_api_key: "a_hashed_key".to_string().into(),
            prefix: "pre".into(),
            created_at: datetime!(2023-06-01 0:00),
            expires_at: None,
            last_used: None,
        };

        let api = db.insert_api_key(api).await.unwrap();

        let hashed_api_key = api.hashed_api_key.clone();
        let find_call = || async {
            db.find_api_key_by_hash_optional(hashed_api_key.clone())
                .await
        };
        let _: Option<storage::ApiKey> = cache::get_or_populate_in_memory(
            &db,
            &format!(
                "{}_{}",
                merchant_id.get_string_repr(),
                hashed_api_key.clone().into_inner()
            ),
            find_call,
            &ACCOUNTS_CACHE,
        )
        .await
        .unwrap();

        let delete_call = || async { db.revoke_api_key(&merchant_id, &api.key_id).await };

        cache::publish_and_redact(
            &db,
            CacheKind::Accounts(
                format!(
                    "{}_{}",
                    merchant_id.get_string_repr(),
                    hashed_api_key.clone().into_inner()
                )
                .into(),
            ),
            delete_call,
        )
        .await
        .unwrap();

        assert!(ACCOUNTS_CACHE
            .get_val::<storage::ApiKey>(CacheKey {
                key: format!(
                    "{}_{}",
                    merchant_id.get_string_repr(),
                    hashed_api_key.into_inner()
                ),
                prefix: String::default(),
            },)
            .await
            .is_none())
    }
}


// File: crates/router/src/db/dashboard_metadata.rs

use common_utils::id_type;
use diesel_models::{enums, user::dashboard_metadata as storage};
use error_stack::{report, ResultExt};
use router_env::{instrument, tracing};
use storage_impl::MockDb;

use crate::{
    connection,
    core::errors::{self, CustomResult},
    services::Store,
};

#[async_trait::async_trait]
pub trait DashboardMetadataInterface {
    async fn insert_metadata(
        &self,
        metadata: storage::DashboardMetadataNew,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError>;

    async fn update_metadata(
        &self,
        user_id: Option<String>,
        merchant_id: id_type::MerchantId,
        org_id: id_type::OrganizationId,
        data_key: enums::DashboardMetadata,
        dashboard_metadata_update: storage::DashboardMetadataUpdate,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError>;

    async fn find_user_scoped_dashboard_metadata(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
        org_id: &id_type::OrganizationId,
        data_keys: Vec<enums::DashboardMetadata>,
    ) -> CustomResult<Vec<storage::DashboardMetadata>, errors::StorageError>;

    async fn find_merchant_scoped_dashboard_metadata(
        &self,
        merchant_id: &id_type::MerchantId,
        org_id: &id_type::OrganizationId,
        data_keys: Vec<enums::DashboardMetadata>,
    ) -> CustomResult<Vec<storage::DashboardMetadata>, errors::StorageError>;

    async fn delete_all_user_scoped_dashboard_metadata_by_merchant_id(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError>;

    async fn delete_user_scoped_dashboard_metadata_by_merchant_id_data_key(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
        data_key: enums::DashboardMetadata,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError>;
}

#[async_trait::async_trait]
impl DashboardMetadataInterface for Store {
    #[instrument(skip_all)]
    async fn insert_metadata(
        &self,
        metadata: storage::DashboardMetadataNew,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        metadata
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_metadata(
        &self,
        user_id: Option<String>,
        merchant_id: id_type::MerchantId,
        org_id: id_type::OrganizationId,
        data_key: enums::DashboardMetadata,
        dashboard_metadata_update: storage::DashboardMetadataUpdate,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        storage::DashboardMetadata::update(
            &conn,
            user_id,
            merchant_id,
            org_id,
            data_key,
            dashboard_metadata_update,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_user_scoped_dashboard_metadata(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
        org_id: &id_type::OrganizationId,
        data_keys: Vec<enums::DashboardMetadata>,
    ) -> CustomResult<Vec<storage::DashboardMetadata>, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;
        storage::DashboardMetadata::find_user_scoped_dashboard_metadata(
            &conn,
            user_id.to_owned(),
            merchant_id.to_owned(),
            org_id.to_owned(),
            data_keys,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_merchant_scoped_dashboard_metadata(
        &self,
        merchant_id: &id_type::MerchantId,
        org_id: &id_type::OrganizationId,
        data_keys: Vec<enums::DashboardMetadata>,
    ) -> CustomResult<Vec<storage::DashboardMetadata>, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;
        storage::DashboardMetadata::find_merchant_scoped_dashboard_metadata(
            &conn,
            merchant_id.to_owned(),
            org_id.to_owned(),
            data_keys,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn delete_all_user_scoped_dashboard_metadata_by_merchant_id(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        storage::DashboardMetadata::delete_all_user_scoped_dashboard_metadata_by_merchant_id(
            &conn,
            user_id.to_owned(),
            merchant_id.to_owned(),
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn delete_user_scoped_dashboard_metadata_by_merchant_id_data_key(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
        data_key: enums::DashboardMetadata,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        storage::DashboardMetadata::delete_user_scoped_dashboard_metadata_by_merchant_id_data_key(
            &conn,
            user_id.to_owned(),
            merchant_id.to_owned(),
            data_key,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl DashboardMetadataInterface for MockDb {
    async fn insert_metadata(
        &self,
        metadata: storage::DashboardMetadataNew,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
        let mut dashboard_metadata = self.dashboard_metadata.lock().await;
        if dashboard_metadata.iter().any(|metadata_inner| {
            metadata_inner.user_id == metadata.user_id
                && metadata_inner.merchant_id == metadata.merchant_id
                && metadata_inner.org_id == metadata.org_id
                && metadata_inner.data_key == metadata.data_key
        }) {
            Err(errors::StorageError::DuplicateValue {
                entity: "user_id, merchant_id, org_id and data_key",
                key: None,
            })?
        }
        let metadata_new = storage::DashboardMetadata {
            id: i32::try_from(dashboard_metadata.len())
                .change_context(errors::StorageError::MockDbError)?,
            user_id: metadata.user_id,
            merchant_id: metadata.merchant_id,
            org_id: metadata.org_id,
            data_key: metadata.data_key,
            data_value: metadata.data_value,
            created_by: metadata.created_by,
            created_at: metadata.created_at,
            last_modified_by: metadata.last_modified_by,
            last_modified_at: metadata.last_modified_at,
        };
        dashboard_metadata.push(metadata_new.clone());
        Ok(metadata_new)
    }

    async fn update_metadata(
        &self,
        user_id: Option<String>,
        merchant_id: id_type::MerchantId,
        org_id: id_type::OrganizationId,
        data_key: enums::DashboardMetadata,
        dashboard_metadata_update: storage::DashboardMetadataUpdate,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
        let mut dashboard_metadata = self.dashboard_metadata.lock().await;

        let dashboard_metadata_to_update = dashboard_metadata
            .iter_mut()
            .find(|metadata| {
                metadata.user_id == user_id
                    && metadata.merchant_id == merchant_id
                    && metadata.org_id == org_id
                    && metadata.data_key == data_key
            })
            .ok_or(errors::StorageError::MockDbError)?;

        match dashboard_metadata_update {
            storage::DashboardMetadataUpdate::UpdateData {
                data_key,
                data_value,
                last_modified_by,
            } => {
                dashboard_metadata_to_update.data_key = data_key;
                dashboard_metadata_to_update.data_value = data_value;
                dashboard_metadata_to_update.last_modified_by = last_modified_by;
                dashboard_metadata_to_update.last_modified_at = common_utils::date_time::now();
            }
        }
        Ok(dashboard_metadata_to_update.clone())
    }

    async fn find_user_scoped_dashboard_metadata(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
        org_id: &id_type::OrganizationId,
        data_keys: Vec<enums::DashboardMetadata>,
    ) -> CustomResult<Vec<storage::DashboardMetadata>, errors::StorageError> {
        let dashboard_metadata = self.dashboard_metadata.lock().await;
        let query_result = dashboard_metadata
            .iter()
            .filter(|metadata_inner| {
                metadata_inner
                    .user_id
                    .clone()
                    .map(|user_id_inner| user_id_inner == user_id)
                    .unwrap_or(false)
                    && metadata_inner.merchant_id == *merchant_id
                    && metadata_inner.org_id == *org_id
                    && data_keys.contains(&metadata_inner.data_key)
            })
            .cloned()
            .collect::<Vec<storage::DashboardMetadata>>();

        if query_result.is_empty() {
            return Err(errors::StorageError::ValueNotFound(format!(
                "No dashboard_metadata available for user_id = {user_id},\
                merchant_id = {merchant_id:?}, org_id = {org_id:?} and data_keys = {data_keys:?}",
            ))
            .into());
        }
        Ok(query_result)
    }

    async fn find_merchant_scoped_dashboard_metadata(
        &self,
        merchant_id: &id_type::MerchantId,
        org_id: &id_type::OrganizationId,
        data_keys: Vec<enums::DashboardMetadata>,
    ) -> CustomResult<Vec<storage::DashboardMetadata>, errors::StorageError> {
        let dashboard_metadata = self.dashboard_metadata.lock().await;
        let query_result = dashboard_metadata
            .iter()
            .filter(|metadata_inner| {
                metadata_inner.merchant_id == *merchant_id
                    && metadata_inner.org_id == *org_id
                    && data_keys.contains(&metadata_inner.data_key)
            })
            .cloned()
            .collect::<Vec<storage::DashboardMetadata>>();

        if query_result.is_empty() {
            return Err(errors::StorageError::ValueNotFound(format!(
                "No dashboard_metadata available for merchant_id = {merchant_id:?},\
                      org_id = {org_id:?} and data_keyss = {data_keys:?}",
            ))
            .into());
        }
        Ok(query_result)
    }
    async fn delete_all_user_scoped_dashboard_metadata_by_merchant_id(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut dashboard_metadata = self.dashboard_metadata.lock().await;

        let initial_len = dashboard_metadata.len();

        dashboard_metadata.retain(|metadata_inner| {
            !(metadata_inner
                .user_id
                .clone()
                .map(|user_id_inner| user_id_inner == user_id)
                .unwrap_or(false)
                && metadata_inner.merchant_id == *merchant_id)
        });

        if dashboard_metadata.len() == initial_len {
            return Err(errors::StorageError::ValueNotFound(format!(
                "No user available for user_id = {user_id} and merchant id = {merchant_id:?}"
            ))
            .into());
        }

        Ok(true)
    }

    async fn delete_user_scoped_dashboard_metadata_by_merchant_id_data_key(
        &self,
        user_id: &str,
        merchant_id: &id_type::MerchantId,
        data_key: enums::DashboardMetadata,
    ) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
        let mut dashboard_metadata = self.dashboard_metadata.lock().await;

        let index_to_remove = dashboard_metadata
            .iter()
            .position(|metadata_inner| {
                metadata_inner.user_id.as_deref() == Some(user_id)
                    && metadata_inner.merchant_id == *merchant_id
                    && metadata_inner.data_key == data_key
            })
            .ok_or(errors::StorageError::ValueNotFound(
                "No data found".to_string(),
            ))?;

        let deleted_value = dashboard_metadata.swap_remove(index_to_remove);

        Ok(deleted_value)
    }
}


// File: crates/router/src/db/customers.rs

pub use hyperswitch_domain_models::customer::{CustomerInterface, CustomerListConstraints};


// File: crates/router/src/db/gsm.rs

use diesel_models::gsm as storage;
use error_stack::{report, ResultExt};
use router_env::{instrument, tracing};

use super::MockDb;
use crate::{
    connection,
    core::errors::{self, CustomResult},
    services::Store,
};

#[async_trait::async_trait]
pub trait GsmInterface {
    async fn add_gsm_rule(
        &self,
        rule: hyperswitch_domain_models::gsm::GatewayStatusMap,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError>;
    async fn find_gsm_decision(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<String, errors::StorageError>;
    async fn find_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError>;
    async fn update_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
        data: hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError>;

    async fn delete_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<bool, errors::StorageError>;
}

#[async_trait::async_trait]
impl GsmInterface for Store {
    #[instrument(skip_all)]
    async fn add_gsm_rule(
        &self,
        rule: hyperswitch_domain_models::gsm::GatewayStatusMap,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        let gsm_db_record = diesel_models::gsm::GatewayStatusMappingNew::try_from(rule)
            .change_context(errors::StorageError::SerializationFailed)
            .attach_printable("Failed to convert gsm domain models to diesel models")?
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?;

        hyperswitch_domain_models::gsm::GatewayStatusMap::try_from(gsm_db_record)
            .change_context(errors::StorageError::DeserializationFailed)
            .attach_printable("Failed to convert gsm diesel models to domain models")
    }

    #[instrument(skip_all)]
    async fn find_gsm_decision(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<String, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::GatewayStatusMap::retrieve_decision(
            &conn, connector, flow, sub_flow, code, message,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        let gsm_db_record =
            storage::GatewayStatusMap::find(&conn, connector, flow, sub_flow, code, message)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))?;

        hyperswitch_domain_models::gsm::GatewayStatusMap::try_from(gsm_db_record)
            .change_context(errors::StorageError::DeserializationFailed)
            .attach_printable("Failed to convert gsm diesel models to domain models")
    }

    #[instrument(skip_all)]
    async fn update_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
        data: hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        let gsm_update_data = diesel_models::gsm::GatewayStatusMappingUpdate::try_from(data)
            .change_context(errors::StorageError::SerializationFailed)?;
        let gsm_db_record = storage::GatewayStatusMap::update(
            &conn,
            connector,
            flow,
            sub_flow,
            code,
            message,
            gsm_update_data,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))?;

        hyperswitch_domain_models::gsm::GatewayStatusMap::try_from(gsm_db_record)
            .change_context(errors::StorageError::DeserializationFailed)
            .attach_printable("Failed to convert gsm diesel models to domain models")
    }

    #[instrument(skip_all)]
    async fn delete_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<bool, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        storage::GatewayStatusMap::delete(&conn, connector, flow, sub_flow, code, message)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl GsmInterface for MockDb {
    async fn add_gsm_rule(
        &self,
        _rule: hyperswitch_domain_models::gsm::GatewayStatusMap,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn find_gsm_decision(
        &self,
        _connector: String,
        _flow: String,
        _sub_flow: String,
        _code: String,
        _message: String,
    ) -> CustomResult<String, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn find_gsm_rule(
        &self,
        _connector: String,
        _flow: String,
        _sub_flow: String,
        _code: String,
        _message: String,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn update_gsm_rule(
        &self,
        _connector: String,
        _flow: String,
        _sub_flow: String,
        _code: String,
        _message: String,
        _data: hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn delete_gsm_rule(
        &self,
        _connector: String,
        _flow: String,
        _sub_flow: String,
        _code: String,
        _message: String,
    ) -> CustomResult<bool, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }
}


// File: crates/router/src/db/hyperswitch_ai_interaction.rs

use diesel_models::hyperswitch_ai_interaction as storage;
use error_stack::report;
use router_env::{instrument, tracing};

use super::MockDb;
use crate::{
    connection,
    core::errors::{self, CustomResult},
    services::Store,
};

#[async_trait::async_trait]
pub trait HyperswitchAiInteractionInterface {
    async fn insert_hyperswitch_ai_interaction(
        &self,
        hyperswitch_ai_interaction: storage::HyperswitchAiInteractionNew,
    ) -> CustomResult<storage::HyperswitchAiInteraction, errors::StorageError>;

    async fn list_hyperswitch_ai_interactions(
        &self,
        merchant_id: Option<common_utils::id_type::MerchantId>,
        limit: i64,
        offset: i64,
    ) -> CustomResult<Vec<storage::HyperswitchAiInteraction>, errors::StorageError>;
}

#[async_trait::async_trait]
impl HyperswitchAiInteractionInterface for Store {
    #[instrument(skip_all)]
    async fn insert_hyperswitch_ai_interaction(
        &self,
        hyperswitch_ai_interaction: storage::HyperswitchAiInteractionNew,
    ) -> CustomResult<storage::HyperswitchAiInteraction, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        hyperswitch_ai_interaction
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn list_hyperswitch_ai_interactions(
        &self,
        merchant_id: Option<common_utils::id_type::MerchantId>,
        limit: i64,
        offset: i64,
    ) -> CustomResult<Vec<storage::HyperswitchAiInteraction>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::HyperswitchAiInteraction::filter_by_optional_merchant_id(
            &conn,
            merchant_id.as_ref(),
            limit,
            offset,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl HyperswitchAiInteractionInterface for MockDb {
    async fn insert_hyperswitch_ai_interaction(
        &self,
        hyperswitch_ai_interaction: storage::HyperswitchAiInteractionNew,
    ) -> CustomResult<storage::HyperswitchAiInteraction, errors::StorageError> {
        let mut hyperswitch_ai_interactions = self.hyperswitch_ai_interactions.lock().await;
        let hyperswitch_ai_interaction = storage::HyperswitchAiInteraction {
            id: hyperswitch_ai_interaction.id,
            session_id: hyperswitch_ai_interaction.session_id,
            user_id: hyperswitch_ai_interaction.user_id,
            merchant_id: hyperswitch_ai_interaction.merchant_id,
            profile_id: hyperswitch_ai_interaction.profile_id,
            org_id: hyperswitch_ai_interaction.org_id,
            role_id: hyperswitch_ai_interaction.role_id,
            user_query: hyperswitch_ai_interaction.user_query,
            response: hyperswitch_ai_interaction.response,
            database_query: hyperswitch_ai_interaction.database_query,
            interaction_status: hyperswitch_ai_interaction.interaction_status,
            created_at: hyperswitch_ai_interaction.created_at,
        };
        hyperswitch_ai_interactions.push(hyperswitch_ai_interaction.clone());
        Ok(hyperswitch_ai_interaction)
    }

    async fn list_hyperswitch_ai_interactions(
        &self,
        merchant_id: Option<common_utils::id_type::MerchantId>,
        limit: i64,
        offset: i64,
    ) -> CustomResult<Vec<storage::HyperswitchAiInteraction>, errors::StorageError> {
        let hyperswitch_ai_interactions = self.hyperswitch_ai_interactions.lock().await;

        let offset_usize = offset.try_into().unwrap_or_else(|_| {
            common_utils::consts::DEFAULT_LIST_OFFSET
                .try_into()
                .unwrap_or(usize::MIN)
        });

        let limit_usize = limit.try_into().unwrap_or_else(|_| {
            common_utils::consts::DEFAULT_LIST_LIMIT
                .try_into()
                .unwrap_or(usize::MAX)
        });

        let filtered_interactions: Vec<storage::HyperswitchAiInteraction> =
            hyperswitch_ai_interactions
                .iter()
                .filter(
                    |interaction| match (merchant_id.as_ref(), &interaction.merchant_id) {
                        (Some(merchant_id), Some(interaction_merchant_id)) => {
                            interaction_merchant_id == &merchant_id.get_string_repr().to_owned()
                        }
                        (None, _) => true,
                        _ => false,
                    },
                )
                .skip(offset_usize)
                .take(limit_usize)
                .cloned()
                .collect();
        Ok(filtered_interactions)
    }
}


// File: crates/router/src/db/authentication.rs

use diesel_models::authentication::AuthenticationUpdateInternal;
use error_stack::report;
use router_env::{instrument, tracing};

use super::{MockDb, Store};
use crate::{
    connection,
    core::errors::{self, CustomResult},
    types::storage,
};

#[async_trait::async_trait]
pub trait AuthenticationInterface {
    async fn insert_authentication(
        &self,
        authentication: storage::AuthenticationNew,
    ) -> CustomResult<storage::Authentication, errors::StorageError>;

    async fn find_authentication_by_merchant_id_authentication_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        authentication_id: &common_utils::id_type::AuthenticationId,
    ) -> CustomResult<storage::Authentication, errors::StorageError>;

    async fn find_authentication_by_merchant_id_connector_authentication_id(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        connector_authentication_id: String,
    ) -> CustomResult<storage::Authentication, errors::StorageError>;

    async fn update_authentication_by_merchant_id_authentication_id(
        &self,
        previous_state: storage::Authentication,
        authentication_update: storage::AuthenticationUpdate,
    ) -> CustomResult<storage::Authentication, errors::StorageError>;
}

#[async_trait::async_trait]
impl AuthenticationInterface for Store {
    #[instrument(skip_all)]
    async fn insert_authentication(
        &self,
        authentication: storage::AuthenticationNew,
    ) -> CustomResult<storage::Authentication, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        authentication
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_authentication_by_merchant_id_authentication_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        authentication_id: &common_utils::id_type::AuthenticationId,
    ) -> CustomResult<storage::Authentication, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Authentication::find_by_merchant_id_authentication_id(
            &conn,
            merchant_id,
            authentication_id,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    async fn find_authentication_by_merchant_id_connector_authentication_id(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        connector_authentication_id: String,
    ) -> CustomResult<storage::Authentication, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Authentication::find_authentication_by_merchant_id_connector_authentication_id(
            &conn,
            &merchant_id,
            &connector_authentication_id,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_authentication_by_merchant_id_authentication_id(
        &self,
        previous_state: storage::Authentication,
        authentication_update: storage::AuthenticationUpdate,
    ) -> CustomResult<storage::Authentication, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        storage::Authentication::update_by_merchant_id_authentication_id(
            &conn,
            previous_state.merchant_id,
            previous_state.authentication_id,
            authentication_update,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl AuthenticationInterface for MockDb {
    async fn insert_authentication(
        &self,
        authentication: storage::AuthenticationNew,
    ) -> CustomResult<storage::Authentication, errors::StorageError> {
        let mut authentications = self.authentications.lock().await;
        if authentications.iter().any(|authentication_inner| {
            authentication_inner.authentication_id == authentication.authentication_id
        }) {
            Err(errors::StorageError::DuplicateValue {
                entity: "authentication_id",
                key: Some(
                    authentication
                        .authentication_id
                        .get_string_repr()
                        .to_string(),
                ),
            })?
        }
        let authentication = storage::Authentication {
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            authentication_id: authentication.authentication_id,
            merchant_id: authentication.merchant_id,
            authentication_status: authentication.authentication_status,
            authentication_connector: authentication.authentication_connector,
            connector_authentication_id: authentication.connector_authentication_id,
            authentication_data: None,
            payment_method_id: authentication.payment_method_id,
            authentication_type: authentication.authentication_type,
            authentication_lifecycle_status: authentication.authentication_lifecycle_status,
            error_code: authentication.error_code,
            error_message: authentication.error_message,
            connector_metadata: authentication.connector_metadata,
            maximum_supported_version: authentication.maximum_supported_version,
            threeds_server_transaction_id: authentication.threeds_server_transaction_id,
            cavv: authentication.cavv,
            authentication_flow_type: authentication.authentication_flow_type,
            message_version: authentication.message_version,
            eci: authentication.eci,
            trans_status: authentication.trans_status,
            acquirer_bin: authentication.acquirer_bin,
            acquirer_merchant_id: authentication.acquirer_merchant_id,
            three_ds_method_data: authentication.three_ds_method_data,
            three_ds_method_url: authentication.three_ds_method_url,
            acs_url: authentication.acs_url,
            challenge_request: authentication.challenge_request,
            challenge_request_key: authentication.challenge_request_key,
            acs_reference_number: authentication.acs_reference_number,
            acs_trans_id: authentication.acs_trans_id,
            acs_signed_content: authentication.acs_signed_content,
            profile_id: authentication.profile_id,
            payment_id: authentication.payment_id,
            merchant_connector_id: authentication.merchant_connector_id,
            ds_trans_id: authentication.ds_trans_id,
            directory_server_id: authentication.directory_server_id,
            acquirer_country_code: authentication.acquirer_country_code,
            service_details: authentication.service_details,
            organization_id: authentication.organization_id,
            authentication_client_secret: authentication.authentication_client_secret,
            force_3ds_challenge: authentication.force_3ds_challenge,
            psd2_sca_exemption_type: authentication.psd2_sca_exemption_type,
            return_url: authentication.return_url,
            amount: authentication.amount,
            currency: authentication.currency,
            billing_address: authentication.billing_address,
            shipping_address: authentication.shipping_address,
            browser_info: authentication.browser_info,
            email: authentication.email,
            profile_acquirer_id: authentication.profile_acquirer_id,
            challenge_code: authentication.challenge_code,
            challenge_cancel: authentication.challenge_cancel,
            challenge_code_reason: authentication.challenge_code_reason,
            message_extension: authentication.message_extension,
            customer_details: authentication.customer_details,
        };
        authentications.push(authentication.clone());
        Ok(authentication)
    }

    async fn find_authentication_by_merchant_id_authentication_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        authentication_id: &common_utils::id_type::AuthenticationId,
    ) -> CustomResult<storage::Authentication, errors::StorageError> {
        let authentications = self.authentications.lock().await;
        authentications
            .iter()
            .find(|a| a.merchant_id == *merchant_id && a.authentication_id == *authentication_id)
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "cannot find authentication for authentication_id = {authentication_id:?} and merchant_id = {merchant_id:?}"
                )).into(),
            ).cloned()
    }

    async fn find_authentication_by_merchant_id_connector_authentication_id(
        &self,
        _merchant_id: common_utils::id_type::MerchantId,
        _connector_authentication_id: String,
    ) -> CustomResult<storage::Authentication, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn update_authentication_by_merchant_id_authentication_id(
        &self,
        previous_state: storage::Authentication,
        authentication_update: storage::AuthenticationUpdate,
    ) -> CustomResult<storage::Authentication, errors::StorageError> {
        let mut authentications = self.authentications.lock().await;
        let authentication_id = previous_state.authentication_id.clone();
        let merchant_id = previous_state.merchant_id.clone();
        authentications
            .iter_mut()
            .find(|authentication| authentication.authentication_id == authentication_id && authentication.merchant_id == merchant_id)
            .map(|authentication| {
                let authentication_update_internal =
                    AuthenticationUpdateInternal::from(authentication_update);
                let updated_authentication = authentication_update_internal.apply_changeset(previous_state);
                *authentication = updated_authentication.clone();
                updated_authentication
            })
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "cannot find authentication for authentication_id = {authentication_id:?} and merchant_id = {merchant_id:?}"
                ))
                .into(),
            )
    }
}


// File: crates/router/src/db/merchant_key_store.rs

pub use hyperswitch_domain_models::merchant_key_store::{self, MerchantKeyStoreInterface};

#[cfg(test)]
mod tests {
    use std::{borrow::Cow, sync::Arc};

    use common_utils::{
        type_name,
        types::keymanager::{Identifier, KeyManagerState},
    };
    use hyperswitch_domain_models::master_key::MasterKeyInterface;
    use time::macros::datetime;
    use tokio::sync::oneshot;

    use crate::{
        db::{merchant_key_store::MerchantKeyStoreInterface, MockDb},
        routes::{
            self,
            app::{settings::Settings, StorageImpl},
        },
        services,
        types::domain,
    };

    #[tokio::test]
    async fn test_mock_db_merchant_key_store_interface() {
        let conf = Settings::new().expect("invalid settings");
        let tx: oneshot::Sender<()> = oneshot::channel().0;
        let app_state = Box::pin(routes::AppState::with_storage(
            conf,
            StorageImpl::PostgresqlTest,
            tx,
            Box::new(services::MockApiClient),
        ))
        .await;
        let state = &Arc::new(app_state)
            .get_session_state(
                &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(),
                None,
                || {},
            )
            .unwrap();
        let mock_db = MockDb::new(
            &redis_interface::RedisSettings::default(),
            KeyManagerState::new(),
        )
        .await
        .expect("Failed to create mock DB");
        let master_key = mock_db.get_master_key();
        let merchant_id =
            common_utils::id_type::MerchantId::try_from(Cow::from("merchant1")).unwrap();
        let identifier = Identifier::Merchant(merchant_id.clone());
        let key_manager_state = &state.into();
        let merchant_key1 = mock_db
            .insert_merchant_key_store(
                domain::MerchantKeyStore {
                    merchant_id: merchant_id.clone(),
                    key: domain::types::crypto_operation(
                        key_manager_state,
                        type_name!(domain::MerchantKeyStore),
                        domain::types::CryptoOperation::Encrypt(
                            services::generate_aes256_key().unwrap().to_vec().into(),
                        ),
                        identifier.clone(),
                        master_key,
                    )
                    .await
                    .and_then(|val| val.try_into_operation())
                    .unwrap(),
                    created_at: datetime!(2023-02-01 0:00),
                },
                &master_key.to_vec().into(),
            )
            .await
            .unwrap();

        let found_merchant_key1 = mock_db
            .get_merchant_key_store_by_merchant_id(&merchant_id, &master_key.to_vec().into())
            .await
            .unwrap();

        assert_eq!(found_merchant_key1.merchant_id, merchant_key1.merchant_id);
        assert_eq!(found_merchant_key1.key, merchant_key1.key);

        let insert_duplicate_merchant_key1_result = mock_db
            .insert_merchant_key_store(
                domain::MerchantKeyStore {
                    merchant_id: merchant_id.clone(),
                    key: domain::types::crypto_operation(
                        key_manager_state,
                        type_name!(domain::MerchantKeyStore),
                        domain::types::CryptoOperation::Encrypt(
                            services::generate_aes256_key().unwrap().to_vec().into(),
                        ),
                        identifier.clone(),
                        master_key,
                    )
                    .await
                    .and_then(|val| val.try_into_operation())
                    .unwrap(),
                    created_at: datetime!(2023-02-01 0:00),
                },
                &master_key.to_vec().into(),
            )
            .await;
        assert!(insert_duplicate_merchant_key1_result.is_err());

        let non_existent_merchant_id =
            common_utils::id_type::MerchantId::try_from(Cow::from("non_existent")).unwrap();

        let find_non_existent_merchant_key_result = mock_db
            .get_merchant_key_store_by_merchant_id(
                &non_existent_merchant_id,
                &master_key.to_vec().into(),
            )
            .await;
        assert!(find_non_existent_merchant_key_result.is_err());

        let find_merchant_key_with_incorrect_master_key_result = mock_db
            .get_merchant_key_store_by_merchant_id(&merchant_id, &vec![0; 32].into())
            .await;
        assert!(find_merchant_key_with_incorrect_master_key_result.is_err());
    }
}


// File: crates/router/src/db/health_check.rs

use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl};
use diesel_models::ConfigNew;
use error_stack::ResultExt;
use router_env::{instrument, logger, tracing};

use super::{MockDb, Store};
use crate::{
    connection,
    core::errors::{self, CustomResult},
    types::storage,
};

#[async_trait::async_trait]
pub trait HealthCheckDbInterface {
    async fn health_check_db(&self) -> CustomResult<(), errors::HealthCheckDBError>;
}

#[async_trait::async_trait]
impl HealthCheckDbInterface for Store {
    #[instrument(skip_all)]
    async fn health_check_db(&self) -> CustomResult<(), errors::HealthCheckDBError> {
        let conn = connection::pg_connection_write(self)
            .await
            .change_context(errors::HealthCheckDBError::DBError)?;

        conn.transaction_async(|conn| async move {
            let query = diesel::select(diesel::dsl::sql::<diesel::sql_types::Integer>("1 + 1"));
            let _x: i32 = query.get_result_async(&conn).await.map_err(|err| {
                logger::error!(read_err=?err,"Error while reading element in the database");
                errors::HealthCheckDBError::DBReadError
            })?;

            logger::debug!("Database read was successful");

            let config = ConfigNew {
                key: "test_key".to_string(),
                config: "test_value".to_string(),
            };

            config.insert(&conn).await.map_err(|err| {
                logger::error!(write_err=?err,"Error while writing to database");
                errors::HealthCheckDBError::DBWriteError
            })?;

            logger::debug!("Database write was successful");

            storage::Config::delete_by_key(&conn, "test_key")
                .await
                .map_err(|err| {
                    logger::error!(delete_err=?err,"Error while deleting element in the database");
                    errors::HealthCheckDBError::DBDeleteError
                })?;

            logger::debug!("Database delete was successful");

            Ok::<_, errors::HealthCheckDBError>(())
        })
        .await?;

        Ok(())
    }
}

#[async_trait::async_trait]
impl HealthCheckDbInterface for MockDb {
    async fn health_check_db(&self) -> CustomResult<(), errors::HealthCheckDBError> {
        Ok(())
    }
}