// File: crates/hyperswitch_connectors/src/utils.rs
// Module: hyperswitch_connectors
// Public functions: 24
// Public structs: 6

use std::{
    collections::{HashMap, HashSet},
    marker::PhantomData,
    str::FromStr,
    sync::LazyLock,
};

use api_models::payments;
#[cfg(feature = "payouts")]
use api_models::payouts::PayoutVendorAccountDetails;
use base64::Engine;
use common_enums::{
    enums,
    enums::{
        AlbaniaStatesAbbreviation, AndorraStatesAbbreviation, AttemptStatus,
        AustraliaStatesAbbreviation, AustriaStatesAbbreviation, BelarusStatesAbbreviation,
        BelgiumStatesAbbreviation, BosniaAndHerzegovinaStatesAbbreviation,
        BrazilStatesAbbreviation, BulgariaStatesAbbreviation, CanadaStatesAbbreviation,
        CroatiaStatesAbbreviation, CzechRepublicStatesAbbreviation, DenmarkStatesAbbreviation,
        FinlandStatesAbbreviation, FranceStatesAbbreviation, FutureUsage,
        GermanyStatesAbbreviation, GreeceStatesAbbreviation, HungaryStatesAbbreviation,
        IcelandStatesAbbreviation, IndiaStatesAbbreviation, IrelandStatesAbbreviation,
        ItalyStatesAbbreviation, JapanStatesAbbreviation, LatviaStatesAbbreviation,
        LiechtensteinStatesAbbreviation, LithuaniaStatesAbbreviation, LuxembourgStatesAbbreviation,
        MaltaStatesAbbreviation, MoldovaStatesAbbreviation, MonacoStatesAbbreviation,
        MontenegroStatesAbbreviation, NetherlandsStatesAbbreviation, NewZealandStatesAbbreviation,
        NorthMacedoniaStatesAbbreviation, NorwayStatesAbbreviation, PhilippinesStatesAbbreviation,
        PolandStatesAbbreviation, PortugalStatesAbbreviation, RomaniaStatesAbbreviation,
        RussiaStatesAbbreviation, SanMarinoStatesAbbreviation, SerbiaStatesAbbreviation,
        SingaporeStatesAbbreviation, SlovakiaStatesAbbreviation, SloveniaStatesAbbreviation,
        SpainStatesAbbreviation, SwedenStatesAbbreviation, SwitzerlandStatesAbbreviation,
        ThailandStatesAbbreviation, UkraineStatesAbbreviation, UnitedKingdomStatesAbbreviation,
        UsStatesAbbreviation,
    },
};
use common_utils::{
    consts::BASE64_ENGINE,
    errors::{CustomResult, ParsingError, ReportSwitchExt},
    ext_traits::{OptionExt, StringExt, ValueExt},
    id_type,
    pii::{self, Email, IpAddress},
    types::{AmountConvertor, MinorUnit},
};
use error_stack::{report, ResultExt};
#[cfg(feature = "frm")]
use hyperswitch_domain_models::router_request_types::fraud_check::{
    FraudCheckCheckoutData, FraudCheckRecordReturnData, FraudCheckSaleData,
    FraudCheckTransactionData,
};
use hyperswitch_domain_models::{
    address::{Address, AddressDetails, PhoneDetails},
    mandates,
    network_tokenization::NetworkTokenNumber,
    payment_method_data::{self, Card, CardDetailsForNetworkTransactionId, PaymentMethodData},
    router_data::{
        ErrorResponse, L2L3Data, PaymentMethodToken, RecurringMandatePaymentData,
        RouterData as ConnectorRouterData,
    },
    router_request_types::{
        AuthenticationData, AuthoriseIntegrityObject, BrowserInformation, CaptureIntegrityObject,
        CompleteAuthorizeData, ConnectorCustomerData, ExternalVaultProxyPaymentsData,
        MandateRevokeRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsPostSessionTokensData,
        PaymentsPreProcessingData, PaymentsSyncData, RefundIntegrityObject, RefundsData,
        ResponseId, SetupMandateRequestData, SyncIntegrityObject,
    },
    router_response_types::{CaptureSyncResponse, PaymentsResponseData},
    types::{OrderDetailsWithAmount, SetupMandateRouterData},
};
use hyperswitch_interfaces::{api, consts, errors, types::Response};
use image::{DynamicImage, ImageBuffer, ImageFormat, Luma, Rgba};
use masking::{ExposeInterface, PeekInterface, Secret};
use quick_xml::{
    events::{BytesDecl, BytesText, Event},
    Writer,
};
use rand::Rng;
use regex::Regex;
use router_env::logger;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use time::PrimitiveDateTime;
use unicode_normalization::UnicodeNormalization;

#[cfg(feature = "frm")]
use crate::types::FrmTransactionRouterData;
use crate::{constants::UNSUPPORTED_ERROR_MESSAGE, types::RefreshTokenRouterData};

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

pub(crate) fn construct_not_supported_error_report(
    capture_method: enums::CaptureMethod,
    connector_name: &'static str,
) -> error_stack::Report<errors::ConnectorError> {
    errors::ConnectorError::NotSupported {
        message: capture_method.to_string(),
        connector: connector_name,
    }
    .into()
}

pub(crate) fn to_currency_base_unit_with_zero_decimal_check(
    amount: i64,
    currency: enums::Currency,
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
    currency
        .to_currency_base_unit_with_zero_decimal_check(amount)
        .change_context(errors::ConnectorError::RequestEncodingFailed)
}

pub(crate) fn get_timestamp_in_milliseconds(datetime: &PrimitiveDateTime) -> i64 {
    let utc_datetime = datetime.assume_utc();
    utc_datetime.unix_timestamp() * 1000
}

pub(crate) fn get_amount_as_string(
    currency_unit: &api::CurrencyUnit,
    amount: i64,
    currency: enums::Currency,
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
    let amount = match currency_unit {
        api::CurrencyUnit::Minor => amount.to_string(),
        api::CurrencyUnit::Base => to_currency_base_unit(amount, currency)?,
    };
    Ok(amount)
}

pub(crate) fn base64_decode(data: String) -> Result<Vec<u8>, Error> {
    BASE64_ENGINE
        .decode(data)
        .change_context(errors::ConnectorError::ResponseDeserializationFailed)
}

pub(crate) fn to_currency_base_unit(
    amount: i64,
    currency: enums::Currency,
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
    currency
        .to_currency_base_unit(amount)
        .change_context(errors::ConnectorError::ParsingFailed)
}

pub trait ConnectorErrorTypeMapping {
    fn get_connector_error_type(
        &self,
        _error_code: String,
        _error_message: String,
    ) -> ConnectorErrorType {
        ConnectorErrorType::UnknownError
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ErrorCodeAndMessage {
    pub error_code: String,
    pub error_message: String,
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
//Priority of connector_error_type
pub enum ConnectorErrorType {
    UserError = 2,
    BusinessError = 3,
    TechnicalError = 4,
    UnknownError = 1,
}

pub(crate) fn get_error_code_error_message_based_on_priority(
    connector: impl ConnectorErrorTypeMapping,
    error_list: Vec<ErrorCodeAndMessage>,
) -> Option<ErrorCodeAndMessage> {
    let error_type_list = error_list
        .iter()
        .map(|error| {
            connector
                .get_connector_error_type(error.error_code.clone(), error.error_message.clone())
        })
        .collect::<Vec<ConnectorErrorType>>();
    let mut error_zip_list = error_list
        .iter()
        .zip(error_type_list.iter())
        .collect::<Vec<(&ErrorCodeAndMessage, &ConnectorErrorType)>>();
    error_zip_list.sort_by_key(|&(_, error_type)| error_type);
    error_zip_list
        .first()
        .map(|&(error_code_message, _)| error_code_message)
        .cloned()
}

pub trait MultipleCaptureSyncResponse {
    fn get_connector_capture_id(&self) -> String;
    fn get_capture_attempt_status(&self) -> AttemptStatus;
    fn is_capture_response(&self) -> bool;
    fn get_connector_reference_id(&self) -> Option<String> {
        None
    }
    fn get_amount_captured(&self) -> Result<Option<MinorUnit>, error_stack::Report<ParsingError>>;
}

pub(crate) fn construct_captures_response_hashmap<T>(
    capture_sync_response_list: Vec<T>,
) -> CustomResult<HashMap<String, CaptureSyncResponse>, errors::ConnectorError>
where
    T: MultipleCaptureSyncResponse,
{
    let mut hashmap = HashMap::new();
    for capture_sync_response in capture_sync_response_list {
        let connector_capture_id = capture_sync_response.get_connector_capture_id();
        if capture_sync_response.is_capture_response() {
            hashmap.insert(
                connector_capture_id.clone(),
                CaptureSyncResponse::Success {
                    resource_id: ResponseId::ConnectorTransactionId(connector_capture_id),
                    status: capture_sync_response.get_capture_attempt_status(),
                    connector_response_reference_id: capture_sync_response
                        .get_connector_reference_id(),
                    amount: capture_sync_response
                        .get_amount_captured()
                        .change_context(errors::ConnectorError::AmountConversionFailed)
                        .attach_printable(
                            "failed to convert back captured response amount to minor unit",
                        )?,
                },
            );
        }
    }

    Ok(hashmap)
}

#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayWalletData {
    #[serde(rename = "type")]
    pub pm_type: String,
    pub description: String,
    pub info: GooglePayPaymentMethodInfo,
    pub tokenization_data: common_types::payments::GpayTokenizationData,
}

#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayPaymentMethodInfo {
    pub card_network: String,
    pub card_details: String,
}

#[derive(Debug, Serialize)]
pub struct CardMandateInfo {
    pub card_exp_month: Secret<String>,
    pub card_exp_year: Secret<String>,
}

impl TryFrom<payment_method_data::GooglePayWalletData> for GooglePayWalletData {
    type Error = common_utils::errors::ValidationError;

    fn try_from(data: payment_method_data::GooglePayWalletData) -> Result<Self, Self::Error> {
        let tokenization_data = match data.tokenization_data {
            common_types::payments::GpayTokenizationData::Encrypted(encrypted_data) => {
                common_types::payments::GpayTokenizationData::Encrypted(
                    common_types::payments::GpayEcryptedTokenizationData {
                        token_type: encrypted_data.token_type,
                        token: encrypted_data.token,
                    },
                )
            }
            common_types::payments::GpayTokenizationData::Decrypted(_) => {
                return Err(common_utils::errors::ValidationError::InvalidValue {
                    message: "Expected encrypted tokenization data, got decrypted".to_string(),
                });
            }
        };

        Ok(Self {
            pm_type: data.pm_type,
            description: data.description,
            info: GooglePayPaymentMethodInfo {
                card_network: data.info.card_network,
                card_details: data.info.card_details,
            },
            tokenization_data,
        })
    }
}
pub(crate) fn get_amount_as_f64(
    currency_unit: &api::CurrencyUnit,
    amount: i64,
    currency: enums::Currency,
) -> Result<f64, error_stack::Report<errors::ConnectorError>> {
    let amount = match currency_unit {
        api::CurrencyUnit::Base => to_currency_base_unit_asf64(amount, currency)?,
        api::CurrencyUnit::Minor => u32::try_from(amount)
            .change_context(errors::ConnectorError::ParsingFailed)?
            .into(),
    };
    Ok(amount)
}

pub(crate) fn to_currency_base_unit_asf64(
    amount: i64,
    currency: enums::Currency,
) -> Result<f64, error_stack::Report<errors::ConnectorError>> {
    currency
        .to_currency_base_unit_asf64(amount)
        .change_context(errors::ConnectorError::ParsingFailed)
}

pub(crate) fn to_connector_meta_from_secret<T>(
    connector_meta: Option<Secret<Value>>,
) -> Result<T, Error>
where
    T: serde::de::DeserializeOwned,
{
    let connector_meta_secret =
        connector_meta.ok_or_else(missing_field_err("connector_meta_data"))?;
    let json = connector_meta_secret.expose();
    json.parse_value(std::any::type_name::<T>()).switch()
}

pub(crate) fn is_manual_capture(capture_method: Option<enums::CaptureMethod>) -> bool {
    capture_method == Some(enums::CaptureMethod::Manual)
        || capture_method == Some(enums::CaptureMethod::ManualMultiple)
}

pub(crate) fn generate_random_bytes(length: usize) -> Vec<u8> {
    // returns random bytes of length n
    let mut rng = rand::thread_rng();
    (0..length).map(|_| Rng::gen(&mut rng)).collect()
}

pub(crate) fn missing_field_err(
    message: &'static str,
) -> Box<dyn Fn() -> error_stack::Report<errors::ConnectorError> + 'static> {
    Box::new(move || {
        errors::ConnectorError::MissingRequiredField {
            field_name: message,
        }
        .into()
    })
}

pub(crate) fn handle_json_response_deserialization_failure(
    res: Response,
    connector: &'static str,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
    crate::metrics::CONNECTOR_RESPONSE_DESERIALIZATION_FAILURE
        .add(1, router_env::metric_attributes!(("connector", connector)));

    let response_data = String::from_utf8(res.response.to_vec())
        .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

    // check for whether the response is in json format
    match serde_json::from_str::<Value>(&response_data) {
        // in case of unexpected response but in json format
        Ok(_) => Err(errors::ConnectorError::ResponseDeserializationFailed)?,
        // in case of unexpected response but in html or string format
        Err(error_msg) => {
            logger::error!(deserialization_error=?error_msg);
            logger::error!("UNEXPECTED RESPONSE FROM CONNECTOR: {}", response_data);
            Ok(ErrorResponse {
                status_code: res.status_code,
                code: consts::NO_ERROR_CODE.to_string(),
                message: UNSUPPORTED_ERROR_MESSAGE.to_string(),
                reason: Some(response_data),
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        }
    }
}

pub(crate) fn construct_not_implemented_error_report(
    capture_method: enums::CaptureMethod,
    connector_name: &str,
) -> error_stack::Report<errors::ConnectorError> {
    errors::ConnectorError::NotImplemented(format!("{capture_method} for {connector_name}")).into()
}

pub(crate) const SELECTED_PAYMENT_METHOD: &str = "Selected payment method";

pub(crate) fn get_unimplemented_payment_method_error_message(connector: &str) -> String {
    format!("{SELECTED_PAYMENT_METHOD} through {connector}")
}

pub(crate) fn to_connector_meta<T>(connector_meta: Option<Value>) -> Result<T, Error>
where
    T: serde::de::DeserializeOwned,
{
    let json = connector_meta.ok_or_else(missing_field_err("connector_meta_data"))?;
    json.parse_value(std::any::type_name::<T>()).switch()
}

pub(crate) fn convert_amount<T>(
    amount_convertor: &dyn AmountConvertor<Output = T>,
    amount: MinorUnit,
    currency: enums::Currency,
) -> Result<T, error_stack::Report<errors::ConnectorError>> {
    amount_convertor
        .convert(amount, currency)
        .change_context(errors::ConnectorError::AmountConversionFailed)
}

pub(crate) fn validate_currency(
    request_currency: enums::Currency,
    merchant_config_currency: Option<enums::Currency>,
) -> Result<(), errors::ConnectorError> {
    let merchant_config_currency =
        merchant_config_currency.ok_or(errors::ConnectorError::NoConnectorMetaData)?;
    if request_currency != merchant_config_currency {
        Err(errors::ConnectorError::NotSupported {
            message: format!(
                "currency {request_currency} is not supported for this merchant account",
            ),
            connector: "Braintree",
        })?
    }
    Ok(())
}

pub(crate) fn convert_back_amount_to_minor_units<T>(
    amount_convertor: &dyn AmountConvertor<Output = T>,
    amount: T,
    currency: enums::Currency,
) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
    amount_convertor
        .convert_back(amount, currency)
        .change_context(errors::ConnectorError::AmountConversionFailed)
}

pub(crate) fn is_payment_failure(status: AttemptStatus) -> bool {
    match status {
        AttemptStatus::AuthenticationFailed
        | AttemptStatus::AuthorizationFailed
        | AttemptStatus::CaptureFailed
        | AttemptStatus::VoidFailed
        | AttemptStatus::Failure
        | AttemptStatus::Expired => true,
        AttemptStatus::Started
        | AttemptStatus::RouterDeclined
        | AttemptStatus::AuthenticationPending
        | AttemptStatus::AuthenticationSuccessful
        | AttemptStatus::Authorized
        | AttemptStatus::Charged
        | AttemptStatus::Authorizing
        | AttemptStatus::CodInitiated
        | AttemptStatus::Voided
        | AttemptStatus::VoidedPostCharge
        | AttemptStatus::VoidInitiated
        | AttemptStatus::CaptureInitiated
        | AttemptStatus::AutoRefunded
        | AttemptStatus::PartialCharged
        | AttemptStatus::PartialChargedAndChargeable
        | AttemptStatus::Unresolved
        | AttemptStatus::Pending
        | AttemptStatus::PaymentMethodAwaited
        | AttemptStatus::ConfirmationAwaited
        | AttemptStatus::DeviceDataCollectionPending
        | AttemptStatus::IntegrityFailure
        | AttemptStatus::PartiallyAuthorized => false,
    }
}

pub fn is_refund_failure(status: enums::RefundStatus) -> bool {
    match status {
        common_enums::RefundStatus::Failure | common_enums::RefundStatus::TransactionFailure => {
            true
        }
        common_enums::RefundStatus::ManualReview
        | common_enums::RefundStatus::Pending
        | common_enums::RefundStatus::Success => false,
    }
}
// TODO: Make all traits as `pub(crate) trait` once all connectors are moved.
pub trait RouterData {
    fn get_billing(&self) -> Result<&Address, Error>;
    fn get_billing_country(&self) -> Result<api_models::enums::CountryAlpha2, Error>;
    fn get_billing_phone(&self) -> Result<&PhoneDetails, Error>;
    fn get_description(&self) -> Result<String, Error>;
    fn get_billing_address(&self) -> Result<&AddressDetails, Error>;
    fn get_shipping_address(&self) -> Result<&AddressDetails, Error>;
    fn get_shipping_address_with_phone_number(&self) -> Result<&Address, Error>;
    fn get_connector_meta(&self) -> Result<pii::SecretSerdeValue, Error>;
    fn get_session_token(&self) -> Result<String, Error>;
    fn get_billing_first_name(&self) -> Result<Secret<String>, Error>;
    fn get_billing_full_name(&self) -> Result<Secret<String>, Error>;
    fn get_billing_last_name(&self) -> Result<Secret<String>, Error>;
    fn get_billing_line1(&self) -> Result<Secret<String>, Error>;
    fn get_billing_line2(&self) -> Result<Secret<String>, Error>;
    fn get_billing_zip(&self) -> Result<Secret<String>, Error>;
    fn get_billing_state(&self) -> Result<Secret<String>, Error>;
    fn get_billing_state_code(&self) -> Result<Secret<String>, Error>;
    fn get_billing_city(&self) -> Result<String, Error>;
    fn get_billing_email(&self) -> Result<Email, Error>;
    fn get_billing_phone_number(&self) -> Result<Secret<String>, Error>;
    fn to_connector_meta<T>(&self) -> Result<T, Error>
    where
        T: serde::de::DeserializeOwned;
    fn is_three_ds(&self) -> bool;
    fn get_payment_method_token(&self) -> Result<PaymentMethodToken, Error>;
    fn get_customer_id(&self) -> Result<id_type::CustomerId, Error>;
    fn get_optional_customer_id(&self) -> Option<id_type::CustomerId>;
    fn get_connector_customer_id(&self) -> Result<String, Error>;
    fn get_preprocessing_id(&self) -> Result<String, Error>;
    fn get_recurring_mandate_payment_data(&self) -> Result<RecurringMandatePaymentData, Error>;
    #[cfg(feature = "payouts")]
    fn get_payout_method_data(&self) -> Result<api_models::payouts::PayoutMethodData, Error>;
    #[cfg(feature = "payouts")]
    fn get_quote_id(&self) -> Result<String, Error>;

    fn get_optional_billing(&self) -> Option<&Address>;
    fn get_optional_shipping(&self) -> Option<&Address>;
    fn get_optional_shipping_line1(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_line2(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_line3(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_city(&self) -> Option<String>;
    fn get_optional_shipping_country(&self) -> Option<enums::CountryAlpha2>;
    fn get_optional_shipping_zip(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_state(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_first_name(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_last_name(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_full_name(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_phone_number(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_phone_number_without_country_code(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_email(&self) -> Option<Email>;

    fn get_required_shipping_full_name(&self) -> Result<Secret<String>, Error>;
    fn get_required_shipping_line1(&self) -> Result<Secret<String>, Error>;
    fn get_required_shipping_city(&self) -> Result<String, Error>;
    fn get_required_shipping_state(&self) -> Result<Secret<String>, Error>;
    fn get_required_shipping_zip(&self) -> Result<Secret<String>, Error>;
    fn get_required_shipping_phone_number(&self) -> Result<Secret<String>, Error>;

    fn get_optional_billing_full_name(&self) -> Option<Secret<String>>;
    fn get_optional_billing_line1(&self) -> Option<Secret<String>>;
    fn get_optional_billing_line3(&self) -> Option<Secret<String>>;
    fn get_optional_billing_line2(&self) -> Option<Secret<String>>;
    fn get_optional_billing_city(&self) -> Option<String>;
    fn get_optional_billing_country(&self) -> Option<enums::CountryAlpha2>;
    fn get_optional_billing_zip(&self) -> Option<Secret<String>>;
    fn get_optional_billing_state(&self) -> Option<Secret<String>>;
    fn get_optional_billing_state_2_digit(&self) -> Option<Secret<String>>;
    fn get_optional_billing_first_name(&self) -> Option<Secret<String>>;
    fn get_optional_billing_last_name(&self) -> Option<Secret<String>>;
    fn get_optional_billing_phone_number(&self) -> Option<Secret<String>>;
    fn get_optional_billing_email(&self) -> Option<Email>;
    fn get_optional_l2_l3_data(&self) -> Option<L2L3Data>;
}

impl<Flow, Request, Response> RouterData
    for hyperswitch_domain_models::router_data::RouterData<Flow, Request, Response>
{
    fn get_billing(&self) -> Result<&Address, Error> {
        self.address
            .get_payment_method_billing()
            .ok_or_else(missing_field_err("billing"))
    }

    fn get_billing_country(&self) -> Result<api_models::enums::CountryAlpha2, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|a| a.address.as_ref())
            .and_then(|ad| ad.country)
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.country",
            ))
    }

    fn get_billing_phone(&self) -> Result<&PhoneDetails, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|a| a.phone.as_ref())
            .ok_or_else(missing_field_err("billing.phone"))
    }

    fn get_optional_billing(&self) -> Option<&Address> {
        self.address.get_payment_method_billing()
    }

    fn get_optional_shipping(&self) -> Option<&Address> {
        self.address.get_shipping()
    }

    fn get_optional_shipping_first_name(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.first_name)
        })
    }

    fn get_optional_shipping_last_name(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.last_name)
        })
    }

    fn get_optional_shipping_full_name(&self) -> Option<Secret<String>> {
        self.get_optional_shipping()
            .and_then(|shipping_details| shipping_details.address.as_ref())
            .and_then(|shipping_address| shipping_address.get_optional_full_name())
    }

    fn get_optional_shipping_line1(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.line1)
        })
    }

    fn get_optional_shipping_line2(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.line2)
        })
    }

    fn get_optional_shipping_line3(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.line3)
        })
    }

    fn get_optional_shipping_city(&self) -> Option<String> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.city)
        })
    }

    fn get_optional_shipping_state(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.state)
        })
    }

    fn get_optional_shipping_country(&self) -> Option<enums::CountryAlpha2> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.country)
        })
    }

    fn get_optional_shipping_zip(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.zip)
        })
    }

    fn get_optional_shipping_email(&self) -> Option<Email> {
        self.address
            .get_shipping()
            .and_then(|shipping_address| shipping_address.clone().email)
    }

    fn get_optional_shipping_phone_number(&self) -> Option<Secret<String>> {
        self.address
            .get_shipping()
            .and_then(|shipping_address| shipping_address.clone().phone)
            .and_then(|phone_details| phone_details.get_number_with_country_code().ok())
    }

    fn get_optional_shipping_phone_number_without_country_code(&self) -> Option<Secret<String>> {
        self.address
            .get_shipping()
            .and_then(|shipping_address| shipping_address.clone().phone)
            .and_then(|phone_details| phone_details.get_number().ok())
    }

    fn get_description(&self) -> Result<String, Error> {
        self.description
            .clone()
            .ok_or_else(missing_field_err("description"))
    }
    fn get_billing_address(&self) -> Result<&AddressDetails, Error> {
        self.address
            .get_payment_method_billing()
            .as_ref()
            .and_then(|a| a.address.as_ref())
            .ok_or_else(missing_field_err("billing.address"))
    }

    fn get_connector_meta(&self) -> Result<pii::SecretSerdeValue, Error> {
        self.connector_meta_data
            .clone()
            .ok_or_else(missing_field_err("connector_meta_data"))
    }

    fn get_session_token(&self) -> Result<String, Error> {
        self.session_token
            .clone()
            .ok_or_else(missing_field_err("session_token"))
    }

    fn get_billing_first_name(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.first_name.clone())
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.first_name",
            ))
    }

    fn get_billing_full_name(&self) -> Result<Secret<String>, Error> {
        self.get_optional_billing()
            .and_then(|billing_details| billing_details.address.as_ref())
            .and_then(|billing_address| billing_address.get_optional_full_name())
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.first_name",
            ))
    }

    fn get_billing_last_name(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.last_name.clone())
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.last_name",
            ))
    }

    fn get_billing_line1(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.line1.clone())
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.line1",
            ))
    }
    fn get_billing_line2(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.line2.clone())
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.line2",
            ))
    }
    fn get_billing_zip(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.zip.clone())
            })
            .ok_or_else(missing_field_err("payment_method_data.billing.address.zip"))
    }
    fn get_billing_state(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.state.clone())
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.state",
            ))
    }
    fn get_billing_state_code(&self) -> Result<Secret<String>, Error> {
        let country = self.get_billing_country()?;
        let state = self.get_billing_state()?;
        match country {
            api_models::enums::CountryAlpha2::US => Ok(Secret::new(
                UsStatesAbbreviation::foreign_try_from(state.peek().to_string())?.to_string(),
            )),
            api_models::enums::CountryAlpha2::CA => Ok(Secret::new(
                CanadaStatesAbbreviation::foreign_try_from(state.peek().to_string())?.to_string(),
            )),
            _ => Ok(state.clone()),
        }
    }
    fn get_billing_city(&self) -> Result<String, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.city)
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.city",
            ))
    }

    fn get_billing_email(&self) -> Result<Email, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| billing_address.email.clone())
            .ok_or_else(missing_field_err("payment_method_data.billing.email"))
    }

    fn get_billing_phone_number(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| billing_address.clone().phone)
            .map(|phone_details| phone_details.get_number_with_country_code())
            .transpose()?
            .ok_or_else(missing_field_err("payment_method_data.billing.phone"))
    }

    fn get_optional_billing_line1(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.line1)
            })
    }

    fn get_optional_billing_line2(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.line2)
            })
    }

    fn get_optional_billing_line3(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.line3)
            })
    }

    fn get_optional_billing_city(&self) -> Option<String> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.city)
            })
    }

    fn get_optional_billing_country(&self) -> Option<enums::CountryAlpha2> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.country)
            })
    }

    fn get_optional_billing_zip(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.zip)
            })
    }

    fn get_optional_billing_state(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.state)
            })
    }

    fn get_optional_billing_state_2_digit(&self) -> Option<Secret<String>> {
        self.get_optional_billing_state().and_then(|state| {
            if state.clone().expose().len() != 2 {
                None
            } else {
                Some(state)
            }
        })
    }

    fn get_optional_billing_first_name(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.first_name)
            })
    }

    fn get_optional_billing_last_name(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.last_name)
            })
    }

    fn get_optional_billing_phone_number(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .phone
                    .and_then(|phone_data| phone_data.number)
            })
    }

    fn get_optional_billing_email(&self) -> Option<Email> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| billing_address.clone().email)
    }
    fn to_connector_meta<T>(&self) -> Result<T, Error>
    where
        T: serde::de::DeserializeOwned,
    {
        self.get_connector_meta()?
            .parse_value(std::any::type_name::<T>())
            .change_context(errors::ConnectorError::NoConnectorMetaData)
    }

    fn is_three_ds(&self) -> bool {
        matches!(self.auth_type, enums::AuthenticationType::ThreeDs)
    }

    fn get_shipping_address(&self) -> Result<&AddressDetails, Error> {
        self.address
            .get_shipping()
            .and_then(|a| a.address.as_ref())
            .ok_or_else(missing_field_err("shipping.address"))
    }


// Implementation: impl Clone for for PIISerializer
// File: crates/masking/src/serde.rs
// Module: masking
// Methods: 1 total (0 public)

impl Clone for for PIISerializer


// Struct: KlarnaData
// File: crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct KlarnaData


// Struct: PlaidBankAccountCredentialsAccounts
// File: crates/pm_auth/src/connector/plaid/transformers.rs
// Module: pm_auth
// Implementations: 0

pub struct PlaidBankAccountCredentialsAccounts


// Function: remove_quotes_for_integer_values
// File: crates/test_utils/src/newman_runner.rs
// Module: test_utils

pub fn remove_quotes_for_integer_values(
    connector_name: &str,
) -> Result<(&str, Option<String>), io::Error>


// Struct: MerchantAccountNew
// File: crates/diesel_models/src/merchant_account.rs
// Module: diesel_models
// Implementations: 0

pub struct MerchantAccountNew


// Struct: CheckoutRefundResponse
// File: crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CheckoutRefundResponse


// Struct: SubsequentAuthInformation
// File: crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SubsequentAuthInformation


// Implementation: impl api::PaymentToken for for Paybox
// File: crates/hyperswitch_connectors/src/connectors/paybox.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Paybox


// Implementation: impl api::PaymentAuthorize for for Coingate
// File: crates/hyperswitch_connectors/src/connectors/coingate.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Coingate


// Implementation: impl webhooks::IncomingWebhook for for Peachpayments
// File: crates/hyperswitch_connectors/src/connectors/peachpayments.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl webhooks::IncomingWebhook for for Peachpayments


// Struct: PaymentIntentMetricsBucketIdentifier
// File: crates/api_models/src/analytics/payment_intents.rs
// Module: api_models
// Implementations: 3
// Traits: Hash, PartialEq

pub struct PaymentIntentMetricsBucketIdentifier


// Function: get_network_token_resource_object
// File: crates/router/src/core/webhooks/network_tokenization_incoming.rs
// Module: router

pub fn get_network_token_resource_object(
    request_details: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::NetworkTokenizationError>


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

pub struct GetTokenizePayloadRequest


// Function: list_payment_attempts
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn list_payment_attempts(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder


// Struct: PaymentsCapture
// File: crates/router/src/core/payments/operations/payment_capture_v2.rs
// Module: router
// Implementations: 1
// Traits: ValidateStatusForOperation

pub struct PaymentsCapture


// Trait: ValueNode
// File: crates/hyperswitch_constraint_graph/src/types.rs
// Module: hyperswitch_constraint_graph

pub trait ValueNode: fmt::Debug + Clone + hash::Hash + serde::Serialize + PartialEq + Eq


// Function: get_expiry_date_as_yymm
// File: crates/common_types/src/payments.rs
// Module: common_types

// Documentation: Get the expiry date in YYMM format from the Apple Pay pre-decrypt data

pub fn get_expiry_date_as_yymm(&self) -> Result<Secret<String>, errors::ValidationError>


// Struct: AcquirerDetails
// File: crates/api_models/src/authentication.rs
// Module: api_models
// Implementations: 0

pub struct AcquirerDetails


// Struct: PaymentMethodStatusTrackingData
// File: crates/router/src/types/storage/payment_method.rs
// Module: router
// Implementations: 0

pub struct PaymentMethodStatusTrackingData


// Implementation: impl api::ConnectorAccessToken for for Nomupay
// File: crates/hyperswitch_connectors/src/connectors/nomupay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Nomupay


// Function: get_payment_filters_profile
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn get_payment_filters_profile(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
) -> impl Responder


// Function: schedule_refund_execution
// File: crates/router/src/core/refunds_v2.rs
// Module: router

pub fn schedule_refund_execution(
    state: &SessionState,
    refund: diesel_refund::Refund,
    refund_type: api_models::refunds::RefundType,
    merchant_context: &domain::MerchantContext,
    payment_attempt: &storage::PaymentAttempt,
    payment_intent: &storage::PaymentIntent,
    merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,
) -> errors::RouterResult<diesel_refund::Refund>


// Function: update_merchant_routing_dictionary
// File: crates/router/src/core/routing/helpers.rs
// Module: router

pub fn update_merchant_routing_dictionary(
    db: &dyn StorageInterface,
    merchant_id: &str,
    dictionary: routing_types::RoutingDictionary,
) -> RouterResult<()>


// File: crates/router/src/core/fraud_check/operation.rs
// Module: router

pub mod fraud_check_post;
pub mod fraud_check_pre;
use async_trait::async_trait;
use common_enums::FrmSuggestion;
use error_stack::{report, ResultExt};

pub use self::{fraud_check_post::FraudCheckPost, fraud_check_pre::FraudCheckPre};
use super::{
    types::{ConnectorDetailsCore, FrmConfigsObject, PaymentToFrmData},
    FrmData,
};
use crate::{
    core::errors::{self, RouterResult},
    routes::{app::ReqState, SessionState},
    types::{domain, fraud_check::FrmRouterData},
};

pub type BoxedFraudCheckOperation<F, D> = Box<dyn FraudCheckOperation<F, D> + Send + Sync>;

pub trait FraudCheckOperation<F, D>: Send + std::fmt::Debug {
    fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<PaymentToFrmData> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("get tracker interface not found for {self:?}"))
    }
    fn to_domain(&self) -> RouterResult<&dyn Domain<F, D>> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("domain interface not found for {self:?}"))
    }
    fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<FrmData, F, D> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("get tracker interface not found for {self:?}"))
    }
}

#[async_trait]
pub trait GetTracker<D>: Send {
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: D,
        frm_connector_details: ConnectorDetailsCore,
    ) -> RouterResult<Option<FrmData>>;
}

#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait Domain<F, D>: Send + Sync {
    async fn post_payment_frm<'a>(
        &'a self,
        state: &'a SessionState,
        req_state: ReqState,
        payment_data: &mut D,
        frm_data: &mut FrmData,
        merchant_context: &domain::MerchantContext,
        customer: &Option<domain::Customer>,
    ) -> RouterResult<Option<FrmRouterData>>
    where
        F: Send + Clone;

    async fn pre_payment_frm<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: &mut D,
        frm_data: &mut FrmData,
        merchant_context: &domain::MerchantContext,
        customer: &Option<domain::Customer>,
    ) -> RouterResult<FrmRouterData>
    where
        F: Send + Clone;

    // To execute several tasks conditionally based on the result of post_flow.
    // Eg: If the /sale(post flow) is returning the transaction as fraud we can execute refund in post task
    #[allow(clippy::too_many_arguments)]
    async fn execute_post_tasks(
        &self,
        _state: &SessionState,
        _req_state: ReqState,
        frm_data: &mut FrmData,
        _merchant_context: &domain::MerchantContext,
        _frm_configs: FrmConfigsObject,
        _frm_suggestion: &mut Option<FrmSuggestion>,
        _payment_data: &mut D,
        _customer: &Option<domain::Customer>,
        _should_continue_capture: &mut bool,
    ) -> RouterResult<Option<FrmData>>
    where
        F: Send + Clone,
    {
        return Ok(Some(frm_data.to_owned()));
    }
}

#[async_trait]
pub trait UpdateTracker<Fd, F: Clone, D>: Send {
    async fn update_tracker<'b>(
        &'b self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        frm_data: Fd,
        payment_data: &mut D,
        _frm_suggestion: Option<FrmSuggestion>,
        frm_router_data: FrmRouterData,
    ) -> RouterResult<Fd>;
}


// Struct: CardPaymentInformation
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CardPaymentInformation


// Function: get_profile_dispute_filters
// File: crates/router/src/analytics.rs
// Module: router

pub fn get_profile_dispute_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<api_models::analytics::GetDisputeFilterRequest>,
    ) -> impl Responder


// Implementation: impl api::PaymentVoid for for Billwerk
// File: crates/hyperswitch_connectors/src/connectors/billwerk.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentVoid for for Billwerk


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

pub fn get_tokenize_card_form_records(
    form: CardNetworkTokenizeForm,
) -> Result<
    (
        id_type::MerchantId,
        Vec<payment_methods_api::CardNetworkTokenizeRequest>,
    ),
    errors::ApiErrorResponse,
>


// Struct: DigitalvirgoAuthType
// File: crates/hyperswitch_connectors/src/connectors/digitalvirgo/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DigitalvirgoAuthType


// File: crates/router/src/core/chat.rs
// Module: router
// Public functions: 2

use api_models::chat as chat_api;
use common_utils::{
    consts,
    crypto::{DecodeMessage, GcmAes256},
    errors::CustomResult,
    request::{Method, RequestBuilder, RequestContent},
};
use error_stack::ResultExt;
use external_services::http_client;
use hyperswitch_domain_models::chat as chat_domain;
use masking::ExposeInterface;
use router_env::{
    instrument, logger,
    tracing::{self, Instrument},
};

use crate::{
    db::errors::chat::ChatErrors,
    routes::{app::SessionStateInfo, SessionState},
    services::{authentication as auth, authorization::roles, ApplicationResponse},
    utils,
};

#[instrument(skip_all, fields(?session_id))]
pub async fn get_data_from_hyperswitch_ai_workflow(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    req: chat_api::ChatRequest,
    session_id: Option<&str>,
) -> CustomResult<ApplicationResponse<chat_api::ChatResponse>, ChatErrors> {
    let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .change_context(ChatErrors::InternalServerError)
    .attach_printable("Failed to retrieve role information")?;
    let url = format!(
        "{}/webhook",
        state.conf.chat.get_inner().hyperswitch_ai_host
    );
    let request_id = state
        .get_request_id()
        .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());

    let request_body = chat_domain::HyperswitchAiDataRequest {
        query: chat_domain::GetDataMessage {
            message: req.message.clone(),
        },
        org_id: user_from_token.org_id.clone(),
        merchant_id: user_from_token.merchant_id.clone(),
        profile_id: user_from_token.profile_id.clone(),
        entity_type: role_info.get_entity_type(),
    };
    logger::info!("Request for AI service: {:?}", request_body);

    let mut request_builder = RequestBuilder::new()
        .method(Method::Post)
        .url(&url)
        .attach_default_headers()
        .header(consts::X_REQUEST_ID, &request_id)
        .set_body(RequestContent::Json(Box::new(request_body.clone())));

    if let Some(session_id) = session_id {
        request_builder = request_builder.header(consts::X_CHAT_SESSION_ID, session_id);
    }

    let request = request_builder.build();

    let response = http_client::send_request(
        &state.conf.proxy,
        request,
        Some(consts::REQUEST_TIME_OUT_FOR_AI_SERVICE),
    )
    .await
    .change_context(ChatErrors::InternalServerError)
    .attach_printable("Error when sending request to AI service")?
    .json::<chat_api::ChatResponse>()
    .await
    .change_context(ChatErrors::InternalServerError)
    .attach_printable("Error when deserializing response from AI service")?;

    let response_to_return = response.clone();
    tokio::spawn(
        async move {
            let new_hyperswitch_ai_interaction = utils::chat::construct_hyperswitch_ai_interaction(
                &state,
                &user_from_token,
                &req,
                &response,
                &request_id,
            )
            .await;

            match new_hyperswitch_ai_interaction {
                Ok(interaction) => {
                    let db = state.store.as_ref();
                    if let Err(e) = db.insert_hyperswitch_ai_interaction(interaction).await {
                        logger::error!("Failed to insert hyperswitch_ai_interaction: {:?}", e);
                    }
                }
                Err(e) => {
                    logger::error!("Failed to construct hyperswitch_ai_interaction: {:?}", e);
                }
            }
        }
        .in_current_span(),
    );

    Ok(ApplicationResponse::Json(response_to_return))
}

#[instrument(skip_all)]
pub async fn list_chat_conversations(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    req: chat_api::ChatListRequest,
) -> CustomResult<ApplicationResponse<chat_api::ChatListResponse>, ChatErrors> {
    let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .change_context(ChatErrors::InternalServerError)
    .attach_printable("Failed to retrieve role information")?;

    if !role_info.is_internal() {
        return Err(error_stack::Report::new(ChatErrors::UnauthorizedAccess)
            .attach_printable("Only internal roles are allowed for this operation"));
    }

    let db = state.store.as_ref();
    let hyperswitch_ai_interactions = db
        .list_hyperswitch_ai_interactions(
            req.merchant_id,
            req.limit.unwrap_or(consts::DEFAULT_LIST_LIMIT),
            req.offset.unwrap_or(consts::DEFAULT_LIST_OFFSET),
        )
        .await
        .change_context(ChatErrors::InternalServerError)
        .attach_printable("Error when fetching hyperswitch_ai_interactions")?;

    let encryption_key = state.conf.chat.get_inner().encryption_key.clone().expose();
    let key = match hex::decode(&encryption_key) {
        Ok(key) => key,
        Err(e) => {
            router_env::logger::error!("Failed to decode encryption key: {}", e);
            encryption_key.as_bytes().to_vec()
        }
    };

    let mut conversations = Vec::new();

    for interaction in hyperswitch_ai_interactions {
        let user_query_encrypted = interaction
            .user_query
            .ok_or(ChatErrors::InternalServerError)
            .attach_printable("Missing user_query field in hyperswitch_ai_interaction")?;
        let response_encrypted = interaction
            .response
            .ok_or(ChatErrors::InternalServerError)
            .attach_printable("Missing response field in hyperswitch_ai_interaction")?;

        let user_query_decrypted_bytes = GcmAes256
            .decode_message(&key, user_query_encrypted.into_inner())
            .change_context(ChatErrors::InternalServerError)
            .attach_printable("Failed to decrypt user query")?;

        let response_decrypted_bytes = GcmAes256
            .decode_message(&key, response_encrypted.into_inner())
            .change_context(ChatErrors::InternalServerError)
            .attach_printable("Failed to decrypt response")?;

        let user_query_decrypted = String::from_utf8(user_query_decrypted_bytes)
            .change_context(ChatErrors::InternalServerError)
            .attach_printable("Failed to convert decrypted user query to string")?;

        let response_decrypted = serde_json::from_slice(&response_decrypted_bytes)
            .change_context(ChatErrors::InternalServerError)
            .attach_printable("Failed to deserialize decrypted response")?;

        conversations.push(chat_api::ChatConversation {
            id: interaction.id,
            session_id: interaction.session_id,
            user_id: interaction.user_id,
            merchant_id: interaction.merchant_id,
            profile_id: interaction.profile_id,
            org_id: interaction.org_id,
            role_id: interaction.role_id,
            user_query: user_query_decrypted.into(),
            response: response_decrypted,
            database_query: interaction.database_query,
            interaction_status: interaction.interaction_status,
            created_at: interaction.created_at,
        });
    }

    return Ok(ApplicationResponse::Json(chat_api::ChatListResponse {
        conversations,
    }));
}


// Function: get_payout_variant_values
// File: crates/euclid_wasm/src/lib.rs
// Module: euclid_wasm

pub fn get_payout_variant_values(key: &str) -> Result<JsValue, JsValue>


    )
    .change_context(errors::ApiErrorResponse::MaximumRefundCount)?;

    let connector = payment_attempt
        .connector
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("No connector populated in payment attempt")?;
    let (connector_transaction_id, processor_transaction_data) =
        common_utils_types::ConnectorTransactionId::form_id_and_data(connector_payment_id);
    let refund_create_req = diesel_refund::RefundNew {
        id: global_refund_id,
        merchant_reference_id: merchant_reference_id.clone(),
        external_reference_id: Some(merchant_reference_id.get_string_repr().to_string()),
        payment_id: req.payment_id,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        connector_transaction_id,
        connector,
        refund_type: enums::RefundType::foreign_from(req.refund_type.unwrap_or_default()),
        total_amount: payment_attempt.get_total_amount(),
        refund_amount,
        currency,
        created_at: common_utils::date_time::now(),
        modified_at: common_utils::date_time::now(),
        refund_status: enums::RefundStatus::Pending,
        metadata: req.metadata,
        description: req.reason.clone(),
        attempt_id: payment_attempt.id.clone(),
        refund_reason: req.reason,
        profile_id: Some(payment_intent.profile_id.clone()),
        connector_id: payment_attempt.merchant_connector_id.clone(),
        charges: None,
        split_refunds: None,
        connector_refund_id: None,
        sent_to_gateway: Default::default(),
        refund_arn: None,
        updated_by: Default::default(),
        organization_id: merchant_context
            .get_merchant_account()
            .organization_id
            .clone(),
        processor_transaction_data,
        processor_refund_data: None,
    };

    let refund = match db
        .insert_refund(
            refund_create_req,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
    {
        Ok(refund) => {
            Box::pin(schedule_refund_execution(
                state,
                refund.clone(),
                refund_type,
                merchant_context,
                payment_attempt,
                payment_intent,
                merchant_connector_details,
            ))
            .await?
        }
        Err(err) => {
            if err.current_context().is_db_unique_violation() {
                Err(errors::ApiErrorResponse::DuplicateRefundRequest)?
            } else {
                Err(err)
                    .change_context(errors::ApiErrorResponse::RefundFailed { data: None })
                    .attach_printable("Failed to insert refund")?
            }
        }
    };

    let unified_translated_message =
        match (refund.unified_code.clone(), refund.unified_message.clone()) {
            (Some(unified_code), Some(unified_message)) => helpers::get_unified_translation(
                state,
                unified_code,
                unified_message.clone(),
                state.locale.to_string(),
            )
            .await
            .or(Some(unified_message)),
            _ => refund.unified_message,
        };

    let refund = diesel_refund::Refund {
        unified_message: unified_translated_message,
        ..refund
    };

    api::RefundResponse::foreign_try_from(refund)
}

impl ForeignTryFrom<diesel_refund::Refund> for api::RefundResponse {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(refund: diesel_refund::Refund) -> Result<Self, Self::Error> {
        let refund = refund;

        let profile_id = refund
            .profile_id
            .clone()
            .ok_or(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Profile id not found")?;

        let connector_name = refund.connector;
        let connector = Connector::from_str(&connector_name)
            .change_context(errors::ConnectorError::InvalidConnectorName)
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "connector",
            })
            .attach_printable_lazy(|| {
                format!("unable to parse connector name {connector_name:?}")
            })?;

        Ok(Self {
            payment_id: refund.payment_id,
            id: refund.id.clone(),
            amount: refund.refund_amount,
            currency: refund.currency,
            reason: refund.refund_reason,
            status: refunds::RefundStatus::foreign_from(refund.refund_status),
            profile_id,
            metadata: refund.metadata,
            created_at: refund.created_at,
            updated_at: refund.modified_at,
            connector,
            merchant_connector_id: refund.connector_id,
            merchant_reference_id: Some(refund.merchant_reference_id),
            error_details: Some(RefundErrorDetails {
                code: refund.refund_error_code.unwrap_or_default(),
                message: refund.refund_error_message.unwrap_or_default(),
            }),
            connector_refund_reference_id: None,
        })
    }
}

// ********************************************** PROCESS TRACKER **********************************************

#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn schedule_refund_execution(
    state: &SessionState,
    refund: diesel_refund::Refund,
    refund_type: api_models::refunds::RefundType,
    merchant_context: &domain::MerchantContext,
    payment_attempt: &storage::PaymentAttempt,
    payment_intent: &storage::PaymentIntent,
    merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,
) -> errors::RouterResult<diesel_refund::Refund> {
    let db = &*state.store;
    let runner = storage::ProcessTrackerRunner::RefundWorkflowRouter;
    let task = "EXECUTE_REFUND";
    let task_id = format!("{runner}_{task}_{}", refund.id.get_string_repr());

    let refund_process = db
        .find_process_by_id(&task_id)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to find the process id")?;

    let result = match refund.refund_status {
        enums::RefundStatus::Pending | enums::RefundStatus::ManualReview => {
            match (refund.sent_to_gateway, refund_process) {
                (false, None) => {
                    // Execute the refund task based on refund_type
                    match refund_type {
                        api_models::refunds::RefundType::Scheduled => {
                            add_refund_execute_task(db, &refund, runner)
                                .await
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable_lazy(|| format!("Failed while pushing refund execute task to scheduler, refund_id: {}", refund.id.get_string_repr()))?;

                            Ok(refund)
                        }
                        api_models::refunds::RefundType::Instant => {
                            let update_refund =
                                if state.conf.merchant_id_auth.merchant_id_auth_enabled {
                                    let merchant_connector_details =
                                        match merchant_connector_details {
                                            Some(details) => details,
                                            None => {
                                                return Err(report!(
                                            errors::ApiErrorResponse::MissingRequiredField {
                                                field_name: "merchant_connector_details"
                                            }
                                        ));
                                            }
                                        };
                                    Box::pin(internal_trigger_refund_to_gateway(
                                        state,
                                        &refund,
                                        merchant_context,
                                        payment_attempt,
                                        payment_intent,
                                        merchant_connector_details,
                                    ))
                                    .await
                                } else {
                                    Box::pin(trigger_refund_to_gateway(
                                        state,
                                        &refund,
                                        merchant_context,
                                        payment_attempt,
                                        payment_intent,
                                    ))
                                    .await
                                };

                            match update_refund {
                                Ok(updated_refund_data) => {
                                    add_refund_sync_task(db, &updated_refund_data, runner)
                                        .await
                                        .change_context(errors::ApiErrorResponse::InternalServerError)
                                        .attach_printable_lazy(|| format!(
                                            "Failed while pushing refund sync task in scheduler: refund_id: {}",
                                            refund.id.get_string_repr()
                                        ))?;
                                    Ok(updated_refund_data)
                                }
                                Err(err) => Err(err),
                            }
                        }
                    }
                }
                _ => {
                    // Sync the refund for status check
                    //[#300]: return refund status response
                    match refund_type {
                        api_models::refunds::RefundType::Scheduled => {
                            add_refund_sync_task(db, &refund, runner)
                                .await
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable_lazy(|| format!("Failed while pushing refund sync task in scheduler: refund_id: {}", refund.id.get_string_repr()))?;
                            Ok(refund)
                        }
                        api_models::refunds::RefundType::Instant => {
                            // [#255]: This is not possible in schedule_refund_execution as it will always be scheduled
                            // sync_refund_with_gateway(data, &refund).await
                            Ok(refund)
                        }
                    }
                }
            }
        }
        //  [#255]: This is not allowed to be otherwise or all
        _ => Ok(refund),
    }?;
    Ok(result)
}

#[instrument]
pub fn refund_to_refund_core_workflow_model(
    refund: &diesel_refund::Refund,
) -> diesel_refund::RefundCoreWorkflow {
    diesel_refund::RefundCoreWorkflow {
        refund_id: refund.id.clone(),
        connector_transaction_id: refund.connector_transaction_id.clone(),
        merchant_id: refund.merchant_id.clone(),
        payment_id: refund.payment_id.clone(),
        processor_transaction_data: refund.processor_transaction_data.clone(),
    }
}

#[instrument(skip_all)]
pub async fn add_refund_execute_task(
    db: &dyn db::StorageInterface,
    refund: &diesel_refund::Refund,
    runner: storage::ProcessTrackerRunner,
) -> errors::RouterResult<storage::ProcessTracker> {
    let task = "EXECUTE_REFUND";
    let process_tracker_id = format!("{runner}_{task}_{}", refund.id.get_string_repr());
    let tag = ["REFUND"];
    let schedule_time = common_utils::date_time::now();
    let refund_workflow_tracking_data = refund_to_refund_core_workflow_model(refund);
    let process_tracker_entry = storage::ProcessTrackerNew::new(
        process_tracker_id,
        task,
        runner,
        tag,
        refund_workflow_tracking_data,
        None,
        schedule_time,
        common_types::consts::API_VERSION,
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to construct refund execute process tracker task")?;

    let response = db
        .insert_process(process_tracker_entry)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::DuplicateRefundRequest)
        .attach_printable_lazy(|| {
            format!(
                "Failed while inserting task in process_tracker: refund_id: {}",
                refund.id.get_string_repr()
            )
        })?;
    Ok(response)
}

#[instrument(skip_all)]
pub async fn add_refund_sync_task(
    db: &dyn db::StorageInterface,
    refund: &diesel_refund::Refund,
    runner: storage::ProcessTrackerRunner,
) -> errors::RouterResult<storage::ProcessTracker> {
    let task = "SYNC_REFUND";
    let process_tracker_id = format!("{runner}_{task}_{}", refund.id.get_string_repr());
    let schedule_time = common_utils::date_time::now();
    let refund_workflow_tracking_data = refund_to_refund_core_workflow_model(refund);
    let tag = ["REFUND"];
    let process_tracker_entry = storage::ProcessTrackerNew::new(
        process_tracker_id,
        task,
        runner,
        tag,
        refund_workflow_tracking_data,
        None,
        schedule_time,
        common_types::consts::API_VERSION,
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to construct refund sync process tracker task")?;

    let response = db
        .insert_process(process_tracker_entry)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::DuplicateRefundRequest)
        .attach_printable_lazy(|| {
            format!(
                "Failed while inserting task in process_tracker: refund_id: {}",
                refund.id.get_string_repr()
            )
        })?;
    metrics::TASKS_ADDED_COUNT.add(1, router_env::metric_attributes!(("flow", "Refund")));

    Ok(response)
}