// File: crates/hyperswitch_domain_models/src/merchant_key_store.rs

use common_utils::{
    crypto::Encryptable,
    custom_serde, date_time,
    errors::{CustomResult, ValidationError},
    type_name,
    types::keymanager::{self, KeyManagerState},
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use time::PrimitiveDateTime;

use crate::type_encryption::{crypto_operation, CryptoOperation};

#[derive(Clone, Debug, serde::Serialize)]
pub struct MerchantKeyStore {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub key: Encryptable<Secret<Vec<u8>>>,
    #[serde(with = "custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
}

#[async_trait::async_trait]
impl super::behaviour::Conversion for MerchantKeyStore {
    type DstType = diesel_models::merchant_key_store::MerchantKeyStore;
    type NewDstType = diesel_models::merchant_key_store::MerchantKeyStoreNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(diesel_models::merchant_key_store::MerchantKeyStore {
            key: self.key.into(),
            merchant_id: self.merchant_id,
            created_at: self.created_at,
        })
    }

    async fn convert_back(
        state: &KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        _key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        let identifier = keymanager::Identifier::Merchant(item.merchant_id.clone());

        Ok(Self {
            key: crypto_operation(
                state,
                type_name!(Self::DstType),
                CryptoOperation::Decrypt(item.key),
                identifier,
                key.peek(),
            )
            .await
            .and_then(|val| val.try_into_operation())
            .change_context(ValidationError::InvalidValue {
                message: "Failed while decrypting customer data".to_string(),
            })?,
            merchant_id: item.merchant_id,
            created_at: item.created_at,
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(diesel_models::merchant_key_store::MerchantKeyStoreNew {
            merchant_id: self.merchant_id,
            key: self.key.into(),
            created_at: date_time::now(),
        })
    }
}

#[async_trait::async_trait]
pub trait MerchantKeyStoreInterface {
    type Error;
    async fn insert_merchant_key_store(
        &self,
        merchant_key_store: MerchantKeyStore,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<MerchantKeyStore, Self::Error>;

    async fn get_merchant_key_store_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<MerchantKeyStore, Self::Error>;

    async fn delete_merchant_key_store_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, Self::Error>;

    #[cfg(feature = "olap")]
    async fn list_multiple_key_stores(
        &self,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<Vec<MerchantKeyStore>, Self::Error>;

    async fn get_all_key_stores(
        &self,
        key: &Secret<Vec<u8>>,
        from: u32,
        to: u32,
    ) -> CustomResult<Vec<MerchantKeyStore>, Self::Error>;
}


// File: crates/hyperswitch_domain_models/src/routing.rs

use std::collections::HashMap;

use api_models::{enums as api_enums, routing};
use common_utils::id_type;

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RoutingData {
    pub routed_through: Option<String>,

    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,

    pub routing_info: PaymentRoutingInfo,
    pub algorithm: Option<routing::StraightThroughAlgorithm>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RoutingData {
    // TODO: change this to RoutableConnectors enum
    pub routed_through: Option<String>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,

    pub pre_routing_connector_choice: Option<PreRoutingConnectorChoice>,

    pub algorithm_requested: Option<id_type::RoutingId>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
#[serde(from = "PaymentRoutingInfoSerde", into = "PaymentRoutingInfoSerde")]
pub struct PaymentRoutingInfo {
    pub algorithm: Option<routing::StraightThroughAlgorithm>,
    pub pre_routing_results:
        Option<HashMap<api_enums::PaymentMethodType, PreRoutingConnectorChoice>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
#[serde(untagged)]
pub enum PreRoutingConnectorChoice {
    Single(routing::RoutableConnectorChoice),
    Multiple(Vec<routing::RoutableConnectorChoice>),
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentRoutingInfoInner {
    pub algorithm: Option<routing::StraightThroughAlgorithm>,
    pub pre_routing_results:
        Option<HashMap<api_enums::PaymentMethodType, PreRoutingConnectorChoice>>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum PaymentRoutingInfoSerde {
    OnlyAlgorithm(Box<routing::StraightThroughAlgorithm>),
    WithDetails(Box<PaymentRoutingInfoInner>),
}

impl From<PaymentRoutingInfoSerde> for PaymentRoutingInfo {
    fn from(value: PaymentRoutingInfoSerde) -> Self {
        match value {
            PaymentRoutingInfoSerde::OnlyAlgorithm(algo) => Self {
                algorithm: Some(*algo),
                pre_routing_results: None,
            },
            PaymentRoutingInfoSerde::WithDetails(details) => Self {
                algorithm: details.algorithm,
                pre_routing_results: details.pre_routing_results,
            },
        }
    }
}

impl From<PaymentRoutingInfo> for PaymentRoutingInfoSerde {
    fn from(value: PaymentRoutingInfo) -> Self {
        Self::WithDetails(Box::new(PaymentRoutingInfoInner {
            algorithm: value.algorithm,
            pre_routing_results: value.pre_routing_results,
        }))
    }
}


// File: crates/hyperswitch_domain_models/src/mandates.rs

use std::collections::HashMap;

use api_models::payments::{
    MandateAmountData as ApiMandateAmountData, MandateData as ApiMandateData, MandateType,
};
use common_enums::Currency;
use common_types::payments as common_payments_types;
use common_utils::{
    date_time,
    errors::{CustomResult, ParsingError},
    pii,
    types::MinorUnit,
};
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use crate::router_data::RecurringMandatePaymentData;

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct MandateDetails {
    pub update_mandate_id: Option<String>,
}

impl From<MandateDetails> for diesel_models::enums::MandateDetails {
    fn from(value: MandateDetails) -> Self {
        Self {
            update_mandate_id: value.update_mandate_id,
        }
    }
}

impl From<diesel_models::enums::MandateDetails> for MandateDetails {
    fn from(value: diesel_models::enums::MandateDetails) -> Self {
        Self {
            update_mandate_id: value.update_mandate_id,
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum MandateDataType {
    SingleUse(MandateAmountData),
    MultiUse(Option<MandateAmountData>),
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct MandateAmountData {
    pub amount: MinorUnit,
    pub currency: Currency,
    pub start_date: Option<PrimitiveDateTime>,
    pub end_date: Option<PrimitiveDateTime>,
    pub metadata: Option<pii::SecretSerdeValue>,
}

// The fields on this struct are optional, as we want to allow the merchant to provide partial
// information about creating mandates
#[derive(Default, Eq, PartialEq, Debug, Clone, serde::Serialize)]
pub struct MandateData {
    /// A way to update the mandate's payment method details
    pub update_mandate_id: Option<String>,
    /// A consent from the customer to store the payment method
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    /// A way to select the type of mandate used
    pub mandate_type: Option<MandateDataType>,
}

impl From<MandateType> for MandateDataType {
    fn from(mandate_type: MandateType) -> Self {
        match mandate_type {
            MandateType::SingleUse(mandate_amount_data) => {
                Self::SingleUse(mandate_amount_data.into())
            }
            MandateType::MultiUse(mandate_amount_data) => {
                Self::MultiUse(mandate_amount_data.map(|d| d.into()))
            }
        }
    }
}

impl From<MandateDataType> for diesel_models::enums::MandateDataType {
    fn from(value: MandateDataType) -> Self {
        match value {
            MandateDataType::SingleUse(data) => Self::SingleUse(data.into()),
            MandateDataType::MultiUse(None) => Self::MultiUse(None),
            MandateDataType::MultiUse(Some(data)) => Self::MultiUse(Some(data.into())),
        }
    }
}

impl From<diesel_models::enums::MandateDataType> for MandateDataType {
    fn from(value: diesel_models::enums::MandateDataType) -> Self {
        use diesel_models::enums::MandateDataType as DieselMandateDataType;

        match value {
            DieselMandateDataType::SingleUse(data) => Self::SingleUse(data.into()),
            DieselMandateDataType::MultiUse(None) => Self::MultiUse(None),
            DieselMandateDataType::MultiUse(Some(data)) => Self::MultiUse(Some(data.into())),
        }
    }
}

impl From<ApiMandateAmountData> for MandateAmountData {
    fn from(value: ApiMandateAmountData) -> Self {
        Self {
            amount: value.amount,
            currency: value.currency,
            start_date: value.start_date,
            end_date: value.end_date,
            metadata: value.metadata,
        }
    }
}

impl From<MandateAmountData> for diesel_models::enums::MandateAmountData {
    fn from(value: MandateAmountData) -> Self {
        Self {
            amount: value.amount,
            currency: value.currency,
            start_date: value.start_date,
            end_date: value.end_date,
            metadata: value.metadata,
        }
    }
}

impl From<diesel_models::enums::MandateAmountData> for MandateAmountData {
    fn from(value: diesel_models::enums::MandateAmountData) -> Self {
        Self {
            amount: value.amount,
            currency: value.currency,
            start_date: value.start_date,
            end_date: value.end_date,
            metadata: value.metadata,
        }
    }
}

impl From<ApiMandateData> for MandateData {
    fn from(value: ApiMandateData) -> Self {
        Self {
            customer_acceptance: value.customer_acceptance,
            mandate_type: value.mandate_type.map(|d| d.into()),
            update_mandate_id: value.update_mandate_id,
        }
    }
}

impl MandateAmountData {
    pub fn get_end_date(
        &self,
        format: date_time::DateFormat,
    ) -> error_stack::Result<Option<String>, ParsingError> {
        self.end_date
            .map(|date| {
                date_time::format_date(date, format)
                    .change_context(ParsingError::DateTimeParsingError)
            })
            .transpose()
    }
    pub fn get_metadata(&self) -> Option<pii::SecretSerdeValue> {
        self.metadata.clone()
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsMandateReferenceRecord {
    pub connector_mandate_id: String,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<i64>,
    pub original_payment_authorized_currency: Option<Currency>,
    pub mandate_metadata: Option<pii::SecretSerdeValue>,
    pub connector_mandate_status: Option<common_enums::ConnectorMandateStatus>,
    pub connector_mandate_request_reference_id: Option<String>,
    pub connector_customer_id: Option<String>,
}

#[cfg(feature = "v1")]
impl From<&PaymentsMandateReferenceRecord> for RecurringMandatePaymentData {
    fn from(mandate_reference_record: &PaymentsMandateReferenceRecord) -> Self {
        Self {
            payment_method_type: mandate_reference_record.payment_method_type,
            original_payment_authorized_amount: mandate_reference_record
                .original_payment_authorized_amount,
            original_payment_authorized_currency: mandate_reference_record
                .original_payment_authorized_currency,
            mandate_metadata: mandate_reference_record.mandate_metadata.clone(),
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConnectorTokenReferenceRecord {
    pub connector_token: String,
    pub payment_method_subtype: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<MinorUnit>,
    pub original_payment_authorized_currency: Option<Currency>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_token_status: common_enums::ConnectorTokenStatus,
    pub connector_token_request_reference_id: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PayoutsMandateReferenceRecord {
    pub transfer_method_id: Option<String>,
    pub connector_customer_id: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PayoutsMandateReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>,
);

impl std::ops::Deref for PayoutsMandateReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl std::ops::DerefMut for PayoutsMandateReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsTokenReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>,
);

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsMandateReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>,
);

#[cfg(feature = "v1")]
impl std::ops::Deref for PaymentsMandateReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "v1")]
impl std::ops::DerefMut for PaymentsMandateReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v2")]
impl std::ops::Deref for PaymentsTokenReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "v2")]
impl std::ops::DerefMut for PaymentsTokenReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsMandateReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsTokenReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

impl CommonMandateReference {
    pub fn get_mandate_details_value(&self) -> CustomResult<serde_json::Value, ParsingError> {
        let mut payments = self
            .payments
            .as_ref()
            .map_or_else(|| Ok(serde_json::json!({})), serde_json::to_value)
            .change_context(ParsingError::StructParseFailure("payment mandate details"))?;

        self.payouts
            .as_ref()
            .map(|payouts_mandate| {
                serde_json::to_value(payouts_mandate).map(|payouts_mandate_value| {
                    payments.as_object_mut().map(|payments_object| {
                        payments_object.insert("payouts".to_string(), payouts_mandate_value);
                    })
                })
            })
            .transpose()
            .change_context(ParsingError::StructParseFailure("payout mandate details"))?;

        Ok(payments)
    }

    #[cfg(feature = "v2")]
    /// Insert a new payment token reference for the given connector_id
    pub fn insert_payment_token_reference_record(
        &mut self,
        connector_id: &common_utils::id_type::MerchantConnectorAccountId,
        record: ConnectorTokenReferenceRecord,
    ) {
        match self.payments {
            Some(ref mut payments_reference) => {
                payments_reference.insert(connector_id.clone(), record);
            }
            None => {
                let mut payments_reference = HashMap::new();
                payments_reference.insert(connector_id.clone(), record);
                self.payments = Some(PaymentsTokenReference(payments_reference));
            }
        }
    }
}

impl From<diesel_models::CommonMandateReference> for CommonMandateReference {
    fn from(value: diesel_models::CommonMandateReference) -> Self {
        Self {
            payments: value.payments.map(|payments| payments.into()),
            payouts: value.payouts.map(|payouts| payouts.into()),
        }
    }
}

impl From<CommonMandateReference> for diesel_models::CommonMandateReference {
    fn from(value: CommonMandateReference) -> Self {
        Self {
            payments: value.payments.map(|payments| payments.into()),
            payouts: value.payouts.map(|payouts| payouts.into()),
        }
    }
}

impl From<diesel_models::PayoutsMandateReference> for PayoutsMandateReference {
    fn from(value: diesel_models::PayoutsMandateReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

impl From<PayoutsMandateReference> for diesel_models::PayoutsMandateReference {
    fn from(value: PayoutsMandateReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

#[cfg(feature = "v1")]
impl From<diesel_models::PaymentsMandateReference> for PaymentsMandateReference {
    fn from(value: diesel_models::PaymentsMandateReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

#[cfg(feature = "v1")]
impl From<PaymentsMandateReference> for diesel_models::PaymentsMandateReference {
    fn from(value: PaymentsMandateReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

#[cfg(feature = "v2")]
impl From<diesel_models::PaymentsTokenReference> for PaymentsTokenReference {
    fn from(value: diesel_models::PaymentsTokenReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

#[cfg(feature = "v2")]
impl From<PaymentsTokenReference> for diesel_models::PaymentsTokenReference {
    fn from(value: PaymentsTokenReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

impl From<diesel_models::PayoutsMandateReferenceRecord> for PayoutsMandateReferenceRecord {
    fn from(value: diesel_models::PayoutsMandateReferenceRecord) -> Self {
        Self {
            transfer_method_id: value.transfer_method_id,
            connector_customer_id: value.connector_customer_id,
        }
    }
}

impl From<PayoutsMandateReferenceRecord> for diesel_models::PayoutsMandateReferenceRecord {
    fn from(value: PayoutsMandateReferenceRecord) -> Self {
        Self {
            transfer_method_id: value.transfer_method_id,
            connector_customer_id: value.connector_customer_id,
        }
    }
}

#[cfg(feature = "v2")]
impl From<diesel_models::ConnectorTokenReferenceRecord> for ConnectorTokenReferenceRecord {
    fn from(value: diesel_models::ConnectorTokenReferenceRecord) -> Self {
        let diesel_models::ConnectorTokenReferenceRecord {
            connector_token,
            payment_method_subtype,
            original_payment_authorized_amount,
            original_payment_authorized_currency,
            metadata,
            connector_token_status,
            connector_token_request_reference_id,
        } = value;
        Self {
            connector_token,
            payment_method_subtype,
            original_payment_authorized_amount,
            original_payment_authorized_currency,
            metadata,
            connector_token_status,
            connector_token_request_reference_id,
        }
    }
}

#[cfg(feature = "v1")]
impl From<diesel_models::PaymentsMandateReferenceRecord> for PaymentsMandateReferenceRecord {
    fn from(value: diesel_models::PaymentsMandateReferenceRecord) -> Self {
        Self {
            connector_mandate_id: value.connector_mandate_id,
            payment_method_type: value.payment_method_type,
            original_payment_authorized_amount: value.original_payment_authorized_amount,
            original_payment_authorized_currency: value.original_payment_authorized_currency,
            mandate_metadata: value.mandate_metadata,
            connector_mandate_status: value.connector_mandate_status,
            connector_mandate_request_reference_id: value.connector_mandate_request_reference_id,
            connector_customer_id: value.connector_customer_id,
        }
    }
}

#[cfg(feature = "v2")]
impl From<ConnectorTokenReferenceRecord> for diesel_models::ConnectorTokenReferenceRecord {
    fn from(value: ConnectorTokenReferenceRecord) -> Self {
        let ConnectorTokenReferenceRecord {
            connector_token,
            payment_method_subtype,
            original_payment_authorized_amount,
            original_payment_authorized_currency,
            metadata,
            connector_token_status,
            connector_token_request_reference_id,
        } = value;
        Self {
            connector_token,
            payment_method_subtype,
            original_payment_authorized_amount,
            original_payment_authorized_currency,
            metadata,
            connector_token_status,
            connector_token_request_reference_id,
        }
    }
}

#[cfg(feature = "v1")]
impl From<PaymentsMandateReferenceRecord> for diesel_models::PaymentsMandateReferenceRecord {
    fn from(value: PaymentsMandateReferenceRecord) -> Self {
        Self {
            connector_mandate_id: value.connector_mandate_id,
            payment_method_type: value.payment_method_type,
            original_payment_authorized_amount: value.original_payment_authorized_amount,
            original_payment_authorized_currency: value.original_payment_authorized_currency,
            mandate_metadata: value.mandate_metadata,
            connector_mandate_status: value.connector_mandate_status,
            connector_mandate_request_reference_id: value.connector_mandate_request_reference_id,
            connector_customer_id: value.connector_customer_id,
        }
    }
}


// File: crates/hyperswitch_domain_models/src/behaviour.rs

use common_utils::{
    errors::{CustomResult, ValidationError},
    types::keymanager::{Identifier, KeyManagerState},
};
use masking::Secret;

/// Trait for converting domain types to storage models
#[async_trait::async_trait]
pub trait Conversion {
    type DstType;
    type NewDstType;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError>;

    async fn convert_back(
        state: &KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized;

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError>;
}

#[async_trait::async_trait]
pub trait ReverseConversion<SrcType: Conversion> {
    async fn convert(
        self,
        state: &KeyManagerState,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: Identifier,
    ) -> CustomResult<SrcType, ValidationError>;
}

#[async_trait::async_trait]
impl<T: Send, U: Conversion<DstType = T>> ReverseConversion<U> for T {
    async fn convert(
        self,
        state: &KeyManagerState,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: Identifier,
    ) -> CustomResult<U, ValidationError> {
        U::convert_back(state, self, key, key_manager_identifier).await
    }
}


// File: crates/hyperswitch_domain_models/src/api.rs

use std::{collections::HashSet, fmt::Display};

use common_utils::{
    events::{ApiEventMetric, ApiEventsType},
    impl_api_event_type,
};

use super::payment_method_data::PaymentMethodData;

#[derive(Debug, PartialEq)]
pub enum ApplicationResponse<R> {
    Json(R),
    StatusOk,
    TextPlain(String),
    JsonForRedirection(api_models::payments::RedirectionResponse),
    Form(Box<RedirectionFormData>),
    PaymentLinkForm(Box<PaymentLinkAction>),
    FileData((Vec<u8>, mime::Mime)),
    JsonWithHeaders((R, Vec<(String, masking::Maskable<String>)>)),
    GenericLinkForm(Box<GenericLinks>),
}

impl<R> ApplicationResponse<R> {
    /// Get the json response from response
    #[inline]
    pub fn get_json_body(
        self,
    ) -> common_utils::errors::CustomResult<R, common_utils::errors::ValidationError> {
        match self {
            Self::Json(body) | Self::JsonWithHeaders((body, _)) => Ok(body),
            Self::TextPlain(_)
            | Self::JsonForRedirection(_)
            | Self::Form(_)
            | Self::PaymentLinkForm(_)
            | Self::FileData(_)
            | Self::GenericLinkForm(_)
            | Self::StatusOk => Err(common_utils::errors::ValidationError::InvalidValue {
                message: "expected either Json or JsonWithHeaders Response".to_string(),
            }
            .into()),
        }
    }
}

impl<T: ApiEventMetric> ApiEventMetric for ApplicationResponse<T> {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        match self {
            Self::Json(r) => r.get_api_event_type(),
            Self::JsonWithHeaders((r, _)) => r.get_api_event_type(),
            _ => None,
        }
    }
}

impl_api_event_type!(Miscellaneous, (GenericLinkFormData));

#[derive(Debug, PartialEq)]
pub struct RedirectionFormData {
    pub redirect_form: crate::router_response_types::RedirectForm,
    pub payment_method_data: Option<PaymentMethodData>,
    pub amount: String,
    pub currency: String,
}

#[derive(Debug, Eq, PartialEq)]
pub enum PaymentLinkAction {
    PaymentLinkFormData(payment_link::PaymentLinkFormData),
    PaymentLinkStatus(payment_link::PaymentLinkStatusData),
}

#[derive(Debug, Eq, PartialEq)]
pub struct GenericLinks {
    pub allowed_domains: HashSet<String>,
    pub data: GenericLinksData,
    pub locale: String,
}

#[derive(Debug, Eq, PartialEq)]
pub enum GenericLinksData {
    ExpiredLink(GenericExpiredLinkData),
    PaymentMethodCollect(GenericLinkFormData),
    PayoutLink(GenericLinkFormData),
    PayoutLinkStatus(GenericLinkStatusData),
    PaymentMethodCollectStatus(GenericLinkStatusData),
    SecurePaymentLink(payment_link::PaymentLinkFormData),
}

impl Display for GenericLinksData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match *self {
                Self::ExpiredLink(_) => "ExpiredLink",
                Self::PaymentMethodCollect(_) => "PaymentMethodCollect",
                Self::PayoutLink(_) => "PayoutLink",
                Self::PayoutLinkStatus(_) => "PayoutLinkStatus",
                Self::PaymentMethodCollectStatus(_) => "PaymentMethodCollectStatus",
                Self::SecurePaymentLink(_) => "SecurePaymentLink",
            }
        )
    }
}

#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
pub struct GenericExpiredLinkData {
    pub title: String,
    pub message: String,
    pub theme: String,
}

#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
pub struct GenericLinkFormData {
    pub js_data: String,
    pub css_data: String,
    pub sdk_url: url::Url,
    pub html_meta_tags: String,
}

#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
pub struct GenericLinkStatusData {
    pub js_data: String,
    pub css_data: String,
}


// File: crates/hyperswitch_domain_models/src/platform.rs

pub use crate::{merchant_account::MerchantAccount, merchant_key_store::MerchantKeyStore};

/// Provider = The business owner or the governing entity in the hierarchy.
/// In a platform-connected setup this is represented by the platform merchant.
/// For a standard merchant, both provider and processor are represented by the same entity.
#[derive(Clone, Debug)]
pub struct Provider {
    account: MerchantAccount,
    key_store: MerchantKeyStore,
}

impl Provider {
    fn new(account: MerchantAccount, key_store: MerchantKeyStore) -> Self {
        Self { account, key_store }
    }

    /// Returns a reference to the merchant account of the provider.
    pub fn get_account(&self) -> &MerchantAccount {
        &self.account
    }

    /// Returns a reference to the key store associated with the provider.
    pub fn get_key_store(&self) -> &MerchantKeyStore {
        &self.key_store
    }
}

/// Processor = The merchant account whose processor credentials are used
/// to execute the operation.
#[derive(Clone, Debug)]
pub struct Processor {
    account: MerchantAccount,
    key_store: MerchantKeyStore,
}

impl Processor {
    fn new(account: MerchantAccount, key_store: MerchantKeyStore) -> Self {
        Self { account, key_store }
    }

    /// Returns a reference to the merchant account of the processor.
    pub fn get_account(&self) -> &MerchantAccount {
        &self.account
    }

    /// Returns a reference to the key store associated with the processor.
    pub fn get_key_store(&self) -> &MerchantKeyStore {
        &self.key_store
    }
}

/// Platform holds both Provider and Processor together.
/// This struct makes it possible to distinguish the business owner for the org versus whose processor credentials are used for execution.
/// For a standard merchant flow, provider == processor.
#[derive(Clone, Debug)]
pub struct Platform {
    provider: Box<Provider>,
    processor: Box<Processor>,
}

impl Platform {
    /// Creates a Platform pairing from two merchant identities:
    /// one acting as provider and one as processor
    /// Standard merchants can pass the same account/key_store for both provider and processor
    pub fn new(
        provider_account: MerchantAccount,
        provider_key_store: MerchantKeyStore,
        processor_account: MerchantAccount,
        processor_key_store: MerchantKeyStore,
    ) -> Self {
        let provider = Provider::new(provider_account, provider_key_store);
        let processor = Processor::new(processor_account, processor_key_store);
        Self {
            provider: Box::new(provider),
            processor: Box::new(processor),
        }
    }

    /// Returns a reference to the provider.
    pub fn get_provider(&self) -> &Provider {
        &self.provider
    }

    /// Returns a reference to the processor.
    pub fn get_processor(&self) -> &Processor {
        &self.processor
    }
}


// File: crates/hyperswitch_domain_models/src/relay.rs

use common_enums::enums;
use common_utils::{
    self,
    errors::{CustomResult, ValidationError},
    id_type::{self, GenerateId},
    pii,
    types::{keymanager, MinorUnit},
};
use diesel_models::relay::RelayUpdateInternal;
use error_stack::ResultExt;
use masking::{ExposeInterface, Secret};
use serde::{self, Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{router_data::ErrorResponse, router_response_types};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Relay {
    pub id: id_type::RelayId,
    pub connector_resource_id: String,
    pub connector_id: id_type::MerchantConnectorAccountId,
    pub profile_id: id_type::ProfileId,
    pub merchant_id: id_type::MerchantId,
    pub relay_type: enums::RelayType,
    pub request_data: Option<RelayData>,
    pub status: enums::RelayStatus,
    pub connector_reference_id: Option<String>,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    pub response_data: Option<pii::SecretSerdeValue>,
}

impl Relay {
    pub fn new(
        relay_request: &api_models::relay::RelayRequest,
        merchant_id: &id_type::MerchantId,
        profile_id: &id_type::ProfileId,
    ) -> Self {
        let relay_id = id_type::RelayId::generate();
        Self {
            id: relay_id.clone(),
            connector_resource_id: relay_request.connector_resource_id.clone(),
            connector_id: relay_request.connector_id.clone(),
            profile_id: profile_id.clone(),
            merchant_id: merchant_id.clone(),
            relay_type: common_enums::RelayType::Refund,
            request_data: relay_request.data.clone().map(From::from),
            status: common_enums::RelayStatus::Created,
            connector_reference_id: None,
            error_code: None,
            error_message: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            response_data: None,
        }
    }
}

impl From<api_models::relay::RelayData> for RelayData {
    fn from(relay: api_models::relay::RelayData) -> Self {
        match relay {
            api_models::relay::RelayData::Refund(relay_refund_request) => {
                Self::Refund(RelayRefundData {
                    amount: relay_refund_request.amount,
                    currency: relay_refund_request.currency,
                    reason: relay_refund_request.reason,
                })
            }
        }
    }
}

impl From<api_models::relay::RelayRefundRequestData> for RelayRefundData {
    fn from(relay: api_models::relay::RelayRefundRequestData) -> Self {
        Self {
            amount: relay.amount,
            currency: relay.currency,
            reason: relay.reason,
        }
    }
}

impl RelayUpdate {
    pub fn from(
        response: Result<router_response_types::RefundsResponseData, ErrorResponse>,
    ) -> Self {
        match response {
            Err(error) => Self::ErrorUpdate {
                error_code: error.code,
                error_message: error.reason.unwrap_or(error.message),
                status: common_enums::RelayStatus::Failure,
            },
            Ok(response) => Self::StatusUpdate {
                connector_reference_id: Some(response.connector_refund_id),
                status: common_enums::RelayStatus::from(response.refund_status),
            },
        }
    }
}

impl From<RelayData> for api_models::relay::RelayData {
    fn from(relay: RelayData) -> Self {
        match relay {
            RelayData::Refund(relay_refund_request) => {
                Self::Refund(api_models::relay::RelayRefundRequestData {
                    amount: relay_refund_request.amount,
                    currency: relay_refund_request.currency,
                    reason: relay_refund_request.reason,
                })
            }
        }
    }
}

impl From<Relay> for api_models::relay::RelayResponse {
    fn from(value: Relay) -> Self {
        let error = value
            .error_code
            .zip(value.error_message)
            .map(
                |(error_code, error_message)| api_models::relay::RelayError {
                    code: error_code,
                    message: error_message,
                },
            );

        let data = value.request_data.map(|relay_data| match relay_data {
            RelayData::Refund(relay_refund_request) => {
                api_models::relay::RelayData::Refund(api_models::relay::RelayRefundRequestData {
                    amount: relay_refund_request.amount,
                    currency: relay_refund_request.currency,
                    reason: relay_refund_request.reason,
                })
            }
        });
        Self {
            id: value.id,
            status: value.status,
            error,
            connector_resource_id: value.connector_resource_id,
            connector_id: value.connector_id,
            profile_id: value.profile_id,
            relay_type: value.relay_type,
            data,
            connector_reference_id: value.connector_reference_id,
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case", untagged)]
pub enum RelayData {
    Refund(RelayRefundData),
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RelayRefundData {
    pub amount: MinorUnit,
    pub currency: enums::Currency,
    pub reason: Option<String>,
}

#[derive(Debug)]
pub enum RelayUpdate {
    ErrorUpdate {
        error_code: String,
        error_message: String,
        status: enums::RelayStatus,
    },
    StatusUpdate {
        connector_reference_id: Option<String>,
        status: common_enums::RelayStatus,
    },
}

impl From<RelayUpdate> for RelayUpdateInternal {
    fn from(value: RelayUpdate) -> Self {
        match value {
            RelayUpdate::ErrorUpdate {
                error_code,
                error_message,
                status,
            } => Self {
                error_code: Some(error_code),
                error_message: Some(error_message),
                connector_reference_id: None,
                status: Some(status),
                modified_at: common_utils::date_time::now(),
            },
            RelayUpdate::StatusUpdate {
                connector_reference_id,
                status,
            } => Self {
                connector_reference_id,
                status: Some(status),
                error_code: None,
                error_message: None,
                modified_at: common_utils::date_time::now(),
            },
        }
    }
}

#[async_trait::async_trait]
impl super::behaviour::Conversion for Relay {
    type DstType = diesel_models::relay::Relay;
    type NewDstType = diesel_models::relay::RelayNew;

    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(diesel_models::relay::Relay {
            id: self.id,
            connector_resource_id: self.connector_resource_id,
            connector_id: self.connector_id,
            profile_id: self.profile_id,
            merchant_id: self.merchant_id,
            relay_type: self.relay_type,
            request_data: self
                .request_data
                .map(|data| {
                    serde_json::to_value(data).change_context(ValidationError::InvalidValue {
                        message: "Failed while decrypting business profile data".to_string(),
                    })
                })
                .transpose()?
                .map(Secret::new),
            status: self.status,
            connector_reference_id: self.connector_reference_id,
            error_code: self.error_code,
            error_message: self.error_message,
            created_at: self.created_at,
            modified_at: self.modified_at,
            response_data: self.response_data,
        })
    }

    async fn convert_back(
        _state: &keymanager::KeyManagerState,
        item: Self::DstType,
        _key: &Secret<Vec<u8>>,
        _key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError> {
        Ok(Self {
            id: item.id,
            connector_resource_id: item.connector_resource_id,
            connector_id: item.connector_id,
            profile_id: item.profile_id,
            merchant_id: item.merchant_id,
            relay_type: enums::RelayType::Refund,
            request_data: item
                .request_data
                .map(|data| {
                    serde_json::from_value(data.expose()).change_context(
                        ValidationError::InvalidValue {
                            message: "Failed while decrypting business profile data".to_string(),
                        },
                    )
                })
                .transpose()?,
            status: item.status,
            connector_reference_id: item.connector_reference_id,
            error_code: item.error_code,
            error_message: item.error_message,
            created_at: item.created_at,
            modified_at: item.modified_at,
            response_data: item.response_data,
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(diesel_models::relay::RelayNew {
            id: self.id,
            connector_resource_id: self.connector_resource_id,
            connector_id: self.connector_id,
            profile_id: self.profile_id,
            merchant_id: self.merchant_id,
            relay_type: self.relay_type,
            request_data: self
                .request_data
                .map(|data| {
                    serde_json::to_value(data).change_context(ValidationError::InvalidValue {
                        message: "Failed while decrypting business profile data".to_string(),
                    })
                })
                .transpose()?
                .map(Secret::new),
            status: self.status,
            connector_reference_id: self.connector_reference_id,
            error_code: self.error_code,
            error_message: self.error_message,
            created_at: self.created_at,
            modified_at: self.modified_at,
            response_data: self.response_data,
        })
    }
}


// File: crates/hyperswitch_domain_models/src/vault.rs

use api_models::payment_methods;
use serde::{Deserialize, Serialize};

#[cfg(feature = "v2")]
use crate::errors;
use crate::payment_method_data;

#[derive(Debug, Deserialize, Serialize, Clone)]
pub enum PaymentMethodVaultingData {
    Card(payment_methods::CardDetail),
    NetworkToken(payment_method_data::NetworkTokenDetails),
    CardNumber(cards::CardNumber),
}

impl PaymentMethodVaultingData {
    pub fn get_card(&self) -> Option<&payment_methods::CardDetail> {
        match self {
            Self::Card(card) => Some(card),
            Self::NetworkToken(_) => None,
            Self::CardNumber(_) => None,
        }
    }
    pub fn get_payment_methods_data(&self) -> payment_method_data::PaymentMethodsData {
        match self {
            Self::Card(card) => payment_method_data::PaymentMethodsData::Card(
                payment_method_data::CardDetailsPaymentMethod::from(card.clone()),
            ),
            Self::NetworkToken(network_token) => {
                payment_method_data::PaymentMethodsData::NetworkToken(
                    payment_method_data::NetworkTokenDetailsPaymentMethod::from(
                        network_token.clone(),
                    ),
                )
            }
            Self::CardNumber(_card_number) => payment_method_data::PaymentMethodsData::Card(
                payment_method_data::CardDetailsPaymentMethod {
                    last4_digits: None,
                    issuer_country: None,
                    expiry_month: None,
                    expiry_year: None,
                    nick_name: None,
                    card_holder_name: None,
                    card_isin: None,
                    card_issuer: None,
                    card_network: None,
                    card_type: None,
                    saved_to_locker: false,
                    #[cfg(feature = "v1")]
                    co_badged_card_data: None,
                },
            ),
        }
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub enum PaymentMethodCustomVaultingData {
    CardData(CardCustomData),
    NetworkTokenData(NetworkTokenCustomData),
}

impl Default for PaymentMethodCustomVaultingData {
    fn default() -> Self {
        Self::CardData(CardCustomData::default())
    }
}

#[derive(Debug, Default, Deserialize, Serialize, Clone)]
pub struct CardCustomData {
    pub card_number: Option<cards::CardNumber>,
    pub card_exp_month: Option<masking::Secret<String>>,
    pub card_exp_year: Option<masking::Secret<String>>,
    pub card_cvc: Option<masking::Secret<String>>,
}

#[derive(Debug, Default, Deserialize, Serialize, Clone)]
pub struct NetworkTokenCustomData {
    pub network_token: Option<cards::NetworkToken>,
    pub network_token_exp_month: Option<masking::Secret<String>>,
    pub network_token_exp_year: Option<masking::Secret<String>>,
    pub cryptogram: Option<masking::Secret<String>>,
}

pub trait VaultingDataInterface {
    fn get_vaulting_data_key(&self) -> String;
}

impl VaultingDataInterface for PaymentMethodVaultingData {
    fn get_vaulting_data_key(&self) -> String {
        match &self {
            Self::Card(card) => card.card_number.to_string(),
            Self::NetworkToken(network_token) => network_token.network_token.to_string(),
            Self::CardNumber(card_number) => card_number.to_string(),
        }
    }
}

#[cfg(feature = "v2")]
impl TryFrom<payment_methods::PaymentMethodCreateData> for PaymentMethodVaultingData {
    type Error = error_stack::Report<errors::api_error_response::ApiErrorResponse>;
    fn try_from(item: payment_methods::PaymentMethodCreateData) -> Result<Self, Self::Error> {
        match item {
            payment_methods::PaymentMethodCreateData::Card(card) => Ok(Self::Card(card)),
            payment_methods::PaymentMethodCreateData::ProxyCard(card) => Err(
                errors::api_error_response::ApiErrorResponse::UnprocessableEntity {
                    message: "Proxy Card for PaymentMethodCreateData".to_string(),
                }
                .into(),
            ),
        }
    }
}

impl From<PaymentMethodVaultingData> for PaymentMethodCustomVaultingData {
    fn from(item: PaymentMethodVaultingData) -> Self {
        match item {
            PaymentMethodVaultingData::Card(card_data) => Self::CardData(CardCustomData {
                card_number: Some(card_data.card_number),
                card_exp_month: Some(card_data.card_exp_month),
                card_exp_year: Some(card_data.card_exp_year),
                card_cvc: card_data.card_cvc,
            }),
            PaymentMethodVaultingData::NetworkToken(network_token_data) => {
                Self::NetworkTokenData(NetworkTokenCustomData {
                    network_token: Some(network_token_data.network_token),
                    network_token_exp_month: Some(network_token_data.network_token_exp_month),
                    network_token_exp_year: Some(network_token_data.network_token_exp_year),
                    cryptogram: network_token_data.cryptogram,
                })
            }
            PaymentMethodVaultingData::CardNumber(card_number_data) => {
                Self::CardData(CardCustomData {
                    card_number: Some(card_number_data),
                    card_exp_month: None,
                    card_exp_year: None,
                    card_cvc: None,
                })
            }
        }
    }
}


// File: crates/hyperswitch_domain_models/src/master_key.rs

pub trait MasterKeyInterface {
    fn get_master_key(&self) -> &[u8];
}


// File: crates/hyperswitch_domain_models/src/chat.rs

use common_utils::id_type;
use masking::Secret;

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct GetDataMessage {
    pub message: Secret<String>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct HyperswitchAiDataRequest {
    pub merchant_id: id_type::MerchantId,
    pub profile_id: id_type::ProfileId,
    pub org_id: id_type::OrganizationId,
    pub query: GetDataMessage,
    pub entity_type: common_enums::EntityType,
}