// File: crates/hyperswitch_connectors/src/connectors/opennode/transformers.rs

use std::collections::HashMap;

use common_enums::{enums, AttemptStatus};
use common_utils::{request::Method, types::MinorUnit};
use hyperswitch_domain_models::{
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{PaymentsAuthorizeRequestData, RouterData as OtherRouterData},
};

#[derive(Debug, Serialize)]
pub struct OpennodeRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}

impl<T> TryFrom<(MinorUnit, T)> for OpennodeRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from((amount, router_data): (MinorUnit, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount,
            router_data,
        })
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct OpennodePaymentsRequest {
    amount: MinorUnit,
    currency: String,
    description: String,
    auto_settle: bool,
    success_url: String,
    callback_url: String,
    order_id: String,
}

impl TryFrom<&OpennodeRouterData<&PaymentsAuthorizeRouterData>> for OpennodePaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &OpennodeRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        get_crypto_specific_payment_data(item)
    }
}

//TODO: Fill the struct with respective fields
// Auth Struct
pub struct OpennodeAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for OpennodeAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}
// PaymentsResponse
//TODO: Append the remaining status flags
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum OpennodePaymentStatus {
    Unpaid,
    Paid,
    Expired,
    #[default]
    Processing,
    Underpaid,
    Refunded,
    #[serde(other)]
    Unknown,
}

impl From<OpennodePaymentStatus> for AttemptStatus {
    fn from(item: OpennodePaymentStatus) -> Self {
        match item {
            OpennodePaymentStatus::Unpaid => Self::AuthenticationPending,
            OpennodePaymentStatus::Paid => Self::Charged,
            OpennodePaymentStatus::Expired => Self::Failure,
            OpennodePaymentStatus::Underpaid => Self::Unresolved,
            _ => Self::Pending,
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct OpennodePaymentsResponseData {
    id: String,
    hosted_checkout_url: String,
    status: OpennodePaymentStatus,
    order_id: Option<String>,
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct OpennodePaymentsResponse {
    data: OpennodePaymentsResponseData,
}

impl<F, T> TryFrom<ResponseRouterData<F, OpennodePaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, OpennodePaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let form_fields = HashMap::new();
        let redirection_data = RedirectForm::Form {
            endpoint: item.response.data.hosted_checkout_url.to_string(),
            method: Method::Get,
            form_fields,
        };
        let connector_id = ResponseId::ConnectorTransactionId(item.response.data.id);
        let attempt_status = item.response.data.status;
        let response_data = if attempt_status != OpennodePaymentStatus::Underpaid {
            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: connector_id,
                redirection_data: Box::new(Some(redirection_data)),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: item.response.data.order_id,
                incremental_authorization_allowed: None,
                charges: None,
            })
        } else {
            Ok(PaymentsResponseData::TransactionUnresolvedResponse {
                resource_id: connector_id,
                reason: Some(api_models::enums::UnresolvedResponseReason {
                    code: "UNDERPAID".to_string(),
                    message:
                        "Please check the transaction in opennode dashboard and resolve manually"
                            .to_string(),
                }),
                connector_response_reference_id: item.response.data.order_id,
            })
        };
        Ok(Self {
            status: AttemptStatus::from(attempt_status),
            response: response_data,
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct OpennodeRefundRequest {
    pub amount: i64,
}

impl<F> TryFrom<&OpennodeRouterData<&RefundsRouterData<F>>> for OpennodeRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &OpennodeRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: item.router_data.request.refund_amount,
        })
    }
}

// Type definition for Refund Response

#[allow(dead_code)]
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
pub enum RefundStatus {
    Refunded,
    #[default]
    Processing,
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Refunded => Self::Success,
            RefundStatus::Processing => Self::Pending,
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {
    id: String,
    status: RefundStatus,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
#[derive(Debug, Deserialize, Serialize)]
pub struct OpennodeErrorResponse {
    pub message: String,
}

fn get_crypto_specific_payment_data(
    item: &OpennodeRouterData<&PaymentsAuthorizeRouterData>,
) -> Result<OpennodePaymentsRequest, error_stack::Report<errors::ConnectorError>> {
    let amount = item.amount;
    let currency = item.router_data.request.currency.to_string();
    let description = item.router_data.get_description()?;
    let auto_settle = true;
    let success_url = item.router_data.request.get_router_return_url()?;
    let callback_url = item.router_data.request.get_webhook_url()?;
    let order_id = item.router_data.connector_request_reference_id.clone();

    Ok(OpennodePaymentsRequest {
        amount,
        currency,
        description,
        auto_settle,
        success_url,
        callback_url,
        order_id,
    })
}

#[derive(Debug, Serialize, Deserialize)]
pub struct OpennodeWebhookDetails {
    pub id: String,
    pub callback_url: String,
    pub success_url: String,
    pub status: OpennodePaymentStatus,
    pub payment_method: String,
    pub missing_amt: String,
    pub order_id: String,
    pub description: String,
    pub price: String,
    pub fee: String,
    pub auto_settle: String,
    pub fiat_value: String,
    pub net_fiat_value: String,
    pub overpaid_by: String,
    pub hashed_order: String,
}


// File: crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs

use common_enums::enums;
use common_utils::types::StringMajorUnit;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        RefundsRouterData,
    },
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::CardData as _,
};

//TODO: Fill the struct with respective fields
pub struct FiservemeaRouterData<T> {
    pub amount: StringMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for FiservemeaRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        //Todo :  use utils to convert the amount to the type of amount that a connector accepts
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Debug, Serialize)]
pub struct FiservemeaTransactionAmount {
    total: StringMajorUnit,
    currency: common_enums::Currency,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaOrder {
    order_id: String,
}

#[derive(Debug, Serialize)]
pub enum FiservemeaRequestType {
    PaymentCardSaleTransaction,
    PaymentCardPreAuthTransaction,
    PostAuthTransaction,
    VoidPreAuthTransactions,
    ReturnTransaction,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaExpiryDate {
    month: Secret<String>,
    year: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentCard {
    number: cards::CardNumber,
    expiry_date: FiservemeaExpiryDate,
    security_code: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum FiservemeaPaymentMethods {
    PaymentCard(FiservemeaPaymentCard),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentsRequest {
    request_type: FiservemeaRequestType,
    merchant_transaction_id: String,
    transaction_amount: FiservemeaTransactionAmount,
    order: FiservemeaOrder,
    payment_method: FiservemeaPaymentMethods,
}

impl TryFrom<&FiservemeaRouterData<&PaymentsAuthorizeRouterData>> for FiservemeaPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &FiservemeaRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = FiservemeaPaymentCard {
                    number: req_card.card_number.clone(),
                    expiry_date: FiservemeaExpiryDate {
                        month: req_card.card_exp_month.clone(),
                        year: req_card.get_card_expiry_year_2_digit()?,
                    },
                    security_code: req_card.card_cvc,
                };
                let request_type = if matches!(
                    item.router_data.request.capture_method,
                    Some(enums::CaptureMethod::Automatic)
                        | Some(enums::CaptureMethod::SequentialAutomatic)
                ) {
                    FiservemeaRequestType::PaymentCardSaleTransaction
                } else {
                    FiservemeaRequestType::PaymentCardPreAuthTransaction
                };

                Ok(Self {
                    request_type,
                    merchant_transaction_id: item
                        .router_data
                        .request
                        .merchant_order_reference_id
                        .clone()
                        .unwrap_or(item.router_data.connector_request_reference_id.clone()),
                    transaction_amount: FiservemeaTransactionAmount {
                        total: item.amount.clone(),
                        currency: item.router_data.request.currency,
                    },
                    order: FiservemeaOrder {
                        order_id: item.router_data.connector_request_reference_id.clone(),
                    },
                    payment_method: FiservemeaPaymentMethods::PaymentCard(card),
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented(
                "Selected payment method through fiservemea".to_string(),
            )
            .into()),
        }
    }
}

// Auth Struct
#[derive(Clone)]
pub struct FiservemeaAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) secret_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for FiservemeaAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                api_key: api_key.to_owned(),
                secret_key: key1.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

// PaymentsResponse
#[derive(Debug, Serialize, Deserialize)]
pub enum ResponseType {
    BadRequest,
    Unauthenticated,
    Unauthorized,
    NotFound,
    GatewayDeclined,
    EndpointDeclined,
    ServerError,
    EndpointCommunicationError,
    UnsupportedMediaType,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FiservemeaTransactionType {
    Sale,
    Preauth,
    Credit,
    ForcedTicket,
    Void,
    Return,
    Postauth,
    PayerAuth,
    Disbursement,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservemeaTransactionOrigin {
    Ecom,
    Moto,
    Mail,
    Phone,
    Retail,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FiservemeaPaymentStatus {
    Approved,
    Waiting,
    Partial,
    ValidationFailed,
    ProcessingFailed,
    Declined,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FiservemeaPaymentResult {
    Approved,
    Declined,
    Failed,
    Waiting,
    Partial,
    Fraud,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentCardResponse {
    expiry_date: Option<FiservemeaExpiryDate>,
    bin: Option<String>,
    last4: Option<String>,
    brand: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentMethodDetails {
    payment_card: Option<FiservemeaPaymentCardResponse>,
    payment_method_type: Option<String>,
    payment_method_brand: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Components {
    subtotal: Option<f64>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AmountDetails {
    total: Option<f64>,
    currency: Option<common_enums::Currency>,
    components: Option<Components>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AvsResponse {
    street_match: Option<String>,
    postal_code_match: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Processor {
    reference_number: Option<String>,
    authorization_code: Option<String>,
    response_code: Option<String>,
    response_message: Option<String>,
    avs_response: Option<AvsResponse>,
    security_code_response: Option<String>,
}

fn map_status(
    fiservemea_status: Option<FiservemeaPaymentStatus>,
    fiservemea_result: Option<FiservemeaPaymentResult>,
    transaction_type: FiservemeaTransactionType,
) -> common_enums::AttemptStatus {
    match fiservemea_status {
        Some(status) => match status {
            FiservemeaPaymentStatus::Approved => match transaction_type {
                FiservemeaTransactionType::Preauth => common_enums::AttemptStatus::Authorized,
                FiservemeaTransactionType::Void => common_enums::AttemptStatus::Voided,
                FiservemeaTransactionType::Sale | FiservemeaTransactionType::Postauth => {
                    common_enums::AttemptStatus::Charged
                }
                FiservemeaTransactionType::Credit
                | FiservemeaTransactionType::ForcedTicket
                | FiservemeaTransactionType::Return
                | FiservemeaTransactionType::PayerAuth
                | FiservemeaTransactionType::Disbursement => common_enums::AttemptStatus::Failure,
            },
            FiservemeaPaymentStatus::Waiting => common_enums::AttemptStatus::Pending,
            FiservemeaPaymentStatus::Partial => common_enums::AttemptStatus::PartialCharged,
            FiservemeaPaymentStatus::ValidationFailed
            | FiservemeaPaymentStatus::ProcessingFailed
            | FiservemeaPaymentStatus::Declined => common_enums::AttemptStatus::Failure,
        },
        None => match fiservemea_result {
            Some(result) => match result {
                FiservemeaPaymentResult::Approved => match transaction_type {
                    FiservemeaTransactionType::Preauth => common_enums::AttemptStatus::Authorized,
                    FiservemeaTransactionType::Void => common_enums::AttemptStatus::Voided,
                    FiservemeaTransactionType::Sale | FiservemeaTransactionType::Postauth => {
                        common_enums::AttemptStatus::Charged
                    }
                    FiservemeaTransactionType::Credit
                    | FiservemeaTransactionType::ForcedTicket
                    | FiservemeaTransactionType::Return
                    | FiservemeaTransactionType::PayerAuth
                    | FiservemeaTransactionType::Disbursement => {
                        common_enums::AttemptStatus::Failure
                    }
                },
                FiservemeaPaymentResult::Waiting => common_enums::AttemptStatus::Pending,
                FiservemeaPaymentResult::Partial => common_enums::AttemptStatus::PartialCharged,
                FiservemeaPaymentResult::Declined
                | FiservemeaPaymentResult::Failed
                | FiservemeaPaymentResult::Fraud => common_enums::AttemptStatus::Failure,
            },
            None => common_enums::AttemptStatus::Pending,
        },
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentsResponse {
    response_type: Option<ResponseType>,
    #[serde(rename = "type")]
    fiservemea_type: Option<String>,
    client_request_id: Option<String>,
    api_trace_id: Option<String>,
    ipg_transaction_id: String,
    order_id: Option<String>,
    transaction_type: FiservemeaTransactionType,
    transaction_origin: Option<FiservemeaTransactionOrigin>,
    payment_method_details: Option<FiservemeaPaymentMethodDetails>,
    country: Option<Secret<String>>,
    terminal_id: Option<String>,
    merchant_id: Option<String>,
    merchant_transaction_id: Option<String>,
    transaction_time: Option<i64>,
    approved_amount: Option<AmountDetails>,
    transaction_amount: Option<AmountDetails>,
    transaction_status: Option<FiservemeaPaymentStatus>, // FiservEMEA Docs mention that this field is deprecated. We are using it for now because transaction_result is not present in the response.
    transaction_result: Option<FiservemeaPaymentResult>,
    approval_code: Option<String>,
    error_message: Option<String>,
    transaction_state: Option<String>,
    scheme_transaction_id: Option<String>,
    processor: Option<Processor>,
}

impl<F, T> TryFrom<ResponseRouterData<F, FiservemeaPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FiservemeaPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: map_status(
                item.response.transaction_status,
                item.response.transaction_result,
                item.response.transaction_type,
            ),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.ipg_transaction_id),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: item.response.order_id,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaCaptureRequest {
    request_type: FiservemeaRequestType,
    transaction_amount: FiservemeaTransactionAmount,
}

impl TryFrom<&FiservemeaRouterData<&PaymentsCaptureRouterData>> for FiservemeaCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &FiservemeaRouterData<&PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            request_type: FiservemeaRequestType::PostAuthTransaction,
            transaction_amount: FiservemeaTransactionAmount {
                total: item.amount.clone(),
                currency: item.router_data.request.currency,
            },
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaVoidRequest {
    request_type: FiservemeaRequestType,
}

impl TryFrom<&PaymentsCancelRouterData> for FiservemeaVoidRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(_item: &PaymentsCancelRouterData) -> Result<Self, Self::Error> {
        Ok(Self {
            request_type: FiservemeaRequestType::VoidPreAuthTransactions,
        })
    }
}

// REFUND :
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaRefundRequest {
    request_type: FiservemeaRequestType,
    transaction_amount: FiservemeaTransactionAmount,
}

impl<F> TryFrom<&FiservemeaRouterData<&RefundsRouterData<F>>> for FiservemeaRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &FiservemeaRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        Ok(Self {
            request_type: FiservemeaRequestType::ReturnTransaction,
            transaction_amount: FiservemeaTransactionAmount {
                total: item.amount.clone(),
                currency: item.router_data.request.currency,
            },
        })
    }
}

fn map_refund_status(
    fiservemea_status: Option<FiservemeaPaymentStatus>,
    fiservemea_result: Option<FiservemeaPaymentResult>,
) -> Result<enums::RefundStatus, errors::ConnectorError> {
    match fiservemea_status {
        Some(status) => match status {
            FiservemeaPaymentStatus::Approved => Ok(enums::RefundStatus::Success),
            FiservemeaPaymentStatus::Partial | FiservemeaPaymentStatus::Waiting => {
                Ok(enums::RefundStatus::Pending)
            }
            FiservemeaPaymentStatus::ValidationFailed
            | FiservemeaPaymentStatus::ProcessingFailed
            | FiservemeaPaymentStatus::Declined => Ok(enums::RefundStatus::Failure),
        },
        None => match fiservemea_result {
            Some(result) => match result {
                FiservemeaPaymentResult::Approved => Ok(enums::RefundStatus::Success),
                FiservemeaPaymentResult::Partial | FiservemeaPaymentResult::Waiting => {
                    Ok(enums::RefundStatus::Pending)
                }
                FiservemeaPaymentResult::Declined
                | FiservemeaPaymentResult::Failed
                | FiservemeaPaymentResult::Fraud => Ok(enums::RefundStatus::Failure),
            },
            None => Err(errors::ConnectorError::MissingRequiredField {
                field_name: "transactionResult",
            }),
        },
    }
}

impl TryFrom<RefundsResponseRouterData<Execute, FiservemeaPaymentsResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, FiservemeaPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.ipg_transaction_id,
                refund_status: map_refund_status(
                    item.response.transaction_status,
                    item.response.transaction_result,
                )?,
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, FiservemeaPaymentsResponse>>
    for RefundsRouterData<RSync>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, FiservemeaPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.ipg_transaction_id,
                refund_status: map_refund_status(
                    item.response.transaction_status,
                    item.response.transaction_result,
                )?,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ErrorDetails {
    pub field: Option<String>,
    pub message: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FiservemeaError {
    pub code: Option<String>,
    pub message: Option<String>,
    pub details: Option<Vec<ErrorDetails>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FiservemeaErrorResponse {
    #[serde(rename = "type")]
    fiservemea_type: Option<String>,
    client_request_id: Option<String>,
    api_trace_id: Option<String>,
    pub response_type: Option<String>,
    pub error: Option<FiservemeaError>,
}


// File: crates/hyperswitch_connectors/src/connectors/boku/transformers.rs

use std::fmt;

use common_enums::enums;
use common_utils::{request::Method, types::MinorUnit};
use hyperswitch_domain_models::{
    payment_method_data::{PaymentMethodData, WalletData},
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types::{self, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};
use url::Url;
use uuid::Uuid;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{self, AddressDetailsData, RouterData as _},
};

#[derive(Debug, Serialize)]
pub struct BokuRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}

impl<T> From<(MinorUnit, T)> for BokuRouterData<T> {
    fn from((amount, router_data): (MinorUnit, T)) -> Self {
        Self {
            amount,
            router_data,
        }
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum BokuPaymentsRequest {
    BeginSingleCharge(SingleChargeData),
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct SingleChargeData {
    total_amount: MinorUnit,
    currency: String,
    country: String,
    merchant_id: Secret<String>,
    merchant_transaction_id: Secret<String>,
    merchant_request_id: String,
    merchant_item_description: String,
    notification_url: Option<String>,
    payment_method: String,
    charge_type: String,
    hosted: Option<BokuHostedData>,
}

#[derive(Debug, Clone, Serialize)]
pub enum BokuPaymentType {
    Dana,
    Momo,
    Gcash,
    GoPay,
    Kakaopay,
}

impl fmt::Display for BokuPaymentType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Dana => write!(f, "Dana"),
            Self::Momo => write!(f, "Momo"),
            Self::Gcash => write!(f, "Gcash"),
            Self::GoPay => write!(f, "GoPay"),
            Self::Kakaopay => write!(f, "Kakaopay"),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub enum BokuChargeType {
    Hosted,
}

impl fmt::Display for BokuChargeType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Hosted => write!(f, "hosted"),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "kebab-case")]
struct BokuHostedData {
    forward_url: String,
}

impl TryFrom<&BokuRouterData<&types::PaymentsAuthorizeRouterData>> for BokuPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &BokuRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Wallet(wallet_data) => Self::try_from((item, &wallet_data)),
            PaymentMethodData::Card(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("boku"),
                ))?
            }
        }
    }
}

impl
    TryFrom<(
        &BokuRouterData<&types::PaymentsAuthorizeRouterData>,
        &WalletData,
    )> for BokuPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        value: (
            &BokuRouterData<&types::PaymentsAuthorizeRouterData>,
            &WalletData,
        ),
    ) -> Result<Self, Self::Error> {
        let (item_router_data, wallet_data) = value;
        let item = item_router_data.router_data;
        let address = item.get_billing_address()?;
        let country = address.get_country()?.to_string();
        let payment_method = get_wallet_type(wallet_data)?;
        let hosted = get_hosted_data(item);
        let auth_type = BokuAuthType::try_from(&item.connector_auth_type)?;
        let merchant_item_description = item.get_description()?;
        let payment_data = SingleChargeData {
            total_amount: item_router_data.amount,
            currency: item.request.currency.to_string(),
            country,
            merchant_id: auth_type.merchant_id,
            merchant_transaction_id: Secret::new(item.payment_id.to_string()),
            merchant_request_id: Uuid::new_v4().to_string(),
            merchant_item_description,
            notification_url: item.request.webhook_url.clone(),
            payment_method,
            charge_type: BokuChargeType::Hosted.to_string(),
            hosted,
        };

        Ok(Self::BeginSingleCharge(payment_data))
    }
}

fn get_wallet_type(wallet_data: &WalletData) -> Result<String, errors::ConnectorError> {
    match wallet_data {
        WalletData::DanaRedirect { .. } => Ok(BokuPaymentType::Dana.to_string()),
        WalletData::MomoRedirect { .. } => Ok(BokuPaymentType::Momo.to_string()),
        WalletData::GcashRedirect { .. } => Ok(BokuPaymentType::Gcash.to_string()),
        WalletData::GoPayRedirect { .. } => Ok(BokuPaymentType::GoPay.to_string()),
        WalletData::KakaoPayRedirect { .. } => Ok(BokuPaymentType::Kakaopay.to_string()),
        WalletData::AliPayQr(_)
        | WalletData::AliPayRedirect(_)
        | WalletData::AliPayHkRedirect(_)
        | WalletData::AmazonPay(_)
        | WalletData::AmazonPayRedirect(_)
        | WalletData::Paysera(_)
        | WalletData::Skrill(_)
        | WalletData::BluecodeRedirect {}
        | WalletData::ApplePay(_)
        | WalletData::ApplePayRedirect(_)
        | WalletData::ApplePayThirdPartySdk(_)
        | WalletData::GooglePay(_)
        | WalletData::GooglePayRedirect(_)
        | WalletData::GooglePayThirdPartySdk(_)
        | WalletData::MbWayRedirect(_)
        | WalletData::MobilePayRedirect(_)
        | WalletData::PaypalRedirect(_)
        | WalletData::PaypalSdk(_)
        | WalletData::Paze(_)
        | WalletData::SamsungPay(_)
        | WalletData::TwintRedirect {}
        | WalletData::VippsRedirect {}
        | WalletData::TouchNGoRedirect(_)
        | WalletData::WeChatPayRedirect(_)
        | WalletData::WeChatPayQr(_)
        | WalletData::CashappQr(_)
        | WalletData::SwishQr(_)
        | WalletData::Mifinity(_)
        | WalletData::RevolutPay(_) => Err(errors::ConnectorError::NotImplemented(
            utils::get_unimplemented_payment_method_error_message("boku"),
        )),
    }
}

pub struct BokuAuthType {
    pub(super) merchant_id: Secret<String>,
    pub(super) key_id: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for BokuAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                merchant_id: key1.to_owned(),
                key_id: api_key.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename = "query-charge-request")]
#[serde(rename_all = "kebab-case")]
pub struct BokuPsyncRequest {
    country: String,
    merchant_id: Secret<String>,
    merchant_transaction_id: Secret<String>,
}

impl TryFrom<&types::PaymentsSyncRouterData> for BokuPsyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::PaymentsSyncRouterData) -> Result<Self, Self::Error> {
        let address = item.get_billing_address()?;
        let country = address.get_country()?.to_string();
        let auth_type = BokuAuthType::try_from(&item.connector_auth_type)?;

        Ok(Self {
            country,
            merchant_id: auth_type.merchant_id,
            merchant_transaction_id: Secret::new(item.payment_id.to_string()),
        })
    }
}

// Connector Meta Data
#[derive(Debug, Clone, Deserialize)]
pub struct BokuMetaData {
    pub(super) country: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum BokuResponse {
    BeginSingleChargeResponse(BokuPaymentsResponse),
    QueryChargeResponse(BokuPsyncResponse),
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct BokuPaymentsResponse {
    charge_status: String, // xml parse only string to fields
    charge_id: String,
    hosted: Option<HostedUrlResponse>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct HostedUrlResponse {
    redirect_url: Option<Url>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename = "query-charge-response")]
#[serde(rename_all = "kebab-case")]
pub struct BokuPsyncResponse {
    charges: ChargeResponseData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct ChargeResponseData {
    charge: SingleChargeResponseData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct SingleChargeResponseData {
    charge_status: String,
    charge_id: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, BokuResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, BokuResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (status, transaction_id, redirection_data) = match item.response {
            BokuResponse::BeginSingleChargeResponse(response) => get_authorize_response(response),
            BokuResponse::QueryChargeResponse(response) => get_psync_response(response),
        }?;

        Ok(Self {
            status,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(transaction_id),
                redirection_data: Box::new(redirection_data),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

fn get_response_status(status: String) -> enums::AttemptStatus {
    match status.as_str() {
        "Success" => enums::AttemptStatus::Charged,
        "Failure" => enums::AttemptStatus::Failure,
        _ => enums::AttemptStatus::Pending,
    }
}

fn get_authorize_response(
    response: BokuPaymentsResponse,
) -> Result<(enums::AttemptStatus, String, Option<RedirectForm>), errors::ConnectorError> {
    let status = get_response_status(response.charge_status);
    let redirection_data = match response.hosted {
        Some(hosted_value) => Ok(hosted_value
            .redirect_url
            .map(|url| RedirectForm::from((url, Method::Get)))),
        None => Err(errors::ConnectorError::MissingConnectorRedirectionPayload {
            field_name: "redirect_url",
        }),
    }?;

    Ok((status, response.charge_id, redirection_data))
}

fn get_psync_response(
    response: BokuPsyncResponse,
) -> Result<(enums::AttemptStatus, String, Option<RedirectForm>), errors::ConnectorError> {
    let status = get_response_status(response.charges.charge.charge_status);

    Ok((status, response.charges.charge.charge_id, None))
}

// REFUND :
#[derive(Debug, Clone, Serialize)]
#[serde(rename = "refund-charge-request")]
pub struct BokuRefundRequest {
    refund_amount: MinorUnit,
    merchant_id: Secret<String>,
    merchant_request_id: String,
    merchant_refund_id: Secret<String>,
    charge_id: String,
    reason_code: String,
}

#[derive(Debug, Clone, Serialize)]
pub enum BokuRefundReasonCode {
    NonFulfillment,
}

impl fmt::Display for BokuRefundReasonCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::NonFulfillment => write!(f, "8"),
        }
    }
}

impl<F> TryFrom<&BokuRouterData<&RefundsRouterData<F>>> for BokuRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &BokuRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let auth_type = BokuAuthType::try_from(&item.router_data.connector_auth_type)?;
        let payment_data = Self {
            refund_amount: item.amount,
            merchant_id: auth_type.merchant_id,
            merchant_refund_id: Secret::new(item.router_data.request.refund_id.to_string()),
            merchant_request_id: Uuid::new_v4().to_string(),
            charge_id: item
                .router_data
                .request
                .connector_transaction_id
                .to_string(),
            reason_code: BokuRefundReasonCode::NonFulfillment.to_string(),
        };

        Ok(payment_data)
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename = "refund-charge-response")]
pub struct RefundResponse {
    charge_id: String,
    refund_status: String,
}

fn get_refund_status(status: String) -> enums::RefundStatus {
    match status.as_str() {
        "Success" => enums::RefundStatus::Success,
        "Failure" => enums::RefundStatus::Failure,
        _ => enums::RefundStatus::Pending,
    }
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.charge_id,
                refund_status: get_refund_status(item.response.refund_status),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename = "query-refund-request")]
#[serde(rename_all = "kebab-case")]
pub struct BokuRsyncRequest {
    country: String,
    merchant_id: Secret<String>,
    merchant_transaction_id: Secret<String>,
}

impl TryFrom<&types::RefundSyncRouterData> for BokuRsyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::RefundSyncRouterData) -> Result<Self, Self::Error> {
        let address = item.get_billing_address()?;
        let country = address.get_country()?.to_string();
        let auth_type = BokuAuthType::try_from(&item.connector_auth_type)?;

        Ok(Self {
            country,
            merchant_id: auth_type.merchant_id,
            merchant_transaction_id: Secret::new(item.payment_id.to_string()),
        })
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename = "query-refund-response")]
#[serde(rename_all = "kebab-case")]
pub struct BokuRsyncResponse {
    refunds: RefundResponseData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct RefundResponseData {
    refund: SingleRefundResponseData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct SingleRefundResponseData {
    refund_status: String, // quick-xml only parse string as a field
    refund_id: String,
}

impl TryFrom<RefundsResponseRouterData<RSync, BokuRsyncResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, BokuRsyncResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.refunds.refund.refund_id,
                refund_status: get_refund_status(item.response.refunds.refund.refund_status),
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct BokuErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}

fn get_hosted_data(item: &types::PaymentsAuthorizeRouterData) -> Option<BokuHostedData> {
    item.request
        .router_return_url
        .clone()
        .map(|url| BokuHostedData { forward_url: url })
}


// File: crates/hyperswitch_connectors/src/connectors/coinbase/transformers.rs

use std::collections::HashMap;

use common_enums::enums;
use common_utils::{pii, request::Method, types::StringMajorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm},
    types::{self, PaymentsAuthorizeRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        self, AddressDetailsData, PaymentsAuthorizeRequestData, RouterData as OtherRouterData,
    },
};

#[derive(Debug, Serialize)]
pub struct CoinbaseRouterData<T> {
    amount: StringMajorUnit,
    router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for CoinbaseRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Debug, Default, Eq, PartialEq, Serialize)]
pub struct LocalPrice {
    pub amount: StringMajorUnit,
    pub currency: String,
}

#[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Metadata {
    pub customer_id: Option<String>,
    pub customer_name: Option<Secret<String>>,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct CoinbasePaymentsRequest {
    pub name: Option<Secret<String>>,
    pub description: Option<String>,
    pub pricing_type: String,
    pub local_price: LocalPrice,
    pub redirect_url: String,
    pub cancel_url: String,
}

impl TryFrom<&CoinbaseRouterData<&PaymentsAuthorizeRouterData>> for CoinbasePaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &CoinbaseRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        get_crypto_specific_payment_data(item)
    }
}

// Auth Struct
pub struct CoinbaseAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for CoinbaseAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(_auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        if let ConnectorAuthType::HeaderKey { api_key } = _auth_type {
            Ok(Self {
                api_key: api_key.to_owned(),
            })
        } else {
            Err(errors::ConnectorError::FailedToObtainAuthType.into())
        }
    }
}
// PaymentsResponse
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum CoinbasePaymentStatus {
    New,
    #[default]
    Pending,
    Completed,
    Expired,
    Unresolved,
    Resolved,
    Canceled,
    #[serde(rename = "PENDING REFUND")]
    PendingRefund,
    Refunded,
}

impl From<CoinbasePaymentStatus> for enums::AttemptStatus {
    fn from(item: CoinbasePaymentStatus) -> Self {
        match item {
            CoinbasePaymentStatus::Completed | CoinbasePaymentStatus::Resolved => Self::Charged,
            CoinbasePaymentStatus::Expired => Self::Failure,
            CoinbasePaymentStatus::New => Self::AuthenticationPending,
            CoinbasePaymentStatus::Unresolved => Self::Unresolved,
            CoinbasePaymentStatus::Canceled => Self::Voided,
            _ => Self::Pending,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, strum::Display)]
#[serde(rename_all = "UPPERCASE")]
#[strum(serialize_all = "UPPERCASE")]
pub enum UnResolvedContext {
    Underpaid,
    Overpaid,
    Delayed,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Timeline {
    status: CoinbasePaymentStatus,
    context: Option<UnResolvedContext>,
    time: String,
    pub payment: Option<TimelinePayment>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct CoinbasePaymentsResponse {
    // status: CoinbasePaymentStatus,
    // id: String,
    data: CoinbasePaymentResponseData,
}

impl<F, T> TryFrom<ResponseRouterData<F, CoinbasePaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, CoinbasePaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let form_fields = HashMap::new();
        let redirection_data = RedirectForm::Form {
            endpoint: item.response.data.hosted_url.to_string(),
            method: Method::Get,
            form_fields,
        };
        let timeline = item
            .response
            .data
            .timeline
            .last()
            .ok_or(errors::ConnectorError::ResponseHandlingFailed)?
            .clone();
        let connector_id = ResponseId::ConnectorTransactionId(item.response.data.id.clone());
        let attempt_status = timeline.status.clone();
        let response_data = timeline.context.map_or(
            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: connector_id.clone(),
                redirection_data: Box::new(Some(redirection_data)),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.data.id.clone()),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            |context| {
                Ok(PaymentsResponseData::TransactionUnresolvedResponse{
                resource_id: connector_id,
                reason: Some(api_models::enums::UnresolvedResponseReason {
                code: context.to_string(),
                message: "Please check the transaction in coinbase dashboard and resolve manually"
                    .to_string(),
                }),
                connector_response_reference_id: Some(item.response.data.id),
            })
            },
        );
        Ok(Self {
            status: enums::AttemptStatus::from(attempt_status),
            response: response_data,
            ..item.data
        })
    }
}

// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct CoinbaseRefundRequest {}

impl<F> TryFrom<&types::RefundsRouterData<F>> for CoinbaseRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(_item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
        Err(errors::ConnectorError::NotImplemented("try_from RefundsRouterData".to_string()).into())
    }
}

// Type definition for Refund Response

#[allow(dead_code)]
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
pub enum RefundStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Succeeded => Self::Success,
            RefundStatus::Failed => Self::Failure,
            RefundStatus::Processing => Self::Pending,
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>>
    for types::RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        _item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Err(errors::ConnectorError::NotImplemented(
            "try_from RefundsResponseRouterData".to_string(),
        )
        .into())
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for types::RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        _item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Err(errors::ConnectorError::NotImplemented(
            "try_from RefundsResponseRouterData".to_string(),
        )
        .into())
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct CoinbaseErrorData {
    #[serde(rename = "type")]
    pub error_type: String,
    pub message: String,
    pub code: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct CoinbaseErrorResponse {
    pub error: CoinbaseErrorData,
}

#[derive(Default, Debug, Deserialize, PartialEq)]
pub struct CoinbaseConnectorMeta {
    pub pricing_type: String,
}

impl TryFrom<&Option<pii::SecretSerdeValue>> for CoinbaseConnectorMeta {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(meta_data: &Option<pii::SecretSerdeValue>) -> Result<Self, Self::Error> {
        utils::to_connector_meta_from_secret(meta_data.clone())
            .change_context(errors::ConnectorError::InvalidConnectorConfig { config: "metadata" })
    }
}

fn get_crypto_specific_payment_data(
    item: &CoinbaseRouterData<&PaymentsAuthorizeRouterData>,
) -> Result<CoinbasePaymentsRequest, error_stack::Report<errors::ConnectorError>> {
    let billing_address = item
        .router_data
        .get_billing()
        .ok()
        .and_then(|billing_address| billing_address.address.as_ref());
    let name =
        billing_address.and_then(|add| add.get_first_name().ok().map(|name| name.to_owned()));
    let description = item.router_data.get_description().ok();
    let connector_meta = CoinbaseConnectorMeta::try_from(&item.router_data.connector_meta_data)
        .change_context(errors::ConnectorError::InvalidConnectorConfig {
            config: "Merchant connector account metadata",
        })?;
    let pricing_type = connector_meta.pricing_type;
    let local_price = get_local_price(item);
    let redirect_url = item.router_data.request.get_router_return_url()?;
    let cancel_url = item.router_data.request.get_router_return_url()?;

    Ok(CoinbasePaymentsRequest {
        name,
        description,
        pricing_type,
        local_price,
        redirect_url,
        cancel_url,
    })
}

fn get_local_price(item: &CoinbaseRouterData<&PaymentsAuthorizeRouterData>) -> LocalPrice {
    LocalPrice {
        amount: item.amount.clone(),
        currency: item.router_data.request.currency.to_string(),
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CoinbaseWebhookDetails {
    pub attempt_number: i64,
    pub event: Event,
    pub id: String,
    pub scheduled_for: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Event {
    pub api_version: String,
    pub created_at: String,
    pub data: CoinbasePaymentResponseData,
    pub id: String,
    pub resource: String,
    #[serde(rename = "type")]
    pub event_type: WebhookEventType,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum WebhookEventType {
    #[serde(rename = "charge:confirmed")]
    Confirmed,
    #[serde(rename = "charge:created")]
    Created,
    #[serde(rename = "charge:pending")]
    Pending,
    #[serde(rename = "charge:failed")]
    Failed,
    #[serde(rename = "charge:resolved")]
    Resolved,
    #[serde(other)]
    Unknown,
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Redirects {
    cancel_url: Option<String>,
    success_url: Option<String>,
    will_redirect_after_success: bool,
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CoinbasePaymentResponseData {
    pub id: String,
    pub code: String,
    pub name: Option<Secret<String>>,
    pub utxo: Option<bool>,
    pub pricing: HashMap<String, OverpaymentAbsoluteThreshold>,
    pub fee_rate: Option<f64>,
    pub logo_url: Option<String>,
    pub metadata: Option<Metadata>,
    pub payments: Vec<PaymentElement>,
    pub resource: Option<String>,
    pub timeline: Vec<Timeline>,
    pub pwcb_only: bool,
    pub created_at: String,
    pub expires_at: String,
    pub hosted_url: String,
    pub brand_color: String,
    pub description: Option<String>,
    pub confirmed_at: Option<String>,
    pub fees_settled: Option<bool>,
    pub pricing_type: String,
    pub redirects: Redirects,
    pub support_email: pii::Email,
    pub brand_logo_url: String,
    pub offchain_eligible: Option<bool>,
    pub organization_name: String,
    pub payment_threshold: Option<PaymentThreshold>,
    pub coinbase_managed_merchant: Option<bool>,
}

#[derive(Debug, Serialize, Default, Deserialize)]
pub struct PaymentThreshold {
    pub overpayment_absolute_threshold: OverpaymentAbsoluteThreshold,
    pub overpayment_relative_threshold: String,
    pub underpayment_absolute_threshold: OverpaymentAbsoluteThreshold,
    pub underpayment_relative_threshold: String,
}

#[derive(Debug, Clone, Serialize, Default, Deserialize, PartialEq, Eq)]
pub struct OverpaymentAbsoluteThreshold {
    pub amount: StringMajorUnit,
    pub currency: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PaymentElement {
    pub net: CoinbaseProcessingFee,
    pub block: Block,
    pub value: CoinbaseProcessingFee,
    pub status: String,
    pub network: String,
    pub deposited: Deposited,
    pub payment_id: String,
    pub detected_at: String,
    pub transaction_id: String,
    pub coinbase_processing_fee: CoinbaseProcessingFee,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Block {
    pub hash: Option<String>,
    pub height: Option<i64>,
    pub confirmations: Option<i64>,
    pub confirmations_required: Option<i64>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CoinbaseProcessingFee {
    pub local: Option<OverpaymentAbsoluteThreshold>,
    pub crypto: OverpaymentAbsoluteThreshold,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Deposited {
    pub amount: Amount,
    pub status: String,
    pub destination: String,
    pub exchange_rate: Option<serde_json::Value>,
    pub autoconversion_status: String,
    pub autoconversion_enabled: bool,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Amount {
    pub net: CoinbaseProcessingFee,
    pub gross: CoinbaseProcessingFee,
    pub coinbase_fee: CoinbaseProcessingFee,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct TimelinePayment {
    pub value: OverpaymentAbsoluteThreshold,
    pub network: String,
    pub transaction_id: String,
}


// File: crates/hyperswitch_connectors/src/connectors/zsl/transformers.rs

use std::collections::HashMap;

use base64::Engine;
use common_enums::enums;
use common_utils::{crypto::GenerateDigest, date_time, pii::Email, request::Method};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankTransferData, PaymentMethodData},
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_request_types::{PaymentsSyncData, ResponseId},
    router_response_types::{PaymentsResponseData, RedirectForm},
    types,
};
use hyperswitch_interfaces::{api, consts::NO_ERROR_CODE, errors};
use masking::{ExposeInterface, Secret};
use ring::digest;
use serde::{Deserialize, Serialize};

use crate::{
    types::ResponseRouterData,
    utils::{
        get_amount_as_string, get_unimplemented_payment_method_error_message,
        PaymentsAuthorizeRequestData, RouterData as _,
    },
};

mod auth_error {
    pub const INVALID_SIGNATURE: &str = "INVALID_SIGNATURE";
}
mod zsl_version {
    pub const VERSION_1: &str = "1";
}

pub struct ZslRouterData<T> {
    pub amount: String,
    pub router_data: T,
}

impl<T> TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for ZslRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (currency_unit, currency, txn_amount, item): (&api::CurrencyUnit, enums::Currency, i64, T),
    ) -> Result<Self, Self::Error> {
        let amount = get_amount_as_string(currency_unit, txn_amount, currency)?;
        Ok(Self {
            amount,
            router_data: item,
        })
    }
}

pub struct ZslAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) merchant_id: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for ZslAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                api_key: api_key.to_owned(),
                merchant_id: key1.clone(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZslPaymentsRequest {
    process_type: ProcessType,
    process_code: ProcessCode,
    txn_amt: String,
    ccy: api_models::enums::Currency,
    mer_ref: String,
    mer_txn_date: String,
    mer_id: Secret<String>,
    lang: String,
    success_url: String,
    failure_url: String,
    success_s2s_url: String,
    failure_s2s_url: String,
    enctype: EncodingType,
    signature: Secret<String>,
    country: api_models::enums::CountryAlpha2,
    verno: String,
    service_code: ServiceCode,
    cust_tag: String,
    #[serde(flatten)]
    payment_method: ZslPaymentMethods,
    name: Option<Secret<String>>,
    family_name: Option<Secret<String>>,
    tel_phone: Option<Secret<String>>,
    email: Option<Email>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ZslPaymentMethods {
    LocalBankTransfer(LocalBankTransaferRequest),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocalBankTransaferRequest {
    bank_code: Option<String>,
    pay_method: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ProcessType {
    #[serde(rename = "0200")]
    PaymentRequest,
    #[serde(rename = "0208")]
    PaymentResponse,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ProcessCode {
    #[serde(rename = "200002")]
    API,
    #[serde(rename = "200003")]
    CallBack,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EncodingType {
    #[serde(rename = "1")]
    MD5,
    #[serde(rename = "2")]
    Sha1,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum ServiceCode {
    MPG,
}

impl TryFrom<&ZslRouterData<&types::PaymentsAuthorizeRouterData>> for ZslPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &ZslRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let payment_method = match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::BankTransfer(bank_transfer_data) => match *bank_transfer_data {
                BankTransferData::LocalBankTransfer { bank_code } => Ok(
                    ZslPaymentMethods::LocalBankTransfer(LocalBankTransaferRequest {
                        bank_code,
                        pay_method: None,
                    }),
                ),
                BankTransferData::AchBankTransfer { .. }
                | BankTransferData::SepaBankTransfer { .. }
                | BankTransferData::BacsBankTransfer { .. }
                | BankTransferData::MultibancoBankTransfer { .. }
                | BankTransferData::PermataBankTransfer { .. }
                | BankTransferData::BcaBankTransfer { .. }
                | BankTransferData::BniVaBankTransfer { .. }
                | BankTransferData::BriVaBankTransfer { .. }
                | BankTransferData::CimbVaBankTransfer { .. }
                | BankTransferData::DanamonVaBankTransfer { .. }
                | BankTransferData::MandiriVaBankTransfer { .. }
                | BankTransferData::Pix { .. }
                | BankTransferData::Pse {}
                | BankTransferData::InstantBankTransferFinland {}
                | BankTransferData::InstantBankTransferPoland {}
                | BankTransferData::IndonesianBankTransfer { .. }
                | BankTransferData::InstantBankTransfer {} => {
                    Err(errors::ConnectorError::NotImplemented(
                        get_unimplemented_payment_method_error_message(
                            item.router_data.connector.as_str(),
                        ),
                    ))
                }
            },
            PaymentMethodData::Card(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_)
            | PaymentMethodData::OpenBanking(_) => Err(errors::ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message(item.router_data.connector.as_str()),
            )),
        }?;
        let auth_type = ZslAuthType::try_from(&item.router_data.connector_auth_type)?;
        let key: Secret<String> = auth_type.api_key;
        let mer_id = auth_type.merchant_id;
        let mer_txn_date =
            date_time::format_date(date_time::now(), date_time::DateFormat::YYYYMMDDHHmmss)
                .change_context(errors::ConnectorError::RequestEncodingFailed)?;
        let txn_amt = item.amount.clone();
        let ccy = item.router_data.request.currency;
        let mer_ref = item.router_data.connector_request_reference_id.clone();
        let signature = calculate_signature(
            EncodingType::MD5,
            ZslSignatureType::RequestSignature {
                txn_amt: txn_amt.clone(),
                ccy: ccy.to_string(),
                mer_ref: mer_ref.clone(),
                mer_id: mer_id.clone().expose(),
                mer_txn_date: mer_txn_date.clone(),
                key: key.expose(),
            },
        )?;
        let tel_phone = item.router_data.get_optional_billing_phone_number();
        let email = item.router_data.get_optional_billing_email();
        let name = item.router_data.get_optional_billing_first_name();
        let family_name = item.router_data.get_optional_billing_last_name();
        let router_url = item.router_data.request.get_router_return_url()?;
        let webhook_url = item.router_data.request.get_webhook_url()?;
        let billing_country = item.router_data.get_billing_country()?;

        let lang = item
            .router_data
            .request
            .browser_info
            .as_ref()
            .and_then(|browser_data| {
                browser_data.language.as_ref().map(|language| {
                    language
                        .split_once('-')
                        .map_or(language.to_uppercase(), |(lang, _)| lang.to_uppercase())
                })
            })
            .ok_or(errors::ConnectorError::MissingRequiredField {
                field_name: "browser_info.language",
            })?;

        let cust_tag = item
            .router_data
            .customer_id
            .clone()
            .and_then(|customer_id| {
                let cust_id = customer_id.get_string_repr().replace(['_', '-'], "");
                let id_len = cust_id.len();
                if id_len > 10 {
                    cust_id.get(id_len - 10..id_len).map(|id| id.to_string())
                } else {
                    Some(cust_id)
                }
            })
            .ok_or(errors::ConnectorError::MissingRequiredField {
                field_name: "customer_id",
            })?;

        Ok(Self {
            process_type: ProcessType::PaymentRequest,
            process_code: ProcessCode::API,
            txn_amt,
            ccy,
            mer_ref,
            mer_txn_date,
            mer_id,
            lang,
            success_url: router_url.clone(),
            failure_url: router_url.clone(),
            success_s2s_url: webhook_url.clone(),
            failure_s2s_url: webhook_url.clone(),
            enctype: EncodingType::MD5,
            signature,
            verno: zsl_version::VERSION_1.to_owned(),
            service_code: ServiceCode::MPG,
            country: billing_country,
            payment_method,
            name,
            family_name,
            tel_phone,
            email,
            cust_tag,
        })
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZslPaymentsResponse {
    process_type: ProcessType,
    process_code: ProcessCode,
    status: String,
    mer_ref: String,
    mer_id: String,
    enctype: EncodingType,
    txn_url: String,
    signature: Secret<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, ZslPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, ZslPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        if item.response.status.eq("0") && !item.response.txn_url.is_empty() {
            let auth_type = ZslAuthType::try_from(&item.data.connector_auth_type)?;
            let key: Secret<String> = auth_type.api_key;
            let mer_id = auth_type.merchant_id;
            let calculated_signature = calculate_signature(
                item.response.enctype,
                ZslSignatureType::ResponseSignature {
                    status: item.response.status.clone(),
                    txn_url: item.response.txn_url.clone(),
                    mer_ref: item.response.mer_ref.clone(),
                    mer_id: mer_id.clone().expose(),
                    key: key.expose(),
                },
            )?;

            if calculated_signature.clone().eq(&item.response.signature) {
                let decoded_redirect_url_bytes: Vec<u8> = base64::engine::general_purpose::STANDARD
                    .decode(item.response.txn_url.clone())
                    .change_context(errors::ConnectorError::RequestEncodingFailed)?;

                let redirect_url = String::from_utf8(decoded_redirect_url_bytes)
                    .change_context(errors::ConnectorError::RequestEncodingFailed)?;

                Ok(Self {
                    status: enums::AttemptStatus::AuthenticationPending, // Redirect is always expected after success response
                    response: Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::NoResponseId,
                        redirection_data: Box::new(Some(RedirectForm::Form {
                            endpoint: redirect_url,
                            method: Method::Get,
                            form_fields: HashMap::new(),
                        })),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: Some(item.response.mer_ref.clone()),
                        incremental_authorization_allowed: None,
                        charges: None,
                    }),
                    ..item.data
                })
            } else {
                // When the signature check fails
                Ok(Self {
                    status: enums::AttemptStatus::Failure,
                    response: Err(ErrorResponse {
                        code: NO_ERROR_CODE.to_string(),
                        message: auth_error::INVALID_SIGNATURE.to_string(),
                        reason: Some(auth_error::INVALID_SIGNATURE.to_string()),
                        status_code: item.http_code,
                        attempt_status: Some(enums::AttemptStatus::Failure),
                        connector_transaction_id: Some(item.response.mer_ref.clone()),
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    }),
                    ..item.data
                })
            }
        } else {
            let error_reason =
                ZslResponseStatus::try_from(item.response.status.clone())?.to_string();
            Ok(Self {
                status: enums::AttemptStatus::Failure,
                response: Err(ErrorResponse {
                    code: item.response.status.clone(),
                    message: error_reason.clone(),
                    reason: Some(error_reason.clone()),
                    status_code: item.http_code,
                    attempt_status: Some(enums::AttemptStatus::Failure),
                    connector_transaction_id: Some(item.response.mer_ref.clone()),
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
                ..item.data
            })
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZslWebhookResponse {
    pub process_type: ProcessType,
    pub process_code: ProcessCode,
    pub status: String,
    pub txn_id: String,
    pub txn_date: String,
    pub paid_ccy: api_models::enums::Currency,
    pub paid_amt: String,
    pub consr_paid_ccy: Option<api_models::enums::Currency>,
    pub consr_paid_amt: String,
    pub service_fee_ccy: Option<api_models::enums::Currency>,
    pub service_fee: Option<String>,
    pub txn_amt: String,
    pub ccy: String,
    pub mer_ref: String,
    pub mer_txn_date: String,
    pub mer_id: String,
    pub enctype: EncodingType,
    pub signature: Secret<String>,
}

pub(crate) fn get_status(status: String) -> api_models::webhooks::IncomingWebhookEvent {
    match status.as_str() {
        //any response with status != 0 are a failed deposit transaction
        "0" => api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess,
        _ => api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure,
    }
}

impl<F> TryFrom<ResponseRouterData<F, ZslWebhookResponse, PaymentsSyncData, PaymentsResponseData>>
    for RouterData<F, PaymentsSyncData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, ZslWebhookResponse, PaymentsSyncData, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let paid_amount = item
            .response
            .consr_paid_amt
            .parse::<i64>()
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        if item.response.status == "0" {
            Ok(Self {
                status: enums::AttemptStatus::Charged,
                amount_captured: Some(paid_amount),
                response: Ok(PaymentsResponseData::TransactionResponse {
                    resource_id: ResponseId::ConnectorTransactionId(item.response.txn_id.clone()),
                    redirection_data: Box::new(None),
                    mandate_reference: Box::new(None),
                    connector_metadata: None,
                    network_txn_id: None,
                    connector_response_reference_id: Some(item.response.mer_ref.clone()),
                    incremental_authorization_allowed: None,
                    charges: None,
                }),
                ..item.data
            })
        } else {
            let error_reason =
                ZslResponseStatus::try_from(item.response.status.clone())?.to_string();
            Ok(Self {
                status: enums::AttemptStatus::Failure,
                response: Err(ErrorResponse {
                    code: item.response.status.clone(),
                    message: error_reason.clone(),
                    reason: Some(error_reason.clone()),
                    status_code: item.http_code,
                    attempt_status: Some(enums::AttemptStatus::Failure),
                    connector_transaction_id: Some(item.response.mer_ref.clone()),
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
                ..item.data
            })
        }
    }
}

impl TryFrom<String> for ZslResponseStatus {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(status: String) -> Result<Self, Self::Error> {
        match status.as_str() {
            "0" => Ok(Self::Normal),
            "1000" => Ok(Self::InternalError),
            "1001" => Ok(Self::BreakDownMessageError),
            "1002" => Ok(Self::FormatError),
            "1004" => Ok(Self::InvalidTransaction),
            "1005" => Ok(Self::TransactionCountryNotFound),
            "1006" => Ok(Self::MerchantIdNotFound),
            "1007" => Ok(Self::AccountDisabled),
            "1008" => Ok(Self::DuplicateMerchantReference),
            "1009" => Ok(Self::InvalidPayAmount),
            "1010" => Ok(Self::PayAmountNotFound),
            "1011" => Ok(Self::InvalidCurrencyCode),
            "1012" => Ok(Self::CurrencyCodeNotFound),
            "1013" => Ok(Self::ReferenceNotFound),
            "1014" => Ok(Self::TransmissionTimeNotFound),
            "1015" => Ok(Self::PayMethodNotFound),
            "1016" => Ok(Self::BankCodeNotFound),
            "1017" => Ok(Self::InvalidShowPayPage),
            "1018" => Ok(Self::ShowPayPageNotFound),
            "1019" => Ok(Self::SuccessUrlNotFound),
            "1020" => Ok(Self::SuccessCallbackUrlNotFound),
            "1021" => Ok(Self::FailUrlNotFound),
            "1022" => Ok(Self::FailCallbackUrlNotFound),
            "1023" => Ok(Self::MacNotFound),
            "1025" => Ok(Self::OriginalTransactionNotFound),
            "1026" => Ok(Self::DeblockDataError),
            "1028" => Ok(Self::PspAckNotYetReturn),
            "1029" => Ok(Self::BankBranchNameNotFound),
            "1030" => Ok(Self::BankAccountIDNotFound),
            "1031" => Ok(Self::BankAccountNameNotFound),
            "1032" => Ok(Self::IdentityIDNotFound),
            "1033" => Ok(Self::ErrorConnectingToPsp),
            "1034" => Ok(Self::CountryPspNotAvailable),
            "1035" => Ok(Self::UnsupportedPayAmount),
            "1036" => Ok(Self::RecordMismatch),
            "1037" => Ok(Self::NoRecord),
            "1038" => Ok(Self::PspError),
            "1039" => Ok(Self::UnsupportedEncryptionType),
            "1040" => Ok(Self::ExceedTransactionLimitCount),
            "1041" => Ok(Self::ExceedTransactionLimitAmount),
            "1042" => Ok(Self::ExceedTransactionAccountLimitCount),
            "1043" => Ok(Self::ExceedTransactionAccountLimitAmount),
            "1044" => Ok(Self::ExchangeRateError),
            "1045" => Ok(Self::InvalidEncoding),
            "1046" => Ok(Self::CustomerNameNotFound),
            "1047" => Ok(Self::CustomerFamilyNameNotFound),
            "1048" => Ok(Self::CustomerTelPhoneNotFound),
            "1049" => Ok(Self::InsufficientFund),
            "1050" => Ok(Self::ServiceCodeIsMissing),
            "1051" => Ok(Self::CurrencyIdNotMatch),
            "1052" => Ok(Self::NoPendingRecord),
            "1053" => Ok(Self::NoLoadBalancerRuleDefineForTransaction),
            "1054" => Ok(Self::NoPaymentProviderAvailable),
            "1055" => Ok(Self::UnsupportedPayMethod),
            "1056" => Ok(Self::PendingTransaction),
            "1057" => Ok(Self::OtherError1059),
            "1058" => Ok(Self::OtherError1058),
            "1059" => Ok(Self::OtherError1059),
            "1084" => Ok(Self::InvalidRequestId),
            "5043" => Ok(Self::BeneficiaryBankAccountIsNotAvailable),
            "5053" => Ok(Self::BaidNotFound),
            "5057" => Ok(Self::InvalidBaid),
            "5059" => Ok(Self::InvalidBaidStatus),
            "5107" => Ok(Self::AutoUploadBankDisabled),
            "5108" => Ok(Self::InvalidNature),
            "5109" => Ok(Self::SmsCreateDateNotFound),
            "5110" => Ok(Self::InvalidSmsCreateDate),
            "5111" => Ok(Self::RecordNotFound),
            "5112" => Ok(Self::InsufficientBaidAvailableBalance),
            "5113" => Ok(Self::ExceedTxnAmountLimit),
            "5114" => Ok(Self::BaidBalanceNotFound),
            "5115" => Ok(Self::AutoUploadIndicatorNotFound),
            "5116" => Ok(Self::InvalidBankAcctStatus),
            "5117" => Ok(Self::InvalidAutoUploadIndicator),
            "5118" => Ok(Self::InvalidPidStatus),
            "5119" => Ok(Self::InvalidProviderStatus),
            "5120" => Ok(Self::InvalidBankAccountSystemSwitchEnabled),
            "5121" => Ok(Self::AutoUploadProviderDisabled),
            "5122" => Ok(Self::AutoUploadBankNotFound),
            "5123" => Ok(Self::AutoUploadBankAcctNotFound),
            "5124" => Ok(Self::AutoUploadProviderNotFound),
            "5125" => Ok(Self::UnsupportedBankCode),
            "5126" => Ok(Self::BalanceOverrideIndicatorNotFound),
            "5127" => Ok(Self::InvalidBalanceOverrideIndicator),
            "10000" => Ok(Self::VernoInvalid),
            "10001" => Ok(Self::ServiceCodeInvalid),
            "10002" => Ok(Self::PspResponseSignatureIsNotValid),
            "10003" => Ok(Self::ProcessTypeNotFound),
            "10004" => Ok(Self::ProcessCodeNotFound),
            "10005" => Ok(Self::EnctypeNotFound),
            "10006" => Ok(Self::VernoNotFound),
            "10007" => Ok(Self::DepositBankNotFound),
            "10008" => Ok(Self::DepositFlowNotFound),
            "10009" => Ok(Self::CustDepositDateNotFound),
            "10010" => Ok(Self::CustTagNotFound),
            "10011" => Ok(Self::CountryValueInvalid),
            "10012" => Ok(Self::CurrencyCodeValueInvalid),
            "10013" => Ok(Self::MerTxnDateInvalid),
            "10014" => Ok(Self::CustDepositDateInvalid),
            "10015" => Ok(Self::TxnAmtInvalid),
            "10016" => Ok(Self::SuccessCallbackUrlInvalid),
            "10017" => Ok(Self::DepositFlowInvalid),
            "10018" => Ok(Self::ProcessTypeInvalid),
            "10019" => Ok(Self::ProcessCodeInvalid),
            "10020" => Ok(Self::UnsupportedMerRefLength),
            "10021" => Ok(Self::DepositBankLengthOverLimit),
            "10022" => Ok(Self::CustTagLengthOverLimit),
            "10023" => Ok(Self::SignatureLengthOverLimit),
            "10024" => Ok(Self::RequestContainInvalidTag),
            "10025" => Ok(Self::RequestSignatureNotMatch),
            "10026" => Ok(Self::InvalidCustomer),
            "10027" => Ok(Self::SchemeNotFound),
            "10028" => Ok(Self::PspResponseFieldsMissing),
            "10029" => Ok(Self::PspResponseMerRefNotMatchWithRequestMerRef),
            "10030" => Ok(Self::PspResponseMerIdNotMatchWithRequestMerId),
            "10031" => Ok(Self::UpdateDepositFailAfterResponse),
            "10032" => Ok(Self::UpdateUsedLimitTransactionCountFailAfterSuccessResponse),
            "10033" => Ok(Self::UpdateCustomerLastDepositRecordAfterSuccessResponse),
            "10034" => Ok(Self::CreateDepositFail),
            "10035" => Ok(Self::CreateDepositMsgFail),
            "10036" => Ok(Self::UpdateStatusSubStatusFail),
            "10037" => Ok(Self::AddDepositRecordToSchemeAccount),
            "10038" => Ok(Self::EmptyResponse),
            "10039" => Ok(Self::AubConfirmErrorFromPh),
            "10040" => Ok(Self::ProviderEmailAddressNotFound),
            "10041" => Ok(Self::AubConnectionTimeout),
            "10042" => Ok(Self::AubConnectionIssue),
            "10043" => Ok(Self::AubMsgTypeMissing),
            "10044" => Ok(Self::AubMsgCodeMissing),
            "10045" => Ok(Self::AubVersionMissing),
            "10046" => Ok(Self::AubEncTypeMissing),
            "10047" => Ok(Self::AubSignMissing),
            "10048" => Ok(Self::AubInfoMissing),
            "10049" => Ok(Self::AubErrorCodeMissing),
            "10050" => Ok(Self::AubMsgTypeInvalid),
            "10051" => Ok(Self::AubMsgCodeInvalid),
            "10052" => Ok(Self::AubBaidMissing),
            "10053" => Ok(Self::AubResponseSignNotMatch),
            "10054" => Ok(Self::SmsConnectionTimeout),
            "10055" => Ok(Self::SmsConnectionIssue),
            "10056" => Ok(Self::SmsConfirmErrorFromPh),
            "10057" => Ok(Self::SmsMsgTypeMissing),
            "10058" => Ok(Self::SmsMsgCodeMissing),
            "10059" => Ok(Self::SmsVersionMissing),
            "10060" => Ok(Self::SmsEncTypeMissing),
            "10061" => Ok(Self::SmsSignMissing),
            "10062" => Ok(Self::SmsInfoMissing),
            "10063" => Ok(Self::SmsErrorCodeMissing),
            "10064" => Ok(Self::SmsMsgTypeInvalid),
            "10065" => Ok(Self::SmsMsgCodeInvalid),
            "10066" => Ok(Self::SmsResponseSignNotMatch),
            "10067" => Ok(Self::SmsRequestReachMaximumLimit),
            "10068" => Ok(Self::SyncConnectionTimeout),
            "10069" => Ok(Self::SyncConnectionIssue),
            "10070" => Ok(Self::SyncConfirmErrorFromPh),
            "10071" => Ok(Self::SyncMsgTypeMissing),
            "10072" => Ok(Self::SyncMsgCodeMissing),
            "10073" => Ok(Self::SyncVersionMissing),
            "10074" => Ok(Self::SyncEncTypeMissing),
            "10075" => Ok(Self::SyncSignMissing),
            "10076" => Ok(Self::SyncInfoMissing),
            "10077" => Ok(Self::SyncErrorCodeMissing),
            "10078" => Ok(Self::SyncMsgTypeInvalid),
            "10079" => Ok(Self::SyncMsgCodeInvalid),
            "10080" => Ok(Self::SyncResponseSignNotMatch),
            "10081" => Ok(Self::AccountExpired),
            "10082" => Ok(Self::ExceedMaxMinAmount),
            "10083" => Ok(Self::WholeNumberAmountLessThanOne),
            "10084" => Ok(Self::AddDepositRecordToSchemeChannel),
            "10085" => Ok(Self::UpdateUtilizedAmountFailAfterSuccessResponse),
            "10086" => Ok(Self::PidResponseInvalidFormat),
            "10087" => Ok(Self::PspNameNotFound),
            "10088" => Ok(Self::LangIsMissing),
            "10089" => Ok(Self::FailureCallbackUrlInvalid),
            "10090" => Ok(Self::SuccessRedirectUrlInvalid),
            "10091" => Ok(Self::FailureRedirectUrlInvalid),
            "10092" => Ok(Self::LangValueInvalid),
            "10093" => Ok(Self::OnlineDepositSessionTimeout),
            "10094" => Ok(Self::AccessPaymentPageRouteFieldMissing),
            "10095" => Ok(Self::AmountNotMatch),
            "10096" => Ok(Self::PidCallbackFieldsMissing),
            "10097" => Ok(Self::TokenNotMatch),
            "10098" => Ok(Self::OperationDuplicated),
            "10099" => Ok(Self::PayPageDomainNotAvailable),
            "10100" => Ok(Self::PayPageConfirmSignatureNotMatch),
            "10101" => Ok(Self::PaymentPageConfirmationFieldMissing),
            "10102" => Ok(Self::MultipleCallbackFromPsp),
            "10103" => Ok(Self::PidNotAvailable),
            "10104" => Ok(Self::PidDepositUrlNotValidOrEmp),
            "10105" => Ok(Self::PspSelfRedirectTagNotValid),
            "20000" => Ok(Self::InternalError20000),
            "20001" => Ok(Self::DepositTimeout),
            _ => Err(errors::ConnectorError::ResponseHandlingFailed.into()),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, strum::Display)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ZslResponseStatus {
    Normal,
    InternalError,
    BreakDownMessageError,
    FormatError,
    InvalidTransaction,
    TransactionCountryNotFound,
    MerchantIdNotFound,
    AccountDisabled,
    DuplicateMerchantReference,
    InvalidPayAmount,
    PayAmountNotFound,
    InvalidCurrencyCode,
    CurrencyCodeNotFound,
    ReferenceNotFound,
    TransmissionTimeNotFound,
    PayMethodNotFound,
    BankCodeNotFound,
    InvalidShowPayPage,
    ShowPayPageNotFound,
    SuccessUrlNotFound,
    SuccessCallbackUrlNotFound,
    FailUrlNotFound,
    FailCallbackUrlNotFound,
    MacNotFound,
    OriginalTransactionNotFound,
    DeblockDataError,
    PspAckNotYetReturn,
    BankBranchNameNotFound,
    BankAccountIDNotFound,
    BankAccountNameNotFound,
    IdentityIDNotFound,
    ErrorConnectingToPsp,
    CountryPspNotAvailable,
    UnsupportedPayAmount,
    RecordMismatch,
    NoRecord,
    PspError,
    UnsupportedEncryptionType,
    ExceedTransactionLimitCount,
    ExceedTransactionLimitAmount,
    ExceedTransactionAccountLimitCount,
    ExceedTransactionAccountLimitAmount,
    ExchangeRateError,
    InvalidEncoding,
    CustomerNameNotFound,
    CustomerFamilyNameNotFound,
    CustomerTelPhoneNotFound,
    InsufficientFund,
    ServiceCodeIsMissing,
    CurrencyIdNotMatch,
    NoPendingRecord,
    NoLoadBalancerRuleDefineForTransaction,
    NoPaymentProviderAvailable,
    UnsupportedPayMethod,
    PendingTransaction,
    OtherError1059,
    OtherError1058,
    InvalidRequestId,
    BeneficiaryBankAccountIsNotAvailable,
    BaidNotFound,
    InvalidBaid,
    InvalidBaidStatus,
    AutoUploadBankDisabled,
    InvalidNature,
    SmsCreateDateNotFound,
    InvalidSmsCreateDate,
    RecordNotFound,
    InsufficientBaidAvailableBalance,
    ExceedTxnAmountLimit,
    BaidBalanceNotFound,
    AutoUploadIndicatorNotFound,
    InvalidBankAcctStatus,
    InvalidAutoUploadIndicator,
    InvalidPidStatus,
    InvalidProviderStatus,
    InvalidBankAccountSystemSwitchEnabled,
    AutoUploadProviderDisabled,
    AutoUploadBankNotFound,
    AutoUploadBankAcctNotFound,
    AutoUploadProviderNotFound,
    UnsupportedBankCode,
    BalanceOverrideIndicatorNotFound,
    InvalidBalanceOverrideIndicator,
    VernoInvalid,
    ServiceCodeInvalid,
    PspResponseSignatureIsNotValid,
    ProcessTypeNotFound,
    ProcessCodeNotFound,
    EnctypeNotFound,
    VernoNotFound,
    DepositBankNotFound,
    DepositFlowNotFound,
    CustDepositDateNotFound,
    CustTagNotFound,
    CountryValueInvalid,
    CurrencyCodeValueInvalid,
    MerTxnDateInvalid,
    CustDepositDateInvalid,
    TxnAmtInvalid,
    SuccessCallbackUrlInvalid,
    DepositFlowInvalid,
    ProcessTypeInvalid,
    ProcessCodeInvalid,
    UnsupportedMerRefLength,
    DepositBankLengthOverLimit,
    CustTagLengthOverLimit,
    SignatureLengthOverLimit,
    RequestContainInvalidTag,
    RequestSignatureNotMatch,
    InvalidCustomer,
    SchemeNotFound,
    PspResponseFieldsMissing,
    PspResponseMerRefNotMatchWithRequestMerRef,
    PspResponseMerIdNotMatchWithRequestMerId,
    UpdateDepositFailAfterResponse,
    UpdateUsedLimitTransactionCountFailAfterSuccessResponse,
    UpdateCustomerLastDepositRecordAfterSuccessResponse,
    CreateDepositFail,
    CreateDepositMsgFail,
    UpdateStatusSubStatusFail,
    AddDepositRecordToSchemeAccount,
    EmptyResponse,
    AubConfirmErrorFromPh,
    ProviderEmailAddressNotFound,
    AubConnectionTimeout,
    AubConnectionIssue,
    AubMsgTypeMissing,
    AubMsgCodeMissing,
    AubVersionMissing,
    AubEncTypeMissing,
    AubSignMissing,
    AubInfoMissing,
    AubErrorCodeMissing,
    AubMsgTypeInvalid,
    AubMsgCodeInvalid,
    AubBaidMissing,
    AubResponseSignNotMatch,
    SmsConnectionTimeout,
    SmsConnectionIssue,
    SmsConfirmErrorFromPh,
    SmsMsgTypeMissing,
    SmsMsgCodeMissing,
    SmsVersionMissing,
    SmsEncTypeMissing,
    SmsSignMissing,
    SmsInfoMissing,
    SmsErrorCodeMissing,
    SmsMsgTypeInvalid,
    SmsMsgCodeInvalid,
    SmsResponseSignNotMatch,
    SmsRequestReachMaximumLimit,
    SyncConnectionTimeout,
    SyncConnectionIssue,
    SyncConfirmErrorFromPh,
    SyncMsgTypeMissing,
    SyncMsgCodeMissing,
    SyncVersionMissing,
    SyncEncTypeMissing,
    SyncSignMissing,
    SyncInfoMissing,
    SyncErrorCodeMissing,
    SyncMsgTypeInvalid,
    SyncMsgCodeInvalid,
    SyncResponseSignNotMatch,
    AccountExpired,
    ExceedMaxMinAmount,
    WholeNumberAmountLessThanOne,
    AddDepositRecordToSchemeChannel,
    UpdateUtilizedAmountFailAfterSuccessResponse,
    PidResponseInvalidFormat,
    PspNameNotFound,
    LangIsMissing,
    FailureCallbackUrlInvalid,
    SuccessRedirectUrlInvalid,
    FailureRedirectUrlInvalid,
    LangValueInvalid,
    OnlineDepositSessionTimeout,
    AccessPaymentPageRouteFieldMissing,
    AmountNotMatch,
    PidCallbackFieldsMissing,
    TokenNotMatch,
    OperationDuplicated,
    PayPageDomainNotAvailable,
    PayPageConfirmSignatureNotMatch,
    PaymentPageConfirmationFieldMissing,
    MultipleCallbackFromPsp,
    PidNotAvailable,
    PidDepositUrlNotValidOrEmp,
    PspSelfRedirectTagNotValid,
    InternalError20000,
    DepositTimeout,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZslErrorResponse {
    pub status: String,
}

pub enum ZslSignatureType {
    RequestSignature {
        txn_amt: String,
        ccy: String,
        mer_ref: String,
        mer_id: String,
        mer_txn_date: String,
        key: String,
    },
    ResponseSignature {
        status: String,
        txn_url: String,
        mer_ref: String,
        mer_id: String,
        key: String,
    },
    WebhookSignature {
        status: String,
        txn_id: String,
        txn_date: String,
        paid_ccy: String,
        paid_amt: String,
        mer_ref: String,
        mer_id: String,
        key: String,
    },
}

pub fn calculate_signature(
    enctype: EncodingType,
    signature_data: ZslSignatureType,
) -> Result<Secret<String>, error_stack::Report<errors::ConnectorError>> {
    let signature_data = match signature_data {
        ZslSignatureType::RequestSignature {
            txn_amt,
            ccy,
            mer_ref,
            mer_id,
            mer_txn_date,
            key,
        } => format!("{txn_amt}{ccy}{mer_ref}{mer_id}{mer_txn_date}{key}"),
        ZslSignatureType::ResponseSignature {
            status,
            txn_url,
            mer_ref,
            mer_id,
            key,
        } => {
            format!("{status}{txn_url}{mer_ref}{mer_id}{key}")
        }
        ZslSignatureType::WebhookSignature {
            status,
            txn_id,
            txn_date,
            paid_ccy,
            paid_amt,
            mer_ref,
            mer_id,
            key,
        } => format!("{status}{txn_id}{txn_date}{paid_ccy}{paid_amt}{mer_ref}{mer_id}{key}"),
    };
    let message = signature_data.as_bytes();

    let encoded_data = match enctype {
        EncodingType::MD5 => hex::encode(
            common_utils::crypto::Md5
                .generate_digest(message)
                .change_context(errors::ConnectorError::RequestEncodingFailed)?,
        ),
        EncodingType::Sha1 => {
            hex::encode(digest::digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, message))
        }
    };
    Ok(Secret::new(encoded_data))
}