pub mod transformers;

use std::fmt::Debug;

use common_enums::CurrencyUnit;
use common_utils::{
    errors::CustomResult, events, ext_traits::ByteSliceExt, types::StringMajorUnit,
};
use domain_types::{
    connector_flow::{
        Authorize, Capture, CreateConnectorCustomer, CreateOrder, PSync, RSync, Refund,
        RepeatPayment, ServerAuthenticationToken, SetupMandate, Void,
    },
    connector_types::{
        ConnectorCustomerData, ConnectorCustomerResponse, PaymentCreateOrderData,
        PaymentCreateOrderResponse, PaymentFlowData, PaymentVoidData, PaymentsAuthorizeData,
        PaymentsCaptureData, PaymentsResponseData, PaymentsSyncData, RefundFlowData,
        RefundSyncData, RefundsData, RefundsResponseData, RepeatPaymentData, ResponseId,
        ServerAuthenticationTokenRequestData, ServerAuthenticationTokenResponseData,
        SetupMandateRequestData,
    },
    merchant_authentication_flow_data::MerchantAuthenticationFlowData,
    payment_method_data::PaymentMethodDataTypes,
    router_data::{ConnectorSpecificConfig, ErrorResponse},
    router_data_v2::RouterDataV2,
    router_response_types::Response,
    types::Connectors,
};
use error_stack::ResultExt;
use hyperswitch_masking::{ExposeInterface, Maskable, PeekInterface};
use interfaces::{
    api::ConnectorCommon, connector_integration_v2::ConnectorIntegrationV2, connector_types,
    decode::BodyDecoding, verification::SourceVerification,
};
use serde::Serialize;
use transformers::{
    self as airwallex, AirwallexAccessTokenRequest, AirwallexAccessTokenResponse,
    AirwallexAuthorizeRequest, AirwallexCaptureRequest, AirwallexCaptureResponse,
    AirwallexCustomerRequest, AirwallexCustomerResponse, AirwallexIntentRequest,
    AirwallexIntentResponse, AirwallexPaymentsResponse, AirwallexRefundRequest,
    AirwallexRefundResponse, AirwallexRefundSyncResponse, AirwallexRepeatPaymentRequest,
    AirwallexRepeatPaymentResponse, AirwallexSetupMandateRequest, AirwallexSetupMandateResponse,
    AirwallexSyncResponse, AirwallexVoidRequest, AirwallexVoidResponse,
};

use super::macros;
use crate::types::ResponseRouterData;
use crate::with_error_response_body;
use domain_types::errors::ConnectorError;
use domain_types::errors::IntegrationError;

pub(crate) mod headers {
    pub(crate) const CONTENT_TYPE: &str = "Content-Type";
    pub(crate) const AUTHORIZATION: &str = "Authorization";
}

// Airwallex struct will be generated by create_all_prerequisites! macro

// ===== CONNECTOR SERVICE TRAIT IMPLEMENTATIONS =====
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::ConnectorServiceTrait<T> for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentAuthorizeV2<T> for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentSyncV2 for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentVoidV2 for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentCapture for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::RefundV2 for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::RefundSyncV2 for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::SetupMandateV2<T> for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::RepeatPaymentV2<T> for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::ServerAuthentication for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentOrderCreate for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::IncomingWebhook for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::VerifyRedirectResponse for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize> SourceVerification
    for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize> BodyDecoding
    for Airwallex<T>
{
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::ValidationTrait for Airwallex<T>
{
    fn should_do_access_token(&self, _payment_method: Option<common_enums::PaymentMethod>) -> bool {
        true
    }

    fn should_do_order_create(&self) -> bool {
        true // Enable 2-step flow: ServerAuthenticationToken → CreateOrder → Authorize
    }
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::CreateConnectorCustomer for Airwallex<T>
{
}

// ===== MACRO-BASED IMPLEMENTATION =====

macros::create_all_prerequisites!(
    connector_name: Airwallex,
    generic_type: T,
    api: [
        (
            flow: Authorize,
            request_body: AirwallexAuthorizeRequest,
            response_body: AirwallexPaymentsResponse,
            router_data: RouterDataV2<Authorize, PaymentFlowData, PaymentsAuthorizeData<T>, PaymentsResponseData>,
        ),
        (
            flow: PSync,
            response_body: AirwallexSyncResponse,
            router_data: RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
        ),
        (
            flow: Capture,
            request_body: AirwallexCaptureRequest,
            response_body: AirwallexCaptureResponse,
            router_data: RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>,
        ),
        (
            flow: Refund,
            request_body: AirwallexRefundRequest,
            response_body: AirwallexRefundResponse,
            router_data: RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>,
        ),
        (
            flow: RSync,
            response_body: AirwallexRefundSyncResponse,
            router_data: RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>,
        ),
        (
            flow: Void,
            request_body: AirwallexVoidRequest,
            response_body: AirwallexVoidResponse,
            router_data: RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>,
        ),
        (
            flow: ServerAuthenticationToken,
            request_body: AirwallexAccessTokenRequest,
            response_body: AirwallexAccessTokenResponse,
            router_data: RouterDataV2<ServerAuthenticationToken, MerchantAuthenticationFlowData, ServerAuthenticationTokenRequestData, ServerAuthenticationTokenResponseData>,
        ),
        (
            flow: CreateOrder,
            request_body: AirwallexIntentRequest,
            response_body: AirwallexIntentResponse,
            router_data: RouterDataV2<CreateOrder, PaymentFlowData, PaymentCreateOrderData, PaymentCreateOrderResponse>,
        ),
        (
            flow: SetupMandate,
            request_body: AirwallexSetupMandateRequest,
            response_body: AirwallexSetupMandateResponse,
            router_data: RouterDataV2<SetupMandate, PaymentFlowData, SetupMandateRequestData<T>, PaymentsResponseData>,
        ),
        (
            flow: RepeatPayment,
            request_body: AirwallexRepeatPaymentRequest,
            response_body: AirwallexRepeatPaymentResponse,
            router_data: RouterDataV2<RepeatPayment, PaymentFlowData, RepeatPaymentData<T>, PaymentsResponseData>,
        ),
        (
            flow: CreateConnectorCustomer,
            request_body: AirwallexCustomerRequest,
            response_body: AirwallexCustomerResponse,
            router_data: RouterDataV2<CreateConnectorCustomer, PaymentFlowData, ConnectorCustomerData, ConnectorCustomerResponse>,
        )
    ],
    amount_converters: [
        amount_converter: StringMajorUnit
    ],
    member_functions: {
        /// Build headers with OAuth Bearer token - works for all flow types
        pub fn build_headers(
            &self,
            access_token: &str,
        ) -> Vec<(String, Maskable<String>)> {
            vec![
                (
                    headers::CONTENT_TYPE.to_string(),
                    self.common_get_content_type().to_string().into(),
                ),
                (
                    headers::AUTHORIZATION.to_string(),
                    format!("Bearer {access_token}").into(),
                ),
            ]
        }
    }
);

macros::macro_connector_payout_implementation!(
    connector: Airwallex,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize]
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexAuthorizeRequest),
    curl_response: AirwallexPaymentsResponse,
    flow_name: Authorize,
    resource_common_data: PaymentFlowData,
    flow_request: PaymentsAuthorizeData<T>,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<Authorize, PaymentFlowData, PaymentsAuthorizeData<T>, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<Authorize, PaymentFlowData, PaymentsAuthorizeData<T>, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            // 2-step flow: Authorize confirms the payment intent created by CreateOrder.
            // Get order_id from reference_id (stored after CreateOrder via set_order_reference_id).
            let order_id = req.resource_common_data.connector_order_id
                .as_ref()
                .ok_or(IntegrationError::MissingRequiredField { field_name: "connector_order_id", context: Default::default() })?;
            // When HS re-invokes Authorize after a card 3DS redirect, finish the payment on the
            // same intent via `confirm_continue` instead of `confirm`. Same gate as the request
            // body builder in transformers.rs, so the URL and the body cannot disagree.
            let endpoint = if transformers::is_card_three_ds_continue(&req.request) {
                "confirm_continue"
            } else {
                "confirm"
            };
            Ok(format!(
                "{}/pa/payment_intents/{}/{}",
                req.resource_common_data.connectors.airwallex.base_url,
                order_id,
                endpoint
            ))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_response: AirwallexSyncResponse,
    flow_name: PSync,
    resource_common_data: PaymentFlowData,
    flow_request: PaymentsSyncData,
    flow_response: PaymentsResponseData,
    http_method: Get,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            let payment_id = req.request.get_connector_transaction_id()?;
            Ok(format!("{}/pa/payment_intents/{}", req.resource_common_data.connectors.airwallex.base_url, payment_id))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexCaptureRequest),
    curl_response: AirwallexCaptureResponse,
    flow_name: Capture,
    resource_common_data: PaymentFlowData,
    flow_request: PaymentsCaptureData,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            let payment_id = match &req.request.connector_transaction_id {
                ResponseId::ConnectorTransactionId(id) => id,
                _ => return Err(IntegrationError::MissingConnectorTransactionID { context: Default::default() }.into())
};
            Ok(format!("{}/pa/payment_intents/{}/capture", req.resource_common_data.connectors.airwallex.base_url, payment_id))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexRefundRequest),
    curl_response: AirwallexRefundResponse,
    flow_name: Refund,
    resource_common_data: RefundFlowData,
    flow_request: RefundsData,
    flow_response: RefundsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!("{}/pa/refunds/create", req.resource_common_data.connectors.airwallex.base_url))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_response: AirwallexRefundSyncResponse,
    flow_name: RSync,
    resource_common_data: RefundFlowData,
    flow_request: RefundSyncData,
    flow_response: RefundsResponseData,
    http_method: Get,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            let refund_id = req.request.connector_refund_id.clone();
            Ok(format!("{}/pa/refunds/{}", req.resource_common_data.connectors.airwallex.base_url, refund_id))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexAccessTokenRequest),
    curl_response: AirwallexAccessTokenResponse,
    flow_name: ServerAuthenticationToken,
    resource_common_data: MerchantAuthenticationFlowData,
    flow_request: ServerAuthenticationTokenRequestData,
    flow_response: ServerAuthenticationTokenResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<ServerAuthenticationToken, MerchantAuthenticationFlowData, ServerAuthenticationTokenRequestData, ServerAuthenticationTokenResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let auth = airwallex::AirwallexAuthType::try_from(&req.connector_config)
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(vec![
                (
                    headers::CONTENT_TYPE.to_string(),
                    "application/json".to_string().into(),
                ),
                (
                    "x-api-key".to_string(),
                    auth.api_key.expose().to_string().into(),
                ),
                (
                    "x-client-id".to_string(),
                    auth.client_id.expose().to_string().into(),
                ),
            ])
        }
        fn get_url(
            &self,
            req: &RouterDataV2<ServerAuthenticationToken, MerchantAuthenticationFlowData, ServerAuthenticationTokenRequestData, ServerAuthenticationTokenResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!("{}/authentication/login", req.resource_common_data.connectors.airwallex.base_url))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexIntentRequest),
    curl_response: AirwallexIntentResponse,
    flow_name: CreateOrder,
    resource_common_data: PaymentFlowData,
    flow_request: PaymentCreateOrderData,
    flow_response: PaymentCreateOrderResponse,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<CreateOrder, PaymentFlowData, PaymentCreateOrderData, PaymentCreateOrderResponse>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<CreateOrder, PaymentFlowData, PaymentCreateOrderData, PaymentCreateOrderResponse>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!("{}/pa/payment_intents/create", req.resource_common_data.connectors.airwallex.base_url))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexSetupMandateRequest),
    curl_response: AirwallexSetupMandateResponse,
    flow_name: SetupMandate,
    resource_common_data: PaymentFlowData,
    flow_request: SetupMandateRequestData<T>,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<SetupMandate, PaymentFlowData, SetupMandateRequestData<T>, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<SetupMandate, PaymentFlowData, SetupMandateRequestData<T>, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            // SetupMandate confirms the payment intent created by CreateOrder (auto-chained via should_do_order_create).
            let order_id = req.resource_common_data.connector_order_id
                .as_ref()
                .ok_or(IntegrationError::MissingRequiredField { field_name: "connector_order_id", context: Default::default() })?;
            Ok(format!(
                "{}/pa/payment_intents/{}/confirm",
                req.resource_common_data.connectors.airwallex.base_url,
                order_id
            ))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexRepeatPaymentRequest),
    curl_response: AirwallexRepeatPaymentResponse,
    flow_name: RepeatPayment,
    resource_common_data: PaymentFlowData,
    flow_request: RepeatPaymentData<T>,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<RepeatPayment, PaymentFlowData, RepeatPaymentData<T>, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<RepeatPayment, PaymentFlowData, RepeatPaymentData<T>, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            // Airwallex MIT requires a fresh PaymentIntent; caller must run CreateOrder
            // first and pass connector_order_id via PaymentFlowData.connector_order_id,
            // or (fallback) via connector_feature_data JSON: {"connector_order_id":"..."}.
            // The fallback is required because RecurringPaymentServiceChargeRequest (proto)
            // has no top-level `order_id` field, and its ForeignTryFrom for PaymentFlowData
            // leaves `connector_order_id = None`.
            let order_id = req
                .resource_common_data
                .connector_order_id
                .clone()
                .or_else(|| {
                    req.resource_common_data
                        .connector_feature_data
                        .as_ref()
                        .and_then(|v| v.peek().get("connector_order_id").and_then(|id| id.as_str().map(|s| s.to_string())))
                })
                .ok_or(IntegrationError::MissingRequiredField {
                    field_name: "connector_order_id",
                    context: Default::default(),
                })?;
            Ok(format!(
                "{}/pa/payment_intents/{}/confirm",
                req.resource_common_data.connectors.airwallex.base_url,
                order_id
            ))
        }
    }
);

// ===== EMPTY IMPLEMENTATIONS FOR OTHER FLOWS =====
// These will be implemented separately as needed

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexVoidRequest),
    curl_response: AirwallexVoidResponse,
    flow_name: Void,
    resource_common_data: PaymentFlowData,
    flow_request: PaymentVoidData,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req.resource_common_data.get_access_token()
                .change_context(IntegrationError::FailedToObtainAuthType { context: Default::default() })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            let payment_id = req.request.connector_transaction_id.clone();
            Ok(format!("{}/pa/payment_intents/{}/cancel", req.resource_common_data.connectors.airwallex.base_url, payment_id))
        }
    }
);

// Payment Void Post Capture

// ClientAuthenticationToken - Empty implementation (Airwallex doesn't support SDK session tokens)

// Payment Capture - implemented using macro above

// Refund - implemented using macro above

// Refund Sync - implemented using macro above

// Setup Mandate - implemented via macro below

// ServerAuthenticationToken - Airwallex Authentication Flow - will be implemented using macro

// Repeat Payment - implemented using macro below

// Order Create - implemented using macro above

// Session Token

// Dispute Accept

// Dispute Defend

// Submit Evidence

// Payment Token (required by PaymentTokenV2 trait)

// Access Token implementation is above - removing duplicate

// ===== AUTHENTICATION FLOW CONNECTOR INTEGRATIONS =====
// Pre Authentication

// Authentication

// Post Authentication

// ===== CONNECTOR CUSTOMER CONNECTOR INTEGRATIONS =====
// Create Connector Customer — POST /api/v1/pa/customers/create.
//
// Airwallex requires a Bearer access token (from ServerAuthenticationToken).
// CustomerServiceCreateRequest has no `state` field in the proto, so the access
// token is passed manually via `connector_feature_data` as JSON of the shape
// `{"access_token": "<token>"}`. This mirrors the existing RepeatPayment fallback
// pattern for `connector_order_id`.
macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Airwallex,
    curl_request: Json(AirwallexCustomerRequest),
    curl_response: AirwallexCustomerResponse,
    flow_name: CreateConnectorCustomer,
    resource_common_data: PaymentFlowData,
    flow_request: ConnectorCustomerData,
    flow_response: ConnectorCustomerResponse,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<CreateConnectorCustomer, PaymentFlowData, ConnectorCustomerData, ConnectorCustomerResponse>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            let access_token = req
                .resource_common_data
                .get_access_token()
                .ok()
                .or_else(|| {
                    req.resource_common_data
                        .connector_feature_data
                        .as_ref()
                        .and_then(|v| {
                            v.peek()
                                .get("access_token")
                                .and_then(|tok| tok.as_str().map(|s| s.to_string()))
                        })
                })
                .ok_or(IntegrationError::FailedToObtainAuthType {
                    context: Default::default(),
                })?;
            Ok(self.build_headers(&access_token))
        }
        fn get_url(
            &self,
            req: &RouterDataV2<CreateConnectorCustomer, PaymentFlowData, ConnectorCustomerData, ConnectorCustomerResponse>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!(
                "{}/pa/customers/create",
                req.resource_common_data.connectors.airwallex.base_url
            ))
        }
    }
);

// Incremental Authorization

// ===== CONNECTOR COMMON IMPLEMENTATION =====
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize> ConnectorCommon
    for Airwallex<T>
{
    fn id(&self) -> &'static str {
        "airwallex"
    }

    fn get_currency_unit(&self) -> CurrencyUnit {
        CurrencyUnit::Base
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.airwallex.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorSpecificConfig,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
        // Note: This method should not be used for OAuth-based connectors like Airwallex
        // Use build_payment_headers or build_refund_headers instead for OAuth flows
        // This method is only used for ServerAuthenticationToken flow
        let auth = airwallex::AirwallexAuthType::try_from(auth_type).change_context(
            IntegrationError::FailedToObtainAuthType {
                context: Default::default(),
            },
        )?;
        Ok(vec![
            (
                "x-api-key".to_string(),
                auth.api_key.expose().to_string().into(),
            ),
            (
                "x-client-id".to_string(),
                auth.client_id.expose().to_string().into(),
            ),
        ])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut events::Event>,
        _connector_config: &ConnectorSpecificConfig,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        let response: airwallex::AirwallexErrorResponse = res
            .response
            .parse_struct("AirwallexErrorResponse")
            .change_context(
                crate::utils::response_deserialization_fail(
                    res.status_code,
                "airwallex: response body did not match the expected format; confirm API version and connector documentation."),
            )?;

        with_error_response_body!(event_builder, response);

        let error_message = match response.source {
            Some(ref source) => format!("{}; {}", response.message, source),
            None => response.message.clone(),
        };

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.code,
            message: error_message.clone(),
            reason: Some(error_message),
            attempt_status: None,
            connector_transaction_id: None,
            network_decline_code: None,
            network_advice_code: None,
            network_error_message: None,
        })
    }
}

macros::macro_connector_flow_status_impls!(
    connector: Airwallex,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    not_implemented: [
        GetConnectorCustomer,
        ClientAuthenticationToken,
        MandateRevoke,
        ServerSessionAuthenticationToken,
        Accept,
        DefendDispute,
        SubmitEvidence,
        PaymentMethodToken,
        PreAuthenticate,
        Authenticate,
        PostAuthenticate,
        IncrementalAuthorization,
    ],
    not_supported: [
        VoidPostRefund,
        VoidPC,
    ],
);
