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

use bytes::Bytes;
use common_enums::{
    self, AttemptStatus, AuthorizationStatus, CaptureMethod, Currency, FutureUsage,
    PaymentMethodStatus, RefundStatus,
};
use common_utils::{date_time, ext_traits::Encode, pii, types::MinorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    address::AddressDetails,
    payment_method_data::{Card, PaymentMethodData, WalletData},
    router_data::{ConnectorAuthType, ErrorResponse, PaymentMethodToken, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::{
        AuthenticationData, PaymentsIncrementalAuthorizationData, ResponseId,
        SetupMandateRequestData,
    },
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsIncrementalAuthorizationRouterData, PaymentsSyncRouterData, RefundsRouterData,
        SetupMandateRouterData,
    },
};
use hyperswitch_interfaces::{consts, errors};
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::{
        PaymentsCancelResponseRouterData, PaymentsCaptureResponseRouterData,
        PaymentsResponseRouterData, PaymentsSyncResponseRouterData, RefundsResponseRouterData,
        ResponseRouterData,
    },
    unimplemented_payment_method,
    utils::{
        self, AddressData, AddressDetailsData, CardData, CardIssuer, PaymentsAuthorizeRequestData,
        RouterData as _,
    },
};

const THREE_DS_MAX_SUPPORTED_VERSION: &str = "2.2.0";

#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
#[serde(transparent)]
pub struct ArchipelTenantId(pub String);

impl From<String> for ArchipelTenantId {
    fn from(value: String) -> Self {
        Self(value)
    }
}

pub struct ArchipelRouterData<T> {
    pub amount: MinorUnit,
    pub tenant_id: ArchipelTenantId,
    pub router_data: T,
}

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

pub struct ArchipelAuthType {
    pub(super) ca_certificate: Option<Secret<String>>,
}

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

#[derive(Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct ArchipelConfigData {
    pub tenant_id: ArchipelTenantId,
    pub platform_url: String,
}

impl TryFrom<&Option<pii::SecretSerdeValue>> for ArchipelConfigData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(connector_metadata: &Option<pii::SecretSerdeValue>) -> Result<Self, Self::Error> {
        let config_data = utils::to_connector_meta_from_secret::<Self>(connector_metadata.clone())
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "metadata. Required fields: tenant_id, platform_url",
            })?;
        Ok(config_data)
    }
}

#[derive(Debug, Default, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "UPPERCASE")]
pub enum ArchipelPaymentInitiator {
    #[default]
    Customer,
    Merchant,
}

#[derive(Debug, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ArchipelWalletProvider {
    ApplePay,
    GooglePay,
    SamsungPay,
}

#[derive(Debug, Default, Serialize, Eq, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum ArchipelPaymentCertainty {
    #[default]
    Final,
    Estimated,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelOrderRequest {
    amount: MinorUnit,
    currency: String,
    certainty: ArchipelPaymentCertainty,
    initiator: ArchipelPaymentInitiator,
}

#[derive(Debug, Serialize, Eq, PartialEq, Clone)]
pub struct CardExpiryDate {
    month: Secret<String>,
    year: Secret<String>,
}

#[derive(Debug, Serialize, Default, Eq, PartialEq, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ApplicationSelectionIndicator {
    #[default]
    ByDefault,
    CustomerChoice,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Archipel3DS {
    #[serde(rename = "acsTransID")]
    acs_trans_id: Option<Secret<String>>,
    #[serde(rename = "dsTransID")]
    ds_trans_id: Option<Secret<String>>,
    #[serde(rename = "3DSRequestorName")]
    three_ds_requestor_name: Option<Secret<String>>,
    #[serde(rename = "3DSAuthDate")]
    three_ds_auth_date: Option<String>,
    #[serde(rename = "3DSAuthAmt")]
    three_ds_auth_amt: Option<MinorUnit>,
    #[serde(rename = "3DSAuthStatus")]
    three_ds_auth_status: Option<String>,
    #[serde(rename = "3DSMaxSupportedVersion")]
    three_ds_max_supported_version: String,
    #[serde(rename = "3DSVersion")]
    three_ds_version: Option<common_utils::types::SemanticVersion>,
    authentication_value: Secret<String>,
    authentication_method: Option<Secret<String>>,
    eci: Option<String>,
}

impl From<AuthenticationData> for Archipel3DS {
    fn from(three_ds_data: AuthenticationData) -> Self {
        let now = date_time::date_as_yyyymmddthhmmssmmmz().ok();
        Self {
            acs_trans_id: None,
            ds_trans_id: three_ds_data.ds_trans_id.map(Secret::new),
            three_ds_requestor_name: None,
            three_ds_auth_date: now,
            three_ds_auth_amt: None,
            three_ds_auth_status: None,
            three_ds_max_supported_version: THREE_DS_MAX_SUPPORTED_VERSION.into(),
            three_ds_version: three_ds_data.message_version,
            authentication_value: three_ds_data.cavv,
            authentication_method: None,
            eci: three_ds_data.eci,
        }
    }
}

#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelCardHolder {
    billing_address: Option<ArchipelBillingAddress>,
}

impl From<Option<ArchipelBillingAddress>> for ArchipelCardHolder {
    fn from(value: Option<ArchipelBillingAddress>) -> Self {
        Self {
            billing_address: value,
        }
    }
}

#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelBillingAddress {
    address: Option<Secret<String>>,
    postal_code: Option<Secret<String>>,
}

pub trait ToArchipelBillingAddress {
    fn to_archipel_billing_address(&self) -> Option<ArchipelBillingAddress>;
}

impl ToArchipelBillingAddress for AddressDetails {
    fn to_archipel_billing_address(&self) -> Option<ArchipelBillingAddress> {
        let address = self.get_combined_address_line().ok();
        let postal_code = self.get_optional_zip();

        match (address, postal_code) {
            (None, None) => None,
            (addr, zip) => Some(ArchipelBillingAddress {
                address: addr,
                postal_code: zip,
            }),
        }
    }
}

#[derive(Debug, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "UPPERCASE")]
pub enum ArchipelCredentialIndicatorStatus {
    Initial,
    Subsequent,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelCredentialIndicator {
    status: ArchipelCredentialIndicatorStatus,
    recurring: Option<bool>,
    transaction_id: Option<String>,
}

#[derive(Debug, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCardData {
    card_data: ArchipelTokenizedCard,
    wallet_information: ArchipelWalletInformation,
}

impl TryFrom<(&WalletData, &Option<PaymentMethodToken>)> for TokenizedCardData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (wallet_data, pm_token): (&WalletData, &Option<PaymentMethodToken>),
    ) -> Result<Self, Self::Error> {
        let WalletData::ApplePay(apple_pay_data) = wallet_data else {
            return Err(error_stack::Report::from(
                errors::ConnectorError::NotSupported {
                    message: "Wallet type used".to_string(),
                    connector: "Archipel",
                },
            ));
        };

        let Some(PaymentMethodToken::ApplePayDecrypt(apple_pay_decrypt_data)) = pm_token else {
            return Err(error_stack::Report::from(unimplemented_payment_method!(
                "Apple Pay",
                "Manual",
                "Archipel"
            )));
        };

        let card_number = apple_pay_decrypt_data
            .application_primary_account_number
            .clone();

        let expiry_year_2_digit = apple_pay_decrypt_data
            .get_two_digit_expiry_year()
            .change_context(errors::ConnectorError::MissingRequiredField {
                field_name: "Apple pay expiry year",
            })?;
        let expiry_month = apple_pay_decrypt_data.get_expiry_month().change_context(
            errors::ConnectorError::InvalidDataFormat {
                field_name: "expiration_month",
            },
        )?;

        Ok(Self {
            card_data: ArchipelTokenizedCard {
                expiry: CardExpiryDate {
                    year: expiry_year_2_digit,
                    month: expiry_month,
                },
                number: card_number,
                scheme: ArchipelCardScheme::from(apple_pay_data.payment_method.network.as_str()),
            },
            wallet_information: {
                ArchipelWalletInformation {
                    wallet_provider: ArchipelWalletProvider::ApplePay,
                    wallet_indicator: apple_pay_decrypt_data.payment_data.eci_indicator.clone(),
                    wallet_cryptogram: apple_pay_decrypt_data
                        .payment_data
                        .online_payment_cryptogram
                        .clone(),
                }
            },
        })
    }
}

#[derive(Debug, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelTokenizedCard {
    number: cards::CardNumber,
    expiry: CardExpiryDate,
    scheme: ArchipelCardScheme,
}

#[derive(Debug, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelCard {
    number: cards::CardNumber,
    expiry: CardExpiryDate,
    security_code: Option<Secret<String>>,
    card_holder_name: Option<Secret<String>>,
    application_selection_indicator: ApplicationSelectionIndicator,
    scheme: ArchipelCardScheme,
}

impl TryFrom<(Option<Secret<String>>, Option<ArchipelCardHolder>, &Card)> for ArchipelCard {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (card_holder_name, card_holder_billing, ccard): (
            Option<Secret<String>>,
            Option<ArchipelCardHolder>,
            &Card,
        ),
    ) -> Result<Self, Self::Error> {
        // NOTE: Archipel does not accept `card.card_holder_name` field without `cardholder` field.
        // So if `card_holder` is None, `card.card_holder_name` must also be None.
        // However, the reverse is allowed — the `cardholder` field can exist without `card.card_holder_name`.
        let card_holder_name = card_holder_billing
            .as_ref()
            .and_then(|_| ccard.card_holder_name.clone().or(card_holder_name.clone()));

        let scheme: ArchipelCardScheme = ccard.get_card_issuer().ok().into();
        Ok(Self {
            number: ccard.card_number.clone(),
            expiry: CardExpiryDate {
                month: ccard.card_exp_month.clone(),
                year: ccard.get_card_expiry_year_2_digit()?,
            },
            security_code: Some(ccard.card_cvc.clone()),
            application_selection_indicator: ApplicationSelectionIndicator::ByDefault,
            card_holder_name,
            scheme,
        })
    }
}

impl
    TryFrom<(
        Option<Secret<String>>,
        Option<ArchipelCardHolder>,
        &hyperswitch_domain_models::payment_method_data::CardDetailsForNetworkTransactionId,
    )> for ArchipelCard
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (card_holder_name, card_holder_billing, card_details): (
            Option<Secret<String>>,
            Option<ArchipelCardHolder>,
            &hyperswitch_domain_models::payment_method_data::CardDetailsForNetworkTransactionId,
        ),
    ) -> Result<Self, Self::Error> {
        // NOTE: Archipel does not accept `card.card_holder_name` field without `cardholder` field.
        // So if `card_holder` is None, `card.card_holder_name` must also be None.
        // However, the reverse is allowed — the `cardholder` field can exist without `card.card_holder_name`.
        let card_holder_name = card_holder_billing.as_ref().and_then(|_| {
            card_details
                .card_holder_name
                .clone()
                .or(card_holder_name.clone())
        });

        let scheme: ArchipelCardScheme = card_details.get_card_issuer().ok().into();
        Ok(Self {
            number: card_details.card_number.clone(),
            expiry: CardExpiryDate {
                month: card_details.card_exp_month.clone(),
                year: card_details.get_card_expiry_year_2_digit()?,
            },
            security_code: None,
            application_selection_indicator: ApplicationSelectionIndicator::ByDefault,
            card_holder_name,
            scheme,
        })
    }
}

#[derive(Debug, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelWalletInformation {
    wallet_indicator: Option<String>,
    wallet_provider: ArchipelWalletProvider,
    wallet_cryptogram: Secret<String>,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelPaymentInformation {
    order: ArchipelOrderRequest,
    cardholder: Option<ArchipelCardHolder>,
    card_holder_name: Option<Secret<String>>,
    credential_indicator: Option<ArchipelCredentialIndicator>,
    stored_on_file: bool,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelWalletAuthorizationRequest {
    order: ArchipelOrderRequest,
    card: ArchipelTokenizedCard,
    cardholder: Option<ArchipelCardHolder>,
    wallet: ArchipelWalletInformation,
    #[serde(rename = "3DS")]
    three_ds: Option<Archipel3DS>,
    credential_indicator: Option<ArchipelCredentialIndicator>,
    stored_on_file: bool,
    tenant_id: ArchipelTenantId,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelCardAuthorizationRequest {
    order: ArchipelOrderRequest,
    card: ArchipelCard,
    cardholder: Option<ArchipelCardHolder>,
    #[serde(rename = "3DS")]
    three_ds: Option<Archipel3DS>,
    credential_indicator: Option<ArchipelCredentialIndicator>,
    stored_on_file: bool,
    tenant_id: ArchipelTenantId,
}

// PaymentsResponse

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "UPPERCASE")]
pub enum ArchipelCardScheme {
    Amex,
    Mastercard,
    Visa,
    Discover,
    Diners,
    Unknown,
}

impl From<&str> for ArchipelCardScheme {
    fn from(input: &str) -> Self {
        match input {
            "Visa" => Self::Visa,
            "Amex" => Self::Amex,
            "Diners" => Self::Diners,
            "MasterCard" => Self::Mastercard,
            "Discover" => Self::Discover,
            _ => Self::Unknown,
        }
    }
}

impl From<Option<CardIssuer>> for ArchipelCardScheme {
    fn from(card_issuer: Option<CardIssuer>) -> Self {
        match card_issuer {
            Some(CardIssuer::Visa) => Self::Visa,
            Some(CardIssuer::Master | CardIssuer::Maestro) => Self::Mastercard,
            Some(CardIssuer::AmericanExpress) => Self::Amex,
            Some(CardIssuer::Discover) => Self::Discover,
            Some(CardIssuer::DinersClub) => Self::Diners,
            _ => Self::Unknown,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub enum ArchipelPaymentStatus {
    #[default]
    Succeeded,
    Failed,
}

impl TryFrom<(AttemptStatus, CaptureMethod)> for ArchipelPaymentFlow {
    type Error = errors::ConnectorError;

    fn try_from(
        (status, capture_method): (AttemptStatus, CaptureMethod),
    ) -> Result<Self, Self::Error> {
        let is_auto_capture = matches!(capture_method, CaptureMethod::Automatic);

        match status {
            AttemptStatus::AuthenticationFailed => Ok(Self::Verify),
            AttemptStatus::Authorizing
            | AttemptStatus::Authorized
            | AttemptStatus::AuthorizationFailed => Ok(Self::Authorize),
            AttemptStatus::Voided | AttemptStatus::VoidInitiated | AttemptStatus::VoidFailed => {
                Ok(Self::Cancel)
            }
            AttemptStatus::CaptureInitiated | AttemptStatus::CaptureFailed => {
                if is_auto_capture {
                    Ok(Self::Pay)
                } else {
                    Ok(Self::Capture)
                }
            }
            AttemptStatus::PaymentMethodAwaited | AttemptStatus::ConfirmationAwaited => {
                if is_auto_capture {
                    Ok(Self::Pay)
                } else {
                    Ok(Self::Authorize)
                }
            }
            _ => Err(errors::ConnectorError::ProcessingStepFailed(Some(
                Bytes::from_static(
                    "Impossible to determine Archipel flow from AttemptStatus".as_bytes(),
                ),
            ))),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ArchipelPaymentFlow {
    Verify,
    Authorize,
    Pay,
    Capture,
    Cancel,
}

struct ArchipelFlowStatus {
    status: ArchipelPaymentStatus,
    flow: ArchipelPaymentFlow,
}
impl ArchipelFlowStatus {
    fn new(status: ArchipelPaymentStatus, flow: ArchipelPaymentFlow) -> Self {
        Self { status, flow }
    }
}

impl From<ArchipelFlowStatus> for AttemptStatus {
    fn from(ArchipelFlowStatus { status, flow }: ArchipelFlowStatus) -> Self {
        match status {
            ArchipelPaymentStatus::Succeeded => match flow {
                ArchipelPaymentFlow::Authorize => Self::Authorized,
                ArchipelPaymentFlow::Pay
                | ArchipelPaymentFlow::Verify
                | ArchipelPaymentFlow::Capture => Self::Charged,
                ArchipelPaymentFlow::Cancel => Self::Voided,
            },
            ArchipelPaymentStatus::Failed => match flow {
                ArchipelPaymentFlow::Authorize | ArchipelPaymentFlow::Pay => {
                    Self::AuthorizationFailed
                }
                ArchipelPaymentFlow::Verify => Self::AuthenticationFailed,
                ArchipelPaymentFlow::Capture => Self::CaptureFailed,
                ArchipelPaymentFlow::Cancel => Self::VoidFailed,
            },
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelOrderResponse {
    id: String,
    amount: Option<i64>,
    currency: Option<Currency>,
    captured_amount: Option<i64>,
    authorized_amount: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ArchipelErrorMessage {
    pub code: String,
    pub description: Option<String>,
}

impl Default for ArchipelErrorMessage {
    fn default() -> Self {
        Self {
            code: consts::NO_ERROR_CODE.to_string(),
            description: Some(consts::NO_ERROR_MESSAGE.to_string()),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct ArchipelErrorMessageWithHttpCode {
    error_message: ArchipelErrorMessage,
    http_code: u16,
}
impl ArchipelErrorMessageWithHttpCode {
    fn new(error_message: ArchipelErrorMessage, http_code: u16) -> Self {
        Self {
            error_message,
            http_code,
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelTransactionMetadata {
    pub transaction_id: String,
    pub transaction_date: String,
    pub financial_network_code: Option<String>,
    pub issuer_transaction_id: Option<String>,
    pub response_code: Option<String>,
    pub authorization_code: Option<String>,
    pub payment_account_reference: Option<Secret<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelPaymentsResponse {
    order: ArchipelOrderResponse,
    transaction_id: String,
    transaction_date: String,
    transaction_result: ArchipelPaymentStatus,
    error: Option<ArchipelErrorMessage>,
    financial_network_code: Option<String>,
    issuer_transaction_id: Option<String>,
    response_code: Option<String>,
    authorization_code: Option<String>,
    payment_account_reference: Option<Secret<String>>,
}

impl From<&ArchipelPaymentsResponse> for ArchipelTransactionMetadata {
    fn from(payment_response: &ArchipelPaymentsResponse) -> Self {
        Self {
            transaction_id: payment_response.transaction_id.clone(),
            transaction_date: payment_response.transaction_date.clone(),
            financial_network_code: payment_response.financial_network_code.clone(),
            issuer_transaction_id: payment_response.issuer_transaction_id.clone(),
            response_code: payment_response.response_code.clone(),
            authorization_code: payment_response.authorization_code.clone(),
            payment_account_reference: payment_response.payment_account_reference.clone(),
        }
    }
}

// AUTHORIZATION FLOW
impl TryFrom<(MinorUnit, &PaymentsAuthorizeRouterData)> for ArchipelPaymentInformation {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        (amount, router_data): (MinorUnit, &PaymentsAuthorizeRouterData),
    ) -> Result<Self, Self::Error> {
        let is_recurring_payment = router_data
            .request
            .mandate_id
            .as_ref()
            .and_then(|mandate_ids| mandate_ids.mandate_id.as_ref())
            .is_some();

        let is_subsequent_trx = router_data
            .request
            .mandate_id
            .as_ref()
            .and_then(|mandate_ids| mandate_ids.mandate_reference_id.as_ref())
            .is_some();

        let is_saved_card_payment = (router_data.request.is_mandate_payment())
            | (router_data.request.setup_future_usage == Some(FutureUsage::OnSession))
            | (router_data.payment_method_status == Some(PaymentMethodStatus::Active));

        let certainty = if router_data.request.request_incremental_authorization {
            if is_recurring_payment {
                ArchipelPaymentCertainty::Final
            } else {
                ArchipelPaymentCertainty::Estimated
            }
        } else {
            ArchipelPaymentCertainty::Final
        };

        let transaction_initiator = if is_recurring_payment {
            ArchipelPaymentInitiator::Merchant
        } else {
            ArchipelPaymentInitiator::Customer
        };

        let order = ArchipelOrderRequest {
            amount,
            currency: router_data.request.currency.to_string(),
            certainty,
            initiator: transaction_initiator.clone(),
        };

        let cardholder = router_data
            .get_billing_address()
            .ok()
            .and_then(|address| address.to_archipel_billing_address())
            .map(|billing_address| ArchipelCardHolder {
                billing_address: Some(billing_address),
            });

        // NOTE: Archipel does not accept `card.card_holder_name` field without `cardholder` field.
        // So if `card_holder` is None, `card.card_holder_name` must also be None.
        // However, the reverse is allowed — the `cardholder` field can exist without `card.card_holder_name`.
        let card_holder_name = cardholder.as_ref().and_then(|_| {
            router_data
                .get_billing()
                .ok()
                .and_then(|billing| billing.get_optional_full_name())
        });

        let indicator_status = if is_subsequent_trx {
            ArchipelCredentialIndicatorStatus::Subsequent
        } else {
            ArchipelCredentialIndicatorStatus::Initial
        };

        let stored_on_file =
            is_saved_card_payment | router_data.request.is_customer_initiated_mandate_payment();

        let credential_indicator = stored_on_file.then(|| ArchipelCredentialIndicator {
            status: indicator_status.clone(),
            recurring: Some(is_recurring_payment),
            transaction_id: match indicator_status {
                ArchipelCredentialIndicatorStatus::Initial => None,
                ArchipelCredentialIndicatorStatus::Subsequent => {
                    router_data.request.get_optional_network_transaction_id()
                }
            },
        });

        Ok(Self {
            order,
            cardholder,
            card_holder_name,
            credential_indicator,
            stored_on_file,
        })
    }
}

impl TryFrom<ArchipelRouterData<&PaymentsAuthorizeRouterData>>
    for ArchipelCardAuthorizationRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ArchipelRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let ArchipelRouterData {
            amount,
            tenant_id,
            router_data,
        } = item;

        let payment_information: ArchipelPaymentInformation =
            ArchipelPaymentInformation::try_from((amount, router_data))?;
        let payment_method_data = match &item.router_data.request.payment_method_data {
            PaymentMethodData::Card(ccard) => ArchipelCard::try_from((
                payment_information.card_holder_name,
                payment_information.cardholder.clone(),
                ccard,
            ))?,
            PaymentMethodData::CardDetailsForNetworkTransactionId(card_details) => {
                ArchipelCard::try_from((
                    payment_information.card_holder_name,
                    payment_information.cardholder.clone(),
                    card_details,
                ))?
            }
            PaymentMethodData::CardRedirect(..)
            | PaymentMethodData::Wallet(..)
            | PaymentMethodData::PayLater(..)
            | PaymentMethodData::BankRedirect(..)
            | PaymentMethodData::BankDebit(..)
            | PaymentMethodData::BankTransfer(..)
            | PaymentMethodData::Crypto(..)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(..)
            | PaymentMethodData::Upi(..)
            | PaymentMethodData::Voucher(..)
            | PaymentMethodData::GiftCard(..)
            | PaymentMethodData::CardToken(..)
            | PaymentMethodData::OpenBanking(..)
            | PaymentMethodData::NetworkToken(..)
            | PaymentMethodData::MobilePayment(..) => Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Archipel"),
            ))?,
        };

        let three_ds: Option<Archipel3DS> = if item.router_data.is_three_ds() {
            let auth_data = item
                .router_data
                .request
                .get_authentication_data()
                .change_context(errors::ConnectorError::NotSupported {
                    message: "Selected 3DS authentication method".to_string(),
                    connector: "archipel",
                })?;
            Some(Archipel3DS::from(auth_data))
        } else {
            None
        };

        Ok(Self {
            order: payment_information.order,
            cardholder: payment_information.cardholder,
            card: payment_method_data,
            three_ds,
            credential_indicator: payment_information.credential_indicator,
            stored_on_file: payment_information.stored_on_file,
            tenant_id,
        })
    }
}

impl TryFrom<ArchipelRouterData<&PaymentsAuthorizeRouterData>>
    for ArchipelWalletAuthorizationRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ArchipelRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let ArchipelRouterData {
            amount,
            tenant_id,
            router_data,
        } = item;

        let payment_information = ArchipelPaymentInformation::try_from((amount, router_data))?;
        let payment_method_data = match &item.router_data.request.payment_method_data {
            PaymentMethodData::Wallet(wallet_data) => {
                TokenizedCardData::try_from((wallet_data, &item.router_data.payment_method_token))?
            }
            PaymentMethodData::Card(..)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(..)
            | PaymentMethodData::CardRedirect(..)
            | PaymentMethodData::PayLater(..)
            | PaymentMethodData::BankRedirect(..)
            | PaymentMethodData::BankDebit(..)
            | PaymentMethodData::BankTransfer(..)
            | PaymentMethodData::Crypto(..)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(..)
            | PaymentMethodData::Upi(..)
            | PaymentMethodData::Voucher(..)
            | PaymentMethodData::GiftCard(..)
            | PaymentMethodData::CardToken(..)
            | PaymentMethodData::OpenBanking(..)
            | PaymentMethodData::NetworkToken(..)
            | PaymentMethodData::MobilePayment(..) => Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Archipel"),
            ))?,
        };

        Ok(Self {
            order: payment_information.order,
            cardholder: payment_information.cardholder,
            card: payment_method_data.card_data.clone(),
            wallet: payment_method_data.wallet_information.clone(),
            three_ds: None,
            credential_indicator: payment_information.credential_indicator,
            stored_on_file: payment_information.stored_on_file,
            tenant_id,
        })
    }
}

// Responses for AUTHORIZATION FLOW
impl TryFrom<PaymentsResponseRouterData<ArchipelPaymentsResponse>> for PaymentsAuthorizeRouterData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsResponseRouterData<ArchipelPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        if let Some(error) = item.response.error {
            return Ok(Self {
                response: Err(ArchipelErrorMessageWithHttpCode::new(error, item.http_code).into()),
                ..item.data
            });
        };

        let capture_method = item
            .data
            .request
            .capture_method
            .ok_or_else(|| errors::ConnectorError::CaptureMethodNotSupported)?;

        let (archipel_flow, is_incremental_allowed) = match capture_method {
            CaptureMethod::Automatic => (ArchipelPaymentFlow::Pay, false),
            _ => (
                ArchipelPaymentFlow::Authorize,
                item.data.request.request_incremental_authorization,
            ),
        };

        let connector_metadata: Option<serde_json::Value> =
            ArchipelTransactionMetadata::from(&item.response)
                .encode_to_value()
                .ok();

        let status: AttemptStatus =
            ArchipelFlowStatus::new(item.response.transaction_result, archipel_flow).into();

        Ok(Self {
            status,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.order.id),
                charges: None,
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata,
                // Save archipel initial transaction uuid for network transaction mit/cit
                network_txn_id: item
                    .data
                    .request
                    .is_customer_initiated_mandate_payment()
                    .then_some(item.response.transaction_id),
                connector_response_reference_id: None,
                incremental_authorization_allowed: Some(is_incremental_allowed),
            }),
            ..item.data
        })
    }
}

// PSYNC FLOW
impl TryFrom<PaymentsSyncResponseRouterData<ArchipelPaymentsResponse>> for PaymentsSyncRouterData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsSyncResponseRouterData<ArchipelPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        if let Some(error) = item.response.error {
            return Ok(Self {
                response: Err(ArchipelErrorMessageWithHttpCode::new(error, item.http_code).into()),
                ..item.data
            });
        };

        let connector_metadata: Option<serde_json::Value> =
            ArchipelTransactionMetadata::from(&item.response)
                .encode_to_value()
                .ok();

        let capture_method = item
            .data
            .request
            .capture_method
            .ok_or_else(|| errors::ConnectorError::CaptureMethodNotSupported)?;

        let archipel_flow: ArchipelPaymentFlow = (item.data.status, capture_method).try_into()?;

        let status: AttemptStatus =
            ArchipelFlowStatus::new(item.response.transaction_result, archipel_flow).into();

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

/* CAPTURE FLOW */

#[derive(Debug, Serialize, Eq, PartialEq)]
pub struct ArchipelCaptureRequest {
    order: ArchipelCaptureOrderRequest,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
pub struct ArchipelCaptureOrderRequest {
    amount: MinorUnit,
}

impl From<ArchipelRouterData<&PaymentsCaptureRouterData>> for ArchipelCaptureRequest {
    fn from(item: ArchipelRouterData<&PaymentsCaptureRouterData>) -> Self {
        Self {
            order: ArchipelCaptureOrderRequest {
                amount: item.amount,
            },
        }
    }
}

impl TryFrom<PaymentsCaptureResponseRouterData<ArchipelPaymentsResponse>>
    for PaymentsCaptureRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsCaptureResponseRouterData<ArchipelPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        if let Some(error) = item.response.error {
            return Ok(Self {
                response: Err(ArchipelErrorMessageWithHttpCode::new(error, item.http_code).into()),
                ..item.data
            });
        };

        let connector_metadata: Option<serde_json::Value> =
            ArchipelTransactionMetadata::from(&item.response)
                .encode_to_value()
                .ok();

        let status: AttemptStatus = ArchipelFlowStatus::new(
            item.response.transaction_result,
            ArchipelPaymentFlow::Capture,
        )
        .into();

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

// Setup Mandate FLow
impl TryFrom<ArchipelRouterData<&SetupMandateRouterData>> for ArchipelCardAuthorizationRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: ArchipelRouterData<&SetupMandateRouterData>) -> Result<Self, Self::Error> {
        let order = ArchipelOrderRequest {
            amount: item.amount,
            currency: item.router_data.request.currency.to_string(),
            certainty: ArchipelPaymentCertainty::Final,
            initiator: ArchipelPaymentInitiator::Customer,
        };

        let cardholder = Some(ArchipelCardHolder {
            billing_address: item
                .router_data
                .get_billing_address()
                .ok()
                .and_then(|address| address.to_archipel_billing_address()),
        });

        // NOTE: Archipel does not accept `card.card_holder_name` field without `cardholder` field.
        // So if `card_holder` is None, `card.card_holder_name` must also be None.
        // However, the reverse is allowed — the `cardholder` field can exist without `card.card_holder_name`.
        let card_holder_name = cardholder.as_ref().and_then(|_| {
            item.router_data
                .get_billing()
                .ok()
                .and_then(|billing| billing.get_optional_full_name())
        });

        let stored_on_file = true;

        let credential_indicator = Some(ArchipelCredentialIndicator {
            status: ArchipelCredentialIndicatorStatus::Initial,
            recurring: Some(false),
            transaction_id: None,
        });

        let payment_information = ArchipelPaymentInformation {
            order,
            cardholder,
            card_holder_name,
            stored_on_file,
            credential_indicator,
        };

        let card_data = match &item.router_data.request.payment_method_data {
            PaymentMethodData::Card(ccard) => ArchipelCard::try_from((
                payment_information.card_holder_name,
                payment_information.cardholder.clone(),
                ccard,
            ))?,
            _ => Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Archipel"),
            ))?,
        };

        Ok(Self {
            order: payment_information.order,
            cardholder: payment_information.cardholder.clone(),
            card: card_data,
            three_ds: None,
            credential_indicator: payment_information.credential_indicator,
            stored_on_file: payment_information.stored_on_file,
            tenant_id: item.tenant_id,
        })
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            ArchipelPaymentsResponse,
            SetupMandateRequestData,
            PaymentsResponseData,
        >,
    > for RouterData<F, SetupMandateRequestData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            ArchipelPaymentsResponse,
            SetupMandateRequestData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        if let Some(error) = item.response.error {
            return Ok(Self {
                response: Err(ArchipelErrorMessageWithHttpCode::new(error, item.http_code).into()),
                ..item.data
            });
        };

        let connector_metadata: Option<serde_json::Value> =
            ArchipelTransactionMetadata::from(&item.response)
                .encode_to_value()
                .ok();

        let status: AttemptStatus = ArchipelFlowStatus::new(
            item.response.transaction_result,
            ArchipelPaymentFlow::Verify,
        )
        .into();

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

//      Void Flow => /cancel/{order_id}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelPaymentsCancelRequest {
    tenant_id: ArchipelTenantId,
}

impl From<ArchipelRouterData<&PaymentsCancelRouterData>> for ArchipelPaymentsCancelRequest {
    fn from(item: ArchipelRouterData<&PaymentsCancelRouterData>) -> Self {
        Self {
            tenant_id: item.tenant_id,
        }
    }
}

impl TryFrom<PaymentsCancelResponseRouterData<ArchipelPaymentsResponse>>
    for PaymentsCancelRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsCancelResponseRouterData<ArchipelPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        if let Some(error) = item.response.error {
            return Ok(Self {
                response: Err(ArchipelErrorMessageWithHttpCode::new(error, item.http_code).into()),
                ..item.data
            });
        };

        let connector_metadata: Option<serde_json::Value> =
            ArchipelTransactionMetadata::from(&item.response)
                .encode_to_value()
                .ok();

        let status: AttemptStatus = ArchipelFlowStatus::new(
            item.response.transaction_result,
            ArchipelPaymentFlow::Cancel,
        )
        .into();

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

#[derive(Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelIncrementalAuthorizationRequest {
    order: ArchipelOrderRequest,
    tenant_id: ArchipelTenantId,
}

// Incremental Authorization status mapping
impl From<ArchipelPaymentStatus> for AuthorizationStatus {
    fn from(status: ArchipelPaymentStatus) -> Self {
        match status {
            ArchipelPaymentStatus::Succeeded => Self::Success,
            ArchipelPaymentStatus::Failed => Self::Failure,
        }
    }
}

impl From<ArchipelRouterData<&PaymentsIncrementalAuthorizationRouterData>>
    for ArchipelIncrementalAuthorizationRequest
{
    fn from(item: ArchipelRouterData<&PaymentsIncrementalAuthorizationRouterData>) -> Self {
        Self {
            order: ArchipelOrderRequest {
                amount: item.amount,
                currency: item.router_data.request.currency.to_string(),
                certainty: ArchipelPaymentCertainty::Estimated,
                initiator: ArchipelPaymentInitiator::Customer,
            },
            tenant_id: item.tenant_id,
        }
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            ArchipelPaymentsResponse,
            PaymentsIncrementalAuthorizationData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsIncrementalAuthorizationData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            ArchipelPaymentsResponse,
            PaymentsIncrementalAuthorizationData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let status = AuthorizationStatus::from(item.response.transaction_result);

        let (error_code, error_message) = match (&status, item.response.error) {
            (AuthorizationStatus::Success, _) | (_, None) => (None, None),
            (_, Some(err)) => (Some(err.code), err.description),
        };

        Ok(Self {
            response: Ok(PaymentsResponseData::IncrementalAuthorizationResponse {
                status,
                error_code,
                error_message,
                connector_authorization_id: None,
            }),
            ..item.data
        })
    }
}

/* REFUND FLOW */
#[derive(Debug, Serialize)]
pub struct ArchipelRefundOrder {
    pub amount: MinorUnit,
    pub currency: Currency,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelRefundRequest {
    pub order: ArchipelRefundOrder,
    pub tenant_id: ArchipelTenantId,
}

impl<F> From<ArchipelRouterData<&RefundsRouterData<F>>> for ArchipelRefundRequest {
    fn from(item: ArchipelRouterData<&RefundsRouterData<F>>) -> Self {
        Self {
            order: ArchipelRefundOrder {
                amount: item.amount,
                currency: item.router_data.request.currency,
            },
            tenant_id: item.tenant_id,
        }
    }
}

// Type definition for Refund Response
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
#[serde(rename_all = "UPPERCASE")]
pub enum ArchipelRefundStatus {
    Accepted,
    Failed,
    #[default]
    Pending,
}

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

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct ArchipelRefundOrderResponse {
    id: String,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArchipelRefundResponse {
    order: ArchipelRefundOrderResponse,
    status: ArchipelRefundStatus,
    transaction_result: ArchipelPaymentStatus,
    transaction_id: Option<String>,
    transaction_date: Option<String>,
    error: Option<ArchipelErrorMessage>,
}

impl TryFrom<ArchipelRefundResponse> for RefundsResponseData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(resp: ArchipelRefundResponse) -> Result<Self, Self::Error> {
        Ok(Self {
            connector_refund_id: resp
                .transaction_id
                .ok_or_else(|| errors::ConnectorError::ParsingFailed)?,
            refund_status: RefundStatus::from(resp.transaction_result),
        })
    }
}

impl TryFrom<RefundsResponseRouterData<Execute, ArchipelRefundResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, ArchipelRefundResponse>,
    ) -> Result<Self, Self::Error> {
        let response = match item.response.error {
            None => Ok(RefundsResponseData::try_from(item.response)?),
            Some(error) => Err(ArchipelErrorMessageWithHttpCode::new(error, item.http_code).into()),
        };

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

impl TryFrom<RefundsResponseRouterData<RSync, ArchipelRefundResponse>>
    for RefundsRouterData<RSync>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, ArchipelRefundResponse>,
    ) -> Result<Self, Self::Error> {
        let response = match item.response.error {
            None => Ok(RefundsResponseData::try_from(item.response)?),
            Some(error) => Err(ArchipelErrorMessageWithHttpCode::new(error, item.http_code).into()),
        };

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

impl From<ArchipelErrorMessageWithHttpCode> for ErrorResponse {
    fn from(
        ArchipelErrorMessageWithHttpCode {
            error_message,
            http_code,
        }: ArchipelErrorMessageWithHttpCode,
    ) -> Self {
        Self {
            status_code: http_code,
            code: error_message.code,
            attempt_status: None,
            connector_transaction_id: None,
            message: error_message
                .description
                .clone()
                .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
            reason: error_message.description,
            network_decline_code: None,
            network_advice_code: None,
            network_error_message: None,
            connector_metadata: None,
        }
    }
}


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

use std::collections::HashMap;

use api_models::webhooks::IncomingWebhookEvent;
use common_enums::enums;
use common_utils::{ext_traits::ValueExt, types::StringMajorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    address::AddressDetails,
    payment_method_data::PaymentMethodData,
    router_data::{
        AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData,
        ErrorResponse, RouterData,
    },
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{MandateReference, PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, RefundsRouterData,
        SetupMandateRouterData,
    },
};
use hyperswitch_interfaces::{
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    errors,
};
use masking::{ExposeOptionInterface, Secret};
use serde::Deserialize;

use super::{requests, responses};
use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        get_unimplemented_payment_method_error_message, is_manual_capture, AddressDetailsData,
        CardData, PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData,
        RouterData as OtherRouterData,
    },
};

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

fn build_payload_cards_request_data(
    payment_method_data: &PaymentMethodData,
    connector_auth_type: &ConnectorAuthType,
    currency: enums::Currency,
    amount: StringMajorUnit,
    billing_address: &AddressDetails,
    capture_method: Option<enums::CaptureMethod>,
    is_mandate: bool,
) -> Result<requests::PayloadCardsRequestData, Error> {
    if let PaymentMethodData::Card(req_card) = payment_method_data {
        let payload_auth = PayloadAuth::try_from((connector_auth_type, currency))?;

        let card = requests::PayloadCard {
            number: req_card.clone().card_number,
            expiry: req_card
                .clone()
                .get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned())?,
            cvc: req_card.card_cvc.clone(),
        };

        let city = billing_address.get_city()?.to_owned();
        let country = billing_address.get_country()?.to_owned();
        let postal_code = billing_address.get_zip()?.to_owned();
        let state_province = billing_address.get_state()?.to_owned();
        let street_address = billing_address.get_line1()?.to_owned();

        let billing_address = requests::BillingAddress {
            city,
            country,
            postal_code,
            state_province,
            street_address,
        };

        // For manual capture, set status to "authorized"
        let status = if is_manual_capture(capture_method) {
            Some(responses::PayloadPaymentStatus::Authorized)
        } else {
            None
        };

        Ok(requests::PayloadCardsRequestData {
            amount,
            card,
            transaction_types: requests::TransactionTypes::Payment,
            payment_method_type: "card".to_string(),
            status,
            billing_address,
            processing_id: payload_auth.processing_account_id,
            keep_active: is_mandate,
        })
    } else {
        Err(
            errors::ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                "Payload",
            ))
            .into(),
        )
    }
}

pub struct PayloadRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

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

// Auth Struct
#[derive(Debug, Clone, Deserialize)]
pub struct PayloadAuth {
    pub api_key: Secret<String>,
    pub processing_account_id: Option<Secret<String>>,
}

#[derive(Debug, Clone)]
pub struct PayloadAuthType {
    pub auths: HashMap<enums::Currency, PayloadAuth>,
}

impl TryFrom<(&ConnectorAuthType, enums::Currency)> for PayloadAuth {
    type Error = Error;
    fn try_from(value: (&ConnectorAuthType, enums::Currency)) -> Result<Self, Self::Error> {
        let (auth_type, currency) = value;
        match auth_type {
            ConnectorAuthType::CurrencyAuthKey { auth_key_map } => {
                let auth_key = auth_key_map.get(&currency).ok_or(
                    errors::ConnectorError::CurrencyNotSupported {
                        message: currency.to_string(),
                        connector: "Payload",
                    },
                )?;

                auth_key
                    .to_owned()
                    .parse_value("PayloadAuth")
                    .change_context(errors::ConnectorError::FailedToObtainAuthType)
            }
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

impl TryFrom<&ConnectorAuthType> for PayloadAuthType {
    type Error = Error;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::CurrencyAuthKey { auth_key_map } => {
                let auths = auth_key_map
                    .iter()
                    .map(|(currency, auth_key)| {
                        let auth: PayloadAuth = auth_key
                            .to_owned()
                            .parse_value("PayloadAuth")
                            .change_context(errors::ConnectorError::InvalidDataFormat {
                                field_name: "auth_key_map",
                            })?;
                        Ok((*currency, auth))
                    })
                    .collect::<Result<_, Self::Error>>()?;
                Ok(Self { auths })
            }
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

impl TryFrom<&SetupMandateRouterData> for requests::PayloadCardsRequestData {
    type Error = Error;
    fn try_from(item: &SetupMandateRouterData) -> Result<Self, Self::Error> {
        match item.request.amount {
            Some(amount) if amount > 0 => Err(errors::ConnectorError::FlowNotSupported {
                flow: "Setup mandate with non zero amount".to_string(),
                connector: "Payload".to_string(),
            }
            .into()),
            _ => {
                let billing_address = item.get_billing_address()?;
                let is_mandate = item.request.is_customer_initiated_mandate_payment();

                build_payload_cards_request_data(
                    &item.request.payment_method_data,
                    &item.connector_auth_type,
                    item.request.currency,
                    StringMajorUnit::zero(),
                    billing_address,
                    item.request.capture_method,
                    is_mandate,
                )
            }
        }
    }
}

impl TryFrom<&PayloadRouterData<&PaymentsAuthorizeRouterData>>
    for requests::PayloadPaymentsRequest
{
    type Error = Error;
    fn try_from(
        item: &PayloadRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        if item.router_data.is_three_ds() {
            Err(errors::ConnectorError::NotSupported {
                message: "Cards 3DS".to_string(),
                connector: "Payload",
            })?
        }

        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(_) => {
                let billing_address = item.router_data.get_billing_address()?;
                let is_mandate = item.router_data.request.is_mandate_payment();

                let cards_data = build_payload_cards_request_data(
                    &item.router_data.request.payment_method_data,
                    &item.router_data.connector_auth_type,
                    item.router_data.request.currency,
                    item.amount.clone(),
                    billing_address,
                    item.router_data.request.capture_method,
                    is_mandate,
                )?;

                Ok(Self::PayloadCardsRequest(Box::new(cards_data)))
            }
            PaymentMethodData::MandatePayment => {
                // For manual capture, set status to "authorized"
                let status = if is_manual_capture(item.router_data.request.capture_method) {
                    Some(responses::PayloadPaymentStatus::Authorized)
                } else {
                    None
                };

                Ok(Self::PayloadMandateRequest(Box::new(
                    requests::PayloadMandateRequestData {
                        amount: item.amount.clone(),
                        transaction_types: requests::TransactionTypes::Payment,
                        payment_method_id: Secret::new(
                            item.router_data.request.get_connector_mandate_id()?,
                        ),
                        status,
                    },
                )))
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

impl From<responses::PayloadPaymentStatus> for common_enums::AttemptStatus {
    fn from(item: responses::PayloadPaymentStatus) -> Self {
        match item {
            responses::PayloadPaymentStatus::Authorized => Self::Authorized,
            responses::PayloadPaymentStatus::Processed => Self::Charged,
            responses::PayloadPaymentStatus::Processing => Self::Pending,
            responses::PayloadPaymentStatus::Rejected
            | responses::PayloadPaymentStatus::Declined => Self::Failure,
            responses::PayloadPaymentStatus::Voided => Self::Voided,
        }
    }
}

impl<F: 'static, T>
    TryFrom<ResponseRouterData<F, responses::PayloadPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
where
    T: 'static,
{
    type Error = Error;
    fn try_from(
        item: ResponseRouterData<F, responses::PayloadPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        match item.response.clone() {
            responses::PayloadPaymentsResponse::PayloadCardsResponse(response) => {
                let status = enums::AttemptStatus::from(response.status);

                let router_data: &dyn std::any::Any = &item.data;
                let is_mandate_payment = router_data
                    .downcast_ref::<PaymentsAuthorizeRouterData>()
                    .is_some_and(|router_data| router_data.request.is_mandate_payment())
                    || router_data
                        .downcast_ref::<SetupMandateRouterData>()
                        .is_some();

                let mandate_reference = if is_mandate_payment {
                    let connector_payment_method_id =
                        response.connector_payment_method_id.clone().expose_option();
                    if connector_payment_method_id.is_some() {
                        Some(MandateReference {
                            connector_mandate_id: connector_payment_method_id,
                            payment_method_id: None,
                            mandate_metadata: None,
                            connector_mandate_request_reference_id: None,
                        })
                    } else {
                        None
                    }
                } else {
                    None
                };

                let connector_response = {
                    response.avs.map(|avs_response| {
                        let payment_checks = serde_json::json!({
                            "avs_result": avs_response
                        });
                        AdditionalPaymentMethodConnectorResponse::Card {
                            authentication_data: None,
                            payment_checks: Some(payment_checks),
                            card_network: None,
                            domestic_network: None,
                        }
                    })
                }
                .map(ConnectorResponseData::with_additional_payment_method_data);

                let response_result = if status == enums::AttemptStatus::Failure {
                    Err(ErrorResponse {
                        attempt_status: None,
                        code: response
                            .status_code
                            .clone()
                            .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
                        message: response
                            .status_message
                            .clone()
                            .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                        reason: response.status_message,
                        status_code: item.http_code,
                        connector_transaction_id: Some(response.transaction_id.clone()),
                        network_decline_code: None,
                        network_advice_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(response.transaction_id),
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(mandate_reference),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: response.ref_number,
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                };
                Ok(Self {
                    status,
                    response: response_result,
                    connector_response,
                    ..item.data
                })
            }
        }
    }
}

impl<T> TryFrom<&PayloadRouterData<T>> for requests::PayloadCancelRequest {
    type Error = Error;
    fn try_from(_item: &PayloadRouterData<T>) -> Result<Self, Self::Error> {
        Ok(Self {
            status: responses::PayloadPaymentStatus::Voided,
        })
    }
}

impl TryFrom<&PayloadRouterData<&PaymentsCaptureRouterData>> for requests::PayloadCaptureRequest {
    type Error = Error;
    fn try_from(
        _item: &PayloadRouterData<&PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: responses::PayloadPaymentStatus::Processed,
        })
    }
}

impl<F> TryFrom<&PayloadRouterData<&RefundsRouterData<F>>> for requests::PayloadRefundRequest {
    type Error = Error;
    fn try_from(item: &PayloadRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let connector_transaction_id = item.router_data.request.connector_transaction_id.clone();

        Ok(Self {
            transaction_type: requests::TransactionTypes::Refund,
            amount: item.amount.to_owned(),
            ledger_assoc_transaction_id: connector_transaction_id,
        })
    }
}

impl From<responses::RefundStatus> for enums::RefundStatus {
    fn from(item: responses::RefundStatus) -> Self {
        match item {
            responses::RefundStatus::Processed => Self::Success,
            responses::RefundStatus::Processing => Self::Pending,
            responses::RefundStatus::Declined | responses::RefundStatus::Rejected => Self::Failure,
        }
    }
}

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

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

// Webhook transformations
impl From<responses::PayloadWebhooksTrigger> for IncomingWebhookEvent {
    fn from(trigger: responses::PayloadWebhooksTrigger) -> Self {
        match trigger {
            // Payment Success Events
            responses::PayloadWebhooksTrigger::Processed => Self::PaymentIntentSuccess,
            responses::PayloadWebhooksTrigger::Authorized => {
                Self::PaymentIntentAuthorizationSuccess
            }
            // Payment Processing Events
            responses::PayloadWebhooksTrigger::Payment
            | responses::PayloadWebhooksTrigger::AutomaticPayment => Self::PaymentIntentProcessing,
            // Payment Failure Events
            responses::PayloadWebhooksTrigger::Decline
            | responses::PayloadWebhooksTrigger::Reject
            | responses::PayloadWebhooksTrigger::BankAccountReject => Self::PaymentIntentFailure,
            responses::PayloadWebhooksTrigger::Void
            | responses::PayloadWebhooksTrigger::Reversal => Self::PaymentIntentCancelled,
            // Dispute Events
            responses::PayloadWebhooksTrigger::Chargeback => Self::DisputeOpened,
            responses::PayloadWebhooksTrigger::ChargebackReversal => Self::DisputeWon,
            // Other payment-related events
            // Events not supported by our standard flows
            responses::PayloadWebhooksTrigger::PaymentActivationStatus
            | responses::PayloadWebhooksTrigger::Refund
            | responses::PayloadWebhooksTrigger::Credit
            | responses::PayloadWebhooksTrigger::Deposit
            | responses::PayloadWebhooksTrigger::PaymentLinkStatus
            | responses::PayloadWebhooksTrigger::ProcessingStatus
            | responses::PayloadWebhooksTrigger::TransactionOperation
            | responses::PayloadWebhooksTrigger::TransactionOperationClear => {
                Self::EventNotSupported
            }
        }
    }
}

impl TryFrom<responses::PayloadWebhooksTrigger> for responses::PayloadPaymentStatus {
    type Error = Error;
    fn try_from(trigger: responses::PayloadWebhooksTrigger) -> Result<Self, Self::Error> {
        match trigger {
            // Payment Success Events
            responses::PayloadWebhooksTrigger::Processed => Ok(Self::Processed),
            responses::PayloadWebhooksTrigger::Authorized => Ok(Self::Authorized),
            // Payment Processing Events
            responses::PayloadWebhooksTrigger::Payment
            | responses::PayloadWebhooksTrigger::AutomaticPayment
            | responses::PayloadWebhooksTrigger::Reversal => Ok(Self::Processing),
            // Payment Failure Events
            responses::PayloadWebhooksTrigger::Decline
            | responses::PayloadWebhooksTrigger::Reject
            | responses::PayloadWebhooksTrigger::BankAccountReject => Ok(Self::Declined),
            responses::PayloadWebhooksTrigger::Void => Ok(Self::Voided),
            responses::PayloadWebhooksTrigger::Refund => {
                Err(errors::ConnectorError::NotSupported {
                    message: "Refund Webhook".to_string(),
                    connector: "Payload",
                }
                .into())
            }
            responses::PayloadWebhooksTrigger::Chargeback
            | responses::PayloadWebhooksTrigger::ChargebackReversal
            | responses::PayloadWebhooksTrigger::PaymentActivationStatus
            | responses::PayloadWebhooksTrigger::Credit
            | responses::PayloadWebhooksTrigger::Deposit
            | responses::PayloadWebhooksTrigger::PaymentLinkStatus
            | responses::PayloadWebhooksTrigger::ProcessingStatus
            | responses::PayloadWebhooksTrigger::TransactionOperation
            | responses::PayloadWebhooksTrigger::TransactionOperationClear => {
                Err(errors::ConnectorError::WebhookEventTypeNotFound.into())
            }
        }
    }
}

impl TryFrom<responses::PayloadWebhookEvent> for responses::PayloadPaymentsResponse {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(webhook_body: responses::PayloadWebhookEvent) -> Result<Self, Self::Error> {
        let status = responses::PayloadPaymentStatus::try_from(webhook_body.trigger.clone())?;
        Ok(Self::PayloadCardsResponse(
            responses::PayloadCardsResponseData {
                amount: None,
                avs: None,
                customer_id: None,
                transaction_id: webhook_body
                    .triggered_on
                    .transaction_id
                    .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                connector_payment_method_id: None,
                processing_id: None,
                processing_method_id: None,
                ref_number: None,
                status,
                status_code: None,
                status_message: None,
                response_type: None,
            },
        ))
    }
}


// File: crates/hyperswitch_connectors/src/connectors/payload/responses.rs

use masking::Secret;
use serde::{Deserialize, Serialize};

// PaymentsResponse
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum PayloadPaymentStatus {
    Authorized,
    Declined,
    Processed,
    #[default]
    Processing,
    Rejected,
    Voided,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PayloadPaymentsResponse {
    PayloadCardsResponse(PayloadCardsResponseData),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum AvsResponse {
    Unknown,
    NoMatch,
    Zip,
    Street,
    StreetAndZip,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PayloadCardsResponseData {
    pub amount: Option<f64>,
    pub avs: Option<AvsResponse>,
    pub customer_id: Option<Secret<String>>,
    #[serde(rename = "id")]
    pub transaction_id: String,
    #[serde(rename = "payment_method_id")]
    pub connector_payment_method_id: Option<Secret<String>>,
    pub processing_id: Option<Secret<String>>,
    pub processing_method_id: Option<String>,
    pub ref_number: Option<String>,
    pub status: PayloadPaymentStatus,
    pub status_code: Option<String>,
    pub status_message: Option<String>,
    #[serde(rename = "type")]
    pub response_type: Option<String>,
}

// Type definition for Refund Response
// Added based on assumptions since this is not provided in the documentation
#[derive(Debug, Copy, Serialize, Default, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum RefundStatus {
    Declined,
    Processed,
    #[default]
    Processing,
    Rejected,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundsLedger {
    pub amount: f64,
    #[serde(rename = "assoc_transaction_id")]
    pub associated_transaction_id: String, // Connector transaction id
    #[serde(rename = "id")]
    pub ledger_id: Secret<String>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct PayloadRefundResponse {
    pub amount: Option<f64>,
    #[serde(rename = "id")]
    pub transaction_id: String,
    pub ledger: Vec<RefundsLedger>,
    #[serde(rename = "payment_method_id")]
    pub connector_payment_method_id: Option<Secret<String>>,
    pub processing_id: Option<Secret<String>>,
    pub ref_number: Option<String>,
    pub status: RefundStatus,
    pub status_code: Option<String>,
    pub status_message: Option<String>,
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct PayloadErrorResponse {
    pub error_type: String,
    pub error_description: String,
    pub object: String,
    /// Payload returns arbitrary details in JSON format
    pub details: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum PayloadWebhooksTrigger {
    Payment,
    Processed,
    Authorized,
    Credit,
    Refund,
    Reversal,
    Void,
    AutomaticPayment,
    Decline,
    Deposit,
    Reject,
    #[serde(rename = "payment_activation:status")]
    PaymentActivationStatus,
    #[serde(rename = "payment_link:status")]
    PaymentLinkStatus,
    ProcessingStatus,
    BankAccountReject,
    Chargeback,
    ChargebackReversal,
    #[serde(rename = "transaction:operation")]
    TransactionOperation,
    #[serde(rename = "transaction:operation:clear")]
    TransactionOperationClear,
}

// Webhook response structures
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayloadWebhookEvent {
    pub object: String, // Added to match actual webhook structure
    pub trigger: PayloadWebhooksTrigger,
    pub webhook_id: String,
    pub triggered_at: String, // Added to match actual webhook structure
    // Webhooks Payload
    pub triggered_on: PayloadEventDetails,
    pub url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PayloadEventDetails {
    #[serde(rename = "id")]
    pub transaction_id: Option<String>,
    pub object: String,
    pub value: Option<serde_json::Value>, // Changed to handle any value type including null
}


// File: crates/hyperswitch_connectors/src/connectors/payload/requests.rs

use common_utils::types::StringMajorUnit;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::connectors::payload::responses;

#[derive(Debug, Serialize, PartialEq)]
#[serde(untagged)]
pub enum PayloadPaymentsRequest {
    PayloadCardsRequest(Box<PayloadCardsRequestData>),
    PayloadMandateRequest(Box<PayloadMandateRequestData>),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum TransactionTypes {
    Credit,
    Chargeback,
    ChargebackReversal,
    Deposit,
    Payment,
    Refund,
    Reversal,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BillingAddress {
    #[serde(rename = "payment_method[billing_address][city]")]
    pub city: String,
    #[serde(rename = "payment_method[billing_address][country_code]")]
    pub country: common_enums::CountryAlpha2,
    #[serde(rename = "payment_method[billing_address][postal_code]")]
    pub postal_code: Secret<String>,
    #[serde(rename = "payment_method[billing_address][state_province]")]
    pub state_province: Secret<String>,
    #[serde(rename = "payment_method[billing_address][street_address]")]
    pub street_address: Secret<String>,
}

#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct PayloadCardsRequestData {
    pub amount: StringMajorUnit,
    #[serde(flatten)]
    pub card: PayloadCard,
    #[serde(rename = "type")]
    pub transaction_types: TransactionTypes,
    // For manual capture, set status to "authorized", otherwise omit
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<responses::PayloadPaymentStatus>,
    #[serde(rename = "payment_method[type]")]
    pub payment_method_type: String,
    // Billing address fields are for AVS validation
    #[serde(flatten)]
    pub billing_address: BillingAddress,
    pub processing_id: Option<Secret<String>>,
    /// Allows one-time payment by customer without saving their payment method
    /// This is true by default
    #[serde(rename = "payment_method[keep_active]")]
    pub keep_active: bool,
}

#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct PayloadMandateRequestData {
    pub amount: StringMajorUnit,
    #[serde(rename = "type")]
    pub transaction_types: TransactionTypes,
    // Based on the connectors' response, we can do recurring payment either based on a default payment method id saved in the customer profile or a specific payment method id
    // Connector by default, saves every payment method
    pub payment_method_id: Secret<String>,
    // For manual capture, set status to "authorized", otherwise omit
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<responses::PayloadPaymentStatus>,
}

#[derive(Default, Clone, Debug, Serialize, Eq, PartialEq)]
pub struct PayloadCard {
    #[serde(rename = "payment_method[card][card_number]")]
    pub number: cards::CardNumber,
    #[serde(rename = "payment_method[card][expiry]")]
    pub expiry: Secret<String>,
    #[serde(rename = "payment_method[card][card_code]")]
    pub cvc: Secret<String>,
}

#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct PayloadCancelRequest {
    pub status: responses::PayloadPaymentStatus,
}

// Type definition for CaptureRequest
#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct PayloadCaptureRequest {
    pub status: responses::PayloadPaymentStatus,
}

// Type definition for RefundRequest
#[derive(Debug, Serialize)]
pub struct PayloadRefundRequest {
    #[serde(rename = "type")]
    pub transaction_type: TransactionTypes,
    pub amount: StringMajorUnit,
    #[serde(rename = "ledger[0][assoc_transaction_id]")]
    pub ledger_assoc_transaction_id: String,
}


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

use common_enums::{AttemptStatus, RefundStatus};
use common_utils::types::StringMinorUnit;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors::ConnectorError;
use masking::Secret;
use serde::{Deserialize, Serialize};

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

//TODO: Fill the struct with respective fields
pub struct WellsfargopayoutRouterData<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 WellsfargopayoutRouterData<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 WellsfargopayoutPaymentsRequest {
    amount: StringMinorUnit,
    card: WellsfargopayoutCard,
}

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

impl TryFrom<&WellsfargopayoutRouterData<&PaymentsAuthorizeRouterData>>
    for WellsfargopayoutPaymentsRequest
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: &WellsfargopayoutRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = WellsfargopayoutCard {
                    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(ConnectorError::NotImplemented("Payment methods".to_string()).into()),
        }
    }
}

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

impl TryFrom<&ConnectorAuthType> for WellsfargopayoutAuthType {
    type Error = error_stack::Report<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(ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}
// PaymentsResponse
//TODO: Append the remaining status flags
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum WellsfargopayoutPaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

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

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

impl<F, T> TryFrom<ResponseRouterData<F, WellsfargopayoutPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, WellsfargopayoutPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: 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 WellsfargopayoutRefundRequest {
    pub amount: StringMinorUnit,
}

impl<F> TryFrom<&WellsfargopayoutRouterData<&RefundsRouterData<F>>>
    for WellsfargopayoutRefundRequest
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: &WellsfargopayoutRouterData<&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 WellsfargopayoutRefundStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<WellsfargopayoutRefundStatus> for RefundStatus {
    fn from(item: WellsfargopayoutRefundStatus) -> Self {
        match item {
            WellsfargopayoutRefundStatus::Succeeded => Self::Success,
            WellsfargopayoutRefundStatus::Failed => Self::Failure,
            WellsfargopayoutRefundStatus::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: WellsfargopayoutRefundStatus,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<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: RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<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: RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct WellsfargopayoutErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}