use common_utils::errors::CustomResult;

use crate::{core::errors, logger};

pub trait StorageErrorExt<T, E> {
    #[track_caller]
    fn to_not_found_response(self, not_found_response: E) -> error_stack::Result<T, E>;

    #[track_caller]
    fn to_duplicate_response(self, duplicate_response: E) -> error_stack::Result<T, E>;
}

impl<T> StorageErrorExt<T, errors::CustomersErrorResponse>
    for error_stack::Result<T, errors::StorageError>
{
    #[track_caller]
    fn to_not_found_response(
        self,
        not_found_response: errors::CustomersErrorResponse,
    ) -> error_stack::Result<T, errors::CustomersErrorResponse> {
        self.map_err(|err| match err.current_context() {
            error if error.is_db_not_found() => err.change_context(not_found_response),
            errors::StorageError::CustomerRedacted => {
                err.change_context(errors::CustomersErrorResponse::CustomerRedacted)
            }
            _ => err.change_context(errors::CustomersErrorResponse::InternalServerError),
        })
    }

    fn to_duplicate_response(
        self,
        duplicate_response: errors::CustomersErrorResponse,
    ) -> error_stack::Result<T, errors::CustomersErrorResponse> {
        self.map_err(|err| {
            if err.current_context().is_db_unique_violation() {
                err.change_context(duplicate_response)
            } else {
                err.change_context(errors::CustomersErrorResponse::InternalServerError)
            }
        })
    }
}

impl<T> StorageErrorExt<T, errors::ApiErrorResponse>
    for error_stack::Result<T, errors::StorageError>
{
    #[track_caller]
    fn to_not_found_response(
        self,
        not_found_response: errors::ApiErrorResponse,
    ) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let new_err = match err.current_context() {
                errors::StorageError::ValueNotFound(_) => not_found_response,
                errors::StorageError::CustomerRedacted => {
                    errors::ApiErrorResponse::CustomerRedacted
                }
                errors::StorageError::InvalidDataFormat(err) => {
                    errors::ApiErrorResponse::InvalidRequestData {
                        message: format!("InvalidRequestData: {}", err),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(new_err)
        })
    }

    #[track_caller]
    fn to_duplicate_response(
        self,
        duplicate_response: errors::ApiErrorResponse,
    ) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let new_err = match err.current_context() {
                errors::StorageError::DuplicateValue { .. } => duplicate_response,
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(new_err)
        })
    }
}

pub trait ConnectorErrorExt<T> {
    #[track_caller]
    fn to_refund_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_payment_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_setup_mandate_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_dispute_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_files_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[cfg(feature = "payouts")]
    #[track_caller]
    fn to_payout_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_vault_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_webhook_configuration_failed_response(
        self,
    ) -> error_stack::Result<T, errors::ApiErrorResponse>;

    // Validates if the result, is Ok(..) or WebhookEventTypeNotFound all the other error variants
    // are cascaded while these two event types are handled via `Option`
    #[track_caller]
    fn allow_webhook_event_type_not_found(
        self,
        enabled: bool,
    ) -> error_stack::Result<Option<T>, errors::ConnectorError>;
}

impl<T> ConnectorErrorExt<T> for error_stack::Result<T, errors::ConnectorError> {
    fn to_refund_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| match err.current_context() {
            errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                let response_str = std::str::from_utf8(bytes);
                let data = match response_str {
                    Ok(s) => serde_json::from_str(s)
                        .map_err(
                            |error| logger::error!(%error,"Failed to convert response to JSON"),
                        )
                        .ok(),
                    Err(error) => {
                        logger::error!(%error,"Failed to convert response to UTF8 string");
                        None
                    }
                };
                err.change_context(errors::ApiErrorResponse::RefundFailed { data })
            }
            errors::ConnectorError::NotImplemented(reason) => {
                errors::ApiErrorResponse::NotImplemented {
                    message: errors::NotImplementedMessage::Reason(reason.to_string()),
                }
                .into()
            }
            errors::ConnectorError::NotSupported { message, connector } => {
                errors::ApiErrorResponse::NotSupported {
                    message: format!("{message} is not supported by {connector}"),
                }
                .into()
            }
            errors::ConnectorError::CaptureMethodNotSupported => {
                errors::ApiErrorResponse::NotSupported {
                    message: "Capture Method Not Supported".to_owned(),
                }
                .into()
            }
            errors::ConnectorError::FailedToObtainIntegrationUrl
            | errors::ConnectorError::RequestEncodingFailed
            | errors::ConnectorError::RequestEncodingFailedWithReason(_)
            | errors::ConnectorError::ParsingFailed
            | errors::ConnectorError::ResponseDeserializationFailed
            | errors::ConnectorError::UnexpectedResponseError(_)
            | errors::ConnectorError::RoutingRulesParsingError
            | errors::ConnectorError::FailedToObtainPreferredConnector
            | errors::ConnectorError::ProcessingStepFailed(_)
            | errors::ConnectorError::InvalidConnectorName
            | errors::ConnectorError::InvalidWallet
            | errors::ConnectorError::ResponseHandlingFailed
            | errors::ConnectorError::MissingRequiredField { .. }
            | errors::ConnectorError::MissingRequiredFields { .. }
            | errors::ConnectorError::FailedToObtainAuthType
            | errors::ConnectorError::FailedToObtainCertificate
            | errors::ConnectorError::NoConnectorMetaData
            | errors::ConnectorError::NoConnectorWalletDetails
            | errors::ConnectorError::FailedToObtainCertificateKey
            | errors::ConnectorError::MaxFieldLengthViolated { .. }
            | errors::ConnectorError::FlowNotSupported { .. }
            | errors::ConnectorError::MissingConnectorMandateID
            | errors::ConnectorError::MissingConnectorMandateMetadata
            | errors::ConnectorError::MissingConnectorTransactionID
            | errors::ConnectorError::MissingConnectorRefundID
            | errors::ConnectorError::MissingApplePayTokenData
            | errors::ConnectorError::WebhooksNotImplemented
            | errors::ConnectorError::WebhookBodyDecodingFailed
            | errors::ConnectorError::WebhookSignatureNotFound
            | errors::ConnectorError::WebhookSourceVerificationFailed
            | errors::ConnectorError::WebhookVerificationSecretNotFound
            | errors::ConnectorError::WebhookVerificationSecretInvalid
            | errors::ConnectorError::WebhookReferenceIdNotFound
            | errors::ConnectorError::WebhookEventTypeNotFound
            | errors::ConnectorError::WebhookResourceObjectNotFound
            | errors::ConnectorError::WebhookResponseEncodingFailed
            | errors::ConnectorError::InvalidDateFormat
            | errors::ConnectorError::DateFormattingFailed
            | errors::ConnectorError::InvalidDataFormat { .. }
            | errors::ConnectorError::MismatchedPaymentData
            | errors::ConnectorError::MandatePaymentDataMismatch { .. }
            | errors::ConnectorError::InvalidWalletToken { .. }
            | errors::ConnectorError::MissingConnectorRelatedTransactionID { .. }
            | errors::ConnectorError::FileValidationFailed { .. }
            | errors::ConnectorError::MissingConnectorRedirectionPayload { .. }
            | errors::ConnectorError::FailedAtConnector { .. }
            | errors::ConnectorError::MissingPaymentMethodType
            | errors::ConnectorError::InSufficientBalanceInPaymentMethod
            | errors::ConnectorError::RequestTimeoutReceived
            | errors::ConnectorError::CurrencyNotSupported { .. }
            | errors::ConnectorError::InvalidConnectorConfig { .. }
            | errors::ConnectorError::AmountConversionFailed
            | errors::ConnectorError::GenericError { .. } => {
                err.change_context(errors::ApiErrorResponse::RefundFailed { data: None })
            }
        })
    }

    fn to_payment_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let data = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |error| logger::error!(%error,"Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(error) => {
                            logger::error!(%error,"Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::PaymentAuthorizationFailed { data }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields { field_names: field_names.to_vec() }
                }
                errors::ConnectorError::NotImplemented(reason) => {
                    errors::ApiErrorResponse::NotImplemented {
                        message: errors::NotImplementedMessage::Reason(
                            reason.to_string(),
                        ),
                    }
                }
                errors::ConnectorError::MismatchedPaymentData => {
                    errors::ApiErrorResponse::InvalidDataValue {
                        field_name:
                            "payment_method_data, payment_method_type and payment_experience does not match",
                    }
                },
                errors::ConnectorError::MandatePaymentDataMismatch {fields}=> {
                    errors::ApiErrorResponse::MandatePaymentDataMismatch {
                        fields: fields.to_owned(),
                    }
                },
                errors::ConnectorError::NotSupported { message, connector } => {
                    errors::ApiErrorResponse::NotSupported { message: format!("{message} is not supported by {connector}") }
                },
                errors::ConnectorError::FlowNotSupported{ flow, connector } => {
                    errors::ApiErrorResponse::FlowNotSupported { flow: flow.to_owned(), connector: connector.to_owned() }
                },
                errors::ConnectorError::MaxFieldLengthViolated{ connector, field_name,  max_length, received_length} => {
                    errors::ApiErrorResponse::MaxFieldLengthViolated { connector: connector.to_string(), field_name: field_name.to_string(), max_length: *max_length, received_length: *received_length }
                },
                errors::ConnectorError::InvalidDataFormat { field_name } => {
                    errors::ApiErrorResponse::InvalidDataValue { field_name }
                },
                errors::ConnectorError::CaptureMethodNotSupported => {
                    errors::ApiErrorResponse::NotSupported {
                        message: "Capture Method Not Supported".to_owned(),
                    }
                }
                errors::ConnectorError::InvalidWalletToken {wallet_name} => errors::ApiErrorResponse::InvalidWalletToken {wallet_name: wallet_name.to_string()},
                errors::ConnectorError::CurrencyNotSupported { message, connector} => errors::ApiErrorResponse::CurrencyNotSupported { message: format!("Credentials for the currency {message} are not configured with the connector {connector}/hyperswitch") },
                errors::ConnectorError::FailedToObtainAuthType =>  errors::ApiErrorResponse::InvalidConnectorConfiguration {config: "connector_account_details".to_string()},
                errors::ConnectorError::InvalidConnectorConfig { config }  => errors::ApiErrorResponse::InvalidConnectorConfiguration { config: config.to_string() },
                errors::ConnectorError::FailedToObtainIntegrationUrl |
                errors::ConnectorError::RequestEncodingFailed |
                errors::ConnectorError::RequestEncodingFailedWithReason(_) |
                errors::ConnectorError::ParsingFailed |
                errors::ConnectorError::ResponseDeserializationFailed |
                errors::ConnectorError::UnexpectedResponseError(_) |
                errors::ConnectorError::RoutingRulesParsingError |
                errors::ConnectorError::FailedToObtainPreferredConnector |
                errors::ConnectorError::InvalidConnectorName |
                errors::ConnectorError::InvalidWallet |
                errors::ConnectorError::ResponseHandlingFailed |
                errors::ConnectorError::FailedToObtainCertificate |
                errors::ConnectorError::NoConnectorMetaData | errors::ConnectorError::NoConnectorWalletDetails |
                errors::ConnectorError::FailedToObtainCertificateKey |
                errors::ConnectorError::MissingConnectorMandateID |
                errors::ConnectorError::MissingConnectorMandateMetadata |
                errors::ConnectorError::MissingConnectorTransactionID |
                errors::ConnectorError::MissingConnectorRefundID |
                errors::ConnectorError::MissingApplePayTokenData |
                errors::ConnectorError::WebhooksNotImplemented |
                errors::ConnectorError::WebhookBodyDecodingFailed |
                errors::ConnectorError::WebhookSignatureNotFound |
                errors::ConnectorError::WebhookSourceVerificationFailed |
                errors::ConnectorError::WebhookVerificationSecretNotFound |
                errors::ConnectorError::WebhookVerificationSecretInvalid |
                errors::ConnectorError::WebhookReferenceIdNotFound |
                errors::ConnectorError::WebhookEventTypeNotFound |
                errors::ConnectorError::WebhookResourceObjectNotFound |
                errors::ConnectorError::WebhookResponseEncodingFailed |
                errors::ConnectorError::InvalidDateFormat |
                errors::ConnectorError::DateFormattingFailed |
                errors::ConnectorError::MissingConnectorRelatedTransactionID { .. } |
                errors::ConnectorError::FileValidationFailed { .. } |
                errors::ConnectorError::MissingConnectorRedirectionPayload { .. } |
                errors::ConnectorError::FailedAtConnector { .. } |
                errors::ConnectorError::MissingPaymentMethodType |
                errors::ConnectorError::InSufficientBalanceInPaymentMethod |
                errors::ConnectorError::RequestTimeoutReceived |
                errors::ConnectorError::ProcessingStepFailed(None)|
                errors::ConnectorError::GenericError {..} |
                errors::ConnectorError::AmountConversionFailed => errors::ApiErrorResponse::InternalServerError
            };
            err.change_context(error)
        })
    }

    fn to_setup_mandate_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = err.current_context();
            let data = match error {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let error_response = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |err| logger::error!(%err, "Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(err) => {
                            logger::error!(%err, "Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::PaymentAuthorizationFailed {
                        data: error_response,
                    }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::FailedToObtainIntegrationUrl => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: "connector_account_details".to_string(),
                    }
                }
                errors::ConnectorError::InvalidConnectorConfig { config: field_name } => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: field_name.to_string(),
                    }
                }
                errors::ConnectorError::InvalidWalletToken { wallet_name } => {
                    errors::ApiErrorResponse::InvalidWalletToken {
                        wallet_name: wallet_name.to_string(),
                    }
                }
                errors::ConnectorError::CaptureMethodNotSupported => {
                    errors::ApiErrorResponse::NotSupported {
                        message: "Capture Method Not Supported".to_owned(),
                    }
                }
                errors::ConnectorError::RequestEncodingFailed
                | errors::ConnectorError::RequestEncodingFailedWithReason(_)
                | errors::ConnectorError::ParsingFailed
                | errors::ConnectorError::ResponseDeserializationFailed
                | errors::ConnectorError::UnexpectedResponseError(_)
                | errors::ConnectorError::RoutingRulesParsingError
                | errors::ConnectorError::FailedToObtainPreferredConnector
                | errors::ConnectorError::InvalidConnectorName
                | errors::ConnectorError::InvalidWallet
                | errors::ConnectorError::ResponseHandlingFailed
                | errors::ConnectorError::MissingRequiredFields { .. }
                | errors::ConnectorError::FailedToObtainAuthType
                | errors::ConnectorError::FailedToObtainCertificate
                | errors::ConnectorError::NoConnectorMetaData
                | errors::ConnectorError::NoConnectorWalletDetails
                | errors::ConnectorError::FailedToObtainCertificateKey
                | errors::ConnectorError::NotImplemented(_)
                | errors::ConnectorError::NotSupported { .. }
                | errors::ConnectorError::MaxFieldLengthViolated { .. }
                | errors::ConnectorError::FlowNotSupported { .. }
                | errors::ConnectorError::MissingConnectorMandateID
                | errors::ConnectorError::MissingConnectorMandateMetadata
                | errors::ConnectorError::MissingConnectorTransactionID
                | errors::ConnectorError::MissingConnectorRefundID
                | errors::ConnectorError::MissingApplePayTokenData
                | errors::ConnectorError::WebhooksNotImplemented
                | errors::ConnectorError::WebhookBodyDecodingFailed
                | errors::ConnectorError::WebhookSignatureNotFound
                | errors::ConnectorError::WebhookSourceVerificationFailed
                | errors::ConnectorError::WebhookVerificationSecretNotFound
                | errors::ConnectorError::WebhookVerificationSecretInvalid
                | errors::ConnectorError::WebhookReferenceIdNotFound
                | errors::ConnectorError::WebhookEventTypeNotFound
                | errors::ConnectorError::WebhookResourceObjectNotFound
                | errors::ConnectorError::WebhookResponseEncodingFailed
                | errors::ConnectorError::InvalidDateFormat
                | errors::ConnectorError::DateFormattingFailed
                | errors::ConnectorError::InvalidDataFormat { .. }
                | errors::ConnectorError::MismatchedPaymentData
                | errors::ConnectorError::MandatePaymentDataMismatch { .. }
                | errors::ConnectorError::MissingConnectorRelatedTransactionID { .. }
                | errors::ConnectorError::FileValidationFailed { .. }
                | errors::ConnectorError::MissingConnectorRedirectionPayload { .. }
                | errors::ConnectorError::FailedAtConnector { .. }
                | errors::ConnectorError::MissingPaymentMethodType
                | errors::ConnectorError::InSufficientBalanceInPaymentMethod
                | errors::ConnectorError::RequestTimeoutReceived
                | errors::ConnectorError::CurrencyNotSupported { .. }
                | errors::ConnectorError::ProcessingStepFailed(None)
                | errors::ConnectorError::AmountConversionFailed
                | errors::ConnectorError::GenericError { .. } => {
                    logger::error!(%error,"Setup Mandate flow failed");
                    errors::ApiErrorResponse::PaymentAuthorizationFailed { data: None }
                }
            };
            err.change_context(data)
        })
    }

    fn to_dispute_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let data = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |error| logger::error!(%error,"Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(error) => {
                            logger::error!(%error,"Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::DisputeFailed { data }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    fn to_files_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let data = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |error| logger::error!(%error,"Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(error) => {
                            logger::error!(%error,"Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::DisputeFailed { data }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    #[cfg(feature = "payouts")]
    fn to_payout_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let data = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |error| logger::error!(%error,"Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(error) => {
                            logger::error!(%error,"Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::PayoutFailed { data }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                errors::ConnectorError::NotSupported { message, connector } => {
                    errors::ApiErrorResponse::NotSupported {
                        message: format!("{message} by {connector}"),
                    }
                }
                errors::ConnectorError::NotImplemented(reason) => {
                    errors::ApiErrorResponse::NotImplemented {
                        message: errors::NotImplementedMessage::Reason(reason.to_string()),
                    }
                }
                errors::ConnectorError::InvalidConnectorConfig { config } => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: config.to_string(),
                    }
                }
                errors::ConnectorError::InvalidDataFormat { field_name } => {
                    errors::ApiErrorResponse::InvalidDataValue { field_name }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    fn to_vault_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(_) => {
                    errors::ApiErrorResponse::ExternalVaultFailed
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                errors::ConnectorError::NotSupported { message, connector } => {
                    errors::ApiErrorResponse::NotSupported {
                        message: format!("{message} by {connector}"),
                    }
                }
                errors::ConnectorError::NotImplemented(reason) => {
                    errors::ApiErrorResponse::NotImplemented {
                        message: errors::NotImplementedMessage::Reason(reason.to_string()),
                    }
                }
                errors::ConnectorError::InvalidConnectorConfig { config } => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: config.to_string(),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    fn to_webhook_configuration_failed_response(
        self,
    ) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(_) => {
                    errors::ApiErrorResponse::ExternalVaultFailed
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                errors::ConnectorError::NotSupported { message, connector } => {
                    errors::ApiErrorResponse::NotSupported {
                        message: format!("{message} by {connector}"),
                    }
                }
                errors::ConnectorError::NotImplemented(reason) => {
                    errors::ApiErrorResponse::NotImplemented {
                        message: errors::NotImplementedMessage::Reason(reason.to_string()),
                    }
                }
                errors::ConnectorError::InvalidConnectorConfig { config } => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: config.to_string(),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    fn allow_webhook_event_type_not_found(
        self,
        enabled: bool,
    ) -> CustomResult<Option<T>, errors::ConnectorError> {
        match self {
            Ok(event_type) => Ok(Some(event_type)),
            Err(error) => match error.current_context() {
                errors::ConnectorError::WebhookEventTypeNotFound if enabled => Ok(None),
                _ => Err(error),
            },
        }
    }
}

pub trait RedisErrorExt {
    #[track_caller]
    fn to_redis_failed_response(self, key: &str) -> error_stack::Report<errors::StorageError>;
}

impl RedisErrorExt for error_stack::Report<errors::RedisError> {
    fn to_redis_failed_response(self, key: &str) -> error_stack::Report<errors::StorageError> {
        match self.current_context() {
            errors::RedisError::NotFound => self.change_context(
                errors::StorageError::ValueNotFound(format!("Data does not exist for key {key}")),
            ),
            errors::RedisError::SetNxFailed => {
                self.change_context(errors::StorageError::DuplicateValue {
                    entity: "redis",
                    key: Some(key.to_string()),
                })
            }
            _ => self.change_context(errors::StorageError::KVError),
        }
    }
}

#[cfg(feature = "olap")]
impl<T> StorageErrorExt<T, errors::UserErrors> for error_stack::Result<T, errors::StorageError> {
    #[track_caller]
    fn to_not_found_response(
        self,
        not_found_response: errors::UserErrors,
    ) -> error_stack::Result<T, errors::UserErrors> {
        self.map_err(|e| {
            if e.current_context().is_db_not_found() {
                e.change_context(not_found_response)
            } else {
                e.change_context(errors::UserErrors::InternalServerError)
            }
        })
    }

    #[track_caller]
    fn to_duplicate_response(
        self,
        duplicate_response: errors::UserErrors,
    ) -> error_stack::Result<T, errors::UserErrors> {
        self.map_err(|e| {
            if e.current_context().is_db_unique_violation() {
                e.change_context(duplicate_response)
            } else {
                e.change_context(errors::UserErrors::InternalServerError)
            }
        })
    }
}
