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

use common_utils::types::StringMinorUnit;
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{ExternalVaultInsertFlow, ExternalVaultRetrieveFlow},
    router_request_types::VaultRequestData,
    router_response_types::{VaultIdType, VaultResponseData},
    types::VaultRouterData,
    vault::{PaymentMethodCustomVaultingData, PaymentMethodVaultingData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::types::ResponseRouterData;

pub struct TokenexRouterData<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 TokenexRouterData<T> {
    fn from((amount, item): (StringMinorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Default, Debug, Serialize, PartialEq)]
pub struct TokenexInsertRequest {
    data: cards::CardNumber, //Currently only card number is tokenized. Data can be stringified and can be tokenized
}

impl<F> TryFrom<&VaultRouterData<F>> for TokenexInsertRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &VaultRouterData<F>) -> Result<Self, Self::Error> {
        match item.request.payment_method_vaulting_data.clone() {
            Some(PaymentMethodCustomVaultingData::CardData(req_card)) => Ok(Self {
                data: req_card.card_number.clone().ok_or(
                    errors::ConnectorError::MissingRequiredField {
                        field_name: "card_number",
                    },
                )?,
            }),
            _ => Err(errors::ConnectorError::NotImplemented(
                "Payment method apart from card".to_string(),
            )
            .into()),
        }
    }
}
pub struct TokenexAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) tokenex_id: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for TokenexAuthType {
    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(),
                tokenex_id: key1.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TokenexInsertResponse {
    token: Option<String>,
    first_six: Option<String>,
    last_four: Option<String>,
    success: bool,
    error: String,
    message: Option<String>,
}
impl
    TryFrom<
        ResponseRouterData<
            ExternalVaultInsertFlow,
            TokenexInsertResponse,
            VaultRequestData,
            VaultResponseData,
        >,
    > for RouterData<ExternalVaultInsertFlow, VaultRequestData, VaultResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            ExternalVaultInsertFlow,
            TokenexInsertResponse,
            VaultRequestData,
            VaultResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let resp = item.response;
        match resp.success && resp.error.is_empty() {
            true => {
                let token = resp
                    .token
                    .clone()
                    .ok_or(errors::ConnectorError::ResponseDeserializationFailed)
                    .attach_printable("Token is missing in tokenex response")?;
                Ok(Self {
                    status: common_enums::AttemptStatus::Started,
                    response: Ok(VaultResponseData::ExternalVaultInsertResponse {
                        connector_vault_id: VaultIdType::SingleVaultId(token.clone()),
                        //fingerprint is not provided by tokenex, using token as fingerprint
                        fingerprint_id: token.clone(),
                    }),
                    ..item.data
                })
            }
            false => {
                let (code, message) = resp.error.split_once(':').unwrap_or(("", ""));

                let response = Err(ErrorResponse {
                    code: code.to_string(),
                    message: message.to_string(),
                    reason: resp.message,
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_decline_code: None,
                    network_advice_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                });

                Ok(Self {
                    response,
                    ..item.data
                })
            }
        }
    }
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TokenexRetrieveRequest {
    token: Secret<String>, //Currently only card number is tokenized. Data can be stringified and can be tokenized
    cache_cvv: bool,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TokenexRetrieveResponse {
    value: Option<cards::CardNumber>,
    success: bool,
    error: String,
    message: Option<String>,
}

impl<F> TryFrom<&VaultRouterData<F>> for TokenexRetrieveRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &VaultRouterData<F>) -> Result<Self, Self::Error> {
        let connector_vault_id = item.request.connector_vault_id.as_ref().ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "connector_vault_id",
            },
        )?;
        Ok(Self {
            token: Secret::new(connector_vault_id.clone()),
            cache_cvv: false, //since cvv is not stored at tokenex
        })
    }
}

impl
    TryFrom<
        ResponseRouterData<
            ExternalVaultRetrieveFlow,
            TokenexRetrieveResponse,
            VaultRequestData,
            VaultResponseData,
        >,
    > for RouterData<ExternalVaultRetrieveFlow, VaultRequestData, VaultResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            ExternalVaultRetrieveFlow,
            TokenexRetrieveResponse,
            VaultRequestData,
            VaultResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let resp = item.response;

        match resp.success && resp.error.is_empty() {
            true => {
                let data = resp
                    .value
                    .clone()
                    .ok_or(errors::ConnectorError::ResponseDeserializationFailed)
                    .attach_printable("Card number is missing in tokenex response")?;
                Ok(Self {
                    status: common_enums::AttemptStatus::Started,
                    response: Ok(VaultResponseData::ExternalVaultRetrieveResponse {
                        vault_data: PaymentMethodVaultingData::CardNumber(data),
                    }),
                    ..item.data
                })
            }
            false => {
                let (code, message) = resp.error.split_once(':').unwrap_or(("", ""));

                let response = Err(ErrorResponse {
                    code: code.to_string(),
                    message: message.to_string(),
                    reason: resp.message,
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_decline_code: None,
                    network_advice_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                });

                Ok(Self {
                    response,
                    ..item.data
                })
            }
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TokenexErrorResponse {
    pub error: String,
    pub message: String,
}


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

use cards::CardNumber;
use common_enums::enums;
use common_utils::{pii::Email, request::Method, types::StringMajorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankDebitData, BankRedirectData, PaymentMethodData, WalletData},
    router_data::{ConnectorAuthType, PaymentMethodToken, RouterData},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types,
};
use hyperswitch_interfaces::errors;
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    unimplemented_payment_method,
    utils::{
        AddressDetailsData, BrowserInformationData, CardData as CardDataUtil,
        PaymentMethodTokenizationRequestData, PaymentsAuthorizeRequestData, RouterData as _,
    },
};

type Error = error_stack::Report<errors::ConnectorError>;

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

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

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MolliePaymentsRequest {
    amount: Amount,
    description: String,
    redirect_url: String,
    cancel_url: Option<String>,
    webhook_url: String,
    locale: Option<String>,
    #[serde(flatten)]
    payment_method_data: MolliePaymentMethodData,
    metadata: Option<MollieMetadata>,
    sequence_type: SequenceType,
    mandate_id: Option<Secret<String>>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct Amount {
    currency: enums::Currency,
    value: StringMajorUnit,
}

#[derive(Debug, Serialize)]
#[serde(tag = "method")]
#[serde(rename_all = "lowercase")]
pub enum MolliePaymentMethodData {
    Applepay(Box<ApplePayMethodData>),
    Eps,
    Giropay,
    Ideal(Box<IdealMethodData>),
    Paypal(Box<PaypalMethodData>),
    Sofort,
    Przelewy24(Box<Przelewy24MethodData>),
    Bancontact,
    CreditCard(Box<CreditCardMethodData>),
    DirectDebit(Box<DirectDebitMethodData>),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayMethodData {
    apple_pay_payment_token: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IdealMethodData {
    issuer: Option<Secret<String>>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaypalMethodData {
    billing_address: Option<Address>,
    shipping_address: Option<Address>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Przelewy24MethodData {
    billing_email: Option<Email>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DirectDebitMethodData {
    consumer_name: Option<Secret<String>>,
    consumer_account: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreditCardMethodData {
    billing_address: Option<Address>,
    shipping_address: Option<Address>,
    card_token: Option<Secret<String>>,
}

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SequenceType {
    #[default]
    Oneoff,
    First,
    Recurring,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Address {
    pub street_and_number: Secret<String>,
    pub postal_code: Secret<String>,
    pub city: String,
    pub region: Option<Secret<String>>,
    pub country: api_models::enums::CountryAlpha2,
}

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

impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MolliePaymentsRequest {
    type Error = Error;
    fn try_from(
        item: &MollieRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let amount = Amount {
            currency: item.router_data.request.currency,
            value: item.amount.clone(),
        };
        let description = item.router_data.get_description()?;
        let redirect_url = item.router_data.request.get_router_return_url()?;
        let payment_method_data = match item.router_data.request.capture_method.unwrap_or_default()
        {
            enums::CaptureMethod::Automatic | enums::CaptureMethod::SequentialAutomatic => {
                match &item.router_data.request.payment_method_data {
                    PaymentMethodData::Card(_) => {
                        let pm_token = item.router_data.get_payment_method_token()?;
                        Ok(MolliePaymentMethodData::CreditCard(Box::new(
                            CreditCardMethodData {
                                billing_address: get_billing_details(item.router_data)?,
                                shipping_address: get_shipping_details(item.router_data)?,
                                card_token: Some(match pm_token {
                                    PaymentMethodToken::Token(token) => token,
                                    PaymentMethodToken::ApplePayDecrypt(_) => {
                                        Err(unimplemented_payment_method!(
                                            "Apple Pay",
                                            "Simplified",
                                            "Mollie"
                                        ))?
                                    }
                                    PaymentMethodToken::PazeDecrypt(_) => {
                                        Err(unimplemented_payment_method!("Paze", "Mollie"))?
                                    }
                                    PaymentMethodToken::GooglePayDecrypt(_) => {
                                        Err(unimplemented_payment_method!("Google Pay", "Mollie"))?
                                    }
                                }),
                            },
                        )))
                    }
                    PaymentMethodData::BankRedirect(ref redirect_data) => {
                        MolliePaymentMethodData::try_from((item.router_data, redirect_data))
                    }
                    PaymentMethodData::Wallet(ref wallet_data) => {
                        get_payment_method_for_wallet(item.router_data, wallet_data)
                    }
                    PaymentMethodData::BankDebit(ref directdebit_data) => {
                        MolliePaymentMethodData::try_from((directdebit_data, item.router_data))
                    }
                    _ => Err(
                        errors::ConnectorError::NotImplemented("Payment Method".to_string()).into(),
                    ),
                }
            }
            _ => Err(errors::ConnectorError::FlowNotSupported {
                flow: format!(
                    "{} capture",
                    item.router_data.request.capture_method.unwrap_or_default()
                ),
                connector: "Mollie".to_string(),
            }
            .into()),
        }?;
        Ok(Self {
            amount,
            description,
            redirect_url,
            cancel_url: None,
            /* webhook_url is a mandatory field.
            But we can't support webhook in our core hence keeping it as empty string */
            webhook_url: "".to_string(),
            locale: None,
            payment_method_data,
            metadata: Some(MollieMetadata {
                order_id: item.router_data.connector_request_reference_id.clone(),
            }),
            sequence_type: SequenceType::Oneoff,
            mandate_id: None,
        })
    }
}

impl TryFrom<(&types::PaymentsAuthorizeRouterData, &BankRedirectData)> for MolliePaymentMethodData {
    type Error = Error;
    fn try_from(
        (item, value): (&types::PaymentsAuthorizeRouterData, &BankRedirectData),
    ) -> Result<Self, Self::Error> {
        match value {
            BankRedirectData::Eps { .. } => Ok(Self::Eps),
            BankRedirectData::Giropay { .. } => Ok(Self::Giropay),
            BankRedirectData::Ideal { .. } => {
                Ok(Self::Ideal(Box::new(IdealMethodData {
                    // To do if possible this should be from the payment request
                    issuer: None,
                })))
            }
            BankRedirectData::Sofort { .. } => Ok(Self::Sofort),
            BankRedirectData::Przelewy24 { .. } => {
                Ok(Self::Przelewy24(Box::new(Przelewy24MethodData {
                    billing_email: item.get_optional_billing_email(),
                })))
            }
            BankRedirectData::BancontactCard { .. } => Ok(Self::Bancontact),
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

impl TryFrom<(&BankDebitData, &types::PaymentsAuthorizeRouterData)> for MolliePaymentMethodData {
    type Error = Error;
    fn try_from(
        (bank_debit_data, item): (&BankDebitData, &types::PaymentsAuthorizeRouterData),
    ) -> Result<Self, Self::Error> {
        match bank_debit_data {
            BankDebitData::SepaBankDebit { iban, .. } => {
                Ok(Self::DirectDebit(Box::new(DirectDebitMethodData {
                    consumer_name: item.get_optional_billing_full_name(),
                    consumer_account: iban.clone(),
                })))
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MollieCardTokenRequest {
    card_holder: Secret<String>,
    card_number: CardNumber,
    card_cvv: Secret<String>,
    card_expiry_date: Secret<String>,
    locale: String,
    testmode: bool,
    profile_token: Secret<String>,
}

impl TryFrom<&types::TokenizationRouterData> for MollieCardTokenRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::TokenizationRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(ccard) => {
                let auth = MollieAuthType::try_from(&item.connector_auth_type)?;
                let card_holder = item
                    .get_optional_billing_full_name()
                    .unwrap_or(Secret::new("".to_string()));
                let card_number = ccard.card_number.clone();
                let card_expiry_date =
                    ccard.get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned())?;
                let card_cvv = ccard.card_cvc;
                let locale = item.request.get_browser_info()?.get_language()?;
                let testmode =
                    item.test_mode
                        .ok_or(errors::ConnectorError::MissingRequiredField {
                            field_name: "test_mode",
                        })?;
                let profile_token = auth
                    .profile_token
                    .ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
                Ok(Self {
                    card_holder,
                    card_number,
                    card_cvv,
                    card_expiry_date,
                    locale,
                    testmode,
                    profile_token,
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented(
                "Payment Method".to_string(),
            ))?,
        }
    }
}

fn get_payment_method_for_wallet(
    item: &types::PaymentsAuthorizeRouterData,
    wallet_data: &WalletData,
) -> Result<MolliePaymentMethodData, Error> {
    match wallet_data {
        WalletData::PaypalRedirect { .. } => Ok(MolliePaymentMethodData::Paypal(Box::new(
            PaypalMethodData {
                billing_address: get_billing_details(item)?,
                shipping_address: get_shipping_details(item)?,
            },
        ))),
        WalletData::ApplePay(applepay_wallet_data) => {
            let apple_pay_encrypted_data = applepay_wallet_data
                .payment_data
                .get_encrypted_apple_pay_payment_data_mandatory()
                .change_context(errors::ConnectorError::MissingRequiredField {
                    field_name: "Apple pay encrypted data",
                })?;
            Ok(MolliePaymentMethodData::Applepay(Box::new(
                ApplePayMethodData {
                    apple_pay_payment_token: Secret::new(apple_pay_encrypted_data.to_owned()),
                },
            )))
        }
        _ => Err(errors::ConnectorError::NotImplemented("Payment Method".to_string()).into()),
    }
}

fn get_shipping_details(
    item: &types::PaymentsAuthorizeRouterData,
) -> Result<Option<Address>, Error> {
    let shipping_address = item
        .get_optional_shipping()
        .and_then(|shipping| shipping.address.as_ref());
    get_address_details(shipping_address)
}

fn get_billing_details(
    item: &types::PaymentsAuthorizeRouterData,
) -> Result<Option<Address>, Error> {
    let billing_address = item
        .get_optional_billing()
        .and_then(|billing| billing.address.as_ref());
    get_address_details(billing_address)
}

fn get_address_details(
    address: Option<&hyperswitch_domain_models::address::AddressDetails>,
) -> Result<Option<Address>, Error> {
    let address_details = match address {
        Some(address) => {
            let street_and_number = address.get_combined_address_line()?;
            let postal_code = address.get_zip()?.to_owned();
            let city = address.get_city()?.to_owned();
            let region = None;
            let country = address.get_country()?.to_owned();
            Some(Address {
                street_and_number,
                postal_code,
                city,
                region,
                country,
            })
        }
        None => None,
    };
    Ok(address_details)
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MolliePaymentsResponse {
    pub resource: String,
    pub id: String,
    pub amount: Amount,
    pub description: Option<String>,
    pub metadata: Option<MollieMetadata>,
    pub status: MolliePaymentStatus,
    pub is_cancelable: Option<bool>,
    pub sequence_type: SequenceType,
    pub redirect_url: Option<String>,
    pub webhook_url: Option<String>,
    #[serde(rename = "_links")]
    pub links: Links,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum MolliePaymentStatus {
    Open,
    Canceled,
    #[default]
    Pending,
    Authorized,
    Expired,
    Failed,
    Paid,
}

impl From<MolliePaymentStatus> for enums::AttemptStatus {
    fn from(item: MolliePaymentStatus) -> Self {
        match item {
            MolliePaymentStatus::Paid => Self::Charged,
            MolliePaymentStatus::Failed => Self::Failure,
            MolliePaymentStatus::Pending => Self::Pending,
            MolliePaymentStatus::Open => Self::AuthenticationPending,
            MolliePaymentStatus::Canceled => Self::Voided,
            MolliePaymentStatus::Authorized => Self::Authorized,
            MolliePaymentStatus::Expired => Self::Failure,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Link {
    href: Url,
    #[serde(rename = "type")]
    type_: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Links {
    #[serde(rename = "self")]
    self_: Option<Link>,
    checkout: Option<Link>,
    dashboard: Option<Link>,
    documentation: Option<Link>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CardDetails {
    pub card_number: Secret<String>,
    pub card_holder: Secret<String>,
    pub card_expiry_date: Secret<String>,
    pub card_cvv: Secret<String>,
}

pub struct MollieAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) profile_token: Option<Secret<String>>,
}

impl TryFrom<&ConnectorAuthType> for MollieAuthType {
    type Error = Error;
    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(),
                profile_token: None,
            }),
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                api_key: api_key.to_owned(),
                profile_token: Some(key1.to_owned()),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType)?,
        }
    }
}

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

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

impl<F, T> TryFrom<ResponseRouterData<F, MolliePaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = Error;
    fn try_from(
        item: ResponseRouterData<F, MolliePaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let url = item
            .response
            .links
            .checkout
            .map(|link| RedirectForm::from((link.href, Method::Get)));
        Ok(Self {
            status: enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
                redirection_data: Box::new(url),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.id),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

// REFUND :
#[derive(Default, Debug, Serialize)]
pub struct MollieRefundRequest {
    amount: Amount,
    description: Option<String>,
    metadata: Option<MollieMetadata>,
}

impl<F> TryFrom<&MollieRouterData<&types::RefundsRouterData<F>>> for MollieRefundRequest {
    type Error = Error;
    fn try_from(
        item: &MollieRouterData<&types::RefundsRouterData<F>>,
    ) -> Result<Self, Self::Error> {
        let amount = Amount {
            currency: item.router_data.request.currency,
            value: item.amount.clone(),
        };
        Ok(Self {
            amount,
            description: item.router_data.request.reason.to_owned(),
            metadata: Some(MollieMetadata {
                order_id: item.router_data.request.refund_id.clone(),
            }),
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RefundResponse {
    resource: String,
    id: String,
    amount: Amount,
    settlement_id: Option<String>,
    settlement_amount: Option<Amount>,
    status: MollieRefundStatus,
    description: Option<String>,
    metadata: Option<MollieMetadata>,
    payment_id: String,
    #[serde(rename = "_links")]
    links: Links,
}

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MollieRefundStatus {
    Queued,
    #[default]
    Pending,
    Processing,
    Refunded,
    Failed,
    Canceled,
}

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

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

#[derive(Debug, Deserialize, Serialize)]
pub struct MollieErrorResponse {
    pub status: u16,
    pub title: Option<String>,
    pub detail: String,
    pub field: Option<String>,
}


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

use common_utils::types::MinorUnit;
use error_stack::Report;
use hyperswitch_domain_models::router_data::ConnectorAuthType;
use hyperswitch_interfaces::errors::ConnectorError;
use masking::Secret;
use serde::Serialize;

#[cfg(feature = "payouts")]
pub mod payouts;
#[cfg(feature = "payouts")]
pub use payouts::*;

// Error signature
type Error = Report<ConnectorError>;

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

impl TryFrom<&ConnectorAuthType> for AdyenplatformAuthType {
    type Error = Error;
    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(ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

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

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


// File: crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs

use api_models::{payouts, webhooks};
use common_enums::enums;
use common_utils::pii;
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::types::{self, PayoutsRouterData};
use hyperswitch_interfaces::errors::ConnectorError;
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};

use super::{AdyenPlatformRouterData, Error};
use crate::{
    connectors::adyen::transformers as adyen,
    types::PayoutsResponseRouterData,
    utils::{
        self, AddressDetailsData, CardData, PayoutFulfillRequestData, PayoutsData as _,
        RouterData as _,
    },
};

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

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

#[serde_with::skip_serializing_none]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenTransferRequest {
    amount: adyen::Amount,
    balance_account_id: Secret<String>,
    category: AdyenPayoutMethod,
    counterparty: AdyenPayoutMethodDetails,
    priority: Option<AdyenPayoutPriority>,
    reference: String,
    reference_for_beneficiary: String,
    description: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AdyenPayoutMethod {
    Bank,
    Card,
    PlatformPayment,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum AdyenPayoutMethodDetails {
    BankAccount(AdyenBankAccountDetails),
    Card(AdyenCardDetails),
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenBankAccountDetails {
    account_holder: AdyenAccountHolder,
    account_identification: AdyenBankAccountIdentification,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenAccountHolder {
    address: Option<AdyenAddress>,
    first_name: Option<Secret<String>>,
    last_name: Option<Secret<String>>,
    full_name: Option<Secret<String>>,
    email: Option<pii::Email>,
    #[serde(rename = "reference")]
    customer_id: Option<String>,
    #[serde(rename = "type")]
    entity_type: Option<EntityType>,
}

#[serde_with::skip_serializing_none]
#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenAddress {
    line1: Secret<String>,
    line2: Secret<String>,
    postal_code: Option<Secret<String>>,
    state_or_province: Option<Secret<String>>,
    city: String,
    country: enums::CountryAlpha2,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AdyenBankAccountIdentification {
    #[serde(rename = "type")]
    bank_type: String,
    #[serde(flatten)]
    account_details: AdyenBankAccountIdentificationDetails,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AdyenBankAccountIdentificationDetails {
    Sepa(SepaDetails),
}

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

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenCardDetails {
    card_holder: AdyenAccountHolder,
    card_identification: AdyenCardIdentification,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum AdyenCardIdentification {
    Card(AdyenRawCardIdentification),
    Stored(AdyenStoredCardIdentification),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenRawCardIdentification {
    #[serde(rename = "number")]
    card_number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    issue_number: Option<String>,
    start_month: Option<String>,
    start_year: Option<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenStoredCardIdentification {
    stored_payment_method_id: Secret<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AdyenPayoutPriority {
    Instant,
    Fast,
    Regular,
    Wire,
    CrossBorder,
    Internal,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntityType {
    Individual,
    Organization,
    Unknown,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenTransferResponse {
    id: String,
    account_holder: AdyenPlatformAccountHolder,
    amount: adyen::Amount,
    balance_account: AdyenBalanceAccount,
    category: AdyenPayoutMethod,
    category_data: Option<AdyenCategoryData>,
    direction: AdyenTransactionDirection,
    reference: String,
    reference_for_beneficiary: String,
    status: AdyenTransferStatus,
    #[serde(rename = "type")]
    transaction_type: AdyenTransactionType,
    reason: String,
}

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

#[derive(Debug, Serialize, Deserialize)]
pub struct AdyenCategoryData {
    priority: AdyenPayoutPriority,
    #[serde(rename = "type")]
    category: AdyenPayoutMethod,
}

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

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AdyenTransactionDirection {
    Incoming,
    Outgoing,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, strum::Display)]
#[serde(rename_all = "lowercase")]
pub enum AdyenTransferStatus {
    Authorised,
    Refused,
    Error,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AdyenTransactionType {
    BankTransfer,
    CardTransfer,
    InternalTransfer,
    Payment,
    Refund,
}

impl TryFrom<&hyperswitch_domain_models::address::AddressDetails> for AdyenAddress {
    type Error = Error;

    fn try_from(
        address: &hyperswitch_domain_models::address::AddressDetails,
    ) -> Result<Self, Self::Error> {
        let line1 = address
            .get_line1()
            .change_context(ConnectorError::MissingRequiredField {
                field_name: "billing.address.line1",
            })?
            .clone();
        let line2 = address
            .get_line2()
            .change_context(ConnectorError::MissingRequiredField {
                field_name: "billing.address.line2",
            })?
            .clone();
        Ok(Self {
            line1,
            line2,
            postal_code: address.get_optional_zip(),
            state_or_province: address.get_optional_state(),
            city: address.get_city()?.to_owned(),
            country: address.get_country()?.to_owned(),
        })
    }
}

impl<F> TryFrom<(&PayoutsRouterData<F>, &payouts::CardPayout)> for AdyenAccountHolder {
    type Error = Error;

    fn try_from(
        (router_data, card): (&PayoutsRouterData<F>, &payouts::CardPayout),
    ) -> Result<Self, Self::Error> {
        let billing_address = router_data.get_optional_billing();

        let address = billing_address
            .and_then(|billing| billing.address.as_ref().map(|addr| addr.try_into()))
            .transpose()?
            .ok_or(ConnectorError::MissingRequiredField {
                field_name: "address",
            })?;

        let (first_name, last_name) = if let Some(card_holder_name) = &card.card_holder_name {
            let exposed_name = card_holder_name.clone().expose();
            let name_parts: Vec<&str> = exposed_name.split_whitespace().collect();
            let first_name = name_parts
                .first()
                .map(|s| Secret::new(s.to_string()))
                .ok_or(ConnectorError::MissingRequiredField {
                    field_name: "card_holder_name.first_name",
                })?;
            let last_name = if name_parts.len() > 1 {
                let remaining_names: Vec<&str> = name_parts.iter().skip(1).copied().collect();
                Some(Secret::new(remaining_names.join(" ")))
            } else {
                return Err(ConnectorError::MissingRequiredField {
                    field_name: "card_holder_name.last_name",
                }
                .into());
            };
            (Some(first_name), last_name)
        } else {
            return Err(ConnectorError::MissingRequiredField {
                field_name: "card_holder_name",
            }
            .into());
        };

        let customer_id_reference = router_data.get_connector_customer_id()?;

        Ok(Self {
            address: Some(address),
            first_name,
            last_name,
            full_name: None,
            email: router_data.get_optional_billing_email(),
            customer_id: Some(customer_id_reference),
            entity_type: Some(EntityType::from(router_data.request.entity_type)),
        })
    }
}

impl<F> TryFrom<(&PayoutsRouterData<F>, &payouts::Bank)> for AdyenAccountHolder {
    type Error = Error;

    fn try_from(
        (router_data, _bank): (&PayoutsRouterData<F>, &payouts::Bank),
    ) -> Result<Self, Self::Error> {
        let billing_address = router_data.get_optional_billing();

        let address = billing_address
            .and_then(|billing| billing.address.as_ref().map(|addr| addr.try_into()))
            .transpose()?
            .ok_or(ConnectorError::MissingRequiredField {
                field_name: "address",
            })?;

        let full_name = router_data.get_billing_full_name()?;

        let customer_id_reference = router_data.get_connector_customer_id()?;

        Ok(Self {
            address: Some(address),
            first_name: None,
            last_name: None,
            full_name: Some(full_name),
            email: router_data.get_optional_billing_email(),
            customer_id: Some(customer_id_reference),
            entity_type: Some(EntityType::from(router_data.request.entity_type)),
        })
    }
}

#[derive(Debug)]
pub struct StoredPaymentCounterparty<'a, F> {
    pub item: &'a AdyenPlatformRouterData<&'a PayoutsRouterData<F>>,
    pub stored_payment_method_id: String,
}

#[derive(Debug)]
pub struct RawPaymentCounterparty<'a, F> {
    pub item: &'a AdyenPlatformRouterData<&'a PayoutsRouterData<F>>,
    pub raw_payout_method_data: payouts::PayoutMethodData,
}

impl<F> TryFrom<StoredPaymentCounterparty<'_, F>>
    for (AdyenPayoutMethodDetails, Option<AdyenPayoutPriority>)
{
    type Error = Error;

    fn try_from(stored_payment: StoredPaymentCounterparty<'_, F>) -> Result<Self, Self::Error> {
        let request = &stored_payment.item.router_data.request;
        let payout_type = request.get_payout_type()?;

        match payout_type {
            enums::PayoutType::Card => {
                let billing_address = stored_payment.item.router_data.get_optional_billing();
                let address = billing_address
                    .and_then(|billing| billing.address.as_ref())
                    .ok_or(ConnectorError::MissingRequiredField {
                        field_name: "address",
                    })?
                    .try_into()?;

                let customer_id_reference = stored_payment
                    .item
                    .router_data
                    .get_connector_customer_id()?;

                let card_holder = AdyenAccountHolder {
                    address: Some(address),
                    first_name: stored_payment
                        .item
                        .router_data
                        .get_optional_billing_first_name(),
                    last_name: stored_payment
                        .item
                        .router_data
                        .get_optional_billing_last_name(),
                    full_name: stored_payment
                        .item
                        .router_data
                        .get_optional_billing_full_name(),
                    email: stored_payment.item.router_data.get_optional_billing_email(),
                    customer_id: Some(customer_id_reference),
                    entity_type: Some(EntityType::from(request.entity_type)),
                };

                let card_identification =
                    AdyenCardIdentification::Stored(AdyenStoredCardIdentification {
                        stored_payment_method_id: Secret::new(
                            stored_payment.stored_payment_method_id,
                        ),
                    });

                let counterparty = AdyenPayoutMethodDetails::Card(AdyenCardDetails {
                    card_holder,
                    card_identification,
                });

                Ok((counterparty, None))
            }
            _ => Err(ConnectorError::NotSupported {
                message: "Stored payment method is only supported for card payouts".to_string(),
                connector: "Adyenplatform",
            }
            .into()),
        }
    }
}

impl<F> TryFrom<RawPaymentCounterparty<'_, F>>
    for (AdyenPayoutMethodDetails, Option<AdyenPayoutPriority>)
{
    type Error = Error;

    fn try_from(raw_payment: RawPaymentCounterparty<'_, F>) -> Result<Self, Self::Error> {
        let request = &raw_payment.item.router_data.request;

        match raw_payment.raw_payout_method_data {
            payouts::PayoutMethodData::Wallet(_)
            | payouts::PayoutMethodData::BankRedirect(_)
            | payouts::PayoutMethodData::Passthrough(_) => Err(ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Adyenplatform"),
            ))?,
            payouts::PayoutMethodData::Card(c) => {
                let card_holder: AdyenAccountHolder =
                    (raw_payment.item.router_data, &c).try_into()?;

                let card_identification =
                    AdyenCardIdentification::Card(AdyenRawCardIdentification {
                        expiry_year: c.get_expiry_year_4_digit(),
                        card_number: c.card_number,
                        expiry_month: c.expiry_month,
                        issue_number: None,
                        start_month: None,
                        start_year: None,
                    });

                let counterparty = AdyenPayoutMethodDetails::Card(AdyenCardDetails {
                    card_holder,
                    card_identification,
                });

                Ok((counterparty, None))
            }
            payouts::PayoutMethodData::Bank(bd) => {
                let account_holder: AdyenAccountHolder =
                    (raw_payment.item.router_data, &bd).try_into()?;
                let bank_details = match bd {
                    payouts::Bank::Sepa(b) => AdyenBankAccountIdentification {
                        bank_type: "iban".to_string(),
                        account_details: AdyenBankAccountIdentificationDetails::Sepa(SepaDetails {
                            iban: b.iban,
                        }),
                    },
                    payouts::Bank::Ach(..) => Err(ConnectorError::NotSupported {
                        message: "Bank transfer via ACH is not supported".to_string(),
                        connector: "Adyenplatform",
                    })?,
                    payouts::Bank::Bacs(..) => Err(ConnectorError::NotSupported {
                        message: "Bank transfer via Bacs is not supported".to_string(),
                        connector: "Adyenplatform",
                    })?,
                    payouts::Bank::Pix(..) => Err(ConnectorError::NotSupported {
                        message: "Bank transfer via Pix is not supported".to_string(),
                        connector: "Adyenplatform",
                    })?,
                };
                let counterparty = AdyenPayoutMethodDetails::BankAccount(AdyenBankAccountDetails {
                    account_holder,
                    account_identification: bank_details,
                });
                let priority = request
                    .priority
                    .ok_or(ConnectorError::MissingRequiredField {
                        field_name: "priority",
                    })?;

                Ok((counterparty, Some(AdyenPayoutPriority::from(priority))))
            }
        }
    }
}

impl<F> TryFrom<&AdyenPlatformRouterData<&PayoutsRouterData<F>>> for AdyenTransferRequest {
    type Error = Error;
    fn try_from(
        item: &AdyenPlatformRouterData<&PayoutsRouterData<F>>,
    ) -> Result<Self, Self::Error> {
        let request = &item.router_data.request;
        let stored_payment_method_result =
            item.router_data.request.get_connector_transfer_method_id();
        let raw_payout_method_result = item.router_data.get_payout_method_data();

        let (counterparty, priority) =
            if let Ok(stored_payment_method_id) = stored_payment_method_result {
                StoredPaymentCounterparty {
                    item,
                    stored_payment_method_id,
                }
                .try_into()?
            } else if let Ok(raw_payout_method_data) = raw_payout_method_result {
                RawPaymentCounterparty {
                    item,
                    raw_payout_method_data,
                }
                .try_into()?
            } else {
                return Err(ConnectorError::MissingRequiredField {
                    field_name: "payout_method_data or stored_payment_method_id",
                }
                .into());
            };
        let adyen_connector_metadata_object =
            AdyenPlatformConnectorMetadataObject::try_from(&item.router_data.connector_meta_data)?;
        let balance_account_id = adyen_connector_metadata_object
            .source_balance_account
            .ok_or(ConnectorError::InvalidConnectorConfig {
                config: "metadata.source_balance_account",
            })?;
        let payout_type = request.get_payout_type()?;
        Ok(Self {
            amount: adyen::Amount {
                value: item.amount,
                currency: request.destination_currency,
            },
            balance_account_id,
            category: AdyenPayoutMethod::try_from(payout_type)?,
            counterparty,
            priority,
            reference: item.router_data.connector_request_reference_id.clone(),
            reference_for_beneficiary: item.router_data.connector_request_reference_id.clone(),
            description: item.router_data.description.clone(),
        })
    }
}

impl<F> TryFrom<PayoutsResponseRouterData<F, AdyenTransferResponse>> for PayoutsRouterData<F> {
    type Error = Error;
    fn try_from(
        item: PayoutsResponseRouterData<F, AdyenTransferResponse>,
    ) -> Result<Self, Self::Error> {
        let response: AdyenTransferResponse = item.response;
        let status = enums::PayoutStatus::from(response.status);

        if matches!(status, enums::PayoutStatus::Failed) {
            return Ok(Self {
                response: Err(hyperswitch_domain_models::router_data::ErrorResponse {
                    code: response.status.to_string(),
                    message: if !response.reason.is_empty() {
                        response.reason
                    } else {
                        response.status.to_string()
                    },
                    reason: None,
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: Some(response.id),
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
                ..item.data
            });
        }

        Ok(Self {
            response: Ok(types::PayoutsResponseData {
                status: Some(status),
                connector_payout_id: Some(response.id),
                payout_eligible: None,
                should_add_next_step_to_process_tracker: false,
                error_code: None,
                error_message: None,
                payout_connector_metadata: None,
            }),
            ..item.data
        })
    }
}

impl From<AdyenTransferStatus> for enums::PayoutStatus {
    fn from(adyen_status: AdyenTransferStatus) -> Self {
        match adyen_status {
            AdyenTransferStatus::Authorised => Self::Initiated,
            AdyenTransferStatus::Error | AdyenTransferStatus::Refused => Self::Failed,
        }
    }
}

impl From<enums::PayoutEntityType> for EntityType {
    fn from(entity: enums::PayoutEntityType) -> Self {
        match entity {
            enums::PayoutEntityType::Individual
            | enums::PayoutEntityType::Personal
            | enums::PayoutEntityType::NaturalPerson => Self::Individual,

            enums::PayoutEntityType::Company | enums::PayoutEntityType::Business => {
                Self::Organization
            }
            _ => Self::Unknown,
        }
    }
}

impl From<enums::PayoutSendPriority> for AdyenPayoutPriority {
    fn from(entity: enums::PayoutSendPriority) -> Self {
        match entity {
            enums::PayoutSendPriority::Instant => Self::Instant,
            enums::PayoutSendPriority::Fast => Self::Fast,
            enums::PayoutSendPriority::Regular => Self::Regular,
            enums::PayoutSendPriority::Wire => Self::Wire,
            enums::PayoutSendPriority::CrossBorder => Self::CrossBorder,
            enums::PayoutSendPriority::Internal => Self::Internal,
        }
    }
}

impl TryFrom<enums::PayoutType> for AdyenPayoutMethod {
    type Error = Error;
    fn try_from(payout_type: enums::PayoutType) -> Result<Self, Self::Error> {
        match payout_type {
            enums::PayoutType::Bank => Ok(Self::Bank),
            enums::PayoutType::Card => Ok(Self::Card),
            enums::PayoutType::Wallet | enums::PayoutType::BankRedirect => {
                Err(report!(ConnectorError::NotSupported {
                    message: "Bakredirect or wallet payouts".to_string(),
                    connector: "Adyenplatform",
                }))
            }
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenplatformIncomingWebhook {
    pub data: AdyenplatformIncomingWebhookData,
    #[serde(rename = "type")]
    pub webhook_type: AdyenplatformWebhookEventType,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenplatformIncomingWebhookData {
    pub status: AdyenplatformWebhookStatus,
    pub reference: String,
    pub tracking: Option<AdyenplatformTrackingData>,
    pub reason: Option<String>,
    pub category: Option<AdyenPayoutMethod>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenplatformTrackingData {
    pub status: TrackingStatus,
    pub reason: Option<String>,
    pub estimated_arrival_time: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TrackingStatus {
    Accepted,
    Pending,
    Credited,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum AdyenplatformWebhookEventType {
    #[serde(rename = "balancePlatform.transfer.created")]
    PayoutCreated,
    #[serde(rename = "balancePlatform.transfer.updated")]
    PayoutUpdated,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AdyenplatformWebhookStatus {
    Authorised,
    Booked,
    Pending,
    Failed,
    Returned,
    Received,
}
pub fn get_adyen_payout_webhook_event(
    event_type: AdyenplatformWebhookEventType,
    status: AdyenplatformWebhookStatus,
    tracking_data: Option<AdyenplatformTrackingData>,
) -> webhooks::IncomingWebhookEvent {
    match (event_type, status, tracking_data) {
        (AdyenplatformWebhookEventType::PayoutCreated, _, _) => {
            webhooks::IncomingWebhookEvent::PayoutCreated
        }
        (AdyenplatformWebhookEventType::PayoutUpdated, _, Some(tracking_data)) => {
            match tracking_data.status {
                TrackingStatus::Credited | TrackingStatus::Accepted => {
                    webhooks::IncomingWebhookEvent::PayoutSuccess
                }
                TrackingStatus::Pending => webhooks::IncomingWebhookEvent::PayoutProcessing,
            }
        }
        (AdyenplatformWebhookEventType::PayoutUpdated, status, _) => match status {
            AdyenplatformWebhookStatus::Authorised | AdyenplatformWebhookStatus::Received => {
                webhooks::IncomingWebhookEvent::PayoutCreated
            }
            AdyenplatformWebhookStatus::Booked | AdyenplatformWebhookStatus::Pending => {
                webhooks::IncomingWebhookEvent::PayoutProcessing
            }
            AdyenplatformWebhookStatus::Failed => webhooks::IncomingWebhookEvent::PayoutFailure,
            AdyenplatformWebhookStatus::Returned => webhooks::IncomingWebhookEvent::PayoutReversed,
        },
    }
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenTransferErrorResponse {
    pub error_code: String,
    #[serde(rename = "type")]
    pub error_type: String,
    pub status: u16,
    pub title: String,
    pub detail: Option<String>,
    pub request_id: Option<String>,
    pub invalid_fields: Option<Vec<AdyenInvalidField>>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenInvalidField {
    pub name: Option<String>,
    pub value: Option<String>,
    pub message: Option<String>,
}