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},
    id_type, pii,
    types::MinorUnit,
};
use error_stack::ResultExt;
use hyperswitch_masking::Secret;
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 MandateDataType {
    /// Convert to the API `MandateData` wrapper (used in `intent_data.mandate_payment`).
    pub fn to_api_mandate_data(&self) -> ApiMandateData {
        match self {
            Self::SingleUse(single_use_mandate_data) => ApiMandateData {
                mandate_type: Some(MandateType::SingleUse(ApiMandateAmountData {
                    amount: single_use_mandate_data.amount,
                    currency: single_use_mandate_data.currency,
                    start_date: single_use_mandate_data.start_date,
                    end_date: single_use_mandate_data.end_date,
                    metadata: single_use_mandate_data.metadata.clone(),
                })),
                customer_acceptance: None,
                update_mandate_id: None,
            },
            Self::MultiUse(Some(multi_use_mandate_data)) => ApiMandateData {
                mandate_type: Some(MandateType::MultiUse(Some(ApiMandateAmountData {
                    amount: multi_use_mandate_data.amount,
                    currency: multi_use_mandate_data.currency,
                    start_date: multi_use_mandate_data.start_date,
                    end_date: multi_use_mandate_data.end_date,
                    metadata: multi_use_mandate_data.metadata.clone(),
                }))),
                customer_acceptance: None,
                update_mandate_id: None,
            },
            Self::MultiUse(None) => ApiMandateData {
                mandate_type: Some(MandateType::MultiUse(None)),
                customer_acceptance: None,
                update_mandate_id: None,
            },
        }
    }

    /// Convert to the bare API `MandateType` (used in top-level response `mandate_payment` field).
    pub fn to_api_mandate_type(&self) -> MandateType {
        match self {
            Self::SingleUse(single_use_mandate_data) => {
                MandateType::SingleUse(ApiMandateAmountData {
                    amount: single_use_mandate_data.amount,
                    currency: single_use_mandate_data.currency,
                    start_date: single_use_mandate_data.start_date,
                    end_date: single_use_mandate_data.end_date,
                    metadata: single_use_mandate_data.metadata.clone(),
                })
            }
            Self::MultiUse(Some(multi_use_mandate_data)) => {
                MandateType::MultiUse(Some(ApiMandateAmountData {
                    amount: multi_use_mandate_data.amount,
                    currency: multi_use_mandate_data.currency,
                    start_date: multi_use_mandate_data.start_date,
                    end_date: multi_use_mandate_data.end_date,
                    metadata: multi_use_mandate_data.metadata.clone(),
                }))
            }
            Self::MultiUse(None) => MandateType::MultiUse(None),
        }
    }
}

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(),
        }
    }
}

#[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>,
    pub connector_customer_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<id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>,
);

impl std::ops::Deref for PayoutsMandateReference {
    type Target = HashMap<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
    }
}

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

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

#[cfg(feature = "v1")]
impl PaymentsMandateReference {
    pub fn is_active_connector_mandate_available(&self) -> bool {
        self.clone().0.into_iter().any(|detail| {
            detail
                .1
                .connector_mandate_status
                .map(|connector_mandate_status| connector_mandate_status.is_active())
                .unwrap_or(false)
        })
    }
}

#[cfg(feature = "v1")]
impl std::ops::Deref for PaymentsMandateReference {
    type Target = HashMap<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
    }
}

impl std::ops::Deref for PaymentsTokenReference {
    type Target = HashMap<id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>;

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

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: &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,
            connector_customer_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,
            connector_customer_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,
            connector_customer_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,
            connector_customer_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,
        }
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq, Copy)]
#[serde(rename_all = "snake_case")]
pub enum MandateTransactionType {
    NewMandateTransaction,
    RecurringMandateTransaction,
}

#[derive(Default, Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct MandateIds {
    pub mandate_id: Option<String>,
    pub mandate_reference_id: Option<MandateReferenceId>,
}

impl MandateIds {
    pub fn is_network_transaction_id_flow(&self) -> bool {
        matches!(
            self.mandate_reference_id,
            Some(MandateReferenceId::NetworkMandateId(_))
        )
    }

    pub fn get_connector_mandate_id(&self) -> Option<String> {
        match &self.mandate_reference_id {
            Some(MandateReferenceId::ConnectorMandateId(data)) => data.connector_mandate_id.clone(),
            _ => None,
        }
    }

    pub fn get_connector_mandate_metadata(&self) -> Option<pii::SecretSerdeValue> {
        match &self.mandate_reference_id {
            Some(MandateReferenceId::ConnectorMandateId(data)) => data.mandate_metadata.clone(),
            _ => None,
        }
    }

    pub fn get_updated_mandate_details_of_connector_mandate_id(
        &self,
    ) -> Option<UpdatedMandateDetails> {
        match &self.mandate_reference_id {
            Some(MandateReferenceId::ConnectorMandateId(data)) => {
                data.updated_mandate_details.clone()
            }
            _ => None,
        }
    }
}

impl MandateIds {
    pub fn new(mandate_id: String) -> Self {
        Self {
            mandate_id: Some(mandate_id),
            mandate_reference_id: None,
        }
    }
}

#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)]
pub enum MandateReferenceId {
    ConnectorMandateId(ConnectorMandateReferenceId), // mandate_id send by connector
    NetworkMandateId(NetworkMandateIdRef), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns along with card data
    NetworkTokenWithNTI(NetworkTokenWithNTIRef), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns along with network token data
    CardWithLimitedData(CardWithLimitedDataRef), // indicates the recurring transaction is done by card data only
}

impl MandateReferenceId {
    pub fn is_network_mandate_id(&self) -> bool {
        matches!(self, Self::NetworkMandateId(_))
    }
}

/// Scheme-level identifiers for PSP-agnostic MIT flows (raw card path).
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)]
pub struct NetworkMandateIdRef {
    pub network_transaction_id: String,
    /// The Mastercard Transaction Link Identifier (TLID) provided by the card network during a CIT (Customer Initiated Transaction),
    /// when `setup_future_usage` is set to `off_session`.
    pub transaction_link_id: Option<String>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)]
pub struct NetworkTokenWithNTIRef {
    pub network_transaction_id: String,
    /// The Mastercard Transaction Link Identifier (TLID) provided by the card network during a CIT (Customer Initiated Transaction),
    /// when `setup_future_usage` is set to `off_session`.
    pub transaction_link_id: Option<String>,
    pub token_exp_month: Option<Secret<String>>,
    pub token_exp_year: Option<Secret<String>>,
}

#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CardWithLimitedDataRef {
    pub network_transaction_id: Option<String>,
    /// The Mastercard Transaction Link Identifier (TLID) provided by the card network during a CIT (Customer Initiated Transaction),
    /// when `setup_future_usage` is set to `off_session`.
    pub transaction_link_id: Option<String>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)]
pub struct ConnectorMandateReferenceId {
    connector_mandate_id: Option<String>,
    payment_method_id: Option<String>,
    update_history: Option<Vec<UpdateHistory>>,
    mandate_metadata: Option<pii::SecretSerdeValue>,
    connector_mandate_request_reference_id: Option<String>,
    updated_mandate_details: Option<UpdatedMandateDetails>,
}

impl ConnectorMandateReferenceId {
    pub fn new(
        connector_mandate_id: Option<String>,
        payment_method_id: Option<String>,
        update_history: Option<Vec<UpdateHistory>>,
        mandate_metadata: Option<pii::SecretSerdeValue>,
        connector_mandate_request_reference_id: Option<String>,
        updated_mandate_details: Option<UpdatedMandateDetails>,
    ) -> Self {
        Self {
            connector_mandate_id,
            payment_method_id,
            update_history,
            mandate_metadata,
            connector_mandate_request_reference_id,
            updated_mandate_details,
        }
    }

    pub fn get_connector_mandate_id(&self) -> Option<String> {
        self.connector_mandate_id.clone()
    }
    pub fn get_payment_method_id(&self) -> Option<String> {
        self.payment_method_id.clone()
    }
    pub fn get_mandate_metadata(&self) -> Option<pii::SecretSerdeValue> {
        self.mandate_metadata.clone()
    }
    pub fn get_connector_mandate_request_reference_id(&self) -> Option<String> {
        self.connector_mandate_request_reference_id.clone()
    }

    pub fn update(
        &mut self,
        connector_mandate_id: Option<String>,
        payment_method_id: Option<String>,
        update_history: Option<Vec<UpdateHistory>>,
        mandate_metadata: Option<pii::SecretSerdeValue>,
        connector_mandate_request_reference_id: Option<String>,
    ) {
        self.connector_mandate_id = connector_mandate_id.or(self.connector_mandate_id.clone());
        self.payment_method_id = payment_method_id.or(self.payment_method_id.clone());
        self.update_history = update_history.or(self.update_history.clone());
        self.mandate_metadata = mandate_metadata.or(self.mandate_metadata.clone());
        self.connector_mandate_request_reference_id = connector_mandate_request_reference_id
            .or(self.connector_mandate_request_reference_id.clone());
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct UpdatedMandateDetails {
    pub card_network: Option<common_enums::CardNetwork>,
    pub card_exp_month: Option<Secret<String>>,
    pub card_exp_year: Option<Secret<String>>,
    pub card_isin: Option<String>,
}

impl From<api_models::payments::AdditionalCardInfo> for UpdatedMandateDetails {
    fn from(card_info: api_models::payments::AdditionalCardInfo) -> Self {
        Self {
            card_network: card_info.card_network,
            card_exp_month: card_info.card_exp_month,
            card_exp_year: card_info.card_exp_year,
            card_isin: card_info.card_isin,
        }
    }
}

impl From<&UpdatedMandateDetails> for api_models::payments::AdditionalCardInfo {
    fn from(card_info: &UpdatedMandateDetails) -> Self {
        Self {
            card_network: card_info.card_network.clone(),
            card_exp_month: card_info.card_exp_month.clone(),
            card_exp_year: card_info.card_exp_year.clone(),
            card_isin: card_info.card_isin.clone(),
            card_issuer: None,
            card_type: None,
            card_issuing_country: None,
            card_issuing_country_code: None,
            bank_code: None,
            last4: None,
            card_extended_bin: None,
            card_holder_name: None,
            payment_checks: None,
            authentication_data: None,
            is_regulated: None,
            signature_network: None,
            auth_code: None,
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct UpdateHistory {
    pub connector_mandate_id: Option<String>,
    pub payment_method_id: String,
    pub original_payment_id: Option<id_type::PaymentId>,
}
