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

use common_types::primitive_wrappers::CustomerListLimit;
use common_utils::{
    crypto::Encryptable,
    errors::ReportSwitchExt,
    ext_traits::AsyncExt,
    id_type, pii, type_name,
    types::{
        keymanager::{Identifier, KeyManagerState, ToEncryptable},
        Description,
    },
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::payment_methods as payment_methods_domain;
use masking::{ExposeInterface, Secret, SwitchStrategy};
use payment_methods::controller::PaymentMethodsController;
use router_env::{instrument, tracing};

#[cfg(feature = "v2")]
use crate::core::payment_methods::cards::create_encrypted_data;
#[cfg(feature = "v1")]
use crate::utils::CustomerAddress;
use crate::{
    core::{
        errors::{self, StorageErrorExt},
        payment_methods::{cards, network_tokenization},
    },
    db::StorageInterface,
    pii::PeekInterface,
    routes::{metrics, SessionState},
    services,
    types::{
        api::customers,
        domain::{self, types},
        storage::{self, enums},
        transformers::ForeignFrom,
    },
};

pub const REDACTED: &str = "Redacted";

#[instrument(skip(state))]
pub async fn create_customer(
    state: SessionState,
    platform: domain::Platform,
    customer_data: customers::CustomerRequest,
    connector_customer_details: Option<Vec<payment_methods_domain::ConnectorCustomerDetails>>,
) -> errors::CustomerResponse<customers::CustomerResponse> {
    let db: &dyn StorageInterface = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let merchant_reference_id = customer_data.get_merchant_reference_id();

    let merchant_id = platform.get_processor().get_account().get_id();

    let merchant_reference_id_customer = MerchantReferenceIdForCustomer {
        merchant_reference_id: merchant_reference_id.as_ref(),
        merchant_id,
        merchant_account: platform.get_processor().get_account(),
        key_store: platform.get_processor().get_key_store(),
    };

    // We first need to validate whether the customer with the given customer id already exists
    // this may seem like a redundant db call, as the insert_customer will anyway return this error
    //
    // Consider a scenario where the address is inserted and then when inserting the customer,
    // it errors out, now the address that was inserted is not deleted

    merchant_reference_id_customer
        .verify_if_merchant_reference_not_present_by_optional_merchant_reference_id(db)
        .await?;

    let domain_customer = customer_data
        .create_domain_model_from_request(
            &connector_customer_details,
            db,
            &merchant_reference_id,
            &platform,
            key_manager_state,
            &state,
        )
        .await?;

    let customer = db
        .insert_customer(
            domain_customer,
            platform.get_processor().get_key_store(),
            platform.get_processor().get_account().storage_scheme,
        )
        .await
        .to_duplicate_response(errors::CustomersErrorResponse::CustomerAlreadyExists)?;

    customer_data.generate_response(&customer)
}

#[async_trait::async_trait]
trait CustomerCreateBridge {
    async fn create_domain_model_from_request<'a>(
        &'a self,
        connector_customer_details: &'a Option<
            Vec<payment_methods_domain::ConnectorCustomerDetails>,
        >,
        db: &'a dyn StorageInterface,
        merchant_reference_id: &'a Option<id_type::CustomerId>,
        platform: &'a domain::Platform,
        key_manager_state: &'a KeyManagerState,
        state: &'a SessionState,
    ) -> errors::CustomResult<domain::Customer, errors::CustomersErrorResponse>;

    fn generate_response<'a>(
        &'a self,
        customer: &'a domain::Customer,
    ) -> errors::CustomerResponse<customers::CustomerResponse>;
}

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl CustomerCreateBridge for customers::CustomerRequest {
    async fn create_domain_model_from_request<'a>(
        &'a self,
        connector_customer_details: &'a Option<
            Vec<payment_methods_domain::ConnectorCustomerDetails>,
        >,
        db: &'a dyn StorageInterface,
        merchant_reference_id: &'a Option<id_type::CustomerId>,
        platform: &'a domain::Platform,
        key_manager_state: &'a KeyManagerState,
        state: &'a SessionState,
    ) -> errors::CustomResult<domain::Customer, errors::CustomersErrorResponse> {
        // Setting default billing address to Db
        let address = self.get_address();
        let merchant_id = platform.get_processor().get_account().get_id();
        let key = platform
            .get_processor()
            .get_key_store()
            .key
            .get_inner()
            .peek();

        let customer_billing_address_struct = AddressStructForDbEntry {
            address: address.as_ref(),
            customer_data: self,
            merchant_id,
            customer_id: merchant_reference_id.as_ref(),
            storage_scheme: platform.get_processor().get_account().storage_scheme,
            key_store: platform.get_processor().get_key_store(),
            state,
        };

        let address_from_db = customer_billing_address_struct
            .encrypt_customer_address_and_set_to_db(db)
            .await?;

        let encrypted_data = types::crypto_operation(
            key_manager_state,
            type_name!(domain::Customer),
            types::CryptoOperation::BatchEncrypt(
                domain::FromRequestEncryptableCustomer::to_encryptable(
                    domain::FromRequestEncryptableCustomer {
                        name: self.name.clone(),
                        email: self.email.clone().map(|a| a.expose().switch_strategy()),
                        phone: self.phone.clone(),
                        tax_registration_id: self.tax_registration_id.clone(),
                    },
                ),
            ),
            Identifier::Merchant(platform.get_processor().get_key_store().merchant_id.clone()),
            key,
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .switch()
        .attach_printable("Failed while encrypting Customer")?;

        let encryptable_customer =
            domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                .change_context(errors::CustomersErrorResponse::InternalServerError)?;

        let connector_customer = connector_customer_details.as_ref().map(|details_vec| {
            let mut map = serde_json::Map::new();
            for details in details_vec {
                let merchant_connector_id =
                    details.merchant_connector_id.get_string_repr().to_string();
                let connector_customer_id = details.connector_customer_id.clone();
                map.insert(merchant_connector_id, connector_customer_id.into());
            }
            pii::SecretSerdeValue::new(serde_json::Value::Object(map))
        });

        Ok(domain::Customer {
            customer_id: merchant_reference_id
                .to_owned()
                .ok_or(errors::CustomersErrorResponse::InternalServerError)?,
            merchant_id: merchant_id.to_owned(),
            name: encryptable_customer.name,
            email: encryptable_customer.email.map(|email| {
                let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> = Encryptable::new(
                    email.clone().into_inner().switch_strategy(),
                    email.into_encrypted(),
                );
                encryptable
            }),
            phone: encryptable_customer.phone,
            description: self.description.clone(),
            phone_country_code: self.phone_country_code.clone(),
            metadata: self.metadata.clone(),
            connector_customer,
            address_id: address_from_db.clone().map(|addr| addr.address_id),
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            default_payment_method_id: None,
            updated_by: None,
            version: common_types::consts::API_VERSION,
            tax_registration_id: encryptable_customer.tax_registration_id,
            // TODO: Populate created_by from authentication context once it is integrated in auth data
            created_by: None,
            last_modified_by: None,
        })
    }

    fn generate_response<'a>(
        &'a self,
        customer: &'a domain::Customer,
    ) -> errors::CustomerResponse<customers::CustomerResponse> {
        let address = self.get_address();
        Ok(services::ApplicationResponse::Json(
            customers::CustomerResponse::foreign_from((customer.clone(), address)),
        ))
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl CustomerCreateBridge for customers::CustomerRequest {
    async fn create_domain_model_from_request<'a>(
        &'a self,
        connector_customer_details: &'a Option<
            Vec<payment_methods_domain::ConnectorCustomerDetails>,
        >,
        _db: &'a dyn StorageInterface,
        merchant_reference_id: &'a Option<id_type::CustomerId>,
        platform: &'a domain::Platform,
        key_state: &'a KeyManagerState,
        state: &'a SessionState,
    ) -> errors::CustomResult<domain::Customer, errors::CustomersErrorResponse> {
        let default_customer_billing_address = self.get_default_customer_billing_address();
        let encrypted_customer_billing_address = default_customer_billing_address
            .async_map(|billing_address| {
                create_encrypted_data(
                    key_state,
                    platform.get_processor().get_key_store(),
                    billing_address,
                )
            })
            .await
            .transpose()
            .change_context(errors::CustomersErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt default customer billing address")?;
        let default_customer_shipping_address = self.get_default_customer_shipping_address();
        let encrypted_customer_shipping_address = default_customer_shipping_address
            .async_map(|shipping_address| {
                create_encrypted_data(
                    key_state,
                    platform.get_processor().get_key_store(),
                    shipping_address,
                )
            })
            .await
            .transpose()
            .change_context(errors::CustomersErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt default customer shipping address")?;

        let merchant_id = platform.get_processor().get_account().get_id().clone();
        let key = platform
            .get_processor()
            .get_key_store()
            .key
            .get_inner()
            .peek();

        let encrypted_data = types::crypto_operation(
            key_state,
            type_name!(domain::Customer),
            types::CryptoOperation::BatchEncrypt(
                domain::FromRequestEncryptableCustomer::to_encryptable(
                    domain::FromRequestEncryptableCustomer {
                        name: Some(self.name.clone()),
                        email: Some(self.email.clone().expose().switch_strategy()),
                        phone: self.phone.clone(),
                        tax_registration_id: self.tax_registration_id.clone(),
                    },
                ),
            ),
            Identifier::Merchant(platform.get_processor().get_key_store().merchant_id.clone()),
            key,
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .switch()
        .attach_printable("Failed while encrypting Customer")?;

        let encryptable_customer =
            domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                .change_context(errors::CustomersErrorResponse::InternalServerError)?;

        let connector_customer = connector_customer_details.as_ref().map(|details_vec| {
            let map: std::collections::HashMap<_, _> = details_vec
                .iter()
                .map(|details| {
                    (
                        details.merchant_connector_id.clone(),
                        details.connector_customer_id.to_string(),
                    )
                })
                .collect();
            common_types::customers::ConnectorCustomerMap::new(map)
        });

        Ok(domain::Customer {
            id: id_type::GlobalCustomerId::generate(&state.conf.cell_information.id),
            merchant_reference_id: merchant_reference_id.to_owned(),
            merchant_id,
            name: encryptable_customer.name,
            email: encryptable_customer.email.map(|email| {
                let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> = Encryptable::new(
                    email.clone().into_inner().switch_strategy(),
                    email.into_encrypted(),
                );
                encryptable
            }),
            phone: encryptable_customer.phone,
            description: self.description.clone(),
            phone_country_code: self.phone_country_code.clone(),
            metadata: self.metadata.clone(),
            connector_customer,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            default_payment_method_id: None,
            updated_by: None,
            default_billing_address: encrypted_customer_billing_address.map(Into::into),
            default_shipping_address: encrypted_customer_shipping_address.map(Into::into),
            version: common_types::consts::API_VERSION,
            status: common_enums::DeleteStatus::Active,
            tax_registration_id: encryptable_customer.tax_registration_id,
            // TODO: Populate created_by from authentication context once it is integrated in auth data
            created_by: None,
            last_modified_by: None,
        })
    }

    fn generate_response<'a>(
        &'a self,
        customer: &'a domain::Customer,
    ) -> errors::CustomerResponse<customers::CustomerResponse> {
        Ok(services::ApplicationResponse::Json(
            customers::CustomerResponse::foreign_from(customer.clone()),
        ))
    }
}

#[cfg(feature = "v1")]
struct AddressStructForDbEntry<'a> {
    address: Option<&'a api_models::payments::AddressDetails>,
    customer_data: &'a customers::CustomerRequest,
    merchant_id: &'a id_type::MerchantId,
    customer_id: Option<&'a id_type::CustomerId>,
    storage_scheme: common_enums::MerchantStorageScheme,
    key_store: &'a domain::MerchantKeyStore,
    state: &'a SessionState,
}

#[cfg(feature = "v1")]
impl AddressStructForDbEntry<'_> {
    async fn encrypt_customer_address_and_set_to_db(
        &self,
        db: &dyn StorageInterface,
    ) -> errors::CustomResult<Option<domain::Address>, errors::CustomersErrorResponse> {
        let encrypted_customer_address = self
            .address
            .async_map(|addr| async {
                self.customer_data
                    .get_domain_address(
                        self.state,
                        addr.clone(),
                        self.merchant_id,
                        self.customer_id
                            .ok_or(errors::CustomersErrorResponse::InternalServerError)?, // should we raise error since in v1 appilcation is supposed to have this id or generate it at this point.
                        self.key_store.key.get_inner().peek(),
                        self.storage_scheme,
                    )
                    .await
                    .switch()
                    .attach_printable("Failed while encrypting address")
            })
            .await
            .transpose()?;

        encrypted_customer_address
            .async_map(|encrypt_add| async {
                db.insert_address_for_customers(encrypt_add, self.key_store)
                    .await
                    .switch()
                    .attach_printable("Failed while inserting new address")
            })
            .await
            .transpose()
    }
}

struct MerchantReferenceIdForCustomer<'a> {
    merchant_reference_id: Option<&'a id_type::CustomerId>,
    merchant_id: &'a id_type::MerchantId,
    merchant_account: &'a domain::MerchantAccount,
    key_store: &'a domain::MerchantKeyStore,
}

#[cfg(feature = "v1")]
impl<'a> MerchantReferenceIdForCustomer<'a> {
    async fn verify_if_merchant_reference_not_present_by_optional_merchant_reference_id(
        &self,
        db: &dyn StorageInterface,
    ) -> Result<Option<()>, error_stack::Report<errors::CustomersErrorResponse>> {
        self.merchant_reference_id
            .async_map(|cust| async {
                self.verify_if_merchant_reference_not_present_by_merchant_reference_id(cust, db)
                    .await
            })
            .await
            .transpose()
    }

    async fn verify_if_merchant_reference_not_present_by_merchant_reference_id(
        &self,
        cus: &'a id_type::CustomerId,
        db: &dyn StorageInterface,
    ) -> Result<(), error_stack::Report<errors::CustomersErrorResponse>> {
        match db
            .find_customer_by_customer_id_merchant_id(
                cus,
                self.merchant_id,
                self.key_store,
                self.merchant_account.storage_scheme,
            )
            .await
        {
            Err(err) => {
                if !err.current_context().is_db_not_found() {
                    Err(err).switch()
                } else {
                    Ok(())
                }
            }
            Ok(_) => Err(report!(
                errors::CustomersErrorResponse::CustomerAlreadyExists
            )),
        }
    }
}

#[cfg(feature = "v2")]
impl<'a> MerchantReferenceIdForCustomer<'a> {
    async fn verify_if_merchant_reference_not_present_by_optional_merchant_reference_id(
        &self,
        db: &dyn StorageInterface,
    ) -> Result<Option<()>, error_stack::Report<errors::CustomersErrorResponse>> {
        self.merchant_reference_id
            .async_map(|merchant_ref| async {
                self.verify_if_merchant_reference_not_present_by_merchant_reference(
                    merchant_ref,
                    db,
                )
                .await
            })
            .await
            .transpose()
    }

    async fn verify_if_merchant_reference_not_present_by_merchant_reference(
        &self,
        merchant_ref: &'a id_type::CustomerId,
        db: &dyn StorageInterface,
    ) -> Result<(), error_stack::Report<errors::CustomersErrorResponse>> {
        match db
            .find_customer_by_merchant_reference_id_merchant_id(
                merchant_ref,
                self.merchant_id,
                self.key_store,
                self.merchant_account.storage_scheme,
            )
            .await
        {
            Err(err) => {
                if !err.current_context().is_db_not_found() {
                    Err(err).switch()
                } else {
                    Ok(())
                }
            }
            Ok(_) => Err(report!(
                errors::CustomersErrorResponse::CustomerAlreadyExists
            )),
        }
    }
}

#[cfg(feature = "v1")]
#[instrument(skip(state))]
pub async fn retrieve_customer(
    state: SessionState,
    platform: domain::Platform,
    _profile_id: Option<id_type::ProfileId>,
    customer_id: id_type::CustomerId,
) -> errors::CustomerResponse<customers::CustomerResponse> {
    let db = state.store.as_ref();

    let response = db
        .find_customer_optional_with_redacted_customer_details_by_customer_id_merchant_id(
            &customer_id,
            platform.get_processor().get_account().get_id(),
            platform.get_processor().get_key_store(),
            platform.get_processor().get_account().storage_scheme,
        )
        .await
        .switch()?
        .ok_or(errors::CustomersErrorResponse::CustomerNotFound)?;

    let address = match &response.address_id {
        Some(address_id) => Some(api_models::payments::AddressDetails::from(
            db.find_address_by_address_id(address_id, platform.get_processor().get_key_store())
                .await
                .switch()?,
        )),
        None => None,
    };
    Ok(services::ApplicationResponse::Json(
        customers::CustomerResponse::foreign_from((response, address)),
    ))
}

#[cfg(feature = "v2")]
#[instrument(skip(state))]
pub async fn retrieve_customer(
    state: SessionState,
    platform: domain::Platform,
    id: id_type::GlobalCustomerId,
) -> errors::CustomerResponse<customers::CustomerResponse> {
    let db = state.store.as_ref();

    let response = db
        .find_customer_by_global_id(
            &id,
            platform.get_processor().get_key_store(),
            platform.get_processor().get_account().storage_scheme,
        )
        .await
        .switch()?;

    Ok(services::ApplicationResponse::Json(
        customers::CustomerResponse::foreign_from(response),
    ))
}

#[instrument(skip(state))]
pub async fn list_customers(
    state: SessionState,
    merchant_id: id_type::MerchantId,
    _profile_id_list: Option<Vec<id_type::ProfileId>>,
    key_store: domain::MerchantKeyStore,
    request: customers::CustomerListRequest,
) -> errors::CustomerResponse<Vec<customers::CustomerResponse>> {
    let db = state.store.as_ref();

    let customer_list_constraints = crate::db::customers::CustomerListConstraints {
        limit: request
            .limit
            .unwrap_or(crate::consts::DEFAULT_LIST_API_LIMIT),
        offset: request.offset,
        customer_id: request.customer_id,
        time_range: None,
    };

    let domain_customers = db
        .list_customers_by_merchant_id(&merchant_id, &key_store, customer_list_constraints)
        .await
        .switch()?;

    #[cfg(feature = "v1")]
    let customers = domain_customers
        .into_iter()
        .map(|domain_customer| customers::CustomerResponse::foreign_from((domain_customer, None)))
        .collect();

    #[cfg(feature = "v2")]
    let customers = domain_customers
        .into_iter()
        .map(customers::CustomerResponse::foreign_from)
        .collect();

    Ok(services::ApplicationResponse::Json(customers))
}

#[instrument(skip(state))]
pub async fn list_customers_with_count(
    state: SessionState,
    merchant_id: id_type::MerchantId,
    key_store: domain::MerchantKeyStore,
    request: customers::CustomerListRequestWithConstraints,
) -> errors::CustomerResponse<customers::CustomerListResponse> {
    let db = state.store.as_ref();
    let customer_list_constraints = crate::db::customers::CustomerListConstraints {
        limit: request
            .limit
            .map(|l| *l)
            .unwrap_or_else(|| *CustomerListLimit::default()),
        offset: request.offset,
        customer_id: request.customer_id,
        time_range: request.time_range,
    };

    let domain_customers = db
        .list_customers_by_merchant_id_with_count(
            &merchant_id,
            &key_store,
            customer_list_constraints,
        )
        .await
        .switch()?;

    #[cfg(feature = "v1")]
    let customers: Vec<customers::CustomerResponse> = domain_customers
        .0
        .into_iter()
        .map(|domain_customer| customers::CustomerResponse::foreign_from((domain_customer, None)))
        .collect();

    #[cfg(feature = "v2")]
    let customers: Vec<customers::CustomerResponse> = domain_customers
        .0
        .into_iter()
        .map(customers::CustomerResponse::foreign_from)
        .collect();

    Ok(services::ApplicationResponse::Json(
        customers::CustomerListResponse {
            data: customers.into_iter().map(|c| c.0).collect(),
            total_count: domain_customers.1,
        },
    ))
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn delete_customer(
    state: SessionState,
    platform: domain::Platform,
    id: id_type::GlobalCustomerId,
) -> errors::CustomerResponse<customers::CustomerDeleteResponse> {
    let db = &*state.store;
    let key_manager_state = &(&state).into();
    id.redact_customer_details_and_generate_response(db, &platform, key_manager_state, &state)
        .await
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl CustomerDeleteBridge for id_type::GlobalCustomerId {
    async fn redact_customer_details_and_generate_response<'a>(
        &'a self,
        db: &'a dyn StorageInterface,
        platform: &'a domain::Platform,
        key_manager_state: &'a KeyManagerState,
        state: &'a SessionState,
    ) -> errors::CustomerResponse<customers::CustomerDeleteResponse> {
        let customer_orig = db
            .find_customer_by_global_id(
                self,
                platform.get_processor().get_key_store(),
                platform.get_processor().get_account().storage_scheme,
            )
            .await
            .switch()?;

        let merchant_reference_id = customer_orig.merchant_reference_id.clone();

        let customer_mandates = db.find_mandate_by_global_customer_id(self).await.switch()?;

        for mandate in customer_mandates.into_iter() {
            if mandate.mandate_status == enums::MandateStatus::Active {
                Err(errors::CustomersErrorResponse::MandateActive)?
            }
        }

        match db
            .find_payment_method_list_by_global_customer_id(
                platform.get_processor().get_key_store(),
                self,
                None,
            )
            .await
        {
            // check this in review
            Ok(customer_payment_methods) => {
                for pm in customer_payment_methods.into_iter() {
                    if pm.get_payment_method_type() == Some(enums::PaymentMethod::Card) {
                        cards::delete_card_by_locker_id(
                            state,
                            self,
                            platform.get_processor().get_account().get_id(),
                        )
                        .await
                        .switch()?;
                    }
                    // No solution as of now, need to discuss this further with payment_method_v2

                    // db.delete_payment_method(
                    //     key_manager_state,
                    //     key_store,
                    //     pm,
                    // )
                    // .await
                    // .switch()?;
                }
            }
            Err(error) => {
                if error.current_context().is_db_not_found() {
                    Ok(())
                } else {
                    Err(error)
                        .change_context(errors::CustomersErrorResponse::InternalServerError)
                        .attach_printable(
                            "failed find_payment_method_by_customer_id_merchant_id_list",
                        )
                }?
            }
        };

        let key = platform
            .get_processor()
            .get_key_store()
            .key
            .get_inner()
            .peek();

        let identifier =
            Identifier::Merchant(platform.get_processor().get_key_store().merchant_id.clone());
        let redacted_encrypted_value: Encryptable<Secret<_>> = types::crypto_operation(
            key_manager_state,
            type_name!(storage::Address),
            types::CryptoOperation::Encrypt(REDACTED.to_string().into()),
            identifier.clone(),
            key,
        )
        .await
        .and_then(|val| val.try_into_operation())
        .switch()?;

        let redacted_encrypted_email = Encryptable::new(
            redacted_encrypted_value
                .clone()
                .into_inner()
                .switch_strategy(),
            redacted_encrypted_value.clone().into_encrypted(),
        );

        let updated_customer =
            storage::CustomerUpdate::Update(Box::new(storage::CustomerGeneralUpdate {
                name: Some(redacted_encrypted_value.clone()),
                email: Box::new(Some(redacted_encrypted_email)),
                phone: Box::new(Some(redacted_encrypted_value.clone())),
                description: Some(Description::from_str_unchecked(REDACTED)),
                phone_country_code: Some(REDACTED.to_string()),
                metadata: None,
                connector_customer: Box::new(None),
                default_billing_address: None,
                default_shipping_address: None,
                default_payment_method_id: None,
                status: Some(common_enums::DeleteStatus::Redacted),
                tax_registration_id: Some(redacted_encrypted_value),
                last_modified_by: None,
            }));

        db.update_customer_by_global_id(
            self,
            customer_orig,
            updated_customer,
            platform.get_processor().get_key_store(),
            platform.get_processor().get_account().storage_scheme,
        )
        .await
        .switch()?;

        let response = customers::CustomerDeleteResponse {
            id: self.clone(),
            merchant_reference_id,
            customer_deleted: true,
            address_deleted: true,
            payment_methods_deleted: true,
        };
        metrics::CUSTOMER_REDACTED.add(1, &[]);
        Ok(services::ApplicationResponse::Json(response))
    }
}

#[async_trait::async_trait]
trait CustomerDeleteBridge {
    async fn redact_customer_details_and_generate_response<'a>(
        &'a self,
        db: &'a dyn StorageInterface,
        platform: &'a domain::Platform,
        key_manager_state: &'a KeyManagerState,
        state: &'a SessionState,
    ) -> errors::CustomerResponse<customers::CustomerDeleteResponse>;
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn delete_customer(
    state: SessionState,
    platform: domain::Platform,
    customer_id: id_type::CustomerId,
) -> errors::CustomerResponse<customers::CustomerDeleteResponse> {
    let db = &*state.store;
    let key_manager_state = &(&state).into();
    customer_id
        .redact_customer_details_and_generate_response(db, &platform, key_manager_state, &state)
        .await
}

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl CustomerDeleteBridge for id_type::CustomerId {
    async fn redact_customer_details_and_generate_response<'a>(
        &'a self,
        db: &'a dyn StorageInterface,
        platform: &'a domain::Platform,
        key_manager_state: &'a KeyManagerState,
        state: &'a SessionState,
    ) -> errors::CustomerResponse<customers::CustomerDeleteResponse> {
        let customer_orig = db
            .find_customer_by_customer_id_merchant_id(
                self,
                platform.get_processor().get_account().get_id(),
                platform.get_processor().get_key_store(),
                platform.get_processor().get_account().storage_scheme,
            )
            .await
            .switch()?;

        let customer_mandates = db
            .find_mandate_by_merchant_id_customer_id(
                platform.get_processor().get_account().get_id(),
                self,
            )
            .await
            .switch()?;

        for mandate in customer_mandates.into_iter() {
            if mandate.mandate_status == enums::MandateStatus::Active {
                Err(errors::CustomersErrorResponse::MandateActive)?
            }
        }

        match db
            .find_payment_method_by_customer_id_merchant_id_list(
                platform.get_processor().get_key_store(),
                self,
                platform.get_processor().get_account().get_id(),
                None,
            )
            .await
        {
            // check this in review
            Ok(customer_payment_methods) => {
                for pm in customer_payment_methods.into_iter() {
                    if pm.get_payment_method_type() == Some(enums::PaymentMethod::Card) {
                        cards::PmCards { state, platform }
                            .delete_card_from_locker(
                                self,
                                platform.get_processor().get_account().get_id(),
                                pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
                            )
                            .await
                            .switch()?;

                        if let Some(network_token_ref_id) = pm.network_token_requestor_reference_id
                        {
                            network_tokenization::delete_network_token_from_locker_and_token_service(
                            state,
                            self,
                            platform.get_processor().get_account().get_id(),
                            pm.payment_method_id.clone(),
                            pm.network_token_locker_id,
                            network_token_ref_id,
                            platform,
                        )
                        .await
                        .switch()?;
                        }
                    }

                    db.delete_payment_method_by_merchant_id_payment_method_id(
                        platform.get_processor().get_key_store(),
                        platform.get_processor().get_account().get_id(),
                        &pm.payment_method_id,
                    )
                    .await
                    .change_context(errors::CustomersErrorResponse::InternalServerError)
                    .attach_printable(
                        "failed to delete payment method while redacting customer details",
                    )?;
                }
            }
            Err(error) => {
                if error.current_context().is_db_not_found() {
                    Ok(())
                } else {
                    Err(error)
                        .change_context(errors::CustomersErrorResponse::InternalServerError)
                        .attach_printable(
                            "failed find_payment_method_by_customer_id_merchant_id_list",
                        )
                }?
            }
        };

        let key = platform
            .get_processor()
            .get_key_store()
            .key
            .get_inner()
            .peek();
        let identifier =
            Identifier::Merchant(platform.get_processor().get_key_store().merchant_id.clone());
        let redacted_encrypted_value: Encryptable<Secret<_>> = types::crypto_operation(
            key_manager_state,
            type_name!(storage::Address),
            types::CryptoOperation::Encrypt(REDACTED.to_string().into()),
            identifier.clone(),
            key,
        )
        .await
        .and_then(|val| val.try_into_operation())
        .switch()?;

        let redacted_encrypted_email = Encryptable::new(
            redacted_encrypted_value
                .clone()
                .into_inner()
                .switch_strategy(),
            redacted_encrypted_value.clone().into_encrypted(),
        );

        let update_address = storage::AddressUpdate::Update {
            city: Some(REDACTED.to_string()),
            country: None,
            line1: Some(redacted_encrypted_value.clone()),
            line2: Some(redacted_encrypted_value.clone()),
            line3: Some(redacted_encrypted_value.clone()),
            state: Some(redacted_encrypted_value.clone()),
            zip: Some(redacted_encrypted_value.clone()),
            first_name: Some(redacted_encrypted_value.clone()),
            last_name: Some(redacted_encrypted_value.clone()),
            phone_number: Some(redacted_encrypted_value.clone()),
            country_code: Some(REDACTED.to_string()),
            updated_by: platform
                .get_processor()
                .get_account()
                .storage_scheme
                .to_string(),
            email: Some(redacted_encrypted_email),
            origin_zip: Some(redacted_encrypted_value.clone()),
        };

        match db
            .update_address_by_merchant_id_customer_id(
                self,
                platform.get_processor().get_account().get_id(),
                update_address,
                platform.get_processor().get_key_store(),
            )
            .await
        {
            Ok(_) => Ok(()),
            Err(error) => {
                if error.current_context().is_db_not_found() {
                    Ok(())
                } else {
                    Err(error)
                        .change_context(errors::CustomersErrorResponse::InternalServerError)
                        .attach_printable("failed update_address_by_merchant_id_customer_id")
                }
            }
        }?;

        let updated_customer = storage::CustomerUpdate::Update {
            name: Some(redacted_encrypted_value.clone()),
            email: Some(
                types::crypto_operation(
                    key_manager_state,
                    type_name!(storage::Customer),
                    types::CryptoOperation::Encrypt(REDACTED.to_string().into()),
                    identifier,
                    key,
                )
                .await
                .and_then(|val| val.try_into_operation())
                .switch()?,
            ),
            phone: Box::new(Some(redacted_encrypted_value.clone())),
            description: Some(Description::from_str_unchecked(REDACTED)),
            phone_country_code: Some(REDACTED.to_string()),
            metadata: Box::new(None),
            connector_customer: Box::new(None),
            address_id: None,
            tax_registration_id: Some(redacted_encrypted_value.clone()),
            last_modified_by: None,
        };

        db.update_customer_by_customer_id_merchant_id(
            self.clone(),
            platform.get_processor().get_account().get_id().to_owned(),
            customer_orig,
            updated_customer,
            platform.get_processor().get_key_store(),
            platform.get_processor().get_account().storage_scheme,
        )
        .await
        .switch()?;

        let response = customers::CustomerDeleteResponse {
            customer_id: self.clone(),
            customer_deleted: true,
            address_deleted: true,
            payment_methods_deleted: true,
        };
        metrics::CUSTOMER_REDACTED.add(1, &[]);
        Ok(services::ApplicationResponse::Json(response))
    }
}

#[instrument(skip(state))]
pub async fn update_customer(
    state: SessionState,
    platform: domain::Platform,
    update_customer: customers::CustomerUpdateRequestInternal,
) -> errors::CustomerResponse<customers::CustomerResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    //Add this in update call if customer can be updated anywhere else

    #[cfg(feature = "v1")]
    let verify_id_for_update_customer = VerifyIdForUpdateCustomer {
        merchant_reference_id: &update_customer.customer_id,
        merchant_account: platform.get_processor().get_account(),
        key_store: platform.get_processor().get_key_store(),
    };

    #[cfg(feature = "v2")]
    let verify_id_for_update_customer = VerifyIdForUpdateCustomer {
        id: &update_customer.id,
        merchant_account: platform.get_processor().get_account(),
        key_store: platform.get_processor().get_key_store(),
        key_manager_state,
    };

    let customer = verify_id_for_update_customer
        .verify_id_and_get_customer_object(db)
        .await?;

    let updated_customer = update_customer
        .request
        .create_domain_model_from_request(
            &None,
            db,
            &platform,
            key_manager_state,
            &state,
            &customer,
        )
        .await?;

    update_customer.request.generate_response(&updated_customer)
}

#[async_trait::async_trait]
trait CustomerUpdateBridge {
    async fn create_domain_model_from_request<'a>(
        &'a self,
        connector_customer_details: &'a Option<
            Vec<payment_methods_domain::ConnectorCustomerDetails>,
        >,
        db: &'a dyn StorageInterface,
        platform: &'a domain::Platform,
        key_manager_state: &'a KeyManagerState,
        state: &'a SessionState,
        domain_customer: &'a domain::Customer,
    ) -> errors::CustomResult<domain::Customer, errors::CustomersErrorResponse>;

    fn generate_response<'a>(
        &'a self,
        customer: &'a domain::Customer,
    ) -> errors::CustomerResponse<customers::CustomerResponse>;
}

#[cfg(feature = "v1")]
struct AddressStructForDbUpdate<'a> {
    update_customer: &'a customers::CustomerUpdateRequest,
    merchant_account: &'a domain::MerchantAccount,
    key_store: &'a domain::MerchantKeyStore,
    state: &'a SessionState,
    domain_customer: &'a domain::Customer,
}

#[cfg(feature = "v1")]
impl AddressStructForDbUpdate<'_> {
    async fn update_address_if_sent(
        &self,
        db: &dyn StorageInterface,
    ) -> errors::CustomResult<Option<domain::Address>, errors::CustomersErrorResponse> {
        let address = if let Some(addr) = &self.update_customer.address {
            match self.domain_customer.address_id.clone() {
                Some(address_id) => {
                    let customer_address: api_models::payments::AddressDetails = addr.clone();
                    let update_address = self
                        .update_customer
                        .get_address_update(
                            self.state,
                            customer_address,
                            self.key_store.key.get_inner().peek(),
                            self.merchant_account.storage_scheme,
                            self.merchant_account.get_id().clone(),
                        )
                        .await
                        .switch()
                        .attach_printable("Failed while encrypting Address while Update")?;
                    Some(
                        db.update_address(address_id, update_address, self.key_store)
                            .await
                            .switch()
                            .attach_printable(format!(
                            "Failed while updating address: merchant_id: {:?}, customer_id: {:?}",
                            self.merchant_account.get_id(),
                            self.domain_customer.customer_id
                        ))?,
                    )
                }
                None => {
                    let customer_address: api_models::payments::AddressDetails = addr.clone();

                    let address = self
                        .update_customer
                        .get_domain_address(
                            self.state,
                            customer_address,
                            self.merchant_account.get_id(),
                            &self.domain_customer.customer_id,
                            self.key_store.key.get_inner().peek(),
                            self.merchant_account.storage_scheme,
                        )
                        .await
                        .switch()
                        .attach_printable("Failed while encrypting address")?;
                    Some(
                        db.insert_address_for_customers(address, self.key_store)
                            .await
                            .switch()
                            .attach_printable("Failed while inserting new address")?,
                    )
                }
            }
        } else {
            match &self.domain_customer.address_id {
                Some(address_id) => Some(
                    db.find_address_by_address_id(address_id, self.key_store)
                        .await
                        .switch()?,
                ),
                None => None,
            }
        };
        Ok(address)
    }
}

#[cfg(feature = "v1")]
#[derive(Debug)]
struct VerifyIdForUpdateCustomer<'a> {
    merchant_reference_id: &'a id_type::CustomerId,
    merchant_account: &'a domain::MerchantAccount,
    key_store: &'a domain::MerchantKeyStore,
}

#[cfg(feature = "v2")]
#[derive(Debug)]
struct VerifyIdForUpdateCustomer<'a> {
    id: &'a id_type::GlobalCustomerId,
    merchant_account: &'a domain::MerchantAccount,
    key_store: &'a domain::MerchantKeyStore,
    key_manager_state: &'a KeyManagerState,
}

#[cfg(feature = "v1")]
impl VerifyIdForUpdateCustomer<'_> {
    async fn verify_id_and_get_customer_object(
        &self,
        db: &dyn StorageInterface,
    ) -> Result<domain::Customer, error_stack::Report<errors::CustomersErrorResponse>> {
        let customer = db
            .find_customer_by_customer_id_merchant_id(
                self.merchant_reference_id,
                self.merchant_account.get_id(),
                self.key_store,
                self.merchant_account.storage_scheme,
            )
            .await
            .switch()?;

        Ok(customer)
    }
}

#[cfg(feature = "v2")]
impl VerifyIdForUpdateCustomer<'_> {
    async fn verify_id_and_get_customer_object(
        &self,
        db: &dyn StorageInterface,
    ) -> Result<domain::Customer, error_stack::Report<errors::CustomersErrorResponse>> {
        let customer = db
            .find_customer_by_global_id(
                self.id,
                self.key_store,
                self.merchant_account.storage_scheme,
            )
            .await
            .switch()?;

        Ok(customer)
    }
}

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
    async fn create_domain_model_from_request<'a>(
        &'a self,
        _connector_customer_details: &'a Option<
            Vec<payment_methods_domain::ConnectorCustomerDetails>,
        >,
        db: &'a dyn StorageInterface,
        platform: &'a domain::Platform,
        key_manager_state: &'a KeyManagerState,
        state: &'a SessionState,
        domain_customer: &'a domain::Customer,
    ) -> errors::CustomResult<domain::Customer, errors::CustomersErrorResponse> {
        let update_address_for_update_customer = AddressStructForDbUpdate {
            update_customer: self,
            merchant_account: platform.get_processor().get_account(),
            key_store: platform.get_processor().get_key_store(),
            state,
            domain_customer,
        };

        let address = update_address_for_update_customer
            .update_address_if_sent(db)
            .await?;

        let key = platform
            .get_processor()
            .get_key_store()
            .key
            .get_inner()
            .peek();

        let encrypted_data = types::crypto_operation(
            key_manager_state,
            type_name!(domain::Customer),
            types::CryptoOperation::BatchEncrypt(
                domain::FromRequestEncryptableCustomer::to_encryptable(
                    domain::FromRequestEncryptableCustomer {
                        name: self.name.clone(),
                        email: self
                            .email
                            .as_ref()
                            .map(|a| a.clone().expose().switch_strategy()),
                        phone: self.phone.clone(),
                        tax_registration_id: self.tax_registration_id.clone(),
                    },
                ),
            ),
            Identifier::Merchant(platform.get_processor().get_key_store().merchant_id.clone()),
            key,
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .switch()?;

        let encryptable_customer =
            domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                .change_context(errors::CustomersErrorResponse::InternalServerError)?;

        let response = db
            .update_customer_by_customer_id_merchant_id(
                domain_customer.customer_id.to_owned(),
                platform.get_processor().get_account().get_id().to_owned(),
                domain_customer.to_owned(),
                storage::CustomerUpdate::Update {
                    name: encryptable_customer.name,
                    email: encryptable_customer.email.map(|email| {
                        let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> =
                            Encryptable::new(
                                email.clone().into_inner().switch_strategy(),
                                email.into_encrypted(),
                            );
                        encryptable
                    }),
                    phone: Box::new(encryptable_customer.phone),
                    tax_registration_id: encryptable_customer.tax_registration_id,
                    phone_country_code: self.phone_country_code.clone(),
                    metadata: Box::new(self.metadata.clone()),
                    description: self.description.clone(),
                    connector_customer: Box::new(None),
                    address_id: address.clone().map(|addr| addr.address_id),
                    last_modified_by: None,
                },
                platform.get_processor().get_key_store(),
                platform.get_processor().get_account().storage_scheme,
            )
            .await
            .switch()?;

        Ok(response)
    }

    fn generate_response<'a>(
        &'a self,
        customer: &'a domain::Customer,
    ) -> errors::CustomerResponse<customers::CustomerResponse> {
        let address = self.get_address();
        Ok(services::ApplicationResponse::Json(
            customers::CustomerResponse::foreign_from((customer.clone(), address)),
        ))
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
    async fn create_domain_model_from_request<'a>(
        &'a self,
        connector_customer_details: &'a Option<
            Vec<payment_methods_domain::ConnectorCustomerDetails>,
        >,
        db: &'a dyn StorageInterface,
        platform: &'a domain::Platform,
        key_manager_state: &'a KeyManagerState,
        state: &'a SessionState,
        domain_customer: &'a domain::Customer,
    ) -> errors::CustomResult<domain::Customer, errors::CustomersErrorResponse> {
        let default_billing_address = self.get_default_customer_billing_address();
        let encrypted_customer_billing_address = default_billing_address
            .async_map(|billing_address| {
                create_encrypted_data(
                    key_manager_state,
                    platform.get_processor().get_key_store(),
                    billing_address,
                )
            })
            .await
            .transpose()
            .change_context(errors::CustomersErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt default customer billing address")?;

        let default_shipping_address = self.get_default_customer_shipping_address();
        let encrypted_customer_shipping_address = default_shipping_address
            .async_map(|shipping_address| {
                create_encrypted_data(
                    key_manager_state,
                    platform.get_processor().get_key_store(),
                    shipping_address,
                )
            })
            .await
            .transpose()
            .change_context(errors::CustomersErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt default customer shipping address")?;

        let key = platform
            .get_processor()
            .get_key_store()
            .key
            .get_inner()
            .peek();

        let encrypted_data = types::crypto_operation(
            key_manager_state,
            type_name!(domain::Customer),
            types::CryptoOperation::BatchEncrypt(
                domain::FromRequestEncryptableCustomer::to_encryptable(
                    domain::FromRequestEncryptableCustomer {
                        name: self.name.clone(),
                        email: self
                            .email
                            .as_ref()
                            .map(|a| a.clone().expose().switch_strategy()),
                        phone: self.phone.clone(),
                        tax_registration_id: self.tax_registration_id.clone(),
                    },
                ),
            ),
            Identifier::Merchant(platform.get_processor().get_key_store().merchant_id.clone()),
            key,
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .switch()?;

        let encryptable_customer =
            domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                .change_context(errors::CustomersErrorResponse::InternalServerError)?;

        let response = db
            .update_customer_by_global_id(
                &domain_customer.id,
                domain_customer.to_owned(),
                storage::CustomerUpdate::Update(Box::new(storage::CustomerGeneralUpdate {
                    name: encryptable_customer.name,
                    email: Box::new(encryptable_customer.email.map(|email| {
                        let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> =
                            Encryptable::new(
                                email.clone().into_inner().switch_strategy(),
                                email.into_encrypted(),
                            );
                        encryptable
                    })),
                    phone: Box::new(encryptable_customer.phone),
                    tax_registration_id: encryptable_customer.tax_registration_id,
                    phone_country_code: self.phone_country_code.clone(),
                    metadata: self.metadata.clone(),
                    description: self.description.clone(),
                    connector_customer: Box::new(None),
                    default_billing_address: encrypted_customer_billing_address.map(Into::into),
                    default_shipping_address: encrypted_customer_shipping_address.map(Into::into),
                    default_payment_method_id: Some(self.default_payment_method_id.clone()),
                    status: None,
                    last_modified_by: None,
                })),
                platform.get_processor().get_key_store(),
                platform.get_processor().get_account().storage_scheme,
            )
            .await
            .switch()?;
        Ok(response)
    }

    fn generate_response<'a>(
        &'a self,
        customer: &'a domain::Customer,
    ) -> errors::CustomerResponse<customers::CustomerResponse> {
        Ok(services::ApplicationResponse::Json(
            customers::CustomerResponse::foreign_from(customer.clone()),
        ))
    }
}

pub async fn migrate_customers(
    state: SessionState,
    customers_migration: Vec<payment_methods_domain::PaymentMethodCustomerMigrate>,
    platform: domain::Platform,
) -> errors::CustomerResponse<()> {
    for customer_migration in customers_migration {
        match create_customer(
            state.clone(),
            platform.clone(),
            customer_migration.customer,
            customer_migration.connector_customer_details,
        )
        .await
        {
            Ok(_) => (),
            Err(e) => match e.current_context() {
                errors::CustomersErrorResponse::CustomerAlreadyExists => (),
                _ => return Err(e),
            },
        }
    }
    Ok(services::ApplicationResponse::Json(()))
}


// File: crates/router/src/core/card_testing_guard.rs

pub mod utils;

use crate::core::errors;


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

use api_models::gsm as gsm_api_types;
use error_stack::ResultExt;
use router_env::{instrument, tracing};

use crate::{
    core::errors::{self, RouterResponse, StorageErrorExt},
    db::gsm::GsmInterface,
    services,
    types::transformers::{ForeignFrom, ForeignInto},
    SessionState,
};

#[instrument(skip_all)]
pub async fn create_gsm_rule(
    state: SessionState,
    gsm_rule: gsm_api_types::GsmCreateRequest,
) -> RouterResponse<gsm_api_types::GsmResponse> {
    let db = state.store.as_ref();
    GsmInterface::add_gsm_rule(db, gsm_rule.foreign_into())
        .await
        .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
            message: "GSM with given key already exists in our records".to_string(),
        })
        .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into()))
}

#[instrument(skip_all)]
pub async fn retrieve_gsm_rule(
    state: SessionState,
    gsm_request: gsm_api_types::GsmRetrieveRequest,
) -> RouterResponse<gsm_api_types::GsmResponse> {
    let db = state.store.as_ref();
    let gsm_api_types::GsmRetrieveRequest {
        connector,
        flow,
        sub_flow,
        code,
        message,
    } = gsm_request;
    GsmInterface::find_gsm_rule(db, connector.to_string(), flow, sub_flow, code, message)
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "GSM with given key does not exist in our records".to_string(),
        })
        .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into()))
}

#[instrument(skip_all)]
pub async fn update_gsm_rule(
    state: SessionState,
    gsm_request: gsm_api_types::GsmUpdateRequest,
) -> RouterResponse<gsm_api_types::GsmResponse> {
    let db = state.store.as_ref();
    let connector = gsm_request.connector.clone();
    let flow = gsm_request.flow.clone();
    let code = gsm_request.code.clone();
    let sub_flow = gsm_request.sub_flow.clone();
    let message = gsm_request.message.clone();

    let gsm_db_record =
        GsmInterface::find_gsm_rule(db, connector.to_string(), flow, sub_flow, code, message)
            .await
            .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
                message: "GSM with given key does not exist in our records".to_string(),
            })?;

    let inferred_feature_info = <(
        common_enums::GsmFeature,
        common_types::domain::GsmFeatureData,
    )>::foreign_from((&gsm_request, gsm_db_record));

    let gsm_api_types::GsmUpdateRequest {
        connector,
        flow,
        sub_flow,
        code,
        message,
        decision,
        status,
        router_error,
        step_up_possible,
        unified_code,
        unified_message,
        error_category,
        clear_pan_possible,
        feature,
        feature_data,
    } = gsm_request;
    GsmInterface::update_gsm_rule(
        db,
        connector.to_string(),
        flow,
        sub_flow,
        code,
        message,
        hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate {
            decision,
            status,
            router_error: Some(router_error),
            step_up_possible: feature_data
                .as_ref()
                .and_then(|feature_data| feature_data.get_retry_feature_data())
                .map(|retry_feature_data| retry_feature_data.is_step_up_possible())
                .or(step_up_possible),
            unified_code,
            unified_message,
            error_category,
            clear_pan_possible: feature_data
                .as_ref()
                .and_then(|feature_data| feature_data.get_retry_feature_data())
                .map(|retry_feature_data| retry_feature_data.is_clear_pan_possible())
                .or(clear_pan_possible),
            feature_data: feature_data.or(Some(inferred_feature_info.1)),
            feature: feature.or(Some(inferred_feature_info.0)),
        },
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
        message: "GSM with given key does not exist in our records".to_string(),
    })
    .attach_printable("Failed while updating Gsm rule")
    .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into()))
}

#[instrument(skip_all)]
pub async fn delete_gsm_rule(
    state: SessionState,
    gsm_request: gsm_api_types::GsmDeleteRequest,
) -> RouterResponse<gsm_api_types::GsmDeleteResponse> {
    let db = state.store.as_ref();
    let gsm_api_types::GsmDeleteRequest {
        connector,
        flow,
        sub_flow,
        code,
        message,
    } = gsm_request;
    match GsmInterface::delete_gsm_rule(
        db,
        connector.to_string(),
        flow.to_owned(),
        sub_flow.to_owned(),
        code.to_owned(),
        message.to_owned(),
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
        message: "GSM with given key does not exist in our records".to_string(),
    })
    .attach_printable("Failed while Deleting Gsm rule")
    {
        Ok(is_deleted) => {
            if is_deleted {
                Ok(services::ApplicationResponse::Json(
                    gsm_api_types::GsmDeleteResponse {
                        gsm_rule_delete: true,
                        connector,
                        flow,
                        sub_flow,
                        code,
                    },
                ))
            } else {
                Err(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while Deleting Gsm rule, got response as false")
            }
        }
        Err(err) => Err(err),
    }
}