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

use std::collections::HashMap;

use api_models::enums::{AuthenticationType, PaymentMethod};
use common_enums::enums;
use common_utils::{
    pii,
    types::{MinorUnit, StringMajorUnit},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{PaymentMethodData, WalletData},
    router_data::{ConnectorAuthType, ErrorResponse, PaymentMethodToken, RouterData},
    router_flow_types::{Execute, Void},
    router_request_types::{PaymentsCancelData, PaymentsPreProcessingData, ResponseId},
    router_response_types::{
        MandateReference, PaymentsResponseData, PreprocessingResponseId, RedirectForm,
        RefundsResponseData,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData,
        PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, TokenizationRouterData,
    },
};
use hyperswitch_interfaces::{consts, errors};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
    types::{PaymentsCancelResponseRouterData, RefundsResponseRouterData, ResponseRouterData},
    unimplemented_payment_method,
    utils::{
        self, AddressDetailsData, CardData, PaymentsAuthorizeRequestData,
        PaymentsCancelRequestData, PaymentsCompleteAuthorizeRequestData,
        PaymentsPreProcessingRequestData, PaymentsSyncRequestData, RouterData as OtherRouterData,
    },
};

const LANGUAGE: &str = "en";

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

impl<T> TryFrom<(MinorUnit, T)> for PaymeRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from((amount, item): (MinorUnit, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount,
            router_data: item,
        })
    }
}

#[derive(Debug, Serialize)]
pub struct PayRequest {
    buyer_name: Secret<String>,
    buyer_email: pii::Email,
    payme_sale_id: String,
    #[serde(flatten)]
    card: PaymeCard,
    language: String,
}

#[derive(Debug, Serialize)]
pub struct MandateRequest {
    currency: enums::Currency,
    sale_price: MinorUnit,
    transaction_id: String,
    product_name: String,
    sale_return_url: String,
    seller_payme_id: Secret<String>,
    sale_callback_url: String,
    buyer_key: Secret<String>,
    language: String,
}

#[derive(Debug, Serialize)]
pub struct Pay3dsRequest {
    buyer_name: Secret<String>,
    buyer_email: pii::Email,
    buyer_key: Secret<String>,
    payme_sale_id: String,
    meta_data_jwt: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum PaymePaymentRequest {
    MandateRequest(MandateRequest),
    PayRequest(PayRequest),
}

#[derive(Debug, Serialize)]
pub struct PaymeQuerySaleRequest {
    sale_payme_id: String,
    seller_payme_id: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct PaymeQueryTransactionRequest {
    payme_transaction_id: String,
    seller_payme_id: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct PaymeCard {
    credit_card_cvv: Secret<String>,
    credit_card_exp: Secret<String>,
    credit_card_number: cards::CardNumber,
}

#[derive(Debug, Serialize)]
pub struct CaptureBuyerRequest {
    seller_payme_id: Secret<String>,
    #[serde(flatten)]
    card: PaymeCard,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct CaptureBuyerResponse {
    buyer_key: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct GenerateSaleRequest {
    currency: enums::Currency,
    sale_type: SaleType,
    sale_price: MinorUnit,
    transaction_id: String,
    product_name: String,
    sale_return_url: String,
    seller_payme_id: Secret<String>,
    sale_callback_url: String,
    sale_payment_method: SalePaymentMethod,
    services: Option<ThreeDs>,
    language: String,
}

#[derive(Debug, Serialize)]
pub struct ThreeDs {
    name: ThreeDsType,
    settings: ThreeDsSettings,
}

#[derive(Debug, Serialize)]
pub enum ThreeDsType {
    #[serde(rename = "3D Secure")]
    ThreeDs,
}

#[derive(Debug, Serialize)]
pub struct ThreeDsSettings {
    active: bool,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct GenerateSaleResponse {
    payme_sale_id: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, PaymePaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, PaymePaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        match item.response {
            // To handle webhook response
            PaymePaymentsResponse::PaymePaySaleResponse(response) => {
                Self::try_from(ResponseRouterData {
                    response,
                    data: item.data,
                    http_code: item.http_code,
                })
            }
            // To handle PSync response
            PaymePaymentsResponse::SaleQueryResponse(response) => {
                Self::try_from(ResponseRouterData {
                    response,
                    data: item.data,
                    http_code: item.http_code,
                })
            }
        }
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, PaymePaySaleResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, PaymePaySaleResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let status = enums::AttemptStatus::from(item.response.sale_status.clone());
        let response = if utils::is_payment_failure(status) {
            // To populate error message in case of failure
            Err(get_pay_sale_error_response((
                &item.response,
                item.http_code,
            )))
        } else {
            Ok(PaymentsResponseData::try_from(&item.response)?)
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

fn get_pay_sale_error_response(
    (pay_sale_response, http_code): (&PaymePaySaleResponse, u16),
) -> ErrorResponse {
    let code = pay_sale_response
        .status_error_code
        .map(|error_code| error_code.to_string())
        .unwrap_or(consts::NO_ERROR_CODE.to_string());
    ErrorResponse {
        code,
        message: pay_sale_response
            .status_error_details
            .clone()
            .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
        reason: pay_sale_response.status_error_details.to_owned(),
        status_code: http_code,
        attempt_status: None,
        connector_transaction_id: Some(pay_sale_response.payme_sale_id.clone()),
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    }
}

impl TryFrom<&PaymePaySaleResponse> for PaymentsResponseData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(value: &PaymePaySaleResponse) -> Result<Self, Self::Error> {
        let redirection_data = match value.sale_3ds {
            Some(true) => value.redirect_url.clone().map(|url| RedirectForm::Form {
                endpoint: url.to_string(),
                method: common_utils::request::Method::Get,
                form_fields: HashMap::<String, String>::new(),
            }),
            _ => None,
        };
        Ok(Self::TransactionResponse {
            resource_id: ResponseId::ConnectorTransactionId(value.payme_sale_id.clone()),
            redirection_data: Box::new(redirection_data),
            mandate_reference: Box::new(value.buyer_key.clone().map(|buyer_key| {
                MandateReference {
                    connector_mandate_id: Some(buyer_key.expose()),
                    payment_method_id: None,
                    mandate_metadata: None,
                    connector_mandate_request_reference_id: None,
                }
            })),
            connector_metadata: None,
            network_txn_id: None,
            connector_response_reference_id: None,
            incremental_authorization_allowed: None,
            charges: None,
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, SaleQueryResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, SaleQueryResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        // Only one element would be present since we are passing one transaction id in the PSync request
        let transaction_response = item
            .response
            .items
            .first()
            .cloned()
            .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
        let status = enums::AttemptStatus::from(transaction_response.sale_status.clone());
        let response = if utils::is_payment_failure(status) {
            // To populate error message in case of failure
            Err(get_sale_query_error_response((
                &transaction_response,
                item.http_code,
            )))
        } else {
            Ok(PaymentsResponseData::from(&transaction_response))
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

fn get_sale_query_error_response(
    (sale_query_response, http_code): (&SaleQuery, u16),
) -> ErrorResponse {
    ErrorResponse {
        code: sale_query_response
            .sale_error_code
            .clone()
            .unwrap_or(consts::NO_ERROR_CODE.to_string()),
        message: sale_query_response
            .sale_error_text
            .clone()
            .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
        reason: sale_query_response.sale_error_text.clone(),
        status_code: http_code,
        attempt_status: None,
        connector_transaction_id: Some(sale_query_response.sale_payme_id.clone()),
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    }
}

impl From<&SaleQuery> for PaymentsResponseData {
    fn from(value: &SaleQuery) -> Self {
        Self::TransactionResponse {
            resource_id: ResponseId::ConnectorTransactionId(value.sale_payme_id.clone()),
            redirection_data: Box::new(None),
            // mandate reference will be updated with webhooks only. That has been handled with PaymePaySaleResponse struct
            mandate_reference: Box::new(None),
            connector_metadata: None,
            network_txn_id: None,
            connector_response_reference_id: None,
            incremental_authorization_allowed: None,
            charges: None,
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum SaleType {
    Sale,
    Authorize,
    Token,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum SalePaymentMethod {
    CreditCard,
    ApplePay,
}

impl TryFrom<&PaymeRouterData<&PaymentsPreProcessingRouterData>> for GenerateSaleRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &PaymeRouterData<&PaymentsPreProcessingRouterData>,
    ) -> Result<Self, Self::Error> {
        let sale_type = SaleType::try_from(item.router_data)?;
        let seller_payme_id =
            PaymeAuthType::try_from(&item.router_data.connector_auth_type)?.seller_payme_id;
        let order_details = item.router_data.request.get_order_details()?;
        let services = get_services(item.router_data);
        let product_name = order_details
            .first()
            .ok_or_else(utils::missing_field_err("order_details"))?
            .product_name
            .clone();
        let pmd = item
            .router_data
            .request
            .payment_method_data
            .to_owned()
            .ok_or_else(utils::missing_field_err("payment_method_data"))?;
        Ok(Self {
            seller_payme_id,
            sale_price: item.amount.to_owned(),
            currency: item.router_data.request.get_currency()?,
            product_name,
            sale_payment_method: SalePaymentMethod::try_from(&pmd)?,
            sale_type,
            transaction_id: item.router_data.payment_id.clone(),
            sale_return_url: item.router_data.request.get_router_return_url()?,
            sale_callback_url: item.router_data.request.get_webhook_url()?,
            language: LANGUAGE.to_string(),
            services,
        })
    }
}

impl TryFrom<&PaymentMethodData> for SalePaymentMethod {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentMethodData) -> Result<Self, Self::Error> {
        match item {
            PaymentMethodData::Card(_) => Ok(Self::CreditCard),
            PaymentMethodData::Wallet(wallet_data) => match wallet_data {
                WalletData::ApplePayThirdPartySdk(_) => Ok(Self::ApplePay),
                WalletData::AliPayQr(_)
                | WalletData::AliPayRedirect(_)
                | WalletData::BluecodeRedirect {}
                | WalletData::AliPayHkRedirect(_)
                | WalletData::AmazonPay(_)
                | WalletData::AmazonPayRedirect(_)
                | WalletData::Paysera(_)
                | WalletData::Skrill(_)
                | WalletData::MomoRedirect(_)
                | WalletData::KakaoPayRedirect(_)
                | WalletData::GoPayRedirect(_)
                | WalletData::GcashRedirect(_)
                | WalletData::ApplePayRedirect(_)
                | WalletData::DanaRedirect {}
                | 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::ApplePay(_)
                | WalletData::SwishQr(_)
                | WalletData::Mifinity(_)
                | WalletData::RevolutPay(_) => Err(errors::ConnectorError::NotSupported {
                    message: "Wallet".to_string(),
                    connector: "payme",
                }
                .into()),
            },
            PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into())
            }
        }
    }
}

impl TryFrom<&PaymeRouterData<&PaymentsAuthorizeRouterData>> for PaymePaymentRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        value: &PaymeRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let payme_request = if value.router_data.request.mandate_id.is_some() {
            Self::MandateRequest(MandateRequest::try_from(value)?)
        } else {
            Self::PayRequest(PayRequest::try_from(value.router_data)?)
        };
        Ok(payme_request)
    }
}

impl TryFrom<&PaymentsSyncRouterData> for PaymeQuerySaleRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(value: &PaymentsSyncRouterData) -> Result<Self, Self::Error> {
        let seller_payme_id = PaymeAuthType::try_from(&value.connector_auth_type)?.seller_payme_id;
        Ok(Self {
            sale_payme_id: value.request.get_connector_transaction_id()?,
            seller_payme_id,
        })
    }
}

impl TryFrom<&RefundSyncRouterData> for PaymeQueryTransactionRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(value: &RefundSyncRouterData) -> Result<Self, Self::Error> {
        let seller_payme_id = PaymeAuthType::try_from(&value.connector_auth_type)?.seller_payme_id;
        Ok(Self {
            payme_transaction_id: value
                .request
                .connector_refund_id
                .clone()
                .ok_or(errors::ConnectorError::MissingConnectorRefundID)?,
            seller_payme_id,
        })
    }
}

impl<F>
    utils::ForeignTryFrom<(
        ResponseRouterData<
            F,
            GenerateSaleResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
        StringMajorUnit,
    )> for RouterData<F, PaymentsPreProcessingData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn foreign_try_from(
        (item, apple_pay_amount): (
            ResponseRouterData<
                F,
                GenerateSaleResponse,
                PaymentsPreProcessingData,
                PaymentsResponseData,
            >,
            StringMajorUnit,
        ),
    ) -> Result<Self, Self::Error> {
        match item.data.payment_method {
            PaymentMethod::Card => {
                match item.data.auth_type {
                    AuthenticationType::NoThreeDs => {
                        Ok(Self {
                            // We don't get any status from payme, so defaulting it to pending
                            // then move to authorize flow
                            status: enums::AttemptStatus::Pending,
                            preprocessing_id: Some(item.response.payme_sale_id.to_owned()),
                            response: Ok(PaymentsResponseData::PreProcessingResponse {
                                pre_processing_id: PreprocessingResponseId::ConnectorTransactionId(
                                    item.response.payme_sale_id,
                                ),
                                connector_metadata: None,
                                session_token: None,
                                connector_response_reference_id: None,
                            }),
                            ..item.data
                        })
                    }
                    AuthenticationType::ThreeDs => Ok(Self {
                        // We don't go to authorize flow in 3ds,
                        // Response is send directly after preprocessing flow
                        // redirection data is send to run script along
                        // status is made authentication_pending to show redirection
                        status: enums::AttemptStatus::AuthenticationPending,
                        preprocessing_id: Some(item.response.payme_sale_id.to_owned()),
                        response: Ok(PaymentsResponseData::TransactionResponse {
                            resource_id: ResponseId::ConnectorTransactionId(
                                item.response.payme_sale_id.to_owned(),
                            ),
                            redirection_data: Box::new(Some(RedirectForm::Payme)),
                            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
                    }),
                }
            }
            _ => {
                let currency_code = item.data.request.get_currency()?;
                let pmd = item.data.request.payment_method_data.to_owned();
                let payme_auth_type = PaymeAuthType::try_from(&item.data.connector_auth_type)?;

                let session_token = match pmd {
                    Some(PaymentMethodData::Wallet(WalletData::ApplePayThirdPartySdk(
                        _,
                    ))) => Some(api_models::payments::SessionToken::ApplePay(Box::new(
                        api_models::payments::ApplepaySessionTokenResponse {
                            session_token_data: Some(
                                api_models::payments::ApplePaySessionResponse::NoSessionResponse(api_models::payments::NullObject),
                            ),
                            payment_request_data: Some(
                                api_models::payments::ApplePayPaymentRequest {
                                    country_code: item.data.get_billing_country()?,
                                    currency_code,
                                    total: api_models::payments::AmountInfo {
                                        label: "Apple Pay".to_string(),
                                        total_type: None,
                                        amount: apple_pay_amount,
                                    },
                                    merchant_capabilities: None,
                                    supported_networks: None,
                                    merchant_identifier: None,
                                    required_billing_contact_fields: None,
                                    required_shipping_contact_fields: None,
                                    recurring_payment_request: None,
                                },
                            ),
                            connector: "payme".to_string(),
                            delayed_session_token: true,
                            sdk_next_action: api_models::payments::SdkNextAction {
                                next_action: api_models::payments::NextActionCall::Sync,
                            },
                            connector_reference_id: Some(item.response.payme_sale_id.to_owned()),
                            connector_sdk_public_key: Some(
                                payme_auth_type.payme_public_key.expose(),
                            ),
                            connector_merchant_id: payme_auth_type
                                .payme_merchant_id
                                .map(|mid| mid.expose()),
                        },
                    ))),
                    _ => None,
                };
                Ok(Self {
                    // We don't get any status from payme, so defaulting it to pending
                    status: enums::AttemptStatus::Pending,
                    preprocessing_id: Some(item.response.payme_sale_id.to_owned()),
                    response: Ok(PaymentsResponseData::PreProcessingResponse {
                        pre_processing_id: PreprocessingResponseId::ConnectorTransactionId(
                            item.response.payme_sale_id,
                        ),
                        connector_metadata: None,
                        session_token,
                        connector_response_reference_id: None,
                    }),
                    ..item.data
                })
            }
        }
    }
}

impl TryFrom<&PaymeRouterData<&PaymentsAuthorizeRouterData>> for MandateRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymeRouterData<&PaymentsAuthorizeRouterData>) -> Result<Self, Self::Error> {
        let seller_payme_id =
            PaymeAuthType::try_from(&item.router_data.connector_auth_type)?.seller_payme_id;
        let order_details = item.router_data.request.get_order_details()?;
        let product_name = order_details
            .first()
            .ok_or_else(utils::missing_field_err("order_details"))?
            .product_name
            .clone();
        Ok(Self {
            currency: item.router_data.request.currency,
            sale_price: item.amount.to_owned(),
            transaction_id: item.router_data.payment_id.clone(),
            product_name,
            sale_return_url: item.router_data.request.get_router_return_url()?,
            seller_payme_id,
            sale_callback_url: item.router_data.request.get_webhook_url()?,
            buyer_key: Secret::new(item.router_data.request.get_connector_mandate_id()?),
            language: LANGUAGE.to_string(),
        })
    }
}

impl TryFrom<&PaymentsAuthorizeRouterData> for PayRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = PaymeCard {
                    credit_card_cvv: req_card.card_cvc.clone(),
                    credit_card_exp: req_card
                        .get_card_expiry_month_year_2_digit_with_delimiter("".to_string())?,
                    credit_card_number: req_card.card_number,
                };
                let buyer_email = item.request.get_email()?;
                let buyer_name = item.get_billing_address()?.get_full_name()?;
                let payme_sale_id = item.preprocessing_id.to_owned().ok_or(
                    errors::ConnectorError::MissingConnectorRelatedTransactionID {
                        id: "payme_sale_id".to_string(),
                    },
                )?;
                Ok(Self {
                    card,
                    buyer_email,
                    buyer_name,
                    payme_sale_id,
                    language: LANGUAGE.to_string(),
                })
            }
            PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | 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("payme"),
                ))?
            }
        }
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymeRedirectResponseData {
    meta_data: String,
}

impl TryFrom<&PaymentsCompleteAuthorizeRouterData> for Pay3dsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsCompleteAuthorizeRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            Some(PaymentMethodData::Card(_)) => {
                let buyer_email = item.request.get_email()?;
                let buyer_name = item.get_billing_address()?.get_full_name()?;

                let payload_data = item.request.get_redirect_response_payload()?.expose();

                let jwt_data: PaymeRedirectResponseData = serde_json::from_value(payload_data)
                    .change_context(errors::ConnectorError::MissingConnectorRedirectionPayload {
                        field_name: "meta_data_jwt",
                    })?;

                let payme_sale_id = item
                    .request
                    .connector_transaction_id
                    .clone()
                    .ok_or(errors::ConnectorError::MissingConnectorTransactionID)?;
                let pm_token = item.get_payment_method_token()?;
                let buyer_key =
                    match pm_token {
                        PaymentMethodToken::Token(token) => token,
                        PaymentMethodToken::ApplePayDecrypt(_) => Err(
                            unimplemented_payment_method!("Apple Pay", "Simplified", "Payme"),
                        )?,
                        PaymentMethodToken::PazeDecrypt(_) => {
                            Err(unimplemented_payment_method!("Paze", "Payme"))?
                        }
                        PaymentMethodToken::GooglePayDecrypt(_) => {
                            Err(unimplemented_payment_method!("Google Pay", "Payme"))?
                        }
                    };
                Ok(Self {
                    buyer_email,
                    buyer_key,
                    buyer_name,
                    payme_sale_id,
                    meta_data_jwt: Secret::new(jwt_data.meta_data),
                })
            }
            Some(PaymentMethodData::CardRedirect(_))
            | Some(PaymentMethodData::Wallet(_))
            | Some(PaymentMethodData::PayLater(_))
            | Some(PaymentMethodData::BankRedirect(_))
            | Some(PaymentMethodData::BankDebit(_))
            | Some(PaymentMethodData::BankTransfer(_))
            | Some(PaymentMethodData::Crypto(_))
            | Some(PaymentMethodData::MandatePayment)
            | Some(PaymentMethodData::Reward)
            | Some(PaymentMethodData::RealTimePayment(_))
            | Some(PaymentMethodData::MobilePayment(_))
            | Some(PaymentMethodData::Upi(_))
            | Some(PaymentMethodData::Voucher(_))
            | Some(PaymentMethodData::GiftCard(_))
            | Some(PaymentMethodData::OpenBanking(_))
            | Some(PaymentMethodData::CardToken(_))
            | Some(PaymentMethodData::NetworkToken(_))
            | Some(PaymentMethodData::CardDetailsForNetworkTransactionId(_))
            | None => {
                Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into())
            }
        }
    }
}

impl TryFrom<&TokenizationRouterData> for CaptureBuyerRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &TokenizationRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let seller_payme_id =
                    PaymeAuthType::try_from(&item.connector_auth_type)?.seller_payme_id;
                let card = PaymeCard {
                    credit_card_cvv: req_card.card_cvc.clone(),
                    credit_card_exp: req_card
                        .get_card_expiry_month_year_2_digit_with_delimiter("".to_string())?,
                    credit_card_number: req_card.card_number,
                };
                Ok(Self {
                    card,
                    seller_payme_id,
                })
            }
            PaymentMethodData::Wallet(_)
            | 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("Tokenize Flow".to_string()).into())
            }
        }
    }
}

// Auth Struct
pub struct PaymeAuthType {
    #[allow(dead_code)]
    pub(super) payme_public_key: Secret<String>,
    pub(super) seller_payme_id: Secret<String>,
    pub(super) payme_merchant_id: Option<Secret<String>>,
}

impl TryFrom<&ConnectorAuthType> for PaymeAuthType {
    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 {
                seller_payme_id: api_key.to_owned(),
                payme_public_key: key1.to_owned(),
                payme_merchant_id: None,
            }),
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Ok(Self {
                seller_payme_id: api_key.to_owned(),
                payme_public_key: key1.to_owned(),
                payme_merchant_id: Some(api_secret.to_owned()),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

impl TryFrom<&PaymentsPreProcessingRouterData> for SaleType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(value: &PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> {
        let sale_type = if value.request.setup_mandate_details.is_some() {
            // First mandate
            Self::Token
        } else {
            // Normal payments
            match value.request.is_auto_capture()? {
                true => Self::Sale,
                false => Self::Authorize,
            }
        };
        Ok(sale_type)
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, strum::Display)]
#[serde(rename_all = "kebab-case")]
pub enum SaleStatus {
    Initial,
    Completed,
    Refunded,
    PartialRefund,
    Authorized,
    Voided,
    PartialVoid,
    Failed,
    Chargeback,
}

impl From<SaleStatus> for enums::AttemptStatus {
    fn from(item: SaleStatus) -> Self {
        match item {
            SaleStatus::Initial => Self::Authorizing,
            SaleStatus::Completed => Self::Charged,
            SaleStatus::Refunded | SaleStatus::PartialRefund => Self::AutoRefunded,
            SaleStatus::Authorized => Self::Authorized,
            SaleStatus::Voided | SaleStatus::PartialVoid => Self::Voided,
            SaleStatus::Failed => Self::Failure,
            SaleStatus::Chargeback => Self::AutoRefunded,
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum PaymePaymentsResponse {
    PaymePaySaleResponse(PaymePaySaleResponse),
    SaleQueryResponse(SaleQueryResponse),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SaleQueryResponse {
    items: Vec<SaleQuery>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SaleQuery {
    sale_status: SaleStatus,
    sale_payme_id: String,
    sale_error_text: Option<String>,
    sale_error_code: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PaymePaySaleResponse {
    sale_status: SaleStatus,
    payme_sale_id: String,
    payme_transaction_id: Option<String>,
    buyer_key: Option<Secret<String>>,
    status_error_details: Option<String>,
    status_error_code: Option<u32>,
    sale_3ds: Option<bool>,
    redirect_url: Option<Url>,
}

#[derive(Serialize, Deserialize)]
pub struct PaymeMetadata {
    payme_transaction_id: Option<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, CaptureBuyerResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, CaptureBuyerResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_token: Some(PaymentMethodToken::Token(item.response.buyer_key.clone())),
            response: Ok(PaymentsResponseData::TokenizationResponse {
                token: item.response.buyer_key.expose(),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
pub struct PaymentCaptureRequest {
    payme_sale_id: String,
    sale_price: MinorUnit,
}

impl TryFrom<&PaymeRouterData<&PaymentsCaptureRouterData>> for PaymentCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymeRouterData<&PaymentsCaptureRouterData>) -> Result<Self, Self::Error> {
        if item.router_data.request.minor_amount_to_capture
            != item.router_data.request.minor_payment_amount
        {
            Err(errors::ConnectorError::NotSupported {
                message: "Partial Capture".to_string(),
                connector: "Payme",
            })?
        }
        Ok(Self {
            payme_sale_id: item.router_data.request.connector_transaction_id.clone(),
            sale_price: item.amount,
        })
    }
}

// REFUND :
// Type definition for RefundRequest
#[derive(Debug, Serialize)]
pub struct PaymeRefundRequest {
    sale_refund_amount: MinorUnit,
    payme_sale_id: String,
    seller_payme_id: Secret<String>,
    language: String,
}

impl<F> TryFrom<&PaymeRouterData<&RefundsRouterData<F>>> for PaymeRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymeRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let auth_type = PaymeAuthType::try_from(&item.router_data.connector_auth_type)?;
        Ok(Self {
            payme_sale_id: item.router_data.request.connector_transaction_id.clone(),
            seller_payme_id: auth_type.seller_payme_id,
            sale_refund_amount: item.amount.to_owned(),
            language: LANGUAGE.to_string(),
        })
    }
}

impl TryFrom<SaleStatus> for enums::RefundStatus {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(sale_status: SaleStatus) -> Result<Self, Self::Error> {
        match sale_status {
            SaleStatus::Refunded | SaleStatus::PartialRefund => Ok(Self::Success),
            SaleStatus::Failed => Ok(Self::Failure),
            SaleStatus::Initial
            | SaleStatus::Completed
            | SaleStatus::Authorized
            | SaleStatus::Voided
            | SaleStatus::PartialVoid
            | SaleStatus::Chargeback => Err(errors::ConnectorError::ResponseHandlingFailed)?,
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PaymeRefundResponse {
    sale_status: SaleStatus,
    payme_transaction_id: Option<String>,
    status_error_code: Option<u32>,
}

impl TryFrom<RefundsResponseRouterData<Execute, PaymeRefundResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, PaymeRefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::try_from(item.response.sale_status.clone())?;
        let response = if utils::is_refund_failure(refund_status) {
            let payme_response = &item.response;
            let status_error_code = payme_response
                .status_error_code
                .map(|error_code| error_code.to_string());
            Err(ErrorResponse {
                code: status_error_code
                    .clone()
                    .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
                message: status_error_code
                    .clone()
                    .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
                reason: status_error_code,
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: payme_response.payme_transaction_id.clone(),
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            Ok(RefundsResponseData {
                connector_refund_id: item
                    .response
                    .payme_transaction_id
                    .ok_or(errors::ConnectorError::MissingConnectorRefundID)?,
                refund_status,
            })
        };
        Ok(Self {
            response,
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
pub struct PaymeVoidRequest {
    sale_currency: enums::Currency,
    payme_sale_id: String,
    seller_payme_id: Secret<String>,
    language: String,
}

impl TryFrom<&PaymeRouterData<&RouterData<Void, PaymentsCancelData, PaymentsResponseData>>>
    for PaymeVoidRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &PaymeRouterData<&RouterData<Void, PaymentsCancelData, PaymentsResponseData>>,
    ) -> Result<Self, Self::Error> {
        let auth_type = PaymeAuthType::try_from(&item.router_data.connector_auth_type)?;
        Ok(Self {
            payme_sale_id: item.router_data.request.connector_transaction_id.clone(),
            seller_payme_id: auth_type.seller_payme_id,
            sale_currency: item.router_data.request.get_currency()?,
            language: LANGUAGE.to_string(),
        })
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PaymeVoidResponse {
    sale_status: SaleStatus,
    payme_transaction_id: Option<String>,
    status_error_code: Option<u32>,
}

impl TryFrom<PaymentsCancelResponseRouterData<PaymeVoidResponse>> for PaymentsCancelRouterData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsCancelResponseRouterData<PaymeVoidResponse>,
    ) -> Result<Self, Self::Error> {
        let status = enums::AttemptStatus::from(item.response.sale_status.clone());
        let response = if utils::is_payment_failure(status) {
            let payme_response = &item.response;
            let status_error_code = payme_response
                .status_error_code
                .map(|error_code| error_code.to_string());
            Err(ErrorResponse {
                code: status_error_code
                    .clone()
                    .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
                message: status_error_code
                    .clone()
                    .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
                reason: status_error_code,
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: payme_response.payme_transaction_id.clone(),
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            // Since we are not receiving payme_sale_id, we are not populating the transaction response
            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::NoResponseId,
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            })
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PaymeQueryTransactionResponse {
    items: Vec<TransactionQuery>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TransactionQuery {
    sale_status: SaleStatus,
    payme_transaction_id: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, PaymeQueryTransactionResponse, T, RefundsResponseData>>
    for RouterData<F, T, RefundsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, PaymeQueryTransactionResponse, T, RefundsResponseData>,
    ) -> Result<Self, Self::Error> {
        let pay_sale_response = item
            .response
            .items
            .first()
            .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
        let refund_status = enums::RefundStatus::try_from(pay_sale_response.sale_status.clone())?;
        let response = if utils::is_refund_failure(refund_status) {
            Err(ErrorResponse {
                code: consts::NO_ERROR_CODE.to_string(),
                message: consts::NO_ERROR_CODE.to_string(),
                reason: None,
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: Some(pay_sale_response.payme_transaction_id.clone()),
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            Ok(RefundsResponseData {
                refund_status,
                connector_refund_id: pay_sale_response.payme_transaction_id.clone(),
            })
        };
        Ok(Self {
            response,
            ..item.data
        })
    }
}

fn get_services(item: &PaymentsPreProcessingRouterData) -> Option<ThreeDs> {
    match item.auth_type {
        AuthenticationType::ThreeDs => {
            let settings = ThreeDsSettings { active: true };
            Some(ThreeDs {
                name: ThreeDsType::ThreeDs,
                settings,
            })
        }
        AuthenticationType::NoThreeDs => None,
    }
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct PaymeErrorResponse {
    pub status_code: u16,
    pub status_error_details: String,
    pub status_additional_info: serde_json::Value,
    pub status_error_code: u32,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum NotifyType {
    SaleComplete,
    SaleAuthorized,
    Refund,
    SaleFailure,
    SaleChargeback,
    SaleChargebackRefund,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WebhookEventDataResource {
    pub sale_status: SaleStatus,
    pub payme_signature: Secret<String>,
    pub buyer_key: Option<Secret<String>>,
    pub notify_type: NotifyType,
    pub payme_sale_id: String,
    pub payme_transaction_id: String,
    pub status_error_details: Option<String>,
    pub status_error_code: Option<u32>,
    pub price: MinorUnit,
    pub currency: enums::Currency,
}

#[derive(Debug, Deserialize)]
pub struct WebhookEventDataResourceEvent {
    pub notify_type: NotifyType,
}

#[derive(Debug, Deserialize)]
pub struct WebhookEventDataResourceSignature {
    pub payme_signature: Secret<String>,
}

/// This try_from will ensure that webhook body would be properly parsed into PSync response
impl From<WebhookEventDataResource> for PaymePaySaleResponse {
    fn from(value: WebhookEventDataResource) -> Self {
        Self {
            sale_status: value.sale_status,
            payme_sale_id: value.payme_sale_id,
            payme_transaction_id: Some(value.payme_transaction_id),
            buyer_key: value.buyer_key,
            sale_3ds: None,
            redirect_url: None,
            status_error_code: value.status_error_code,
            status_error_details: value.status_error_details,
        }
    }
}

/// This try_from will ensure that webhook body would be properly parsed into RSync response
impl From<WebhookEventDataResource> for PaymeQueryTransactionResponse {
    fn from(value: WebhookEventDataResource) -> Self {
        let item = TransactionQuery {
            sale_status: value.sale_status,
            payme_transaction_id: value.payme_transaction_id,
        };
        Self { items: vec![item] }
    }
}

impl From<NotifyType> for api_models::webhooks::IncomingWebhookEvent {
    fn from(value: NotifyType) -> Self {
        match value {
            NotifyType::SaleComplete => Self::PaymentIntentSuccess,
            NotifyType::Refund => Self::RefundSuccess,
            NotifyType::SaleFailure => Self::PaymentIntentFailure,
            NotifyType::SaleChargeback => Self::DisputeOpened,
            NotifyType::SaleChargebackRefund => Self::DisputeWon,
            NotifyType::SaleAuthorized => Self::EventNotSupported,
        }
    }
}


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

use common_enums::enums;
use common_utils::types::StringMinorUnit;
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, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

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

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

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

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, PartialEq)]
pub struct ThunesPaymentsRequest {
    amount: StringMinorUnit,
    card: ThunesCard,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct ThunesCard {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    complete: bool,
}

impl TryFrom<&ThunesRouterData<&PaymentsAuthorizeRouterData>> for ThunesPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &ThunesRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = ThunesCard {
                    number: req_card.card_number,
                    expiry_month: req_card.card_exp_month,
                    expiry_year: req_card.card_exp_year,
                    cvc: req_card.card_cvc,
                    complete: item.router_data.request.is_auto_capture()?,
                };
                Ok(Self {
                    amount: item.amount.clone(),
                    card,
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
        }
    }
}

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

impl TryFrom<&ConnectorAuthType> for ThunesAuthType {
    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 ThunesPaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<ThunesPaymentStatus> for common_enums::AttemptStatus {
    fn from(item: ThunesPaymentStatus) -> Self {
        match item {
            ThunesPaymentStatus::Succeeded => Self::Charged,
            ThunesPaymentStatus::Failed => Self::Failure,
            ThunesPaymentStatus::Processing => Self::Authorizing,
        }
    }
}

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

impl<F, T> TryFrom<ResponseRouterData<F, ThunesPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, ThunesPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: common_enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id),
                redirection_data: Box::new(None),
                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
        })
    }
}

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

impl<F> TryFrom<&ThunesRouterData<&RefundsRouterData<F>>> for ThunesRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &ThunesRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: item.amount.to_owned(),
        })
    }
}

// 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,
            //TODO: Review mapping
        }
    }
}

//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(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct ThunesErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}


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

use std::str::FromStr;

use api_models::payments::{DeviceChannel, ThreeDsCompletionIndicator};
use base64::Engine;
use common_enums::enums;
use common_utils::{consts::BASE64_ENGINE, date_time, ext_traits::OptionExt as _};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::{ConnectorAuthType, ErrorResponse},
    router_flow_types::authentication::{Authentication, PreAuthentication},
    router_request_types::{
        authentication::{
            AuthNFlowType, ChallengeParams, ConnectorAuthenticationRequestData, MessageCategory,
            PreAuthNRequestData,
        },
        BrowserInformation,
    },
    router_response_types::AuthenticationResponseData,
};
use hyperswitch_interfaces::{api::CurrencyUnit, consts::NO_ERROR_MESSAGE, errors};
use iso_currency::Currency;
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use serde_json::{json, to_string};

use crate::{
    types::{ConnectorAuthenticationRouterData, PreAuthNRouterData, ResponseRouterData},
    utils::{get_card_details, to_connector_meta, AddressDetailsData, CardData as _},
};

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

impl<T> TryFrom<(&CurrencyUnit, enums::Currency, i64, T)> for ThreedsecureioRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (_currency_unit, _currency, amount, item): (&CurrencyUnit, enums::Currency, i64, T),
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: amount.to_string(),
            router_data: item,
        })
    }
}

impl<T> TryFrom<(i64, T)> for ThreedsecureioRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from((amount, router_data): (i64, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: amount.to_string(),
            router_data,
        })
    }
}

impl
    TryFrom<
        ResponseRouterData<
            PreAuthentication,
            ThreedsecureioPreAuthenticationResponse,
            PreAuthNRequestData,
            AuthenticationResponseData,
        >,
    > for PreAuthNRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            PreAuthentication,
            ThreedsecureioPreAuthenticationResponse,
            PreAuthNRequestData,
            AuthenticationResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let response = match item.response {
            ThreedsecureioPreAuthenticationResponse::Success(pre_authn_response) => {
                let three_ds_method_data = json!({
                    "threeDSServerTransID": pre_authn_response.threeds_server_trans_id,
                });
                let three_ds_method_data_str = to_string(&three_ds_method_data)
                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)
                    .attach_printable("error while constructing three_ds_method_data_str")?;
                let three_ds_method_data_base64 = BASE64_ENGINE.encode(three_ds_method_data_str);
                let connector_metadata = serde_json::json!(ThreeDSecureIoConnectorMetaData {
                    ds_start_protocol_version: pre_authn_response.ds_start_protocol_version,
                    ds_end_protocol_version: pre_authn_response.ds_end_protocol_version,
                    acs_start_protocol_version: pre_authn_response.acs_start_protocol_version,
                    acs_end_protocol_version: pre_authn_response.acs_end_protocol_version.clone(),
                });
                Ok(AuthenticationResponseData::PreAuthNResponse {
                    threeds_server_transaction_id: pre_authn_response
                        .threeds_server_trans_id
                        .clone(),
                    maximum_supported_3ds_version: common_utils::types::SemanticVersion::from_str(
                        &pre_authn_response.acs_end_protocol_version,
                    )
                    .change_context(errors::ConnectorError::ParsingFailed)?,
                    connector_authentication_id: pre_authn_response.threeds_server_trans_id,
                    three_ds_method_data: Some(three_ds_method_data_base64),
                    three_ds_method_url: pre_authn_response.threeds_method_url,
                    message_version: common_utils::types::SemanticVersion::from_str(
                        &pre_authn_response.acs_end_protocol_version,
                    )
                    .change_context(errors::ConnectorError::ParsingFailed)?,
                    connector_metadata: Some(connector_metadata),
                    directory_server_id: None,
                })
            }
            ThreedsecureioPreAuthenticationResponse::Failure(error_response) => {
                Err(ErrorResponse {
                    code: error_response.error_code,
                    message: error_response
                        .error_description
                        .clone()
                        .unwrap_or(NO_ERROR_MESSAGE.to_owned()),
                    reason: error_response.error_description,
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                })
            }
        };
        Ok(Self {
            response,
            ..item.data.clone()
        })
    }
}

impl
    TryFrom<
        ResponseRouterData<
            Authentication,
            ThreedsecureioAuthenticationResponse,
            ConnectorAuthenticationRequestData,
            AuthenticationResponseData,
        >,
    > for ConnectorAuthenticationRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            Authentication,
            ThreedsecureioAuthenticationResponse,
            ConnectorAuthenticationRequestData,
            AuthenticationResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let response = match item.response {
            ThreedsecureioAuthenticationResponse::Success(response) => {
                let creq = serde_json::json!({
                    "threeDSServerTransID": response.three_dsserver_trans_id,
                    "acsTransID": response.acs_trans_id,
                    "messageVersion": response.message_version,
                    "messageType": "CReq",
                    "challengeWindowSize": "01",
                });
                let creq_str = to_string(&creq)
                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)
                    .attach_printable("error while constructing creq_str")?;
                let creq_base64 = Engine::encode(&BASE64_ENGINE, creq_str)
                    .trim_end_matches('=')
                    .to_owned();
                Ok(AuthenticationResponseData::AuthNResponse {
                    trans_status: response.trans_status.clone().into(),
                    authn_flow_type: if response.trans_status == ThreedsecureioTransStatus::C {
                        AuthNFlowType::Challenge(Box::new(ChallengeParams {
                            acs_url: response.acs_url,
                            challenge_request: Some(creq_base64),
                            acs_reference_number: Some(response.acs_reference_number.clone()),
                            acs_trans_id: Some(response.acs_trans_id.clone()),
                            three_dsserver_trans_id: Some(response.three_dsserver_trans_id),
                            acs_signed_content: response.acs_signed_content,
                            challenge_request_key: None,
                        }))
                    } else {
                        AuthNFlowType::Frictionless
                    },
                    authentication_value: response.authentication_value,
                    connector_metadata: None,
                    ds_trans_id: Some(response.ds_trans_id),
                    eci: None,
                    challenge_code: None,
                    challenge_cancel: None,
                    challenge_code_reason: None,
                    message_extension: None,
                })
            }
            ThreedsecureioAuthenticationResponse::Error(err_response) => match *err_response {
                ThreedsecureioErrorResponseWrapper::ErrorResponse(resp) => Err(ErrorResponse {
                    code: resp.error_code,
                    message: resp
                        .error_description
                        .clone()
                        .unwrap_or(NO_ERROR_MESSAGE.to_owned()),
                    reason: resp.error_description,
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
                ThreedsecureioErrorResponseWrapper::ErrorString(error) => Err(ErrorResponse {
                    code: error.clone(),
                    message: error.clone(),
                    reason: Some(error),
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
            },
        };
        Ok(Self {
            response,
            ..item.data.clone()
        })
    }
}

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

impl TryFrom<&ConnectorAuthType> for ThreedsecureioAuthType {
    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()),
        }
    }
}

impl TryFrom<&ThreedsecureioRouterData<&ConnectorAuthenticationRouterData>>
    for ThreedsecureioAuthenticationRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &ThreedsecureioRouterData<&ConnectorAuthenticationRouterData>,
    ) -> Result<Self, Self::Error> {
        let request = &item.router_data.request;
        //browser_details are mandatory for Browser flows
        let browser_details = match request.browser_details.clone() {
            Some(details) => Ok::<Option<BrowserInformation>, Self::Error>(Some(details)),
            None => {
                if request.device_channel == DeviceChannel::Browser {
                    Err(errors::ConnectorError::MissingRequiredField {
                        field_name: "browser_info",
                    })?
                } else {
                    Ok(None)
                }
            }
        }?;
        let card_details = get_card_details(request.payment_method_data.clone(), "threedsecureio")?;
        let currency = request
            .currency
            .map(|currency| currency.to_string())
            .ok_or(errors::ConnectorError::RequestEncodingFailed)
            .attach_printable("missing field currency")?;
        let purchase_currency: Currency = Currency::from_code(&currency)
            .ok_or(errors::ConnectorError::RequestEncodingFailed)
            .attach_printable("error while parsing Currency")?;
        let billing_address = request.billing_address.address.clone().ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "billing_address.address",
            },
        )?;
        let billing_state = billing_address.clone().to_state_code()?;
        let billing_country = isocountry::CountryCode::for_alpha2(
            &billing_address
                .country
                .ok_or(errors::ConnectorError::MissingRequiredField {
                    field_name: "billing_address.address.country",
                })?
                .to_string(),
        )
        .change_context(errors::ConnectorError::RequestEncodingFailed)
        .attach_printable("Error parsing billing_address.address.country")?;
        let connector_meta_data: ThreeDSecureIoMetaData = item
            .router_data
            .connector_meta_data
            .clone()
            .parse_value("ThreeDSecureIoMetaData")
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let pre_authentication_data = &request.pre_authentication_data;
        let sdk_information = match request.device_channel {
            DeviceChannel::App => Some(item.router_data.request.sdk_information.clone().ok_or(
                errors::ConnectorError::MissingRequiredField {
                    field_name: "sdk_information",
                },
            )?),
            DeviceChannel::Browser => None,
        };
        let (acquirer_bin, acquirer_merchant_id) = pre_authentication_data
            .acquirer_bin
            .clone()
            .zip(pre_authentication_data.acquirer_merchant_id.clone())
            .get_required_value("acquirer_details")
            .change_context(errors::ConnectorError::MissingRequiredField {
                field_name: "acquirer_details",
            })?;
        let meta: ThreeDSecureIoConnectorMetaData =
            to_connector_meta(request.pre_authentication_data.connector_metadata.clone())?;

        let card_holder_name = billing_address.get_optional_full_name();

        Ok(Self {
            ds_start_protocol_version: meta.ds_start_protocol_version.clone(),
            ds_end_protocol_version: meta.ds_end_protocol_version.clone(),
            acs_start_protocol_version: meta.acs_start_protocol_version.clone(),
            acs_end_protocol_version: meta.acs_end_protocol_version.clone(),
            three_dsserver_trans_id: pre_authentication_data
                .threeds_server_transaction_id
                .clone(),
            acct_number: card_details.card_number.clone(),
            notification_url: request
                .return_url
                .clone()
                .ok_or(errors::ConnectorError::RequestEncodingFailed)
                .attach_printable("missing return_url")?,
            three_dscomp_ind: ThreeDSecureIoThreeDsCompletionIndicator::from(
                request.threeds_method_comp_ind.clone(),
            ),
            three_dsrequestor_url: request.three_ds_requestor_url.clone(),
            acquirer_bin,
            acquirer_merchant_id,
            card_expiry_date: card_details.get_expiry_date_as_yymm()?.expose(),
            bill_addr_city: billing_address
                .city
                .clone()
                .ok_or(errors::ConnectorError::MissingRequiredField {
                    field_name: "billing_address.address.city",
                })?
                .to_string(),
            bill_addr_country: billing_country.numeric_id().to_string().into(),
            bill_addr_line1: billing_address.line1.clone().ok_or(
                errors::ConnectorError::MissingRequiredField {
                    field_name: "billing_address.address.line1",
                },
            )?,
            bill_addr_post_code: billing_address.zip.clone().ok_or(
                errors::ConnectorError::MissingRequiredField {
                    field_name: "billing_address.address.zip",
                },
            )?,
            bill_addr_state: billing_state,
            // Indicates the type of Authentication request, "01" for Payment transaction
            three_dsrequestor_authentication_ind: "01".to_string(),
            device_channel: match item.router_data.request.device_channel.clone() {
                DeviceChannel::App => "01",
                DeviceChannel::Browser => "02",
            }
            .to_string(),
            message_category: match item.router_data.request.message_category.clone() {
                MessageCategory::Payment => "01",
                MessageCategory::NonPayment => "02",
            }
            .to_string(),
            browser_javascript_enabled: browser_details
                .as_ref()
                .and_then(|details| details.java_script_enabled),
            browser_accept_header: browser_details
                .as_ref()
                .and_then(|details| details.accept_header.clone()),
            browser_ip: browser_details
                .clone()
                .and_then(|details| details.ip_address.map(|ip| Secret::new(ip.to_string()))),
            browser_java_enabled: browser_details
                .as_ref()
                .and_then(|details| details.java_enabled),
            browser_language: browser_details
                .as_ref()
                .and_then(|details| details.language.clone()),
            browser_color_depth: browser_details
                .as_ref()
                .and_then(|details| details.color_depth.map(|a| a.to_string())),
            browser_screen_height: browser_details
                .as_ref()
                .and_then(|details| details.screen_height.map(|a| a.to_string())),
            browser_screen_width: browser_details
                .as_ref()
                .and_then(|details| details.screen_width.map(|a| a.to_string())),
            browser_tz: browser_details
                .as_ref()
                .and_then(|details| details.time_zone.map(|a| a.to_string())),
            browser_user_agent: browser_details
                .as_ref()
                .and_then(|details| details.user_agent.clone().map(|a| a.to_string())),
            mcc: connector_meta_data.mcc,
            merchant_country_code: connector_meta_data.merchant_country_code,
            merchant_name: connector_meta_data.merchant_name,
            message_type: "AReq".to_string(),
            message_version: pre_authentication_data.message_version.to_string(),
            purchase_amount: item.amount.clone(),
            purchase_currency: purchase_currency.numeric().to_string(),
            trans_type: "01".to_string(),
            purchase_exponent: purchase_currency
                .exponent()
                .ok_or(errors::ConnectorError::RequestEncodingFailed)
                .attach_printable("missing purchase_exponent")?
                .to_string(),
            purchase_date: date_time::DateTime::<date_time::YYYYMMDDHHmmss>::from(date_time::now())
                .to_string(),
            sdk_app_id: sdk_information
                .as_ref()
                .map(|sdk_info| sdk_info.sdk_app_id.clone()),
            sdk_enc_data: sdk_information
                .as_ref()
                .map(|sdk_info| sdk_info.sdk_enc_data.clone()),
            sdk_ephem_pub_key: sdk_information
                .as_ref()
                .map(|sdk_info| sdk_info.sdk_ephem_pub_key.clone()),
            sdk_reference_number: sdk_information
                .as_ref()
                .map(|sdk_info| sdk_info.sdk_reference_number.clone()),
            sdk_trans_id: sdk_information
                .as_ref()
                .map(|sdk_info| sdk_info.sdk_trans_id.clone()),
            sdk_max_timeout: sdk_information
                .as_ref()
                .map(|sdk_info| sdk_info.sdk_max_timeout.to_string()),
            device_render_options: match request.device_channel {
                DeviceChannel::App => Some(DeviceRenderOptions {
                    // SDK Interface types that the device supports for displaying specific challenge user interfaces within the SDK, 01 for Native
                    sdk_interface: "01".to_string(),
                    // UI types that the device supports for displaying specific challenge user interfaces within the SDK, 01 for Text
                    sdk_ui_type: vec!["01".to_string()],
                }),
                DeviceChannel::Browser => None,
            },
            cardholder_name: card_holder_name,
            email: request.email.clone(),
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreedsecureioErrorResponse {
    pub error_code: String,
    pub error_component: Option<String>,
    pub error_description: Option<String>,
    pub error_detail: Option<String>,
    pub error_message_type: Option<String>,
    pub message_type: Option<String>,
    pub message_version: Option<String>,
    pub three_dsserver_trans_id: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ThreedsecureioErrorResponseWrapper {
    ErrorResponse(ThreedsecureioErrorResponse),
    ErrorString(String),
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ThreedsecureioAuthenticationResponse {
    Success(Box<ThreedsecureioAuthenticationSuccessResponse>),
    Error(Box<ThreedsecureioErrorResponseWrapper>),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreedsecureioAuthenticationSuccessResponse {
    #[serde(rename = "acsChallengeMandated")]
    pub acs_challenge_mandated: Option<String>,
    #[serde(rename = "acsOperatorID")]
    pub acs_operator_id: Option<String>,
    #[serde(rename = "acsReferenceNumber")]
    pub acs_reference_number: String,
    #[serde(rename = "acsTransID")]
    pub acs_trans_id: String,
    #[serde(rename = "acsURL")]
    pub acs_url: Option<url::Url>,
    #[serde(rename = "authenticationType")]
    pub authentication_type: Option<String>,
    #[serde(rename = "dsReferenceNumber")]
    pub ds_reference_number: String,
    #[serde(rename = "dsTransID")]
    pub ds_trans_id: String,
    #[serde(rename = "messageType")]
    pub message_type: Option<String>,
    #[serde(rename = "messageVersion")]
    pub message_version: String,
    #[serde(rename = "threeDSServerTransID")]
    pub three_dsserver_trans_id: String,
    #[serde(rename = "transStatus")]
    pub trans_status: ThreedsecureioTransStatus,
    #[serde(rename = "acsSignedContent")]
    pub acs_signed_content: Option<String>,
    #[serde(rename = "authenticationValue")]
    pub authentication_value: Option<Secret<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum ThreeDSecureIoThreeDsCompletionIndicator {
    Y,
    N,
    U,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreedsecureioAuthenticationRequest {
    pub ds_start_protocol_version: String,
    pub ds_end_protocol_version: String,
    pub acs_start_protocol_version: String,
    pub acs_end_protocol_version: String,
    pub three_dsserver_trans_id: String,
    pub acct_number: cards::CardNumber,
    pub notification_url: String,
    pub three_dscomp_ind: ThreeDSecureIoThreeDsCompletionIndicator,
    pub three_dsrequestor_url: String,
    pub acquirer_bin: String,
    pub acquirer_merchant_id: String,
    pub card_expiry_date: String,
    pub bill_addr_city: String,
    pub bill_addr_country: Secret<String>,
    pub bill_addr_line1: Secret<String>,
    pub bill_addr_post_code: Secret<String>,
    pub bill_addr_state: Secret<String>,
    pub email: Option<common_utils::pii::Email>,
    pub three_dsrequestor_authentication_ind: String,
    pub cardholder_name: Option<Secret<String>>,
    pub device_channel: String,
    pub browser_javascript_enabled: Option<bool>,
    pub browser_accept_header: Option<String>,
    pub browser_ip: Option<Secret<String, common_utils::pii::IpAddress>>,
    pub browser_java_enabled: Option<bool>,
    pub browser_language: Option<String>,
    pub browser_color_depth: Option<String>,
    pub browser_screen_height: Option<String>,
    pub browser_screen_width: Option<String>,
    pub browser_tz: Option<String>,
    pub browser_user_agent: Option<String>,
    pub sdk_app_id: Option<String>,
    pub sdk_enc_data: Option<String>,
    pub sdk_ephem_pub_key: Option<std::collections::HashMap<String, String>>,
    pub sdk_reference_number: Option<String>,
    pub sdk_trans_id: Option<String>,
    pub mcc: String,
    pub merchant_country_code: String,
    pub merchant_name: String,
    pub message_category: String,
    pub message_type: String,
    pub message_version: String,
    pub purchase_amount: String,
    pub purchase_currency: String,
    pub purchase_exponent: String,
    pub purchase_date: String,
    pub trans_type: String,
    pub sdk_max_timeout: Option<String>,
    pub device_render_options: Option<DeviceRenderOptions>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ThreeDSecureIoMetaData {
    pub mcc: String,
    pub merchant_country_code: String,
    pub merchant_name: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ThreeDSecureIoConnectorMetaData {
    pub ds_start_protocol_version: String,
    pub ds_end_protocol_version: String,
    pub acs_start_protocol_version: String,
    pub acs_end_protocol_version: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeviceRenderOptions {
    pub sdk_interface: String,
    pub sdk_ui_type: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreedsecureioPreAuthenticationRequest {
    acct_number: cards::CardNumber,
    ds: Option<DirectoryServer>,
}

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

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreedsecureioPostAuthenticationResponse {
    pub authentication_value: Option<Secret<String>>,
    pub trans_status: ThreedsecureioTransStatus,
    pub eci: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub enum ThreedsecureioTransStatus {
    /// Authentication/ Account Verification Successful
    Y,
    /// Not Authenticated /Account Not Verified; Transaction denied
    N,
    /// Authentication/ Account Verification Could Not Be Performed; Technical or other problem, as indicated in ARes or RReq
    U,
    /// Attempts Processing Performed; Not Authenticated/Verified , but a proof of attempted authentication/verification is provided
    A,
    /// Authentication/ Account Verification Rejected; Issuer is rejecting authentication/verification and request that authorisation not be attempted.
    R,
    C,
}

impl From<ThreeDsCompletionIndicator> for ThreeDSecureIoThreeDsCompletionIndicator {
    fn from(value: ThreeDsCompletionIndicator) -> Self {
        match value {
            ThreeDsCompletionIndicator::Success => Self::Y,
            ThreeDsCompletionIndicator::Failure => Self::N,
            ThreeDsCompletionIndicator::NotAvailable => Self::U,
        }
    }
}

impl From<ThreedsecureioTransStatus> for common_enums::TransactionStatus {
    fn from(value: ThreedsecureioTransStatus) -> Self {
        match value {
            ThreedsecureioTransStatus::Y => Self::Success,
            ThreedsecureioTransStatus::N => Self::Failure,
            ThreedsecureioTransStatus::U => Self::VerificationNotPerformed,
            ThreedsecureioTransStatus::A => Self::NotVerified,
            ThreedsecureioTransStatus::R => Self::Rejected,
            ThreedsecureioTransStatus::C => Self::ChallengeRequired,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum DirectoryServer {
    Standin,
    Visa,
    Mastercard,
    Jcb,
    Upi,
    Amex,
    Protectbuy,
    Sbn,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ThreedsecureioPreAuthenticationResponse {
    Success(Box<ThreedsecureioPreAuthenticationResponseData>),
    Failure(Box<ThreedsecureioErrorResponse>),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreedsecureioPreAuthenticationResponseData {
    pub ds_start_protocol_version: String,
    pub ds_end_protocol_version: String,
    pub acs_start_protocol_version: String,
    pub acs_end_protocol_version: String,
    #[serde(rename = "threeDSMethodURL")]
    pub threeds_method_url: Option<String>,
    #[serde(rename = "threeDSServerTransID")]
    pub threeds_server_trans_id: String,
    pub scheme: Option<String>,
    pub message_type: Option<String>,
}

impl TryFrom<&ThreedsecureioRouterData<&PreAuthNRouterData>>
    for ThreedsecureioPreAuthenticationRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        value: &ThreedsecureioRouterData<&PreAuthNRouterData>,
    ) -> Result<Self, Self::Error> {
        let router_data = value.router_data;
        Ok(Self {
            acct_number: router_data.request.card.card_number.clone(),
            ds: None,
        })
    }
}


// File: crates/hyperswitch_connectors/src/connectors/globalpay/response.rs

use common_enums::Currency;
use common_utils::types::StringMinorUnit;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::utils::deserialize_optional_currency;

#[derive(Debug, Serialize, Deserialize)]
pub struct GlobalpayPaymentsResponse {
    pub status: GlobalpayPaymentStatus,
    pub payment_method: Option<PaymentMethod>,
    pub id: String,
    pub amount: Option<StringMinorUnit>,
    #[serde(deserialize_with = "deserialize_optional_currency")]
    pub currency: Option<Currency>,
    pub reference: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct GlobalpayRefreshTokenResponse {
    pub token: Secret<String>,
    pub seconds_to_expire: i64,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct GlobalpayRefreshTokenErrorResponse {
    pub error_code: String,
    pub detailed_error_description: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PaymentMethod {
    pub apm: Option<Apm>,
    pub card: Option<Card>,
    pub id: Option<Secret<String>>,
    pub message: Option<String>,
    pub result: Option<String>,
}

/// Data associated with the response of an APM transaction.
#[derive(Debug, Serialize, Deserialize)]
pub struct Apm {
    #[serde(alias = "provider_redirect_url")]
    pub redirect_url: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Card {
    pub brand_reference: Option<Secret<String>>,
}

/// Indicates where a transaction is in its lifecycle.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GlobalpayPaymentStatus {
    /// A Transaction has been successfully authorized and captured. The funding
    /// process will commence once the transaction remains in this status.
    Captured,
    /// A Transaction where the payment method provider declined the transfer of
    /// funds between the payer and the merchant.
    Declined,
    /// A Transaction where the funds have transferred between payer and merchant as
    /// expected.
    Funded,
    /// A Transaction has been successfully initiated. An update on its status is
    /// expected via a separate asynchronous notification to a webhook.
    Initiated,
    /// A Transaction has been sent to the payment method provider and are waiting
    /// for a result.
    Pending,
    /// A Transaction has been approved but a capture request is required to
    /// commence the movement of funds.
    Preauthorized,
    /// A Transaction where the funds were expected to transfer between payer and
    /// merchant but the transfer was rejected during the funding process. This rarely happens
    /// but when it does it is usually addressed by Global Payments operations.
    Rejected,
    /// A Transaction that had a status of PENDING, PREAUTHORIZED or CAPTURED has
    /// subsequently been reversed which voids/cancels a transaction before it is funded.
    Reversed,
}

#[derive(Debug, Deserialize)]
pub struct GlobalpayWebhookObjectId {
    pub id: String,
}

#[derive(Debug, Deserialize)]
pub struct GlobalpayWebhookObjectEventType {
    pub status: GlobalpayWebhookStatus,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GlobalpayWebhookStatus {
    Declined,
    Captured,
    #[serde(other)]
    Unknown,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GlobalpayPaymentMethodsResponse {
    #[serde(rename = "id")]
    pub payment_method_token_id: Option<Secret<String>>,
    pub card: Card,
}