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

use common_utils::{errors::CustomResult, types::keymanager};
use error_stack::{report, ResultExt};
use masking::Secret;
use router_env::{instrument, tracing};
use storage_impl::MockDb;

use crate::{
    connection,
    core::errors,
    services::Store,
    types::domain::{
        self,
        behaviour::{Conversion, ReverseConversion},
    },
};

#[async_trait::async_trait]
pub trait UserKeyStoreInterface {
    async fn insert_user_key_store(
        &self,
        user_key_store: domain::UserKeyStore,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::UserKeyStore, errors::StorageError>;

    async fn get_user_key_store_by_user_id(
        &self,
        user_id: &str,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::UserKeyStore, errors::StorageError>;

    async fn get_all_user_key_store(
        &self,
        key: &Secret<Vec<u8>>,
        from: u32,
        limit: u32,
    ) -> CustomResult<Vec<domain::UserKeyStore>, errors::StorageError>;
}

#[async_trait::async_trait]
impl UserKeyStoreInterface for Store {
    #[instrument(skip_all)]
    async fn insert_user_key_store(
        &self,
        user_key_store: domain::UserKeyStore,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::UserKeyStore, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        let user_id = user_key_store.user_id.clone();
        user_key_store
            .construct_new()
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                self.get_keymanager_state()
                    .attach_printable("Missing KeyManagerState")?,
                key,
                keymanager::Identifier::User(user_id),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[instrument(skip_all)]
    async fn get_user_key_store_by_user_id(
        &self,
        user_id: &str,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::UserKeyStore, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;

        diesel_models::user_key_store::UserKeyStore::find_by_user_id(&conn, user_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                self.get_keymanager_state()
                    .attach_printable("Missing KeyManagerState")?,
                key,
                keymanager::Identifier::User(user_id.to_owned()),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn get_all_user_key_store(
        &self,
        key: &Secret<Vec<u8>>,
        from: u32,
        limit: u32,
    ) -> CustomResult<Vec<domain::UserKeyStore>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;

        let key_stores = diesel_models::user_key_store::UserKeyStore::get_all_user_key_stores(
            &conn, from, limit,
        )
        .await
        .map_err(|err| report!(errors::StorageError::from(err)))?;
        futures::future::try_join_all(key_stores.into_iter().map(|key_store| async {
            let user_id = key_store.user_id.clone();
            key_store
                .convert(
                    self.get_keymanager_state()
                        .attach_printable("Missing KeyManagerState")?,
                    key,
                    keymanager::Identifier::User(user_id),
                )
                .await
                .change_context(errors::StorageError::DecryptionError)
        }))
        .await
    }
}

#[async_trait::async_trait]
impl UserKeyStoreInterface for MockDb {
    #[instrument(skip_all)]
    async fn insert_user_key_store(
        &self,
        user_key_store: domain::UserKeyStore,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::UserKeyStore, errors::StorageError> {
        let mut locked_user_key_store = self.user_key_store.lock().await;

        if locked_user_key_store
            .iter()
            .any(|user_key| user_key.user_id == user_key_store.user_id)
        {
            Err(errors::StorageError::DuplicateValue {
                entity: "user_key_store",
                key: Some(user_key_store.user_id.clone()),
            })?;
        }

        let user_key_store = Conversion::convert(user_key_store)
            .await
            .change_context(errors::StorageError::MockDbError)?;
        locked_user_key_store.push(user_key_store.clone());
        let user_id = user_key_store.user_id.clone();
        user_key_store
            .convert(
                self.get_keymanager_state()
                    .attach_printable("Missing KeyManagerState")?,
                key,
                keymanager::Identifier::User(user_id),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn get_all_user_key_store(
        &self,
        key: &Secret<Vec<u8>>,
        _from: u32,
        _limit: u32,
    ) -> CustomResult<Vec<domain::UserKeyStore>, errors::StorageError> {
        let user_key_store = self.user_key_store.lock().await;

        futures::future::try_join_all(user_key_store.iter().map(|user_key| async {
            let user_id = user_key.user_id.clone();
            user_key
                .to_owned()
                .convert(
                    self.get_keymanager_state()
                        .attach_printable("Missing KeyManagerState")?,
                    key,
                    keymanager::Identifier::User(user_id),
                )
                .await
                .change_context(errors::StorageError::DecryptionError)
        }))
        .await
    }

    #[instrument(skip_all)]
    async fn get_user_key_store_by_user_id(
        &self,
        user_id: &str,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::UserKeyStore, errors::StorageError> {
        self.user_key_store
            .lock()
            .await
            .iter()
            .find(|user_key_store| user_key_store.user_id == user_id)
            .cloned()
            .ok_or(errors::StorageError::ValueNotFound(format!(
                "No user_key_store is found for user_id={user_id}",
            )))?
            .convert(
                self.get_keymanager_state()
                    .attach_printable("Missing KeyManagerState")?,
                key,
                keymanager::Identifier::User(user_id.to_owned()),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }
}


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

#[cfg(feature = "v2")]
use common_utils::id_type;
use time::ext::NumericalDuration;

#[cfg(feature = "v2")]
use crate::types::storage::ephemeral_key::{ClientSecretType, ClientSecretTypeNew};
use crate::{
    core::errors::{self, CustomResult},
    db::MockDb,
    types::storage::ephemeral_key::{EphemeralKey, EphemeralKeyNew},
};

#[async_trait::async_trait]
pub trait EphemeralKeyInterface {
    #[cfg(feature = "v1")]
    async fn create_ephemeral_key(
        &self,
        _ek: EphemeralKeyNew,
        _validity: i64,
    ) -> CustomResult<EphemeralKey, errors::StorageError>;

    #[cfg(feature = "v1")]
    async fn get_ephemeral_key(
        &self,
        _key: &str,
    ) -> CustomResult<EphemeralKey, errors::StorageError>;

    #[cfg(feature = "v1")]
    async fn delete_ephemeral_key(
        &self,
        _id: &str,
    ) -> CustomResult<EphemeralKey, errors::StorageError>;
}

#[async_trait::async_trait]
pub trait ClientSecretInterface {
    #[cfg(feature = "v2")]
    async fn create_client_secret(
        &self,
        _ek: ClientSecretTypeNew,
        _validity: i64,
    ) -> CustomResult<ClientSecretType, errors::StorageError>;

    #[cfg(feature = "v2")]
    async fn get_client_secret(
        &self,
        _key: &str,
    ) -> CustomResult<ClientSecretType, errors::StorageError>;

    #[cfg(feature = "v2")]
    async fn delete_client_secret(
        &self,
        _id: &str,
    ) -> CustomResult<ClientSecretType, errors::StorageError>;
}

mod storage {
    use common_utils::date_time;
    #[cfg(feature = "v2")]
    use common_utils::id_type;
    use error_stack::ResultExt;
    #[cfg(feature = "v2")]
    use masking::PeekInterface;
    #[cfg(feature = "v2")]
    use redis_interface::errors::RedisError;
    use redis_interface::HsetnxReply;
    use router_env::{instrument, tracing};
    use storage_impl::redis::kv_store::RedisConnInterface;
    use time::ext::NumericalDuration;

    use super::{ClientSecretInterface, EphemeralKeyInterface};
    #[cfg(feature = "v2")]
    use crate::types::storage::ephemeral_key::{ClientSecretType, ClientSecretTypeNew};
    use crate::{
        core::errors::{self, CustomResult},
        services::Store,
        types::storage::ephemeral_key::{EphemeralKey, EphemeralKeyNew},
    };

    #[async_trait::async_trait]
    impl EphemeralKeyInterface for Store {
        #[cfg(feature = "v1")]
        #[instrument(skip_all)]
        async fn create_ephemeral_key(
            &self,
            new: EphemeralKeyNew,
            validity: i64,
        ) -> CustomResult<EphemeralKey, errors::StorageError> {
            let secret_key = format!("epkey_{}", &new.secret);
            let id_key = format!("epkey_{}", &new.id);

            let created_at = date_time::now();
            let expires = created_at.saturating_add(validity.hours());
            let created_ek = EphemeralKey {
                id: new.id,
                created_at: created_at.assume_utc().unix_timestamp(),
                expires: expires.assume_utc().unix_timestamp(),
                customer_id: new.customer_id,
                merchant_id: new.merchant_id,
                secret: new.secret,
            };

            match self
                .get_redis_conn()
                .map_err(Into::<errors::StorageError>::into)?
                .serialize_and_set_multiple_hash_field_if_not_exist(
                    &[
                        (&secret_key.as_str().into(), &created_ek),
                        (&id_key.as_str().into(), &created_ek),
                    ],
                    "ephkey",
                    None,
                )
                .await
            {
                Ok(v) if v.contains(&HsetnxReply::KeyNotSet) => {
                    Err(errors::StorageError::DuplicateValue {
                        entity: "ephemeral key",
                        key: None,
                    }
                    .into())
                }
                Ok(_) => {
                    let expire_at = expires.assume_utc().unix_timestamp();
                    self.get_redis_conn()
                        .map_err(Into::<errors::StorageError>::into)?
                        .set_expire_at(&secret_key.into(), expire_at)
                        .await
                        .change_context(errors::StorageError::KVError)?;
                    self.get_redis_conn()
                        .map_err(Into::<errors::StorageError>::into)?
                        .set_expire_at(&id_key.into(), expire_at)
                        .await
                        .change_context(errors::StorageError::KVError)?;
                    Ok(created_ek)
                }
                Err(er) => Err(er).change_context(errors::StorageError::KVError),
            }
        }

        #[cfg(feature = "v1")]
        #[instrument(skip_all)]
        async fn get_ephemeral_key(
            &self,
            key: &str,
        ) -> CustomResult<EphemeralKey, errors::StorageError> {
            let key = format!("epkey_{key}");
            self.get_redis_conn()
                .map_err(Into::<errors::StorageError>::into)?
                .get_hash_field_and_deserialize(&key.into(), "ephkey", "EphemeralKey")
                .await
                .change_context(errors::StorageError::KVError)
        }

        #[cfg(feature = "v1")]
        async fn delete_ephemeral_key(
            &self,
            id: &str,
        ) -> CustomResult<EphemeralKey, errors::StorageError> {
            let ek = self.get_ephemeral_key(id).await?;

            self.get_redis_conn()
                .map_err(Into::<errors::StorageError>::into)?
                .delete_key(&format!("epkey_{}", &ek.id).into())
                .await
                .change_context(errors::StorageError::KVError)?;

            self.get_redis_conn()
                .map_err(Into::<errors::StorageError>::into)?
                .delete_key(&format!("epkey_{}", &ek.secret).into())
                .await
                .change_context(errors::StorageError::KVError)?;
            Ok(ek)
        }
    }

    #[async_trait::async_trait]
    impl ClientSecretInterface for Store {
        #[cfg(feature = "v2")]
        #[instrument(skip_all)]
        async fn create_client_secret(
            &self,
            new: ClientSecretTypeNew,
            validity: i64,
        ) -> CustomResult<ClientSecretType, errors::StorageError> {
            let created_at = date_time::now();
            let expires = created_at.saturating_add(validity.hours());
            let id_key = new.id.generate_redis_key();

            let created_client_secret = ClientSecretType {
                id: new.id,
                created_at,
                expires,
                merchant_id: new.merchant_id,
                secret: new.secret,
                resource_id: new.resource_id,
            };
            let secret_key = created_client_secret.generate_secret_key();

            match self
                .get_redis_conn()
                .map_err(Into::<errors::StorageError>::into)?
                .serialize_and_set_multiple_hash_field_if_not_exist(
                    &[
                        (&secret_key.as_str().into(), &created_client_secret),
                        (&id_key.as_str().into(), &created_client_secret),
                    ],
                    "csh",
                    None,
                )
                .await
            {
                Ok(v) if v.contains(&HsetnxReply::KeyNotSet) => {
                    Err(errors::StorageError::DuplicateValue {
                        entity: "ephemeral key",
                        key: None,
                    }
                    .into())
                }
                Ok(_) => {
                    let expire_at = expires.assume_utc().unix_timestamp();
                    self.get_redis_conn()
                        .map_err(Into::<errors::StorageError>::into)?
                        .set_expire_at(&secret_key.into(), expire_at)
                        .await
                        .change_context(errors::StorageError::KVError)?;
                    self.get_redis_conn()
                        .map_err(Into::<errors::StorageError>::into)?
                        .set_expire_at(&id_key.into(), expire_at)
                        .await
                        .change_context(errors::StorageError::KVError)?;
                    Ok(created_client_secret)
                }
                Err(er) => Err(er).change_context(errors::StorageError::KVError),
            }
        }

        #[cfg(feature = "v2")]
        #[instrument(skip_all)]
        async fn get_client_secret(
            &self,
            key: &str,
        ) -> CustomResult<ClientSecretType, errors::StorageError> {
            let key = format!("cs_{key}");
            self.get_redis_conn()
                .map_err(Into::<errors::StorageError>::into)?
                .get_hash_field_and_deserialize(&key.into(), "csh", "ClientSecretType")
                .await
                .change_context(errors::StorageError::KVError)
        }

        #[cfg(feature = "v2")]
        async fn delete_client_secret(
            &self,
            id: &str,
        ) -> CustomResult<ClientSecretType, errors::StorageError> {
            let client_secret = self.get_client_secret(id).await?;
            let redis_id_key = client_secret.id.generate_redis_key();
            let secret_key = client_secret.generate_secret_key();

            self.get_redis_conn()
                .map_err(Into::<errors::StorageError>::into)?
                .delete_key(&redis_id_key.as_str().into())
                .await
                .map_err(|err| match err.current_context() {
                    RedisError::NotFound => {
                        err.change_context(errors::StorageError::ValueNotFound(redis_id_key))
                    }
                    _ => err.change_context(errors::StorageError::KVError),
                })?;

            self.get_redis_conn()
                .map_err(Into::<errors::StorageError>::into)?
                .delete_key(&secret_key.as_str().into())
                .await
                .map_err(|err| match err.current_context() {
                    RedisError::NotFound => {
                        err.change_context(errors::StorageError::ValueNotFound(secret_key))
                    }
                    _ => err.change_context(errors::StorageError::KVError),
                })?;
            Ok(client_secret)
        }
    }
}

#[async_trait::async_trait]
impl EphemeralKeyInterface for MockDb {
    #[cfg(feature = "v1")]
    async fn create_ephemeral_key(
        &self,
        ek: EphemeralKeyNew,
        validity: i64,
    ) -> CustomResult<EphemeralKey, errors::StorageError> {
        let mut ephemeral_keys = self.ephemeral_keys.lock().await;
        let created_at = common_utils::date_time::now();
        let expires = created_at.saturating_add(validity.hours());

        let ephemeral_key = EphemeralKey {
            id: ek.id,
            merchant_id: ek.merchant_id,
            customer_id: ek.customer_id,
            created_at: created_at.assume_utc().unix_timestamp(),
            expires: expires.assume_utc().unix_timestamp(),
            secret: ek.secret,
        };
        ephemeral_keys.push(ephemeral_key.clone());
        Ok(ephemeral_key)
    }

    #[cfg(feature = "v1")]
    async fn get_ephemeral_key(
        &self,
        key: &str,
    ) -> CustomResult<EphemeralKey, errors::StorageError> {
        match self
            .ephemeral_keys
            .lock()
            .await
            .iter()
            .find(|ephemeral_key| ephemeral_key.secret.eq(key))
        {
            Some(ephemeral_key) => Ok(ephemeral_key.clone()),
            None => Err(
                errors::StorageError::ValueNotFound("ephemeral key not found".to_string()).into(),
            ),
        }
    }

    #[cfg(feature = "v1")]
    async fn delete_ephemeral_key(
        &self,
        id: &str,
    ) -> CustomResult<EphemeralKey, errors::StorageError> {
        let mut ephemeral_keys = self.ephemeral_keys.lock().await;
        if let Some(pos) = ephemeral_keys.iter().position(|x| (*x.id).eq(id)) {
            let ek = ephemeral_keys.remove(pos);
            Ok(ek)
        } else {
            return Err(
                errors::StorageError::ValueNotFound("ephemeral key not found".to_string()).into(),
            );
        }
    }
}

#[async_trait::async_trait]
impl ClientSecretInterface for MockDb {
    #[cfg(feature = "v2")]
    async fn create_client_secret(
        &self,
        new: ClientSecretTypeNew,
        validity: i64,
    ) -> CustomResult<ClientSecretType, errors::StorageError> {
        todo!()
    }

    #[cfg(feature = "v2")]
    async fn get_client_secret(
        &self,
        key: &str,
    ) -> CustomResult<ClientSecretType, errors::StorageError> {
        todo!()
    }

    #[cfg(feature = "v2")]
    async fn delete_client_secret(
        &self,
        id: &str,
    ) -> CustomResult<ClientSecretType, errors::StorageError> {
        todo!()
    }
}


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

use super::{MockDb, Store};
use crate::{
    errors::{self, CustomResult},
    types::storage::{
        enums,
        reverse_lookup::{ReverseLookup, ReverseLookupNew},
    },
};

#[async_trait::async_trait]
pub trait ReverseLookupInterface {
    async fn insert_reverse_lookup(
        &self,
        _new: ReverseLookupNew,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<ReverseLookup, errors::StorageError>;
    async fn get_lookup_by_lookup_id(
        &self,
        _id: &str,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<ReverseLookup, errors::StorageError>;
}

#[cfg(not(feature = "kv_store"))]
mod storage {
    use error_stack::report;
    use router_env::{instrument, tracing};

    use super::{ReverseLookupInterface, Store};
    use crate::{
        connection,
        errors::{self, CustomResult},
        types::storage::{
            enums,
            reverse_lookup::{ReverseLookup, ReverseLookupNew},
        },
    };

    #[async_trait::async_trait]
    impl ReverseLookupInterface for Store {
        #[instrument(skip_all)]
        async fn insert_reverse_lookup(
            &self,
            new: ReverseLookupNew,
            _storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<ReverseLookup, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            new.insert(&conn)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn get_lookup_by_lookup_id(
            &self,
            id: &str,
            _storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<ReverseLookup, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            ReverseLookup::find_by_lookup_id(id, &conn)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }
    }
}

#[cfg(feature = "kv_store")]
mod storage {
    use error_stack::{report, ResultExt};
    use redis_interface::SetnxReply;
    use router_env::{instrument, tracing};
    use storage_impl::redis::kv_store::{
        decide_storage_scheme, kv_wrapper, KvOperation, Op, PartitionKey,
    };

    use super::{ReverseLookupInterface, Store};
    use crate::{
        connection,
        core::errors::utils::RedisErrorExt,
        errors::{self, CustomResult},
        types::storage::{
            enums, kv,
            reverse_lookup::{ReverseLookup, ReverseLookupNew},
        },
        utils::db_utils,
    };

    #[async_trait::async_trait]
    impl ReverseLookupInterface for Store {
        #[instrument(skip_all)]
        async fn insert_reverse_lookup(
            &self,
            new: ReverseLookupNew,
            storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<ReverseLookup, errors::StorageError> {
            let storage_scheme = Box::pin(decide_storage_scheme::<_, ReverseLookup>(
                self,
                storage_scheme,
                Op::Insert,
            ))
            .await;
            match storage_scheme {
                enums::MerchantStorageScheme::PostgresOnly => {
                    let conn = connection::pg_connection_write(self).await?;
                    new.insert(&conn)
                        .await
                        .map_err(|error| report!(errors::StorageError::from(error)))
                }
                enums::MerchantStorageScheme::RedisKv => {
                    let created_rev_lookup = ReverseLookup {
                        lookup_id: new.lookup_id.clone(),
                        sk_id: new.sk_id.clone(),
                        pk_id: new.pk_id.clone(),
                        source: new.source.clone(),
                        updated_by: storage_scheme.to_string(),
                    };
                    let redis_entry = kv::TypedSql {
                        op: kv::DBOperation::Insert {
                            insertable: Box::new(kv::Insertable::ReverseLookUp(new)),
                        },
                    };

                    match Box::pin(kv_wrapper::<ReverseLookup, _, _>(
                        self,
                        KvOperation::SetNx(&created_rev_lookup, redis_entry),
                        PartitionKey::CombinationKey {
                            combination: &format!(
                                "reverse_lookup_{}",
                                &created_rev_lookup.lookup_id
                            ),
                        },
                    ))
                    .await
                    .map_err(|err| err.to_redis_failed_response(&created_rev_lookup.lookup_id))?
                    .try_into_setnx()
                    {
                        Ok(SetnxReply::KeySet) => Ok(created_rev_lookup),
                        Ok(SetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue {
                            entity: "reverse_lookup",
                            key: Some(created_rev_lookup.lookup_id.clone()),
                        }
                        .into()),
                        Err(er) => Err(er).change_context(errors::StorageError::KVError),
                    }
                }
            }
        }

        #[instrument(skip_all)]
        async fn get_lookup_by_lookup_id(
            &self,
            id: &str,
            storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<ReverseLookup, errors::StorageError> {
            let database_call = || async {
                let conn = connection::pg_connection_read(self).await?;
                ReverseLookup::find_by_lookup_id(id, &conn)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))
            };
            let storage_scheme = Box::pin(decide_storage_scheme::<_, ReverseLookup>(
                self,
                storage_scheme,
                Op::Find,
            ))
            .await;
            match storage_scheme {
                enums::MerchantStorageScheme::PostgresOnly => database_call().await,
                enums::MerchantStorageScheme::RedisKv => {
                    let redis_fut = async {
                        Box::pin(kv_wrapper(
                            self,
                            KvOperation::<ReverseLookup>::Get,
                            PartitionKey::CombinationKey {
                                combination: &format!("reverse_lookup_{id}"),
                            },
                        ))
                        .await?
                        .try_into_get()
                    };

                    Box::pin(db_utils::try_redis_get_else_try_database_get(
                        redis_fut,
                        database_call,
                    ))
                    .await
                }
            }
        }
    }
}

#[async_trait::async_trait]
impl ReverseLookupInterface for MockDb {
    async fn insert_reverse_lookup(
        &self,
        new: ReverseLookupNew,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<ReverseLookup, errors::StorageError> {
        let reverse_lookup_insert = ReverseLookup::from(new);
        self.reverse_lookups
            .lock()
            .await
            .push(reverse_lookup_insert.clone());
        Ok(reverse_lookup_insert)
    }

    async fn get_lookup_by_lookup_id(
        &self,
        lookup_id: &str,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<ReverseLookup, errors::StorageError> {
        self.reverse_lookups
            .lock()
            .await
            .iter()
            .find(|reverse_lookup| reverse_lookup.lookup_id == lookup_id)
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "No reverse lookup found for lookup_id = {lookup_id}",
                ))
                .into(),
            )
            .cloned()
    }
}


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

use diesel_models;
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::behaviour::{Conversion, ReverseConversion};
use storage_impl::MockDb;

use super::domain;
use crate::{
    connection,
    core::errors::{self, CustomResult},
    db::kafka_store::KafkaStore,
    services::Store,
};

#[async_trait::async_trait]
pub trait RelayInterface {
    async fn insert_relay(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        new: hyperswitch_domain_models::relay::Relay,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError>;

    async fn update_relay(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        current_state: hyperswitch_domain_models::relay::Relay,
        relay_update: hyperswitch_domain_models::relay::RelayUpdate,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError>;

    async fn find_relay_by_id(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        relay_id: &common_utils::id_type::RelayId,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError>;

    async fn find_relay_by_profile_id_connector_reference_id(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_id: &common_utils::id_type::ProfileId,
        connector_reference_id: &str,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError>;
}

#[async_trait::async_trait]
impl RelayInterface for Store {
    async fn insert_relay(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        new: hyperswitch_domain_models::relay::Relay,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        new.construct_new()
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                self.get_keymanager_state()
                    .attach_printable("Missing KeyManagerState")?,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn update_relay(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        current_state: hyperswitch_domain_models::relay::Relay,
        relay_update: hyperswitch_domain_models::relay::RelayUpdate,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        Conversion::convert(current_state)
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .update(
                &conn,
                diesel_models::relay::RelayUpdateInternal::from(relay_update),
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                self.get_keymanager_state()
                    .attach_printable("Missing KeyManagerState")?,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn find_relay_by_id(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        relay_id: &common_utils::id_type::RelayId,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        diesel_models::relay::Relay::find_by_id(&conn, relay_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                self.get_keymanager_state()
                    .attach_printable("Missing KeyManagerState")?,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn find_relay_by_profile_id_connector_reference_id(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_id: &common_utils::id_type::ProfileId,
        connector_reference_id: &str,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        diesel_models::relay::Relay::find_by_profile_id_connector_reference_id(
            &conn,
            profile_id,
            connector_reference_id,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))?
        .convert(
            self.get_keymanager_state()
                .attach_printable("Missing KeyManagerState")?,
            merchant_key_store.key.get_inner(),
            merchant_key_store.merchant_id.clone().into(),
        )
        .await
        .change_context(errors::StorageError::DecryptionError)
    }
}

#[async_trait::async_trait]
impl RelayInterface for MockDb {
    async fn insert_relay(
        &self,
        _merchant_key_store: &domain::MerchantKeyStore,
        _new: hyperswitch_domain_models::relay::Relay,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn update_relay(
        &self,
        _merchant_key_store: &domain::MerchantKeyStore,
        _current_state: hyperswitch_domain_models::relay::Relay,
        _relay_update: hyperswitch_domain_models::relay::RelayUpdate,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn find_relay_by_id(
        &self,
        _merchant_key_store: &domain::MerchantKeyStore,
        _relay_id: &common_utils::id_type::RelayId,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }

    async fn find_relay_by_profile_id_connector_reference_id(
        &self,
        _merchant_key_store: &domain::MerchantKeyStore,
        _profile_id: &common_utils::id_type::ProfileId,
        _connector_reference_id: &str,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        Err(errors::StorageError::MockDbError)?
    }
}

#[async_trait::async_trait]
impl RelayInterface for KafkaStore {
    async fn insert_relay(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        new: hyperswitch_domain_models::relay::Relay,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        self.diesel_store
            .insert_relay(merchant_key_store, new)
            .await
    }

    async fn update_relay(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        current_state: hyperswitch_domain_models::relay::Relay,
        relay_update: hyperswitch_domain_models::relay::RelayUpdate,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        self.diesel_store
            .update_relay(merchant_key_store, current_state, relay_update)
            .await
    }

    async fn find_relay_by_id(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        relay_id: &common_utils::id_type::RelayId,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        self.diesel_store
            .find_relay_by_id(merchant_key_store, relay_id)
            .await
    }

    async fn find_relay_by_profile_id_connector_reference_id(
        &self,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_id: &common_utils::id_type::ProfileId,
        connector_reference_id: &str,
    ) -> CustomResult<hyperswitch_domain_models::relay::Relay, errors::StorageError> {
        self.diesel_store
            .find_relay_by_profile_id_connector_reference_id(
                merchant_key_store,
                profile_id,
                connector_reference_id,
            )
            .await
    }
}


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

use common_utils::id_type;

use super::MockDb;
use crate::{
    core::errors::{self, CustomResult},
    types::storage::{self as storage_types, enums::MerchantStorageScheme},
};

#[async_trait::async_trait]
pub trait MandateInterface {
    async fn find_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError>;

    async fn find_mandate_by_merchant_id_connector_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_mandate_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError>;

    async fn find_mandate_by_merchant_id_customer_id(
        &self,
        merchant_id: &id_type::MerchantId,
        customer_id: &id_type::CustomerId,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError>;

    // Fix this function once we move to mandate v2
    #[cfg(feature = "v2")]
    async fn find_mandate_by_global_customer_id(
        &self,
        id: &id_type::GlobalCustomerId,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError>;

    async fn update_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        mandate_update: storage_types::MandateUpdate,
        mandate: storage_types::Mandate,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError>;

    async fn find_mandates_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_constraints: api_models::mandates::MandateListConstraints,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError>;

    async fn insert_mandate(
        &self,
        mandate: storage_types::MandateNew,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError>;
}

#[cfg(feature = "kv_store")]
mod storage {
    use common_utils::{fallback_reverse_lookup_not_found, id_type};
    use diesel_models::kv;
    use error_stack::{report, ResultExt};
    use redis_interface::HsetnxReply;
    use router_env::{instrument, tracing};
    use storage_impl::redis::kv_store::{
        decide_storage_scheme, kv_wrapper, KvOperation, Op, PartitionKey,
    };

    use super::MandateInterface;
    use crate::{
        connection,
        core::errors::{self, utils::RedisErrorExt, CustomResult},
        db::reverse_lookup::ReverseLookupInterface,
        services::Store,
        types::storage::{self as storage_types, enums::MerchantStorageScheme, MandateDbExt},
        utils::db_utils,
    };

    #[async_trait::async_trait]
    impl MandateInterface for Store {
        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_id: &str,
            storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            let database_call = || async {
                storage_types::Mandate::find_by_merchant_id_mandate_id(
                    &conn,
                    merchant_id,
                    mandate_id,
                )
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
            };
            let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Mandate>(
                self,
                storage_scheme,
                Op::Find,
            ))
            .await;
            match storage_scheme {
                MerchantStorageScheme::PostgresOnly => database_call().await,
                MerchantStorageScheme::RedisKv => {
                    let key = PartitionKey::MerchantIdMandateId {
                        merchant_id,
                        mandate_id,
                    };
                    let field = format!("mandate_{mandate_id}");

                    Box::pin(db_utils::try_redis_get_else_try_database_get(
                        async {
                            Box::pin(kv_wrapper(
                                self,
                                KvOperation::<diesel_models::Mandate>::HGet(&field),
                                key,
                            ))
                            .await?
                            .try_into_hget()
                        },
                        database_call,
                    ))
                    .await
                }
            }
        }

        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_connector_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            connector_mandate_id: &str,
            storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            let database_call = || async {
                storage_types::Mandate::find_by_merchant_id_connector_mandate_id(
                    &conn,
                    merchant_id,
                    connector_mandate_id,
                )
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
            };
            let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Mandate>(
                self,
                storage_scheme,
                Op::Find,
            ))
            .await;
            match storage_scheme {
                MerchantStorageScheme::PostgresOnly => database_call().await,
                MerchantStorageScheme::RedisKv => {
                    let lookup_id = format!(
                        "mid_{}_conn_mandate_{}",
                        merchant_id.get_string_repr(),
                        connector_mandate_id
                    );
                    let lookup = fallback_reverse_lookup_not_found!(
                        self.get_lookup_by_lookup_id(&lookup_id, storage_scheme)
                            .await,
                        database_call().await
                    );

                    let key = PartitionKey::CombinationKey {
                        combination: &lookup.pk_id,
                    };

                    Box::pin(db_utils::try_redis_get_else_try_database_get(
                        async {
                            Box::pin(kv_wrapper(
                                self,
                                KvOperation::<diesel_models::Mandate>::HGet(&lookup.sk_id),
                                key,
                            ))
                            .await?
                            .try_into_hget()
                        },
                        database_call,
                    ))
                    .await
                }
            }
        }

        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_customer_id(
            &self,
            merchant_id: &id_type::MerchantId,
            customer_id: &id_type::CustomerId,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_merchant_id_customer_id(&conn, merchant_id, customer_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[cfg(feature = "v2")]
        #[instrument(skip_all)]
        async fn find_mandate_by_global_customer_id(
            &self,
            id: &id_type::GlobalCustomerId,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_global_customer_id(&conn, id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn update_mandate_by_merchant_id_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_id: &str,
            mandate_update: storage_types::MandateUpdate,
            mandate: storage_types::Mandate,
            storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            let key = PartitionKey::MerchantIdMandateId {
                merchant_id,
                mandate_id,
            };
            let field = format!("mandate_{mandate_id}");
            let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Mandate>(
                self,
                storage_scheme,
                Op::Update(key.clone(), &field, mandate.updated_by.as_deref()),
            ))
            .await;
            match storage_scheme {
                MerchantStorageScheme::PostgresOnly => {
                    storage_types::Mandate::update_by_merchant_id_mandate_id(
                        &conn,
                        merchant_id,
                        mandate_id,
                        mandate_update.convert_to_mandate_update(storage_scheme),
                    )
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))
                }
                MerchantStorageScheme::RedisKv => {
                    let key_str = key.to_string();

                    if let diesel_models::MandateUpdate::ConnectorMandateIdUpdate {
                        connector_mandate_id: Some(val),
                        ..
                    } = &mandate_update
                    {
                        let rev_lookup = diesel_models::ReverseLookupNew {
                            sk_id: field.clone(),
                            pk_id: key_str.clone(),
                            lookup_id: format!(
                                "mid_{}_conn_mandate_{}",
                                merchant_id.get_string_repr(),
                                val
                            ),
                            source: "mandate".to_string(),
                            updated_by: storage_scheme.to_string(),
                        };
                        self.insert_reverse_lookup(rev_lookup, storage_scheme)
                            .await?;
                    }

                    let m_update = mandate_update.convert_to_mandate_update(storage_scheme);
                    let updated_mandate = m_update.clone().apply_changeset(mandate.clone());

                    let redis_value = serde_json::to_string(&updated_mandate)
                        .change_context(errors::StorageError::SerializationFailed)?;

                    let redis_entry = kv::TypedSql {
                        op: kv::DBOperation::Update {
                            updatable: Box::new(kv::Updateable::MandateUpdate(
                                kv::MandateUpdateMems {
                                    orig: mandate,
                                    update_data: m_update,
                                },
                            )),
                        },
                    };

                    Box::pin(kv_wrapper::<(), _, _>(
                        self,
                        KvOperation::<diesel_models::Mandate>::Hset(
                            (&field, redis_value),
                            redis_entry,
                        ),
                        key,
                    ))
                    .await
                    .map_err(|err| err.to_redis_failed_response(&key_str))?
                    .try_into_hset()
                    .change_context(errors::StorageError::KVError)?;

                    Ok(updated_mandate)
                }
            }
        }

        #[instrument(skip_all)]
        async fn find_mandates_by_merchant_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_constraints: api_models::mandates::MandateListConstraints,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::filter_by_constraints(&conn, merchant_id, mandate_constraints)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn insert_mandate(
            &self,
            mut mandate: storage_types::MandateNew,
            storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Mandate>(
                self,
                storage_scheme,
                Op::Insert,
            ))
            .await;
            mandate.update_storage_scheme(storage_scheme);
            match storage_scheme {
                MerchantStorageScheme::PostgresOnly => mandate
                    .insert(&conn)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error))),
                MerchantStorageScheme::RedisKv => {
                    let mandate_id = mandate.mandate_id.clone();
                    let merchant_id = &mandate.merchant_id.to_owned();
                    let connector_mandate_id = mandate.connector_mandate_id.clone();

                    let key = PartitionKey::MerchantIdMandateId {
                        merchant_id,
                        mandate_id: mandate_id.as_str(),
                    };
                    let key_str = key.to_string();
                    let field = format!("mandate_{mandate_id}");

                    let storage_mandate = storage_types::Mandate::from(&mandate);

                    let redis_entry = kv::TypedSql {
                        op: kv::DBOperation::Insert {
                            insertable: Box::new(kv::Insertable::Mandate(mandate)),
                        },
                    };

                    if let Some(connector_val) = connector_mandate_id {
                        let lookup_id = format!(
                            "mid_{}_conn_mandate_{}",
                            merchant_id.get_string_repr(),
                            connector_val
                        );

                        let reverse_lookup_entry = diesel_models::ReverseLookupNew {
                            sk_id: field.clone(),
                            pk_id: key_str.clone(),
                            lookup_id,
                            source: "mandate".to_string(),
                            updated_by: storage_scheme.to_string(),
                        };

                        self.insert_reverse_lookup(reverse_lookup_entry, storage_scheme)
                            .await?;
                    }

                    match Box::pin(kv_wrapper::<diesel_models::Mandate, _, _>(
                        self,
                        KvOperation::<diesel_models::Mandate>::HSetNx(
                            &field,
                            &storage_mandate,
                            redis_entry,
                        ),
                        key,
                    ))
                    .await
                    .map_err(|err| err.to_redis_failed_response(&key_str))?
                    .try_into_hsetnx()
                    {
                        Ok(HsetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue {
                            entity: "mandate",
                            key: Some(storage_mandate.mandate_id),
                        }
                        .into()),
                        Ok(HsetnxReply::KeySet) => Ok(storage_mandate),
                        Err(er) => Err(er).change_context(errors::StorageError::KVError),
                    }
                }
            }
        }
    }
}

#[cfg(not(feature = "kv_store"))]
mod storage {
    use common_utils::id_type;
    use error_stack::report;
    use router_env::{instrument, tracing};

    use super::MandateInterface;
    use crate::{
        connection,
        core::errors::{self, CustomResult},
        services::Store,
        types::storage::{self as storage_types, enums::MerchantStorageScheme, MandateDbExt},
    };

    #[async_trait::async_trait]
    impl MandateInterface for Store {
        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_id: &str,
            _storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_merchant_id_mandate_id(&conn, merchant_id, mandate_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_connector_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            connector_mandate_id: &str,
            _storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_merchant_id_connector_mandate_id(
                &conn,
                merchant_id,
                connector_mandate_id,
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_customer_id(
            &self,
            merchant_id: &id_type::MerchantId,
            customer_id: &id_type::CustomerId,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_merchant_id_customer_id(&conn, merchant_id, customer_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        // Need to fix this once we start moving to mandate v2
        #[cfg(feature = "v2")]
        #[instrument(skip_all)]
        async fn find_mandate_by_global_customer_id(
            &self,
            customer_id: &id_type::GlobalCustomerId,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_global_id(&conn, customer_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn update_mandate_by_merchant_id_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_id: &str,
            mandate_update: storage_types::MandateUpdate,
            _mandate: storage_types::Mandate,
            _storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            storage_types::Mandate::update_by_merchant_id_mandate_id(
                &conn,
                merchant_id,
                mandate_id,
                storage_types::MandateUpdateInternal::from(mandate_update),
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn find_mandates_by_merchant_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_constraints: api_models::mandates::MandateListConstraints,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::filter_by_constraints(&conn, merchant_id, mandate_constraints)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn insert_mandate(
            &self,
            mandate: storage_types::MandateNew,
            _storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            mandate
                .insert(&conn)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }
    }
}

#[async_trait::async_trait]
impl MandateInterface for MockDb {
    async fn find_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
        self.mandates
            .lock()
            .await
            .iter()
            .find(|mandate| mandate.merchant_id == *merchant_id && mandate.mandate_id == mandate_id)
            .cloned()
            .ok_or_else(|| errors::StorageError::ValueNotFound("mandate not found".to_string()))
            .map_err(|err| err.into())
    }

    async fn find_mandate_by_merchant_id_connector_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_mandate_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
        self.mandates
            .lock()
            .await
            .iter()
            .find(|mandate| {
                mandate.merchant_id == *merchant_id
                    && mandate.connector_mandate_id == Some(connector_mandate_id.to_string())
            })
            .cloned()
            .ok_or_else(|| errors::StorageError::ValueNotFound("mandate not found".to_string()))
            .map_err(|err| err.into())
    }

    async fn find_mandate_by_merchant_id_customer_id(
        &self,
        merchant_id: &id_type::MerchantId,
        customer_id: &id_type::CustomerId,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
        return Ok(self
            .mandates
            .lock()
            .await
            .iter()
            .filter(|mandate| {
                mandate.merchant_id == *merchant_id && &mandate.customer_id == customer_id
            })
            .cloned()
            .collect());
    }

    // Need to fix this once we move to v2 mandate
    #[cfg(feature = "v2")]
    async fn find_mandate_by_global_customer_id(
        &self,
        id: &id_type::GlobalCustomerId,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
        todo!()
    }

    async fn update_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        mandate_update: storage_types::MandateUpdate,
        _mandate: storage_types::Mandate,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
        let mut mandates = self.mandates.lock().await;
        match mandates
            .iter_mut()
            .find(|mandate| mandate.merchant_id == *merchant_id && mandate.mandate_id == mandate_id)
        {
            Some(mandate) => {
                let m_update = diesel_models::MandateUpdateInternal::from(mandate_update);
                let updated_mandate = m_update.clone().apply_changeset(mandate.clone());
                Ok(updated_mandate)
            }
            None => {
                Err(errors::StorageError::ValueNotFound("mandate not found".to_string()).into())
            }
        }
    }

    async fn find_mandates_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_constraints: api_models::mandates::MandateListConstraints,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
        let mandates = self.mandates.lock().await;
        let mandates_iter = mandates.iter().filter(|mandate| {
            let mut checker = mandate.merchant_id == *merchant_id;
            if let Some(created_time) = mandate_constraints.created_time {
                checker &= mandate.created_at == created_time;
            }
            if let Some(created_time_lt) = mandate_constraints.created_time_lt {
                checker &= mandate.created_at < created_time_lt;
            }
            if let Some(created_time_gt) = mandate_constraints.created_time_gt {
                checker &= mandate.created_at > created_time_gt;
            }
            if let Some(created_time_lte) = mandate_constraints.created_time_lte {
                checker &= mandate.created_at <= created_time_lte;
            }
            if let Some(created_time_gte) = mandate_constraints.created_time_gte {
                checker &= mandate.created_at >= created_time_gte;
            }
            if let Some(connector) = &mandate_constraints.connector {
                checker &= mandate.connector == *connector;
            }
            if let Some(mandate_status) = mandate_constraints.mandate_status {
                checker &= mandate.mandate_status == mandate_status;
            }
            checker
        });

        #[allow(clippy::as_conversions)]
        let offset = (if mandate_constraints.offset.unwrap_or(0) < 0 {
            0
        } else {
            mandate_constraints.offset.unwrap_or(0)
        }) as usize;

        let mandates: Vec<storage_types::Mandate> = if let Some(limit) = mandate_constraints.limit {
            #[allow(clippy::as_conversions)]
            mandates_iter
                .skip(offset)
                .take((if limit < 0 { 0 } else { limit }) as usize)
                .cloned()
                .collect()
        } else {
            mandates_iter.skip(offset).cloned().collect()
        };
        Ok(mandates)
    }

    async fn insert_mandate(
        &self,
        mandate_new: storage_types::MandateNew,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
        let mut mandates = self.mandates.lock().await;
        let customer_user_agent_extended = mandate_new.get_customer_user_agent_extended();

        let mandate = storage_types::Mandate {
            mandate_id: mandate_new.mandate_id.clone(),
            customer_id: mandate_new.customer_id,
            merchant_id: mandate_new.merchant_id,
            original_payment_id: mandate_new.original_payment_id,
            payment_method_id: mandate_new.payment_method_id,
            mandate_status: mandate_new.mandate_status,
            mandate_type: mandate_new.mandate_type,
            customer_accepted_at: mandate_new.customer_accepted_at,
            customer_ip_address: mandate_new.customer_ip_address,
            customer_user_agent: None,
            network_transaction_id: mandate_new.network_transaction_id,
            previous_attempt_id: mandate_new.previous_attempt_id,
            created_at: mandate_new
                .created_at
                .unwrap_or_else(common_utils::date_time::now),
            mandate_amount: mandate_new.mandate_amount,
            mandate_currency: mandate_new.mandate_currency,
            amount_captured: mandate_new.amount_captured,
            connector: mandate_new.connector,
            connector_mandate_id: mandate_new.connector_mandate_id,
            start_date: mandate_new.start_date,
            end_date: mandate_new.end_date,
            metadata: mandate_new.metadata,
            connector_mandate_ids: mandate_new.connector_mandate_ids,
            merchant_connector_id: mandate_new.merchant_connector_id,
            updated_by: mandate_new.updated_by,
            customer_user_agent_extended,
        };
        mandates.push(mandate.clone());
        Ok(mandate)
    }
}


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

use router_env::{instrument, tracing};

use super::MockDb;
use crate::{
    core::errors::{self, CustomResult},
    types::storage::{self as types, enums},
};

#[async_trait::async_trait]
pub trait CaptureInterface {
    async fn insert_capture(
        &self,
        capture: types::CaptureNew,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<types::Capture, errors::StorageError>;

    async fn find_all_captures_by_merchant_id_payment_id_authorized_attempt_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
        authorized_attempt_id: &str,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<Vec<types::Capture>, errors::StorageError>;

    async fn update_capture_with_capture_id(
        &self,
        this: types::Capture,
        capture: types::CaptureUpdate,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<types::Capture, errors::StorageError>;
}

#[cfg(feature = "kv_store")]
mod storage {
    use error_stack::report;
    use router_env::{instrument, tracing};

    use super::CaptureInterface;
    use crate::{
        connection,
        core::errors::{self, CustomResult},
        services::Store,
        types::storage::{capture::*, enums},
    };

    #[async_trait::async_trait]
    impl CaptureInterface for Store {
        #[instrument(skip_all)]
        async fn insert_capture(
            &self,
            capture: CaptureNew,
            _storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<Capture, errors::StorageError> {
            let db_call = || async {
                let conn = connection::pg_connection_write(self).await?;
                capture
                    .insert(&conn)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))
            };
            db_call().await
        }

        #[instrument(skip_all)]
        async fn update_capture_with_capture_id(
            &self,
            this: Capture,
            capture: CaptureUpdate,
            _storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<Capture, errors::StorageError> {
            let db_call = || async {
                let conn = connection::pg_connection_write(self).await?;
                this.update_with_capture_id(&conn, capture)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))
            };
            db_call().await
        }

        #[instrument(skip_all)]
        async fn find_all_captures_by_merchant_id_payment_id_authorized_attempt_id(
            &self,
            merchant_id: &common_utils::id_type::MerchantId,
            payment_id: &common_utils::id_type::PaymentId,
            authorized_attempt_id: &str,
            _storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<Vec<Capture>, errors::StorageError> {
            let db_call = || async {
                let conn = connection::pg_connection_read(self).await?;
                Capture::find_all_by_merchant_id_payment_id_authorized_attempt_id(
                    merchant_id,
                    payment_id,
                    authorized_attempt_id,
                    &conn,
                )
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
            };
            db_call().await
        }
    }
}

#[cfg(not(feature = "kv_store"))]
mod storage {
    use error_stack::report;
    use router_env::{instrument, tracing};

    use super::CaptureInterface;
    use crate::{
        connection,
        core::errors::{self, CustomResult},
        services::Store,
        types::storage::{capture::*, enums},
    };

    #[async_trait::async_trait]
    impl CaptureInterface for Store {
        #[instrument(skip_all)]
        async fn insert_capture(
            &self,
            capture: CaptureNew,
            _storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<Capture, errors::StorageError> {
            let db_call = || async {
                let conn = connection::pg_connection_write(self).await?;
                capture
                    .insert(&conn)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))
            };
            db_call().await
        }

        #[instrument(skip_all)]
        async fn update_capture_with_capture_id(
            &self,
            this: Capture,
            capture: CaptureUpdate,
            _storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<Capture, errors::StorageError> {
            let db_call = || async {
                let conn = connection::pg_connection_write(self).await?;
                this.update_with_capture_id(&conn, capture)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))
            };
            db_call().await
        }

        #[instrument(skip_all)]
        async fn find_all_captures_by_merchant_id_payment_id_authorized_attempt_id(
            &self,
            merchant_id: &common_utils::id_type::MerchantId,
            payment_id: &common_utils::id_type::PaymentId,
            authorized_attempt_id: &str,
            _storage_scheme: enums::MerchantStorageScheme,
        ) -> CustomResult<Vec<Capture>, errors::StorageError> {
            let db_call = || async {
                let conn = connection::pg_connection_read(self).await?;
                Capture::find_all_by_merchant_id_payment_id_authorized_attempt_id(
                    merchant_id,
                    payment_id,
                    authorized_attempt_id,
                    &conn,
                )
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
            };
            db_call().await
        }
    }
}

#[async_trait::async_trait]
impl CaptureInterface for MockDb {
    async fn insert_capture(
        &self,
        capture: types::CaptureNew,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<types::Capture, errors::StorageError> {
        let mut captures = self.captures.lock().await;
        let capture = types::Capture {
            capture_id: capture.capture_id,
            payment_id: capture.payment_id,
            merchant_id: capture.merchant_id,
            status: capture.status,
            amount: capture.amount,
            currency: capture.currency,
            connector: capture.connector,
            error_message: capture.error_message,
            error_code: capture.error_code,
            error_reason: capture.error_reason,
            tax_amount: capture.tax_amount,
            created_at: capture.created_at,
            modified_at: capture.modified_at,
            authorized_attempt_id: capture.authorized_attempt_id,
            capture_sequence: capture.capture_sequence,
            connector_capture_id: capture.connector_capture_id,
            connector_response_reference_id: capture.connector_response_reference_id,
            processor_capture_data: capture.processor_capture_data,
            // Below fields are deprecated. Please add any new fields above this line.
            connector_capture_data: None,
        };
        captures.push(capture.clone());
        Ok(capture)
    }

    #[instrument(skip_all)]
    async fn update_capture_with_capture_id(
        &self,
        _this: types::Capture,
        _capture: types::CaptureUpdate,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<types::Capture, errors::StorageError> {
        //Implement function for `MockDb`
        Err(errors::StorageError::MockDbError)?
    }
    async fn find_all_captures_by_merchant_id_payment_id_authorized_attempt_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _payment_id: &common_utils::id_type::PaymentId,
        _authorized_attempt_id: &str,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<Vec<types::Capture>, errors::StorageError> {
        //Implement function for `MockDb`
        Err(errors::StorageError::MockDbError)?
    }
}


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

use diesel_models::authorization::AuthorizationUpdateInternal;
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 AuthorizationInterface {
    async fn insert_authorization(
        &self,
        authorization: storage::AuthorizationNew,
    ) -> CustomResult<storage::Authorization, errors::StorageError>;

    async fn find_all_authorizations_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Authorization>, errors::StorageError>;

    async fn update_authorization_by_merchant_id_authorization_id(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        authorization_id: String,
        authorization: storage::AuthorizationUpdate,
    ) -> CustomResult<storage::Authorization, errors::StorageError>;
}

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

    #[instrument(skip_all)]
    async fn find_all_authorizations_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Authorization>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Authorization::find_by_merchant_id_payment_id(&conn, merchant_id, payment_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_authorization_by_merchant_id_authorization_id(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        authorization_id: String,
        authorization: storage::AuthorizationUpdate,
    ) -> CustomResult<storage::Authorization, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        storage::Authorization::update_by_merchant_id_authorization_id(
            &conn,
            merchant_id,
            authorization_id,
            authorization,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl AuthorizationInterface for MockDb {
    async fn insert_authorization(
        &self,
        authorization: storage::AuthorizationNew,
    ) -> CustomResult<storage::Authorization, errors::StorageError> {
        let mut authorizations = self.authorizations.lock().await;
        if authorizations.iter().any(|authorization_inner| {
            authorization_inner.authorization_id == authorization.authorization_id
        }) {
            Err(errors::StorageError::DuplicateValue {
                entity: "authorization_id",
                key: None,
            })?
        }
        let authorization = storage::Authorization {
            authorization_id: authorization.authorization_id,
            merchant_id: authorization.merchant_id,
            payment_id: authorization.payment_id,
            amount: authorization.amount,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            status: authorization.status,
            error_code: authorization.error_code,
            error_message: authorization.error_message,
            connector_authorization_id: authorization.connector_authorization_id,
            previously_authorized_amount: authorization.previously_authorized_amount,
        };
        authorizations.push(authorization.clone());
        Ok(authorization)
    }

    async fn find_all_authorizations_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Authorization>, errors::StorageError> {
        let authorizations = self.authorizations.lock().await;
        let authorizations_found: Vec<storage::Authorization> = authorizations
            .iter()
            .filter(|a| a.merchant_id == *merchant_id && a.payment_id == *payment_id)
            .cloned()
            .collect();

        Ok(authorizations_found)
    }

    async fn update_authorization_by_merchant_id_authorization_id(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        authorization_id: String,
        authorization_update: storage::AuthorizationUpdate,
    ) -> CustomResult<storage::Authorization, errors::StorageError> {
        let mut authorizations = self.authorizations.lock().await;
        authorizations
            .iter_mut()
            .find(|authorization| authorization.authorization_id == authorization_id && authorization.merchant_id == merchant_id)
            .map(|authorization| {
                let authorization_updated =
                    AuthorizationUpdateInternal::from(authorization_update)
                        .create_authorization(authorization.clone());
                *authorization = authorization_updated.clone();
                authorization_updated
            })
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "cannot find authorization for authorization_id = {authorization_id} and merchant_id = {merchant_id:?}"
                ))
                .into(),
            )
    }
}


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

use diesel_models::user_authentication_method 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 UserAuthenticationMethodInterface {
    async fn insert_user_authentication_method(
        &self,
        user_authentication_method: storage::UserAuthenticationMethodNew,
    ) -> CustomResult<storage::UserAuthenticationMethod, errors::StorageError>;

    async fn get_user_authentication_method_by_id(
        &self,
        id: &str,
    ) -> CustomResult<storage::UserAuthenticationMethod, errors::StorageError>;

    async fn list_user_authentication_methods_for_auth_id(
        &self,
        auth_id: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError>;

    async fn list_user_authentication_methods_for_owner_id(
        &self,
        owner_id: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError>;

    async fn update_user_authentication_method(
        &self,
        id: &str,
        user_authentication_method_update: storage::UserAuthenticationMethodUpdate,
    ) -> CustomResult<storage::UserAuthenticationMethod, errors::StorageError>;

    async fn list_user_authentication_methods_for_email_domain(
        &self,
        email_domain: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError>;
}

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

    #[instrument(skip_all)]
    async fn get_user_authentication_method_by_id(
        &self,
        id: &str,
    ) -> CustomResult<storage::UserAuthenticationMethod, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::UserAuthenticationMethod::get_user_authentication_method_by_id(&conn, id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn list_user_authentication_methods_for_auth_id(
        &self,
        auth_id: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::UserAuthenticationMethod::list_user_authentication_methods_for_auth_id(
            &conn, auth_id,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn list_user_authentication_methods_for_owner_id(
        &self,
        owner_id: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::UserAuthenticationMethod::list_user_authentication_methods_for_owner_id(
            &conn, owner_id,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_user_authentication_method(
        &self,
        id: &str,
        user_authentication_method_update: storage::UserAuthenticationMethodUpdate,
    ) -> CustomResult<storage::UserAuthenticationMethod, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        storage::UserAuthenticationMethod::update_user_authentication_method(
            &conn,
            id,
            user_authentication_method_update,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn list_user_authentication_methods_for_email_domain(
        &self,
        email_domain: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::UserAuthenticationMethod::list_user_authentication_methods_for_email_domain(
            &conn,
            email_domain,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl UserAuthenticationMethodInterface for MockDb {
    async fn insert_user_authentication_method(
        &self,
        user_authentication_method: storage::UserAuthenticationMethodNew,
    ) -> CustomResult<storage::UserAuthenticationMethod, errors::StorageError> {
        let mut user_authentication_methods = self.user_authentication_methods.lock().await;
        let existing_auth_id = user_authentication_methods
            .iter()
            .find(|uam| uam.owner_id == user_authentication_method.owner_id)
            .map(|uam| uam.auth_id.clone());

        let auth_id = existing_auth_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
        let user_authentication_method = storage::UserAuthenticationMethod {
            id: uuid::Uuid::new_v4().to_string(),
            auth_id,
            owner_id: user_authentication_method.auth_id,
            owner_type: user_authentication_method.owner_type,
            auth_type: user_authentication_method.auth_type,
            public_config: user_authentication_method.public_config,
            private_config: user_authentication_method.private_config,
            allow_signup: user_authentication_method.allow_signup,
            created_at: user_authentication_method.created_at,
            last_modified_at: user_authentication_method.last_modified_at,
            email_domain: user_authentication_method.email_domain,
        };

        user_authentication_methods.push(user_authentication_method.clone());
        Ok(user_authentication_method)
    }

    #[instrument(skip_all)]
    async fn get_user_authentication_method_by_id(
        &self,
        id: &str,
    ) -> CustomResult<storage::UserAuthenticationMethod, errors::StorageError> {
        let user_authentication_methods = self.user_authentication_methods.lock().await;

        let user_authentication_method = user_authentication_methods
            .iter()
            .find(|&auth_method_inner| auth_method_inner.id == id);

        if let Some(user_authentication_method) = user_authentication_method {
            Ok(user_authentication_method.to_owned())
        } else {
            return Err(errors::StorageError::ValueNotFound(format!(
                "No user authentication method found for id = {id}",
            ))
            .into());
        }
    }

    async fn list_user_authentication_methods_for_auth_id(
        &self,
        auth_id: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError> {
        let user_authentication_methods = self.user_authentication_methods.lock().await;

        let user_authentication_methods_list: Vec<_> = user_authentication_methods
            .iter()
            .filter(|auth_method_inner| auth_method_inner.auth_id == auth_id)
            .cloned()
            .collect();
        if user_authentication_methods_list.is_empty() {
            return Err(errors::StorageError::ValueNotFound(format!(
                "No user authentication method found for auth_id = {auth_id}",
            ))
            .into());
        }

        Ok(user_authentication_methods_list)
    }

    async fn list_user_authentication_methods_for_owner_id(
        &self,
        owner_id: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError> {
        let user_authentication_methods = self.user_authentication_methods.lock().await;

        let user_authentication_methods_list: Vec<_> = user_authentication_methods
            .iter()
            .filter(|auth_method_inner| auth_method_inner.owner_id == owner_id)
            .cloned()
            .collect();
        if user_authentication_methods_list.is_empty() {
            return Err(errors::StorageError::ValueNotFound(format!(
                "No user authentication method found for owner_id = {owner_id}",
            ))
            .into());
        }

        Ok(user_authentication_methods_list)
    }

    async fn update_user_authentication_method(
        &self,
        id: &str,
        user_authentication_method_update: storage::UserAuthenticationMethodUpdate,
    ) -> CustomResult<storage::UserAuthenticationMethod, errors::StorageError> {
        let mut user_authentication_methods = self.user_authentication_methods.lock().await;
        user_authentication_methods
            .iter_mut()
            .find(|auth_method_inner| auth_method_inner.id == id)
            .map(|auth_method_inner| {
                *auth_method_inner = match user_authentication_method_update {
                    storage::UserAuthenticationMethodUpdate::UpdateConfig {
                        private_config,
                        public_config,
                    } => storage::UserAuthenticationMethod {
                        private_config,
                        public_config,
                        last_modified_at: common_utils::date_time::now(),
                        ..auth_method_inner.to_owned()
                    },
                    storage::UserAuthenticationMethodUpdate::EmailDomain { email_domain } => {
                        storage::UserAuthenticationMethod {
                            email_domain: email_domain.to_owned(),
                            last_modified_at: common_utils::date_time::now(),
                            ..auth_method_inner.to_owned()
                        }
                    }
                };
                auth_method_inner.to_owned()
            })
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "No authentication method available for the id = {id}"
                ))
                .into(),
            )
    }

    #[instrument(skip_all)]
    async fn list_user_authentication_methods_for_email_domain(
        &self,
        email_domain: &str,
    ) -> CustomResult<Vec<storage::UserAuthenticationMethod>, errors::StorageError> {
        let user_authentication_methods = self.user_authentication_methods.lock().await;

        let user_authentication_methods_list: Vec<_> = user_authentication_methods
            .iter()
            .filter(|auth_method_inner| auth_method_inner.email_domain == email_domain)
            .cloned()
            .collect();

        Ok(user_authentication_methods_list)
    }
}


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

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 FileMetadataInterface {
    async fn insert_file_metadata(
        &self,
        file: storage::FileMetadataNew,
    ) -> CustomResult<storage::FileMetadata, errors::StorageError>;

    async fn find_file_metadata_by_merchant_id_file_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        file_id: &str,
    ) -> CustomResult<storage::FileMetadata, errors::StorageError>;

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

    async fn update_file_metadata(
        &self,
        this: storage::FileMetadata,
        file_metadata: storage::FileMetadataUpdate,
    ) -> CustomResult<storage::FileMetadata, errors::StorageError>;
}

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

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

    #[instrument(skip_all)]
    async fn delete_file_metadata_by_merchant_id_file_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        file_id: &str,
    ) -> CustomResult<bool, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        storage::FileMetadata::delete_by_merchant_id_file_id(&conn, merchant_id, file_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_file_metadata(
        &self,
        this: storage::FileMetadata,
        file_metadata: storage::FileMetadataUpdate,
    ) -> CustomResult<storage::FileMetadata, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        this.update(&conn, file_metadata)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl FileMetadataInterface for MockDb {
    async fn insert_file_metadata(
        &self,
        _file: storage::FileMetadataNew,
    ) -> CustomResult<storage::FileMetadata, errors::StorageError> {
        // TODO: Implement function for `MockDb`
        Err(errors::StorageError::MockDbError)?
    }

    async fn find_file_metadata_by_merchant_id_file_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _file_id: &str,
    ) -> CustomResult<storage::FileMetadata, errors::StorageError> {
        // TODO: Implement function for `MockDb`
        Err(errors::StorageError::MockDbError)?
    }

    async fn delete_file_metadata_by_merchant_id_file_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _file_id: &str,
    ) -> CustomResult<bool, errors::StorageError> {
        // TODO: Implement function for `MockDb`
        Err(errors::StorageError::MockDbError)?
    }

    async fn update_file_metadata(
        &self,
        _this: storage::FileMetadata,
        _file_metadata: storage::FileMetadataUpdate,
    ) -> CustomResult<storage::FileMetadata, errors::StorageError> {
        // TODO: Implement function for `MockDb`
        Err(errors::StorageError::MockDbError)?
    }
}