// File: crates/hyperswitch_connectors/src/connectors/tokenio/transformers.rs
// Module: hyperswitch_connectors
// Public functions: 3
// Public structs: 12

use std::collections::HashMap;

use api_models::admin::{AdditionalMerchantData, MerchantAccountData, MerchantRecipientData};
use common_enums::enums;
use common_utils::{id_type::MerchantId, request::Method, types::StringMajorUnit};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::{
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    errors,
};
use masking::{ExposeInterface, PeekInterface, Secret};
use serde::{Deserialize, Serialize};

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

//TODO: Fill the struct with respective fields
pub struct TokenioRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for TokenioRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenioPaymentsRequest {
    pub initiation: PaymentInitiation,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PaymentInitiation {
    pub ref_id: String,
    pub remittance_information_primary: MerchantId,
    pub amount: Amount,
    pub local_instrument: LocalInstrument,
    pub creditor: Creditor,
    pub callback_url: Option<String>,
    pub flow_type: FlowType,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Amount {
    pub value: StringMajorUnit,
    pub currency: enums::Currency,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum LocalInstrument {
    Sepa,
    SepaInstant,
    FasterPayments,
    Elixir,
    Bankgiro,
    Plusgiro,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum Creditor {
    FasterPayments {
        #[serde(rename = "sortCode")]
        sort_code: Secret<String>,
        #[serde(rename = "accountNumber")]
        account_number: Secret<String>,
        name: Secret<String>,
    },
    Sepa {
        iban: Secret<String>,
        name: Secret<String>,
    },
    SepaInstant {
        iban: Secret<String>,
        name: Secret<String>,
    },
    ElixirIban {
        iban: Secret<String>,
        name: Secret<String>,
    },
    ElixirAccount {
        #[serde(rename = "accountNumber")]
        account_number: Secret<String>,
        name: Secret<String>,
    },
    Bankgiro {
        #[serde(rename = "bankgiroNumber")]
        bankgiro_number: Secret<String>,
        name: Secret<String>,
    },
    Plusgiro {
        #[serde(rename = "plusgiroNumber")]
        plusgiro_number: Secret<String>,
        name: Secret<String>,
    },
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FlowType {
    ApiOnly,
    FullHostedPages,
    EmbeddedHostedPages,
}

impl TryFrom<&TokenioRouterData<&PaymentsAuthorizeRouterData>> for TokenioPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &TokenioRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::OpenBanking(_) => {
                let (local_instrument, creditor) = match item
                    .router_data
                    .additional_merchant_data
                    .as_ref()
                    .and_then(|data| match data {
                        AdditionalMerchantData::OpenBankingRecipientData(recipient_data) => {
                            match recipient_data {
                                MerchantRecipientData::AccountData(account_data) => {
                                    Some(account_data)
                                }
                                _ => None,
                            }
                        }
                    }) {
                    Some(MerchantAccountData::FasterPayments {
                        account_number,
                        sort_code,
                        name,
                        ..
                    }) => (
                        LocalInstrument::FasterPayments,
                        Creditor::FasterPayments {
                            sort_code: sort_code.clone(),
                            account_number: account_number.clone(),
                            name: name.clone().into(),
                        },
                    ),

                    Some(MerchantAccountData::SepaInstant { iban, name, .. }) => (
                        LocalInstrument::SepaInstant,
                        Creditor::SepaInstant {
                            iban: iban.clone(),
                            name: name.clone().into(),
                        },
                    ),

                    Some(MerchantAccountData::Sepa { iban, name, .. }) => (
                        LocalInstrument::Sepa,
                        Creditor::Sepa {
                            iban: iban.clone(),
                            name: name.clone().into(),
                        },
                    ),

                    Some(MerchantAccountData::Iban { iban, name, .. }) => (
                        LocalInstrument::Sepa, // Assuming IBAN defaults to SEPA
                        Creditor::Sepa {
                            iban: iban.clone(),
                            name: name.clone().into(),
                        },
                    ),

                    Some(MerchantAccountData::Elixir {
                        account_number,
                        iban,
                        name,
                        ..
                    }) => {
                        if !iban.peek().is_empty() {
                            (
                                LocalInstrument::Elixir,
                                Creditor::ElixirIban {
                                    iban: iban.clone(),
                                    name: name.clone().into(),
                                },
                            )
                        } else {
                            (
                                LocalInstrument::Elixir,
                                Creditor::ElixirAccount {
                                    account_number: account_number.clone(),
                                    name: name.clone().into(),
                                },
                            )
                        }
                    }
                    Some(MerchantAccountData::Bacs {
                        account_number,
                        sort_code,
                        name,
                        ..
                    }) => (
                        LocalInstrument::FasterPayments,
                        Creditor::FasterPayments {
                            sort_code: sort_code.clone(),
                            account_number: account_number.clone(),
                            name: name.clone().into(),
                        },
                    ),

                    Some(MerchantAccountData::Bankgiro { number, name, .. }) => (
                        LocalInstrument::Bankgiro,
                        Creditor::Bankgiro {
                            bankgiro_number: number.clone(),
                            name: name.clone().into(),
                        },
                    ),

                    Some(MerchantAccountData::Plusgiro { number, name, .. }) => (
                        LocalInstrument::Plusgiro,
                        Creditor::Plusgiro {
                            plusgiro_number: number.clone(),
                            name: name.clone().into(),
                        },
                    ),

                    None => {
                        return Err(errors::ConnectorError::InvalidConnectorConfig {
                            config: "No valid payment method found in additional merchant data",
                        }
                        .into())
                    }
                };
                Ok(Self {
                    initiation: PaymentInitiation {
                        ref_id: utils::generate_12_digit_number().to_string(),
                        remittance_information_primary: item.router_data.merchant_id.clone(),
                        amount: Amount {
                            value: item.amount.clone(),
                            currency: item.router_data.request.currency,
                        },
                        local_instrument,
                        creditor,
                        callback_url: item.router_data.request.router_return_url.clone(),
                        flow_type: FlowType::FullHostedPages,
                    },
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

pub struct TokenioAuthType {
    pub(super) merchant_id: Secret<String>,
    pub(super) private_key: Secret<String>,
    pub(super) key_id: Secret<String>,
    pub(super) key_algorithm: CryptoAlgorithm,
}

#[derive(Debug, Deserialize, PartialEq)]
pub enum CryptoAlgorithm {
    RS256,
    ES256,
    #[serde(rename = "EdDSA")]
    EDDSA,
}

impl TryFrom<&str> for CryptoAlgorithm {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        match s.to_uppercase().as_str() {
            "RS256" | "rs256" => Ok(Self::RS256),
            "ES256" | "es256" => Ok(Self::ES256),
            "EDDSA" | "eddsa" | "EdDSA" => Ok(Self::EDDSA),
            _ => Err(errors::ConnectorError::InvalidConnectorConfig {
                config: "Unsupported key algorithm. Select from RS256, ES256, EdDSA",
            }
            .into()),
        }
    }
}
impl TryFrom<&ConnectorAuthType> for TokenioAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            } => Ok(Self {
                merchant_id: key1.to_owned(),
                private_key: api_secret.to_owned(),
                key_id: api_key.to_owned(),
                key_algorithm: CryptoAlgorithm::try_from(key2.clone().expose().as_str())?,
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenioApiWrapper {
    pub payment: PaymentResponse,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(untagged)]
pub enum TokenioPaymentsResponse {
    Success(TokenioApiWrapper),
    Error(TokenioErrorResponse),
}

#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentResponse {
    pub id: String,
    pub status: PaymentStatus,
    pub status_reason_information: Option<String>,
    pub authentication: Option<Authentication>,
    pub error_info: Option<ErrorInfo>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PaymentStatus {
    InitiationPending,
    InitiationPendingRedirectAuth,
    InitiationPendingRedirectAuthVerification,
    InitiationPendingRedirectHp,
    InitiationPendingRedemption,
    InitiationPendingRedemptionVerification,
    InitiationProcessing,
    InitiationCompleted,
    InitiationRejected,
    InitiationRejectedInsufficientFunds,
    InitiationFailed,
    InitiationDeclined,
    InitiationExpired,
    InitiationNoFinalStatusAvailable,
    SettlementInProgress,
    SettlementCompleted,
    SettlementIncomplete,
}

#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(untagged)]
pub enum Authentication {
    RedirectUrl {
        #[serde(rename = "redirectUrl")]
        redirect_url: String,
    },
}

#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorInfo {
    pub http_error_code: i32,
    pub message: Option<String>,
    pub token_external_error: Option<bool>,
    pub token_trace_id: Option<String>,
}

impl From<TokenioPaymentsResponse> for common_enums::AttemptStatus {
    fn from(response: TokenioPaymentsResponse) -> Self {
        match response {
            TokenioPaymentsResponse::Success(wrapper) => match wrapper.payment.status {
                // Pending statuses - payment is still in progress
                PaymentStatus::InitiationPending
                | PaymentStatus::InitiationPendingRedirectAuth
                | PaymentStatus::InitiationPendingRedirectAuthVerification
                | PaymentStatus::InitiationPendingRedirectHp
                | PaymentStatus::InitiationPendingRedemption
                | PaymentStatus::InitiationPendingRedemptionVerification => {
                    Self::AuthenticationPending
                }

                // Success statuses
                PaymentStatus::SettlementCompleted => Self::Charged,

                // Settlement in progress - could map to different status based on business logic
                PaymentStatus::SettlementInProgress => Self::Pending,

                // Failure statuses
                PaymentStatus::InitiationRejected
                | PaymentStatus::InitiationFailed
                | PaymentStatus::InitiationExpired
                | PaymentStatus::InitiationRejectedInsufficientFunds
                | PaymentStatus::InitiationDeclined => Self::Failure,

                // Uncertain status
                PaymentStatus::InitiationCompleted
                | PaymentStatus::InitiationProcessing
                | PaymentStatus::InitiationNoFinalStatusAvailable
                | PaymentStatus::SettlementIncomplete => Self::Pending,
            },
            TokenioPaymentsResponse::Error(_) => Self::Failure,
        }
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, TokenioPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, TokenioPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let status = common_enums::AttemptStatus::from(item.response.clone());
        let response = match item.response {
            TokenioPaymentsResponse::Success(wrapper) => {
                let payment = wrapper.payment;
                if let common_enums::AttemptStatus::Failure = status {
                    Err(ErrorResponse {
                        code: payment
                            .error_info
                            .as_ref()
                            .map(|ei| ei.http_error_code.to_string())
                            .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
                        message: payment
                            .error_info
                            .as_ref()
                            .and_then(|ei| ei.message.clone())
                            .or_else(|| payment.status_reason_information.clone())
                            .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                        reason: Some(
                            payment
                                .error_info
                                .as_ref()
                                .and_then(|ei| ei.message.clone())
                                .or_else(|| payment.status_reason_information.clone())
                                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                        ),
                        status_code: item.http_code,
                        attempt_status: None,
                        connector_transaction_id: Some(payment.id.clone()),
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(payment.id.clone()),
                        redirection_data: Box::new(payment.authentication.as_ref().map(|auth| {
                            match auth {
                                Authentication::RedirectUrl { redirect_url } => {
                                    RedirectForm::Form {
                                        endpoint: redirect_url.to_string(),
                                        method: Method::Get,
                                        form_fields: HashMap::new(),
                                    }
                                }
                            }
                        })),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: None,
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                }
            }
            TokenioPaymentsResponse::Error(error_response) => Err(ErrorResponse {
                code: error_response.get_error_code(),
                message: error_response.get_message(),
                reason: Some(error_response.get_message()),
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize)]
pub struct TokenioRefundRequest {
    pub amount: StringMajorUnit,
}

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

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

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

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

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

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

#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(untagged)]
pub enum TokenioErrorResponse {
    Json {
        #[serde(rename = "errorCode")]
        error_code: Option<String>,
        message: Option<String>,
    },
    Text(String),
}
impl TokenioErrorResponse {
    pub fn from_bytes(bytes: &[u8]) -> Self {
        // First try to parse as JSON
        if let Ok(json_response) = serde_json::from_slice::<Self>(bytes) {
            json_response
        } else {
            // If JSON parsing fails, treat as plain text
            let text = String::from_utf8_lossy(bytes).to_string();
            Self::Text(text)
        }
    }
    pub fn get_message(&self) -> String {
        match self {
            Self::Json {
                message,
                error_code,
            } => message
                .as_deref()
                .or(error_code.as_deref())
                .unwrap_or(NO_ERROR_MESSAGE)
                .to_string(),
            Self::Text(text) => text.clone(),
        }
    }

    pub fn get_error_code(&self) -> String {
        match self {
            Self::Json { error_code, .. } => {
                error_code.as_deref().unwrap_or(NO_ERROR_CODE).to_string()
            }
            Self::Text(_) => NO_ERROR_CODE.to_string(),
        }
    }
}

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TokenioWebhookEventType {
    PaymentStatusChanged,
    #[serde(other)]
    Unknown,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TokenioWebhookPaymentStatus {
    InitiationCompleted,
    PaymentCompleted,
    PaymentFailed,
    PaymentCancelled,
    InitiationRejected,
    InitiationProcessing,
    #[serde(other)]
    Unknown,
}

// Base webhook payload structure
#[derive(Debug, Deserialize, Serialize)]
pub struct TokenioWebhookPayload {
    #[serde(rename = "eventType", skip_serializing_if = "Option::is_none")]
    pub event_type: Option<String>,
    pub id: String,
    #[serde(flatten)]
    pub event_data: TokenioWebhookEventData,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum TokenioWebhookEventData {
    PaymentV2 { payment: TokenioPaymentObjectV2 },
}

// Payment v2 structures
#[derive(Debug, Deserialize, Serialize)]
pub struct TokenioPaymentObjectV2 {
    pub id: String,
    pub status: TokenioPaymentStatus,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TokenioPaymentStatus {
    InitiationCompleted,
    PaymentCompleted,
    PaymentFailed,
    PaymentCancelled,
    InitiationRejected,
    InitiationProcessing,
    InitiationPendingRedirectHp,
    #[serde(other)]
    Other,
}


// Struct: PartnerConfigOverride
// File: crates/router/src/types/api/connector_onboarding/paypal.rs
// Module: router
// Implementations: 0

pub struct PartnerConfigOverride


// Function: get_payment_attempt_from_object_reference_id
// File: crates/router/src/core/webhooks/incoming.rs
// Module: router

pub fn get_payment_attempt_from_object_reference_id(
    state: &SessionState,
    object_reference_id: webhooks::ObjectReferenceId,
    merchant_context: &domain::MerchantContext,
) -> CustomResult<PaymentAttempt, errors::ApiErrorResponse>


// Function: delete_network_token_from_locker_and_token_service
// File: crates/router/src/core/payment_methods/network_tokenization.rs
// Module: router

pub fn delete_network_token_from_locker_and_token_service(
    state: &routes::SessionState,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    payment_method_id: String,
    network_token_locker_id: Option<String>,
    network_token_requestor_reference_id: String,
    merchant_context: &domain::MerchantContext,
) -> errors::RouterResult<DeleteCardResp>


// Function: recon_merchant_account_update
// File: crates/router/src/core/recon.rs
// Module: router

pub fn recon_merchant_account_update(
    state: SessionState,
    auth: authentication::AuthenticationData,
    req: recon_api::ReconUpdateMerchantRequest,
) -> RouterResponse<api_types::MerchantAccountResponse>


// Struct: GenerateReportRequest
// File: crates/api_models/src/analytics.rs
// Module: api_models
// Implementations: 0

pub struct GenerateReportRequest


// Struct: PaymentCreate
// File: crates/router/src/core/payments/operations/payment_create.rs
// Module: router
// Implementations: 1

pub struct PaymentCreate


// File: crates/hyperswitch_connectors/src/connectors/flexiti/transformers.rs
// Module: hyperswitch_connectors
// Public structs: 13

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

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

pub struct FlexitiRouterData<T> {
    pub amount: FloatMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

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

#[derive(Debug, Serialize)]
pub struct FlexitiPaymentsRequest {
    merchant_order_id: Option<String>,
    lang: String,
    flow: FlexitiFlow,
    amount_requested: FloatMajorUnit,
    email: Option<Email>,
    fname: Secret<String>,
    billing_information: BillingInformation,
    shipping_information: ShippingInformation,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FlexitiFlow {
    #[serde(rename = "apply/buy")]
    ApplyAndBuy,
    Apply,
    Buy,
}
#[derive(Debug, Serialize)]
pub struct BillingInformation {
    first_name: Secret<String>,
    last_name: Secret<String>,
    address_1: Secret<String>,
    address_2: Secret<String>,
    city: Secret<String>,
    postal_code: Secret<String>,
    province: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct ShippingInformation {
    first_name: Secret<String>,
    last_name: Secret<String>,
    address_1: Secret<String>,
    address_2: Secret<String>,
    city: Secret<String>,
    postal_code: Secret<String>,
    province: Secret<String>,
}

impl TryFrom<&FlexitiRouterData<&PaymentsAuthorizeRouterData>> for FlexitiPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &FlexitiRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::PayLater(pay_later_data) => match pay_later_data {
                hyperswitch_domain_models::payment_method_data::PayLaterData::FlexitiRedirect {  } =>  {
                    let shipping_address = item.router_data.get_shipping_address()?;
                    let shipping_information = ShippingInformation {
                        first_name: shipping_address.get_first_name()?.to_owned(),
                        last_name: shipping_address.get_last_name()?.to_owned(),
                        address_1: shipping_address.get_line1()?.to_owned(),
                        address_2: shipping_address.get_line2()?.to_owned(),
                        city: shipping_address.get_city()?.to_owned().into(),
                        postal_code: shipping_address.get_zip()?.to_owned(),
                        province: shipping_address.to_state_code()?,
                    };
                    let billing_information = BillingInformation {
                        first_name: item.router_data.get_billing_first_name()?,
                        last_name: item.router_data.get_billing_last_name()?,
                        address_1: item.router_data.get_billing_line1()?,
                        address_2: item.router_data.get_billing_line2()?,
                        city: item.router_data.get_billing_city()?.into(),
                        postal_code: item.router_data.get_billing_zip()?,
                        province: item.router_data.get_billing_state_code()?,
                    };
                    Ok(Self {
                        merchant_order_id: item.router_data.request.merchant_order_reference_id.to_owned(),
                        lang: item.router_data.request.get_browser_info()?.get_language()?,
                        flow: FlexitiFlow::ApplyAndBuy,
                        amount_requested: item.amount.to_owned(),
                        email: item.router_data.get_optional_billing_email(),
                        fname: item.router_data.get_billing_first_name()?,
                        billing_information,
                        shipping_information,
                    })
                },
                hyperswitch_domain_models::payment_method_data::PayLaterData::KlarnaRedirect {  } |
                hyperswitch_domain_models::payment_method_data::PayLaterData::KlarnaSdk { .. } |
                hyperswitch_domain_models::payment_method_data::PayLaterData::AffirmRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::BreadpayRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::AfterpayClearpayRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::PayBrightRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::WalleyRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::AlmaRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::AtomeRedirect {  } => {
                                Err(errors::ConnectorError::NotImplemented(
                                utils::get_unimplemented_payment_method_error_message("flexiti"),
                            ))
                            }?,
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

#[derive(Debug, Serialize)]
pub struct FlexitiAccessTokenRequest {
    client_id: Secret<String>,
    client_secret: Secret<String>,
    grant_type: FlexitiGranttype,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FlexitiGranttype {
    Password,
    RefreshToken,
    ClientCredentials,
}

impl TryFrom<&types::RefreshTokenRouterData> for FlexitiAccessTokenRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::RefreshTokenRouterData) -> Result<Self, Self::Error> {
        let auth_details = FlexitiAuthType::try_from(&item.connector_auth_type)?;

        Ok(Self {
            client_id: auth_details.client_id,
            client_secret: auth_details.client_secret,
            grant_type: FlexitiGranttype::ClientCredentials,
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, FlexitiAccessTokenResponse, T, AccessToken>>
    for RouterData<F, T, AccessToken>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FlexitiAccessTokenResponse, T, AccessToken>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(AccessToken {
                token: item.response.access_token,
                expires: item.response.expires_in,
            }),
            ..item.data
        })
    }
}

// Auth Struct
pub struct FlexitiAuthType {
    pub(super) client_id: Secret<String>,
    pub(super) client_secret: Secret<String>,
}

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

#[derive(Debug, Serialize, Deserialize)]
pub struct FlexitiAccessTokenResponse {
    access_token: Secret<String>,
    expires_in: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlexitiPaymentsResponse {
    redirection_url: url::Url,
    online_order_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlexitiSyncResponse {
    transaction_id: String,
    purchase: FlexitiPurchase,
}

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

// Since this is an alpha integration, we don't have access to all the status mapping. This needs to be updated.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FlexitiPurchaseStatus {
    Success,
    Failed,
}

// Since this is an alpha integration, we don't have access to all the status mapping. This needs to be updated.
impl From<FlexitiPurchaseStatus> for common_enums::AttemptStatus {
    fn from(item: FlexitiPurchaseStatus) -> Self {
        match item {
            FlexitiPurchaseStatus::Success => Self::Authorized,
            FlexitiPurchaseStatus::Failed => Self::Failure,
        }
    }
}

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

impl<F, T> TryFrom<ResponseRouterData<F, FlexitiPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FlexitiPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: common_enums::AttemptStatus::AuthenticationPending,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    item.response.online_order_id.to_owned(),
                ),
                redirection_data: Box::new(Some(RedirectForm::from((
                    item.response.redirection_url,
                    Method::Get,
                )))),
                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 FlexitiRefundRequest {
    pub amount: FloatMajorUnit,
}

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

// Type definition for Refund Response

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

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

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

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

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

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


// Struct: RevenueRecoveryRetriggerRequest
// File: crates/api_models/src/process_tracker/revenue_recovery.rs
// Module: api_models
// Implementations: 0

pub struct RevenueRecoveryRetriggerRequest


// Struct: PaymentMethodUpdateMems
// File: crates/diesel_models/src/kv.rs
// Module: diesel_models
// Implementations: 0

pub struct PaymentMethodUpdateMems


// Function: update_feature
// File: crates/api_models/src/routing.rs
// Module: api_models

pub fn update_feature(
        &mut self,
        enabled_feature: DynamicRoutingFeatures,
        dynamic_routing_type: DynamicRoutingType,
    )


// Function: get_optional_card_and_cvc
// File: crates/router/src/core/payment_methods/tokenize/card_executor.rs
// Module: router

pub fn get_optional_card_and_cvc(
        &self,
    ) -> (Option<domain::CardDetail>, Option<masking::Secret<String>>)


// Function: get_resource_name
// File: crates/router/src/services/authorization/permissions.rs
// Module: router

pub fn get_resource_name(resource: Resource, entity_type: EntityType) -> Option<&'static str>


// Implementation: impl NetworkTokenizationBuilder
// File: crates/router/src/core/payment_methods/tokenize/card_executor.rs
// Module: router
// Methods: 2 total (2 public)

impl NetworkTokenizationBuilder


// Struct: ThreeDSRequestChallenge
// File: crates/hyperswitch_connectors/src/connectors/worldpay/requests.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreeDSRequestChallenge


// File: crates/router/src/types/domain/types.rs
// Module: router

use ::payment_methods::state as pm_state;
use common_utils::types::keymanager::KeyManagerState;
pub use hyperswitch_domain_models::type_encryption::{
    crypto_operation, AsyncLift, CryptoOperation, Lift, OptionalEncryptableJsonType,
};

use crate::routes::app;

impl From<&app::SessionState> for KeyManagerState {
    fn from(state: &app::SessionState) -> Self {
        let conf = state.conf.key_manager.get_inner();
        Self {
            global_tenant_id: state.conf.multitenancy.global_tenant.tenant_id.clone(),
            tenant_id: state.tenant.tenant_id.clone(),
            enabled: conf.enabled,
            url: conf.url.clone(),
            client_idle_timeout: state.conf.proxy.idle_pool_connection_timeout,
            #[cfg(feature = "km_forward_x_request_id")]
            request_id: state.request_id,
            #[cfg(feature = "keymanager_mtls")]
            cert: conf.cert.clone(),
            #[cfg(feature = "keymanager_mtls")]
            ca: conf.ca.clone(),
            infra_values: app::AppState::process_env_mappings(state.conf.infra_values.clone()),
        }
    }
}

impl From<&app::SessionState> for pm_state::PaymentMethodsState {
    fn from(state: &app::SessionState) -> Self {
        Self {
            store: state.store.get_payment_methods_store(),
            key_store: None,
            key_manager_state: state.into(),
        }
    }
}


// Struct: EliminationEventResponse
// File: crates/router/src/core/payments/routing/utils.rs
// Module: router
// Implementations: 0

pub struct EliminationEventResponse


// Function: get_connector_auth
// File: crates/router/src/utils/connector_onboarding.rs
// Module: router

pub fn get_connector_auth(
    connector: enums::Connector,
    connector_data: &settings::ConnectorOnboarding,
) -> RouterResult<types::ConnectorAuthType>


// Struct: AnalyticsMetadata
// File: crates/api_models/src/analytics.rs
// Module: api_models
// Implementations: 0

pub struct AnalyticsMetadata


// File: crates/api_models/src/analytics/search.rs
// Module: api_models
// Public functions: 1
// Public structs: 12

use common_utils::{hashing::HashedString, types::TimeRange};
use masking::WithType;
use serde_json::Value;

#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
pub struct SearchFilters {
    pub payment_method: Option<Vec<String>>,
    pub currency: Option<Vec<String>>,
    pub status: Option<Vec<String>>,
    pub customer_email: Option<Vec<HashedString<common_utils::pii::EmailStrategy>>>,
    pub search_tags: Option<Vec<HashedString<WithType>>>,
    pub connector: Option<Vec<String>>,
    pub payment_method_type: Option<Vec<String>>,
    pub card_network: Option<Vec<String>>,
    pub card_last_4: Option<Vec<String>>,
    pub payment_id: Option<Vec<String>>,
    pub amount: Option<Vec<u64>>,
    pub customer_id: Option<Vec<String>>,
}
impl SearchFilters {
    pub fn is_all_none(&self) -> bool {
        self.payment_method.is_none()
            && self.currency.is_none()
            && self.status.is_none()
            && self.customer_email.is_none()
            && self.search_tags.is_none()
            && self.connector.is_none()
            && self.payment_method_type.is_none()
            && self.card_network.is_none()
            && self.card_last_4.is_none()
            && self.payment_id.is_none()
            && self.amount.is_none()
            && self.customer_id.is_none()
    }
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetGlobalSearchRequest {
    pub query: String,
    #[serde(default)]
    pub filters: Option<SearchFilters>,
    #[serde(default)]
    pub time_range: Option<TimeRange>,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetSearchRequest {
    pub offset: i64,
    pub count: i64,
    pub query: String,
    #[serde(default)]
    pub filters: Option<SearchFilters>,
    #[serde(default)]
    pub time_range: Option<TimeRange>,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetSearchRequestWithIndex {
    pub index: SearchIndex,
    pub search_req: GetSearchRequest,
}

#[derive(
    Debug, strum::EnumIter, Clone, serde::Deserialize, serde::Serialize, Copy, Eq, PartialEq,
)]
#[serde(rename_all = "snake_case")]
pub enum SearchIndex {
    PaymentAttempts,
    PaymentIntents,
    Refunds,
    Disputes,
    SessionizerPaymentAttempts,
    SessionizerPaymentIntents,
    SessionizerRefunds,
    SessionizerDisputes,
}

#[derive(Debug, strum::EnumIter, Clone, serde::Deserialize, serde::Serialize, Copy)]
pub enum SearchStatus {
    Success,
    Failure,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetSearchResponse {
    pub count: u64,
    pub index: SearchIndex,
    pub hits: Vec<Value>,
    pub status: SearchStatus,
}

#[derive(Debug, serde::Deserialize)]
pub struct OpenMsearchOutput {
    #[serde(default)]
    pub responses: Vec<OpensearchOutput>,
    pub error: Option<OpensearchErrorDetails>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(untagged)]
pub enum OpensearchOutput {
    Success(OpensearchSuccess),
    Error(OpensearchError),
}

#[derive(Debug, serde::Deserialize)]
pub struct OpensearchError {
    pub error: OpensearchErrorDetails,
    pub status: u16,
}

#[derive(Debug, serde::Deserialize)]
pub struct OpensearchErrorDetails {
    #[serde(rename = "type")]
    pub error_type: String,
    pub reason: String,
}

#[derive(Debug, serde::Deserialize)]
pub struct OpensearchSuccess {
    pub hits: OpensearchHits,
}

#[derive(Debug, serde::Deserialize)]
pub struct OpensearchHits {
    pub total: OpensearchResultsTotal,
    pub hits: Vec<OpensearchHit>,
}

#[derive(Debug, serde::Deserialize)]
pub struct OpensearchResultsTotal {
    pub value: u64,
}

#[derive(Debug, serde::Deserialize)]
pub struct OpensearchHit {
    #[serde(rename = "_source")]
    pub source: Value,
}


// Implementation: impl PaymentMetricsAccumulator
// File: crates/analytics/src/payments/accumulator.rs
// Module: analytics
// Methods: 1 total (1 public)

impl PaymentMetricsAccumulator


// Function: refunds_retrieve_with_body
// File: crates/openapi/src/routes/refunds.rs
// Module: openapi

pub fn refunds_retrieve_with_body()


// Function: server
// File: crates/router/src/routes/app.rs
// Module: router

pub fn server(state: AppState) -> Scope


// Struct: AcceptDisputeResponse
// File: crates/hyperswitch_domain_models/src/router_response_types/disputes.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct AcceptDisputeResponse


// Function: build_unified_connector_service_payment_method
// File: crates/router/src/core/unified_connector_service.rs
// Module: router

pub fn build_unified_connector_service_payment_method(
    payment_method_data: hyperswitch_domain_models::payment_method_data::PaymentMethodData,
    payment_method_type: PaymentMethodType,
) -> CustomResult<payments_grpc::PaymentMethod, UnifiedConnectorServiceError>


// Struct: PayoutVendorAccountDetails
// File: crates/api_models/src/payouts.rs
// Module: api_models
// Implementations: 0

pub struct PayoutVendorAccountDetails


// Implementation: impl quote::ToTokens for for SchemaMeta
// File: crates/router_derive/src/macros/generate_schema.rs
// Module: router_derive
// Methods: 1 total (0 public)

impl quote::ToTokens for for SchemaMeta


// Implementation: impl ConnectorCommon for for Amazonpay
// File: crates/hyperswitch_connectors/src/connectors/amazonpay.rs
// Module: hyperswitch_connectors
// Methods: 5 total (0 public)

impl ConnectorCommon for for Amazonpay


// Function: new
// File: crates/hyperswitch_domain_models/src/relay.rs
// Module: hyperswitch_domain_models

pub fn new(
        relay_request: &api_models::relay::RelayRequest,
        merchant_id: &id_type::MerchantId,
        profile_id: &id_type::ProfileId,
    ) -> Self


// Struct: DisputeResponseData
// File: crates/hyperswitch_connectors/src/connectors/rapyd/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DisputeResponseData


// Struct: LineageIds
// File: crates/external_services/src/grpc_client.rs
// Module: external_services
// Implementations: 1

pub struct LineageIds


// Module Structure
// File: crates/hyperswitch_domain_models/src/router_response_types.rs
// Module: hyperswitch_domain_models

// Public submodules:
pub mod disputes;
pub mod fraud_check;
pub mod revenue_recovery;
pub mod subscriptions;

// Public exports:
pub use disputes::{
    AcceptDisputeResponse, DefendDisputeResponse, DisputeSyncResponse, FetchDisputesResponse,
    SubmitEvidenceResponse,
};


// Implementation: impl SerializableSecret for for i32
// File: crates/masking/src/serde.rs
// Module: masking
// Methods: 0 total (0 public)

impl SerializableSecret for for i32


// Implementation: impl Billwerk
// File: crates/hyperswitch_connectors/src/connectors/billwerk.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl Billwerk


// Function: toggle_elimination_routing
// File: crates/router/src/routes/routing.rs
// Module: router

pub fn toggle_elimination_routing(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<api_models::routing::ToggleDynamicRoutingQuery>,
    path: web::Path<routing_types::ToggleDynamicRoutingPath>,
) -> impl Responder


// Struct: CheckoutResponse
// File: crates/hyperswitch_connectors/src/connectors/zen/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CheckoutResponse


// File: crates/masking/src/lib.rs
// Module: masking

#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide))]
#![cfg_attr(docsrs, doc(cfg_hide(doc)))]
#![warn(missing_docs)]

//! Personal Identifiable Information protection. Wrapper types and traits for secret management which help ensure they aren't accidentally copied, logged, or otherwise exposed (as much as possible), and also ensure secrets are securely wiped from memory when dropped.
//! Secret-keeping library inspired by secrecy.

#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR" ), "/", "README.md"))]

pub use zeroize::{self, DefaultIsZeroes, Zeroize as ZeroizableSecret};

mod strategy;

pub use strategy::{Strategy, WithType, WithoutType};
mod abs;
pub use abs::{ExposeInterface, ExposeOptionInterface, PeekInterface, SwitchStrategy};

mod secret;
mod strong_secret;
#[cfg(feature = "serde")]
pub use secret::JsonMaskStrategy;
pub use secret::Secret;
pub use strong_secret::StrongSecret;

#[cfg(feature = "alloc")]
extern crate alloc;

#[cfg(feature = "alloc")]
mod boxed;

#[cfg(feature = "bytes")]
mod bytes;
#[cfg(feature = "bytes")]
pub use self::bytes::SecretBytesMut;

#[cfg(feature = "alloc")]
mod string;

#[cfg(feature = "alloc")]
mod vec;

#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "serde")]
pub use crate::serde::{
    masked_serialize, Deserialize, ErasedMaskSerialize, SerializableSecret, Serialize,
};

/// This module should be included with asterisk.
///
/// `use masking::prelude::*;`
pub mod prelude {
    pub use super::{ExposeInterface, ExposeOptionInterface, PeekInterface};
}

#[cfg(feature = "diesel")]
mod diesel;

#[cfg(feature = "cassandra")]
mod cassandra;

pub mod maskable;

pub use maskable::*;


// Function: get_payment_intent_metrics_info
// File: crates/analytics/src/utils.rs
// Module: analytics

pub fn get_payment_intent_metrics_info() -> Vec<NameDescription>


// File: crates/hyperswitch_connectors/src/connectors/tokenio.rs
// Module: hyperswitch_connectors
// Public functions: 1
// Public structs: 1

pub mod transformers;
use std::{
    sync::LazyLock,
    time::{SystemTime, UNIX_EPOCH},
};

use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
use common_enums::{enums, FeatureStatus, PaymentMethodType};
use common_utils::{
    crypto::{self, VerifySignature},
    errors::CustomResult,
    ext_traits::{ByteSliceExt, BytesExt},
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::Response,
    webhooks,
};
use masking::{ExposeInterface, Mask, Secret};
use openssl::{ec::EcKey, hash::MessageDigest, pkey::PKey, rsa::Rsa, sign::Signer};
use transformers::{self as tokenio, TokenioPaymentStatus};

use crate::{constants::headers, types::ResponseRouterData, utils};

#[derive(Clone)]
pub struct Tokenio {
    amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}

impl Tokenio {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &StringMajorUnitForConnector,
        }
    }
    fn create_jwt_token(
        &self,
        auth: &tokenio::TokenioAuthType,
        method: &str,
        path: &str,
        body: &RequestContent,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        // Create JWT header
        let exp_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .change_context(errors::ConnectorError::RequestEncodingFailed)?
            .as_millis()
            + 600_000; // 10 minutes

        let header = serde_json::json!({
            "alg": match auth.key_algorithm {
                tokenio::CryptoAlgorithm::RS256 => "RS256",
                tokenio::CryptoAlgorithm::ES256 => "ES256",
                tokenio::CryptoAlgorithm::EDDSA => "EdDSA",
            },
            "exp": exp_time.to_string(), // Convert to string as Token.io expects
            "mid": auth.merchant_id.clone().expose(),
            "kid": auth.key_id.clone().expose(),
            "method": method.to_uppercase(),
            "host": connectors.tokenio.base_url.trim_start_matches("https://").trim_end_matches("/"),
            "path": path,
            "typ": "JWT",
        });

        // For GET requests, use detached JWT (no payload)
        // For POST/PUT requests, include the request body as payload
        let is_get_request = method.to_uppercase() == "GET";
        let payload = if is_get_request {
            None // No payload for GET requests (detached JWT)
        } else {
            // For non-GET requests, include the request body
            match body {
                RequestContent::Json(json_body) => Some(
                    serde_json::to_value(json_body)
                        .change_context(errors::ConnectorError::RequestEncodingFailed)?,
                ),
                _ => Some(serde_json::json!({})),
            }
        };

        // Use compact JSON serialization (no extra whitespace)
        let encoded_header = self.base64url_encode(
            serde_json::to_string(&header)
                .change_context(errors::ConnectorError::RequestEncodingFailed)?
                .as_bytes(),
        )?;

        let (encoded_payload, signing_input) = match payload {
            Some(p) => {
                // Standard JWT with payload
                let encoded_payload = self.base64url_encode(
                    serde_json::to_string(&p)
                        .change_context(errors::ConnectorError::RequestEncodingFailed)?
                        .as_bytes(),
                )?;
                let signing_input = format!("{encoded_header}.{encoded_payload}");
                (encoded_payload, signing_input)
            }
            None => {
                // Detached JWT (GET requests) - sign only the header with a dot
                let signing_input = format!("{encoded_header}.");
                (String::new(), signing_input) // Empty payload for detached JWT
            }
        };

        // Sign the JWT based on algorithm
        let signature = match auth.key_algorithm {
            tokenio::CryptoAlgorithm::RS256 => {
                self.sign_rsa(&auth.private_key.clone().expose(), &signing_input)?
            }
            tokenio::CryptoAlgorithm::ES256 => {
                self.sign_ecdsa(&auth.private_key.clone().expose(), &signing_input)?
            }
            tokenio::CryptoAlgorithm::EDDSA => {
                self.sign_eddsa(&auth.private_key.clone().expose(), &signing_input)?
            }
        };

        let encoded_signature = self.base64url_encode(&signature)?;

        // Assemble JWT - for detached JWT, middle part is empty
        Ok(format!(
            "{encoded_header}.{encoded_payload}.{encoded_signature}",
        ))
    }

    fn base64url_encode(&self, data: &[u8]) -> CustomResult<String, errors::ConnectorError> {
        Ok(URL_SAFE_NO_PAD.encode(data))
    }

    fn sign_rsa(
        &self,
        private_key_pem: &str,
        data: &str,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let rsa = Rsa::private_key_from_pem(private_key_pem.as_bytes())
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let pkey =
            PKey::from_rsa(rsa).change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let mut signer = Signer::new(MessageDigest::sha256(), &pkey)
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        signer
            .update(data.as_bytes())
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let signature = signer
            .sign_to_vec()
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        Ok(signature)
    }

    fn sign_ecdsa(
        &self,
        private_key_pem: &str,
        data: &str,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let ec_key = EcKey::private_key_from_pem(private_key_pem.as_bytes())
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let pkey = PKey::from_ec_key(ec_key)
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let mut signer = Signer::new(MessageDigest::sha256(), &pkey)
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        signer
            .update(data.as_bytes())
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let signature = signer
            .sign_to_vec()
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        Ok(signature)
    }

    fn sign_eddsa(
        &self,
        private_key_pem: &str,
        data: &str,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let pkey = PKey::private_key_from_pem(private_key_pem.as_bytes())
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let mut signer = Signer::new_without_digest(&pkey)
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        signer
            .update(data.as_bytes())
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        let signature = signer
            .sign_to_vec()
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;

        Ok(signature)
    }
}

impl api::Payment for Tokenio {}
impl api::PaymentSession for Tokenio {}
impl api::ConnectorAccessToken for Tokenio {}
impl api::MandateSetup for Tokenio {}
impl api::PaymentAuthorize for Tokenio {}
impl api::PaymentSync for Tokenio {}
impl api::PaymentCapture for Tokenio {}
impl api::PaymentVoid for Tokenio {}
impl api::Refund for Tokenio {}
impl api::RefundExecute for Tokenio {}
impl api::RefundSync for Tokenio {}
impl api::PaymentToken for Tokenio {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Tokenio
{
    // Not Implemented (R)
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Tokenio
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        _req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        // Basic headers - JWT will be added in individual build_request methods
        let header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];

        Ok(header)
    }
}

impl ConnectorCommon for Tokenio {
    fn id(&self) -> &'static str {
        "tokenio"
    }

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

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

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

    fn get_auth_header(
        &self,
        _auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        Ok(vec![])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: tokenio::TokenioErrorResponse = res
            .response
            .parse_struct("TokenioErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

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

impl ConnectorValidation for Tokenio {
    //TODO: implement functions when support enabled
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Tokenio {
    //TODO: implement sessions flow
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Tokenio {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Tokenio {}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Tokenio {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let base_url = self.base_url(connectors);
        Ok(format!("{base_url}/v2/payments"))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;

        let connector_router_data = tokenio::TokenioRouterData::from((amount, req));
        let connector_req = tokenio::TokenioPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let auth = tokenio::TokenioAuthType::try_from(&req.connector_auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;

        let url = self.get_url(req, connectors)?;
        let body = self.get_request_body(req, connectors)?;

        // Create JWT for authentication
        let jwt = self.create_jwt_token(&auth, "POST", "/v2/payments", &body, connectors)?;

        // Build headers with JWT authorization
        let headers = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                "application/json".to_string().into(),
            ),
            (
                headers::AUTHORIZATION.to_string(),
                format!("Bearer {jwt}").into_masked(),
            ),
        ];

        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&url)
                .attach_default_headers()
                .headers(headers)
                .set_body(body)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: tokenio::TokenioPaymentsResponse = res
            .response
            .parse_struct("Tokenio PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Tokenio {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        // For GET requests, we need JWT with detached format (no body)
        let auth = tokenio::TokenioAuthType::try_from(&req.connector_auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;

        // Use empty RequestContent for GET requests - the create_jwt_token method
        // will handle this properly by creating detached JWT
        let empty_body = RequestContent::Json(Box::new(serde_json::json!({})));

        let path = format!(
            "/v2/payments/{}",
            req.request
                .connector_transaction_id
                .get_connector_transaction_id()
                .change_context(errors::ConnectorError::MissingConnectorTransactionID)?
        );

        let jwt = self.create_jwt_token(&auth, "GET", &path, &empty_body, connectors)?;

        let headers = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                "application/json".to_string().into(),
            ),
            (
                headers::AUTHORIZATION.to_string(),
                format!("Bearer {jwt}").into_masked(),
            ),
        ];
        Ok(headers)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_payment_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
        let base_url = self.base_url(connectors);
        Ok(format!("{base_url}/v2/payments/{connector_payment_id}"))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&self.get_url(req, connectors)?)
                .attach_default_headers()
                .headers(self.get_headers(req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: tokenio::TokenioPaymentsResponse = res
            .response
            .parse_struct("tokenio TokenioPaymentsResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Tokenio {
    fn build_request(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Capture".to_string(),
            connector: "Tokenio".to_string(),
        }
        .into())
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Tokenio {
    fn build_request(
        &self,
        _req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Refunds".to_string(),
            connector: "Tokenio".to_string(),
        }
        .into())
    }
}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Tokenio {
    fn build_request(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Refunds".to_string(),
            connector: "Tokenio".to_string(),
        }
        .into())
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Tokenio {
    fn build_request(
        &self,
        _req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Refund Sync".to_string(),
            connector: "Tokenio".to_string(),
        }
        .into())
    }
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Tokenio {
    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let webhook_payload: tokenio::TokenioWebhookPayload = request
            .body
            .parse_struct("TokenioWebhookPayload")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        match &webhook_payload.event_data {
            tokenio::TokenioWebhookEventData::PaymentV2 { payment } => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::ConnectorTransactionId(payment.id.clone()),
                ))
            }
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        // Check token-event header first
        let event_type = if let Some(header_value) = request.headers.get("token-event") {
            header_value
                .to_str()
                .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?
                .to_string()
        } else {
            // Fallback to parsing body for eventType field
            let webhook_payload: tokenio::TokenioWebhookPayload = request
                .body
                .parse_struct("TokenioWebhookPayload")
                .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

            webhook_payload
                .event_type
                .ok_or(errors::ConnectorError::WebhookEventTypeNotFound)?
        };
        match event_type.as_str() {
            "PAYMENT_STATUS_CHANGED" => {
                let webhook_payload: tokenio::TokenioWebhookPayload = request
                    .body
                    .parse_struct("TokenioWebhookPayload")
                    .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

                let tokenio::TokenioWebhookEventData::PaymentV2 { payment } =
                    &webhook_payload.event_data;

                match payment.status {
                    TokenioPaymentStatus::InitiationCompleted => {
                        Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing)
                    }
                    TokenioPaymentStatus::PaymentCompleted => {
                        Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess)
                    }
                    TokenioPaymentStatus::PaymentFailed => {
                        Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure)
                    }
                    TokenioPaymentStatus::PaymentCancelled => {
                        Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentCancelled)
                    }
                    TokenioPaymentStatus::InitiationRejected => {
                        Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure)
                    }
                    TokenioPaymentStatus::InitiationProcessing => {
                        Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing)
                    }
                    TokenioPaymentStatus::InitiationPendingRedirectHp => {
                        Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing)
                    }
                    TokenioPaymentStatus::Other => {
                        Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
                    }
                }
            }
            _ => Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported),
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let webhook_payload: tokenio::TokenioWebhookPayload = request
            .body
            .parse_struct("TokenioWebhookPayload")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(Box::new(webhook_payload))
    }
    async fn verify_webhook_source(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>,
        connector_name: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_name,
                connector_webhook_details,
            )
            .await?;

        // Convert UTF-8 bytes back to base64url string
        let base64url_string = String::from_utf8(connector_webhook_secrets.secret)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
            .attach_printable("Failed to convert public key bytes to base64url string")?;

        // Decode base64url string to actual ED25519 key bytes
        let public_key_bytes = URL_SAFE_NO_PAD
            .decode(&base64url_string)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
            .attach_printable("Failed to decode base64url public key to ED25519 bytes")?;

        // Extract the signature from token-signature header
        let signature_header = request
            .headers
            .get("token-signature")
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?
            .to_str()
            .change_context(errors::ConnectorError::WebhookSignatureNotFound)
            .attach_printable("Failed to convert token-signature header to string")?;

        // Decode the base64url signature to bytes
        let signature_bytes = URL_SAFE_NO_PAD
            .decode(signature_header)
            .change_context(errors::ConnectorError::WebhookSignatureNotFound)
            .attach_printable("Failed to decode base64url signature")?;

        // Get the raw message body (this is what Token.io signed)
        let message_bytes = request.body;

        // Use ED25519 to verify the signature
        let ed25519 = crypto::Ed25519;
        let is_valid = ed25519
            .verify_signature(
                &public_key_bytes,
                &signature_bytes, // ED25519 signature (64 bytes)
                message_bytes,    // Raw webhook body
            )
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
            .attach_printable("ED25519 signature verification failed")?;

        Ok(is_valid)
    }
}

static TOKENIO_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> = LazyLock::new(|| {
    let supported_capture_methods = vec![
        enums::CaptureMethod::Automatic,
        enums::CaptureMethod::Manual,
        enums::CaptureMethod::SequentialAutomatic,
    ];

    let mut tokenio_supported_payment_methods = SupportedPaymentMethods::new();

    // Open Banking - SEPA
    tokenio_supported_payment_methods.add(
        enums::PaymentMethod::OpenBanking,
        PaymentMethodType::OpenBankingPIS,
        PaymentMethodDetails {
            mandates: FeatureStatus::NotSupported,
            refunds: FeatureStatus::NotSupported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );
    tokenio_supported_payment_methods
});

static TOKENIO_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Token.io",
    description: "Token.io is a financial technology company that provides Open Banking and real-time payment solutions across Europe. They enable secure bank-to-bank transfers using various payment rails including SEPA, Faster Payments, and other regional payment systems.",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Sandbox,
};

static TOKENIO_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 1] = [enums::EventClass::Payments];

impl ConnectorSpecifications for Tokenio {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&TOKENIO_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*TOKENIO_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
        Some(&TOKENIO_SUPPORTED_WEBHOOK_FLOWS)
    }
}


// Struct: CustomerDetails
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CustomerDetails


// Implementation: impl api::RefundExecute for for Checkout
// File: crates/hyperswitch_connectors/src/connectors/checkout.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Checkout


// Function: get_filters
// File: crates/analytics/src/sdk_events/core.rs
// Module: analytics

pub fn get_filters(
    pool: &AnalyticsProvider,
    req: GetSdkEventFiltersRequest,
    publishable_key: &String,
) -> AnalyticsResult<SdkEventFiltersResponse>


// Function: execute_query
// File: crates/analytics/src/query.rs
// Module: analytics

pub fn execute_query<R, P>(
        &mut self,
        store: &P,
    ) -> CustomResult<CustomResult<Vec<R>, QueryExecutionError>, QueryBuildingError>
    where
        P: LoadRow<R> + AnalyticsDataSource,
        Aggregate<&'static str>: ToSql<T>,
        Window<&'static str>: ToSql<T>,


// Implementation: impl Responder
// File: crates/router/src/analytics.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Implementation: impl GatewayStatusMappingNew
// File: crates/diesel_models/src/query/gsm.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl GatewayStatusMappingNew


// Struct: AuthenticationSyncPostUpdateRequest
// File: crates/api_models/src/authentication.rs
// Module: api_models
// Implementations: 1
// Traits: ApiEventMetric

pub struct AuthenticationSyncPostUpdateRequest


// Struct: Level
// File: crates/router_env/src/logger/config.rs
// Module: router_env
// Implementations: 1

pub struct Level


// Implementation: impl api::PaymentsCompleteAuthorize for for Bluesnap
// File: crates/hyperswitch_connectors/src/connectors/bluesnap.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentsCompleteAuthorize for for Bluesnap


// Implementation: impl Health
// File: crates/router/src/bin/scheduler.rs
// Module: router
// Methods: 1 total (1 public)

impl Health


// Function: is_true
// File: crates/common_types/src/primitive_wrappers.rs
// Module: common_types

// Documentation: returns the inner bool value

pub fn is_true(&self) -> bool


// Implementation: impl ThemeNew
// File: crates/diesel_models/src/user/theme.rs
// Module: diesel_models
// Methods: 1 total (1 public)

impl ThemeNew


// Struct: ConnectorData
// File: crates/router/src/types/api/connector_mapping.rs
// Module: router
// Implementations: 1

pub struct ConnectorData


// Implementation: impl ConnectorSpecifications for for Tokenio
// File: crates/hyperswitch_connectors/src/connectors/tokenio.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl ConnectorSpecifications for for Tokenio


// Struct: DeutschebankAmount
// File: crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DeutschebankAmount


// Struct: AuthenticationDetails
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AuthenticationDetails


// Struct: CardCapRes
// File: crates/hyperswitch_connectors/src/connectors/jpmorgan/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CardCapRes


// Trait: AddHeaders
// File: crates/external_services/src/grpc_client.rs
// Module: external_services

// Documentation: Trait to add necessary headers to the tonic Request

pub trait AddHeaders


// Function: get_id
// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs
// Module: hyperswitch_domain_models

pub fn get_id(&self) -> id_type::MerchantConnectorAccountId


// Implementation: impl Responder
// File: crates/router/src/routes/three_ds_decision_rule.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Function: get_payment_sync_body
// File: crates/hyperswitch_connectors/src/connectors/bamboraapac/transformers.rs
// Module: hyperswitch_connectors

pub fn get_payment_sync_body(req: &types::PaymentsSyncRouterData) -> Result<Vec<u8>, Error>


// Struct: Bambora
// File: crates/hyperswitch_connectors/src/connectors/bambora.rs
// Module: hyperswitch_connectors
// Implementations: 19
// Traits: api::Payment, api::PaymentToken, api::PaymentAuthorize, api::PaymentVoid, api::MandateSetup, api::ConnectorAccessToken, api::PaymentSync, api::PaymentCapture, api::PaymentSession, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentsCompleteAuthorize, ConnectorCommon, ConnectorValidation, ConnectorRedirectResponse, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Bambora

api::Payment api::PaymentToken api::PaymentAuthorize api::PaymentVoid api::MandateSetup api::ConnectorAccessToken api::PaymentSync api::PaymentCapture api::PaymentSession api::Refund api::RefundExecute api::RefundSync api::PaymentsCompleteAuthorize ConnectorCommon ConnectorValidation ConnectorRedirectResponse webhooks::IncomingWebhook ConnectorSpecifications


// File: crates/hyperswitch_domain_models/src/router_flow_types/files.rs
// Module: hyperswitch_domain_models
// Public structs: 2

#[derive(Debug, Clone)]
pub struct Retrieve;

#[derive(Debug, Clone)]
pub struct Upload;


// Implementation: impl api::PaymentSession for for Sift
// File: crates/hyperswitch_connectors/src/connectors/sift.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSession for for Sift


// Function: from_storage
// File: crates/router/src/services/kafka/payment_intent.rs
// Module: router

pub fn from_storage(intent: &'a PaymentIntent, infra_values: Option<Value>) -> Self


// File: crates/hyperswitch_domain_models/src/router_flow_types/access_token_auth.rs
// Module: hyperswitch_domain_models
// Public structs: 2

#[derive(Clone, Debug)]
pub struct AccessTokenAuthentication;

#[derive(Clone, Debug)]
pub struct AccessTokenAuth;


// Struct: PaymentMethodListResponse
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 0

pub struct PaymentMethodListResponse


// Implementation: impl ConnectorValidation for for Stax
// File: crates/hyperswitch_connectors/src/connectors/stax.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl ConnectorValidation for for Stax


// File: crates/router/src/core/card_testing_guard/utils.rs
// Module: router
// Public functions: 3

use error_stack::ResultExt;
use hyperswitch_domain_models::{
    card_testing_guard_data::CardTestingGuardData, router_request_types::BrowserInformation,
};
use masking::{PeekInterface, Secret};
use router_env::logger;

use super::errors;
use crate::{
    core::{errors::RouterResult, payments::helpers},
    routes::SessionState,
    services,
    types::{api, domain},
    utils::crypto::{self, SignMessage},
};

pub async fn validate_card_testing_guard_checks(
    state: &SessionState,
    request: &api::PaymentsRequest,
    payment_method_data: Option<&api_models::payments::PaymentMethodData>,
    customer_id: &Option<common_utils::id_type::CustomerId>,
    business_profile: &domain::Profile,
) -> RouterResult<Option<CardTestingGuardData>> {
    match &business_profile.card_testing_guard_config {
        Some(card_testing_guard_config) => {
            let fingerprint = generate_fingerprint(payment_method_data, business_profile).await?;

            let card_testing_guard_expiry = card_testing_guard_config.card_testing_guard_expiry;

            let mut card_ip_blocking_cache_key = String::new();
            let mut guest_user_card_blocking_cache_key = String::new();
            let mut customer_id_blocking_cache_key = String::new();

            if card_testing_guard_config.is_card_ip_blocking_enabled {
                if let Some(browser_info) = &request.browser_info {
                    #[cfg(feature = "v1")]
                    {
                        let browser_info =
                            serde_json::from_value::<BrowserInformation>(browser_info.clone())
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable("could not parse browser_info")?;

                        if let Some(browser_info_ip) = browser_info.ip_address {
                            card_ip_blocking_cache_key =
                                helpers::validate_card_ip_blocking_for_business_profile(
                                    state,
                                    browser_info_ip,
                                    fingerprint.clone(),
                                    card_testing_guard_config,
                                )
                                .await?;
                        }
                    }

                    #[cfg(feature = "v2")]
                    {
                        if let Some(browser_info_ip) = browser_info.ip_address {
                            card_ip_blocking_cache_key =
                                helpers::validate_card_ip_blocking_for_business_profile(
                                    state,
                                    browser_info_ip,
                                    fingerprint.clone(),
                                    card_testing_guard_config,
                                )
                                .await?;
                        }
                    }
                }
            }

            if card_testing_guard_config.is_guest_user_card_blocking_enabled {
                guest_user_card_blocking_cache_key =
                    helpers::validate_guest_user_card_blocking_for_business_profile(
                        state,
                        fingerprint.clone(),
                        customer_id.clone(),
                        card_testing_guard_config,
                    )
                    .await?;
            }

            if card_testing_guard_config.is_customer_id_blocking_enabled {
                if let Some(customer_id) = customer_id.clone() {
                    customer_id_blocking_cache_key =
                        helpers::validate_customer_id_blocking_for_business_profile(
                            state,
                            customer_id.clone(),
                            business_profile.get_id(),
                            card_testing_guard_config,
                        )
                        .await?;
                }
            }

            Ok(Some(CardTestingGuardData {
                is_card_ip_blocking_enabled: card_testing_guard_config.is_card_ip_blocking_enabled,
                card_ip_blocking_cache_key,
                is_guest_user_card_blocking_enabled: card_testing_guard_config
                    .is_guest_user_card_blocking_enabled,
                guest_user_card_blocking_cache_key,
                is_customer_id_blocking_enabled: card_testing_guard_config
                    .is_customer_id_blocking_enabled,
                customer_id_blocking_cache_key,
                card_testing_guard_expiry,
            }))
        }
        None => Ok(None),
    }
}

pub async fn generate_fingerprint(
    payment_method_data: Option<&api_models::payments::PaymentMethodData>,
    business_profile: &domain::Profile,
) -> RouterResult<Secret<String>> {
    let card_testing_secret_key = &business_profile.card_testing_secret_key;

    match card_testing_secret_key {
        Some(card_testing_secret_key) => {
            let card_number_fingerprint = payment_method_data
                .as_ref()
                .and_then(|pm_data| match pm_data {
                    api_models::payments::PaymentMethodData::Card(card) => {
                        crypto::HmacSha512::sign_message(
                            &crypto::HmacSha512,
                            card_testing_secret_key.get_inner().peek().as_bytes(),
                            card.card_number.clone().get_card_no().as_bytes(),
                        )
                        .attach_printable("error in pm fingerprint creation")
                        .map_or_else(
                            |err| {
                                logger::error!(error=?err);
                                None
                            },
                            Some,
                        )
                    }
                    _ => None,
                })
                .map(hex::encode);

            card_number_fingerprint.map(Secret::new).ok_or_else(|| {
                error_stack::report!(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Error while masking fingerprint")
            })
        }
        None => Err(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("card testing secret key not configured")?,
    }
}

pub async fn increment_blocked_count_in_cache(
    state: &SessionState,
    card_testing_guard_data: Option<CardTestingGuardData>,
) -> RouterResult<()> {
    if let Some(card_testing_guard_data) = card_testing_guard_data.clone() {
        if card_testing_guard_data.is_card_ip_blocking_enabled
            && !card_testing_guard_data
                .card_ip_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.card_ip_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }

        if card_testing_guard_data.is_guest_user_card_blocking_enabled
            && !card_testing_guard_data
                .guest_user_card_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.guest_user_card_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }

        if card_testing_guard_data.is_customer_id_blocking_enabled
            && !card_testing_guard_data
                .customer_id_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.customer_id_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }
    }
    Ok(())
}


// Struct: NexixpayCompleteAuthorizeResponse
// File: crates/hyperswitch_connectors/src/connectors/nexixpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NexixpayCompleteAuthorizeResponse


// Function: initiate_payment_link
// File: crates/router/src/routes/payment_link.rs
// Module: router

pub fn initiate_payment_link(
    state: web::Data<AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::PaymentId,
    )


// Function: value_type
// File: crates/euclid/src/frontend/ast/parser.rs
// Module: euclid

pub fn value_type(input: &str) -> ParseResult<&str, ast::ValueType>


// Function: should_call_frm
// File: crates/router/src/core/fraud_check.rs
// Module: router

pub fn should_call_frm<F, D>(
    _merchant_context: &domain::MerchantContext,
    _payment_data: &D,
    _state: &SessionState,
) -> RouterResult<(
    bool,
    Option<FrmRoutingAlgorithm>,
    Option<common_utils::id_type::ProfileId>,
    Option<FrmConfigsObject>,
)>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,


// Struct: PaymentIntentListParams
// File: crates/hyperswitch_domain_models/src/payments/payment_intent.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PaymentIntentListParams


// Function: find_payment_intent_from_refund_id_type
// File: crates/router/src/utils.rs
// Module: router

pub fn find_payment_intent_from_refund_id_type(
    state: &SessionState,
    refund_id_type: webhooks::RefundIdType,
    merchant_context: &domain::MerchantContext,
    connector_name: &str,
) -> CustomResult<PaymentIntent, errors::ApiErrorResponse>


// Struct: LabelWithScoreEventResponse
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 0

pub struct LabelWithScoreEventResponse


// Implementation: impl GenericLinkNew
// File: crates/diesel_models/src/query/generic_link.rs
// Module: diesel_models
// Methods: 3 total (0 public)

impl GenericLinkNew


// Struct: SerializeVec
// File: crates/masking/src/serde.rs
// Module: masking
// Implementations: 0

pub struct SerializeVec<T: Serializer>