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

pub mod behaviour {
    pub use hyperswitch_domain_models::behaviour::{Conversion, ReverseConversion};
}

mod payment_attempt {
    pub use hyperswitch_domain_models::payments::payment_attempt::*;
}

mod merchant_account {
    pub use hyperswitch_domain_models::merchant_account::*;
}

#[cfg(feature = "v2")]
mod business_profile {
    pub use hyperswitch_domain_models::business_profile::{
        Profile, ProfileGeneralUpdate, ProfileSetter, ProfileUpdate,
    };
}

#[cfg(feature = "v1")]
mod business_profile {
    pub use hyperswitch_domain_models::business_profile::{
        ExternalVaultDetails, Profile, ProfileGeneralUpdate, ProfileSetter, ProfileUpdate,
    };
}

mod platform {
    pub use hyperswitch_domain_models::platform::{Platform, Processor, Provider};
}
mod customers {
    pub use hyperswitch_domain_models::customer::*;
}

pub mod callback_mapper {
    pub use hyperswitch_domain_models::callback_mapper::CallbackMapper;
}

mod network_tokenization {
    pub use hyperswitch_domain_models::network_tokenization::*;
}

pub use customers::*;
pub use merchant_account::*;

mod address;
mod event;
mod merchant_connector_account;
mod merchant_key_store {
    pub use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore;
}
pub use hyperswitch_domain_models::bulk_tokenization::*;
pub mod payment_methods {
    pub use hyperswitch_domain_models::payment_methods::*;
}
pub mod consts {
    pub use hyperswitch_domain_models::consts::*;
}
pub mod payment_method_data {
    pub use hyperswitch_domain_models::payment_method_data::*;
}

pub mod authentication {
    pub use hyperswitch_domain_models::router_request_types::authentication::*;
}

#[cfg(feature = "v2")]
pub mod vault {
    pub use hyperswitch_domain_models::vault::*;
}

#[cfg(feature = "v2")]
pub mod tokenization {
    pub use hyperswitch_domain_models::tokenization::*;
}

mod routing {
    pub use hyperswitch_domain_models::routing::*;
}

pub mod payments;
pub mod types;
#[cfg(feature = "olap")]
pub mod user;
pub mod user_key_store;

pub use address::*;
pub use business_profile::*;
pub use callback_mapper::*;
pub use consts::*;
pub use event::*;
pub use merchant_connector_account::*;
pub use merchant_key_store::*;
pub use network_tokenization::*;
pub use payment_attempt::*;
pub use payment_method_data::*;
pub use payment_methods::*;
pub use platform::*;
pub use routing::*;
#[cfg(feature = "v2")]
pub use tokenization::*;
#[cfg(feature = "olap")]
pub use user::*;
pub use user_key_store::*;
#[cfg(feature = "v2")]
pub use vault::*;


// File: crates/router/src/types/authentication.rs

pub use hyperswitch_domain_models::{
    router_request_types::authentication::{
        AcquirerDetails, AuthNFlowType, ChallengeParams, ConnectorAuthenticationRequestData,
        ConnectorPostAuthenticationRequestData, PreAuthNRequestData, PreAuthenticationData,
    },
    router_response_types::AuthenticationResponseData,
};

use super::{api, RouterData};
use crate::services;

pub type PreAuthNRouterData =
    RouterData<api::PreAuthentication, PreAuthNRequestData, AuthenticationResponseData>;

pub type PreAuthNVersionCallRouterData =
    RouterData<api::PreAuthenticationVersionCall, PreAuthNRequestData, AuthenticationResponseData>;

pub type ConnectorAuthenticationRouterData =
    RouterData<api::Authentication, ConnectorAuthenticationRequestData, AuthenticationResponseData>;

pub type ConnectorPostAuthenticationRouterData = RouterData<
    api::PostAuthentication,
    ConnectorPostAuthenticationRequestData,
    AuthenticationResponseData,
>;

pub type ConnectorAuthenticationType = dyn services::ConnectorIntegration<
    api::Authentication,
    ConnectorAuthenticationRequestData,
    AuthenticationResponseData,
>;

pub type ConnectorPostAuthenticationType = dyn services::ConnectorIntegration<
    api::PostAuthentication,
    ConnectorPostAuthenticationRequestData,
    AuthenticationResponseData,
>;

pub type ConnectorPreAuthenticationType = dyn services::ConnectorIntegration<
    api::PreAuthentication,
    PreAuthNRequestData,
    AuthenticationResponseData,
>;

pub type ConnectorPreAuthenticationVersionCallType = dyn services::ConnectorIntegration<
    api::PreAuthenticationVersionCall,
    PreAuthNRequestData,
    AuthenticationResponseData,
>;


// File: crates/router/src/types/storage.rs

pub mod address;
pub mod api_keys;
pub mod authentication;
pub mod authorization;
pub mod blocklist;
pub mod blocklist_fingerprint;
pub mod blocklist_lookup;
pub mod business_profile;
pub mod callback_mapper;
pub mod capture;
pub mod cards_info;
pub mod configs;
pub mod customers;
pub mod dashboard_metadata;
pub mod dispute;
pub mod dynamic_routing_stats;
pub mod enums;
pub mod ephemeral_key;
pub mod events;
pub mod file;
pub mod fraud_check;
pub mod generic_link;
pub mod gsm;
pub mod hyperswitch_ai_interaction;
#[cfg(feature = "kv_store")]
pub mod kv;
pub mod locker_mock_up;
pub mod mandate;
pub mod merchant_account;
pub mod merchant_connector_account;
pub mod merchant_key_store;
pub mod payment_attempt;
pub mod payment_link;
pub mod payment_method;
pub mod payout_attempt;
pub mod payouts;
pub mod refund;
#[cfg(feature = "v2")]
pub mod revenue_recovery;
#[cfg(feature = "v2")]
pub mod revenue_recovery_redis_operation;
pub mod reverse_lookup;
pub mod role;
pub mod routing_algorithm;
pub mod unified_translations;
pub mod user;
pub mod user_authentication_method;
pub mod user_role;

pub use diesel_models::{
    process_tracker::business_status, ProcessTracker, ProcessTrackerNew, ProcessTrackerRunner,
    ProcessTrackerUpdate,
};
#[cfg(feature = "v1")]
pub use hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptNew;
#[cfg(feature = "payouts")]
pub use hyperswitch_domain_models::payouts::{
    payout_attempt::{PayoutAttempt, PayoutAttemptNew, PayoutAttemptUpdate},
    payouts::{Payouts, PayoutsNew, PayoutsUpdate},
};
pub use hyperswitch_domain_models::{
    payments::{
        payment_attempt::{PaymentAttempt, PaymentAttemptUpdate},
        payment_intent::{PaymentIntentUpdate, PaymentIntentUpdateFields},
        PaymentIntent,
    },
    routing::{
        PaymentRoutingInfo, PaymentRoutingInfoInner, PreRoutingConnectorChoice, RoutingData,
    },
};
pub use scheduler::db::process_tracker;

pub use self::{
    address::*, api_keys::*, authentication::*, authorization::*, blocklist::*,
    blocklist_fingerprint::*, blocklist_lookup::*, business_profile::*, callback_mapper::*,
    capture::*, cards_info::*, configs::*, customers::*, dashboard_metadata::*, dispute::*,
    dynamic_routing_stats::*, ephemeral_key::*, events::*, file::*, fraud_check::*,
    generic_link::*, gsm::*, hyperswitch_ai_interaction::*, locker_mock_up::*, mandate::*,
    merchant_account::*, merchant_connector_account::*, merchant_key_store::*, payment_link::*,
    payment_method::*, process_tracker::*, refund::*, reverse_lookup::*, role::*,
    routing_algorithm::*, unified_translations::*, user::*, user_authentication_method::*,
    user_role::*,
};


// File: crates/router/src/types/api.rs

pub mod admin;
pub mod api_keys;
pub mod authentication;
pub mod configs;
#[cfg(feature = "olap")]
pub mod connector_onboarding;
pub mod customers;
pub mod disputes;
pub mod enums;
pub mod ephemeral_key;
pub mod files;
#[cfg(feature = "frm")]
pub mod fraud_check;
pub mod mandates;
pub mod payment_link;
pub mod payment_methods;
pub mod payments;
#[cfg(feature = "payouts")]
pub mod payouts;
pub mod poll;
pub mod refunds;
pub mod routing;
#[cfg(feature = "olap")]
pub mod verify_connector;
#[cfg(feature = "olap")]
pub mod webhook_events;
pub mod webhooks;

pub mod authentication_v2;
pub mod connector_mapping;
pub mod disputes_v2;
pub mod feature_matrix;
pub mod files_v2;
#[cfg(feature = "frm")]
pub mod fraud_check_v2;
pub mod payments_v2;
#[cfg(feature = "payouts")]
pub mod payouts_v2;
pub mod refunds_v2;

use std::{fmt::Debug, str::FromStr};

use api_models::routing::{self as api_routing, RoutableConnectorChoice};
use common_enums::RoutableConnectors;
use error_stack::ResultExt;
pub use hyperswitch_domain_models::router_flow_types::{
    access_token_auth::{AccessTokenAuth, AccessTokenAuthentication},
    mandate_revoke::MandateRevoke,
    unified_authentication_service::*,
    webhooks::VerifyWebhookSource,
};
pub use hyperswitch_interfaces::{
    api::{
        authentication::{
            ConnectorAuthentication, ConnectorPostAuthentication, ConnectorPreAuthentication,
            ConnectorPreAuthenticationVersionCall, ExternalAuthentication,
        },
        authentication_v2::{
            ConnectorAuthenticationV2, ConnectorPostAuthenticationV2, ConnectorPreAuthenticationV2,
            ConnectorPreAuthenticationVersionCallV2, ExternalAuthenticationV2,
        },
        fraud_check::FraudCheck,
        revenue_recovery::{
            BillingConnectorInvoiceSyncIntegration, BillingConnectorPaymentsSyncIntegration,
            RevenueRecovery, RevenueRecoveryRecordBack,
        },
        revenue_recovery_v2::RevenueRecoveryV2,
        BoxedConnector, Connector, ConnectorAccessToken, ConnectorAccessTokenV2,
        ConnectorAuthenticationToken, ConnectorAuthenticationTokenV2, ConnectorCommon,
        ConnectorCommonExt, ConnectorMandateRevoke, ConnectorMandateRevokeV2,
        ConnectorTransactionId, ConnectorVerifyWebhookSource, ConnectorVerifyWebhookSourceV2,
        CurrencyUnit,
    },
    connector_integration_v2::{BoxedConnectorV2, ConnectorV2},
};
use rustc_hash::FxHashMap;

#[cfg(feature = "frm")]
pub use self::fraud_check::*;
#[cfg(feature = "payouts")]
pub use self::payouts::*;
pub use self::{
    admin::*, api_keys::*, authentication::*, configs::*, connector_mapping::*, customers::*,
    disputes::*, files::*, payment_link::*, payment_methods::*, payments::*, poll::*, refunds::*,
    refunds_v2::*, webhooks::*,
};
use super::transformers::ForeignTryFrom;
use crate::{
    connector, consts,
    core::{
        errors::{self, CustomResult},
        payments::types as payments_types,
    },
    services::connector_integration_interface::ConnectorEnum,
    types::{self, api::enums as api_enums},
};
#[derive(Clone)]
pub enum ConnectorCallType {
    PreDetermined(ConnectorRoutingData),
    Retryable(Vec<ConnectorRoutingData>),
    SessionMultiple(SessionConnectorDatas),
    #[cfg(feature = "v2")]
    Skip,
}

impl From<ConnectorData> for ConnectorRoutingData {
    fn from(connector_data: ConnectorData) -> Self {
        Self {
            connector_data,
            network: None,
            action_type: None,
        }
    }
}

#[derive(Clone, Debug)]
pub struct SessionConnectorData {
    pub payment_method_sub_type: api_enums::PaymentMethodType,
    pub payment_method_type: api_enums::PaymentMethod,
    pub connector: ConnectorData,
    pub business_sub_label: Option<String>,
}

impl SessionConnectorData {
    pub fn new(
        payment_method_sub_type: api_enums::PaymentMethodType,
        connector: ConnectorData,
        business_sub_label: Option<String>,
        payment_method_type: api_enums::PaymentMethod,
    ) -> Self {
        Self {
            payment_method_sub_type,
            connector,
            business_sub_label,
            payment_method_type,
        }
    }
}

common_utils::create_list_wrapper!(
    SessionConnectorDatas,
    SessionConnectorData,
    impl_functions: {
        pub fn apply_filter_for_session_routing(&self) -> Self {
            let routing_enabled_pmts = &consts::ROUTING_ENABLED_PAYMENT_METHOD_TYPES;
            let routing_enabled_pms = &consts::ROUTING_ENABLED_PAYMENT_METHODS;
            self
                .iter()
                .filter(|connector_data| {
                    routing_enabled_pmts.contains(&connector_data.payment_method_sub_type)
                        || routing_enabled_pms.contains(&connector_data.payment_method_type)
                })
                .cloned()
                .collect()
        }
        pub fn filter_and_validate_for_session_flow(self, routing_results: &FxHashMap<api_enums::PaymentMethodType, Vec<routing::SessionRoutingChoice>>) -> Result<Self, errors::ApiErrorResponse> {
            let mut final_list = Self::new(Vec::new());
            let routing_enabled_pmts = &consts::ROUTING_ENABLED_PAYMENT_METHOD_TYPES;
            for connector_data in self {
                if !routing_enabled_pmts.contains(&connector_data.payment_method_sub_type) {
                    final_list.push(connector_data);
                } else if let Some(choice) = routing_results.get(&connector_data.payment_method_sub_type) {
                    let routing_choice = choice
                        .first()
                        .ok_or(errors::ApiErrorResponse::InternalServerError)?;
                    if connector_data.connector.connector_name == routing_choice.connector.connector_name
                        && connector_data.connector.merchant_connector_id
                            == routing_choice.connector.merchant_connector_id
                    {
                        final_list.push(connector_data);
                    }
                }
            }
            Ok(final_list)
        }
    }
);

pub fn convert_connector_data_to_routable_connectors(
    connectors: &[ConnectorRoutingData],
) -> CustomResult<Vec<RoutableConnectorChoice>, common_utils::errors::ValidationError> {
    connectors
        .iter()
        .map(|connectors_routing_data| {
            RoutableConnectorChoice::foreign_try_from(
                connectors_routing_data.connector_data.clone(),
            )
        })
        .collect()
}

impl ForeignTryFrom<ConnectorData> for RoutableConnectorChoice {
    type Error = error_stack::Report<common_utils::errors::ValidationError>;
    fn foreign_try_from(from: ConnectorData) -> Result<Self, Self::Error> {
        match RoutableConnectors::foreign_try_from(from.connector_name) {
            Ok(connector) => Ok(Self {
                choice_kind: api_routing::RoutableChoiceKind::FullStruct,
                connector,
                merchant_connector_id: from.merchant_connector_id,
            }),
            Err(e) => Err(common_utils::errors::ValidationError::InvalidValue {
                message: format!("This is not a routable connector: {e:?}"),
            })?,
        }
    }
}

/// Session Surcharge type
pub enum SessionSurchargeDetails {
    /// Surcharge is calculated by hyperswitch
    Calculated(payments_types::SurchargeMetadata),
    /// Surcharge is sent by merchant
    PreDetermined(payments_types::SurchargeDetails),
}

impl SessionSurchargeDetails {
    pub fn fetch_surcharge_details(
        &self,
        payment_method: enums::PaymentMethod,
        payment_method_type: enums::PaymentMethodType,
        card_network: Option<&enums::CardNetwork>,
    ) -> Option<payments_types::SurchargeDetails> {
        match self {
            Self::Calculated(surcharge_metadata) => surcharge_metadata
                .get_surcharge_details(payments_types::SurchargeKey::PaymentMethodData(
                    payment_method,
                    payment_method_type,
                    card_network.cloned(),
                ))
                .cloned(),
            Self::PreDetermined(surcharge_details) => Some(surcharge_details.clone()),
        }
    }
}

pub enum ConnectorChoice {
    SessionMultiple(SessionConnectorDatas),
    StraightThrough(serde_json::Value),
    Decide,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_convert_connector_parsing_success() {
        let result = enums::Connector::from_str("aci");
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), enums::Connector::Aci);

        let result = enums::Connector::from_str("shift4");
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), enums::Connector::Shift4);

        let result = enums::Connector::from_str("authorizedotnet");
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), enums::Connector::Authorizedotnet);
    }

    #[test]
    fn test_convert_connector_parsing_fail_for_unknown_type() {
        let result = enums::Connector::from_str("unknowntype");
        assert!(result.is_err());

        let result = enums::Connector::from_str("randomstring");
        assert!(result.is_err());
    }

    #[test]
    fn test_convert_connector_parsing_fail_for_camel_case() {
        let result = enums::Connector::from_str("Paypal");
        assert!(result.is_err());

        let result = enums::Connector::from_str("Authorizedotnet");
        assert!(result.is_err());

        let result = enums::Connector::from_str("Opennode");
        assert!(result.is_err());
    }
}

#[derive(Clone)]
pub struct TaxCalculateConnectorData {
    pub connector: ConnectorEnum,
    pub connector_name: enums::TaxConnectors,
}

impl TaxCalculateConnectorData {
    pub fn get_connector_by_name(name: &str) -> CustomResult<Self, errors::ApiErrorResponse> {
        let connector_name = enums::TaxConnectors::from_str(name)
            .change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)
            .attach_printable_lazy(|| format!("unable to parse connector: {name}"))?;
        let connector = Self::convert_connector(connector_name)?;
        Ok(Self {
            connector,
            connector_name,
        })
    }

    fn convert_connector(
        connector_name: enums::TaxConnectors,
    ) -> CustomResult<ConnectorEnum, errors::ApiErrorResponse> {
        match connector_name {
            enums::TaxConnectors::Taxjar => {
                Ok(ConnectorEnum::Old(Box::new(connector::Taxjar::new())))
            }
        }
    }
}


// File: crates/router/src/types/pm_auth.rs

use std::str::FromStr;

use error_stack::ResultExt;
use pm_auth::{
    connector::plaid,
    types::{
        self as pm_auth_types,
        api::{BoxedPaymentAuthConnector, PaymentAuthConnectorData},
    },
};

use crate::core::{
    errors::{self, ApiErrorResponse},
    pm_auth::helpers::PaymentAuthConnectorDataExt,
};

impl PaymentAuthConnectorDataExt for PaymentAuthConnectorData {
    fn get_connector_by_name(name: &str) -> errors::CustomResult<Self, ApiErrorResponse> {
        let connector_name = pm_auth_types::PaymentMethodAuthConnectors::from_str(name)
            .change_context(ApiErrorResponse::IncorrectConnectorNameGiven)
            .attach_printable_lazy(|| {
                format!("unable to parse connector: {:?}", name.to_string())
            })?;
        let connector = Self::convert_connector(connector_name.clone())?;
        Ok(Self {
            connector,
            connector_name,
        })
    }
    fn convert_connector(
        connector_name: pm_auth_types::PaymentMethodAuthConnectors,
    ) -> errors::CustomResult<BoxedPaymentAuthConnector, ApiErrorResponse> {
        match connector_name {
            pm_auth_types::PaymentMethodAuthConnectors::Plaid => Ok(Box::new(&plaid::Plaid)),
        }
    }
}


// File: crates/router/src/types/storage/dynamic_routing_stats.rs

pub use diesel_models::dynamic_routing_stats::{
    DynamicRoutingStats, DynamicRoutingStatsNew, DynamicRoutingStatsUpdate,
};


// File: crates/router/src/types/storage/dispute.rs

use async_bb8_diesel::AsyncRunQueryDsl;
use common_utils::errors::CustomResult;
use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, QueryDsl};
pub use diesel_models::dispute::{Dispute, DisputeNew, DisputeUpdate};
use diesel_models::{errors, query::generics::db_metrics, schema::dispute::dsl};
use error_stack::ResultExt;
use hyperswitch_domain_models::disputes;

use crate::{connection::PgPooledConn, logger};

#[async_trait::async_trait]
pub trait DisputeDbExt: Sized {
    async fn filter_by_constraints(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_list_constraints: &disputes::DisputeListConstraints,
    ) -> CustomResult<Vec<Self>, errors::DatabaseError>;

    async fn get_dispute_status_with_count(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
        time_range: &common_utils::types::TimeRange,
    ) -> CustomResult<Vec<(common_enums::enums::DisputeStatus, i64)>, errors::DatabaseError>;
}

#[async_trait::async_trait]
impl DisputeDbExt for Dispute {
    async fn filter_by_constraints(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_list_constraints: &disputes::DisputeListConstraints,
    ) -> CustomResult<Vec<Self>, errors::DatabaseError> {
        let mut filter = <Self as HasTable>::table()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .order(dsl::modified_at.desc())
            .into_boxed();

        let mut search_by_payment_or_dispute_id = false;

        if let (Some(payment_id), Some(dispute_id)) = (
            &dispute_list_constraints.payment_id,
            &dispute_list_constraints.dispute_id,
        ) {
            search_by_payment_or_dispute_id = true;
            filter = filter.filter(
                dsl::payment_id
                    .eq(payment_id.to_owned())
                    .or(dsl::dispute_id.eq(dispute_id.to_owned())),
            );
        };

        if !search_by_payment_or_dispute_id {
            if let Some(payment_id) = &dispute_list_constraints.payment_id {
                filter = filter.filter(dsl::payment_id.eq(payment_id.to_owned()));
            };
        }
        if !search_by_payment_or_dispute_id {
            if let Some(dispute_id) = &dispute_list_constraints.dispute_id {
                filter = filter.filter(dsl::dispute_id.eq(dispute_id.clone()));
            };
        }

        if let Some(time_range) = dispute_list_constraints.time_range {
            filter = filter.filter(dsl::created_at.ge(time_range.start_time));

            if let Some(end_time) = time_range.end_time {
                filter = filter.filter(dsl::created_at.le(end_time));
            }
        }

        if let Some(profile_id) = &dispute_list_constraints.profile_id {
            filter = filter.filter(dsl::profile_id.eq_any(profile_id.clone()));
        }
        if let Some(connector_list) = &dispute_list_constraints.connector {
            filter = filter.filter(dsl::connector.eq_any(connector_list.clone()));
        }

        if let Some(reason) = &dispute_list_constraints.reason {
            filter = filter.filter(dsl::connector_reason.eq(reason.clone()));
        }
        if let Some(dispute_stage) = &dispute_list_constraints.dispute_stage {
            filter = filter.filter(dsl::dispute_stage.eq_any(dispute_stage.clone()));
        }
        if let Some(dispute_status) = &dispute_list_constraints.dispute_status {
            filter = filter.filter(dsl::dispute_status.eq_any(dispute_status.clone()));
        }
        if let Some(currency_list) = &dispute_list_constraints.currency {
            filter = filter.filter(dsl::dispute_currency.eq_any(currency_list.clone()));
        }
        if let Some(merchant_connector_id) = &dispute_list_constraints.merchant_connector_id {
            filter = filter.filter(dsl::merchant_connector_id.eq(merchant_connector_id.clone()))
        }
        if let Some(limit) = dispute_list_constraints.limit {
            filter = filter.limit(limit.into());
        }
        if let Some(offset) = dispute_list_constraints.offset {
            filter = filter.offset(offset.into());
        }

        logger::debug!(query = %diesel::debug_query::<diesel::pg::Pg, _>(&filter).to_string());

        db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
            filter.get_results_async(conn),
            db_metrics::DatabaseOperation::Filter,
        )
        .await
        .change_context(errors::DatabaseError::NotFound)
        .attach_printable_lazy(|| "Error filtering records by predicate")
    }

    async fn get_dispute_status_with_count(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
        time_range: &common_utils::types::TimeRange,
    ) -> CustomResult<Vec<(common_enums::DisputeStatus, i64)>, errors::DatabaseError> {
        let mut query = <Self as HasTable>::table()
            .group_by(dsl::dispute_status)
            .select((dsl::dispute_status, diesel::dsl::count_star()))
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .into_boxed();

        if let Some(profile_id) = profile_id_list {
            query = query.filter(dsl::profile_id.eq_any(profile_id));
        }

        query = query.filter(dsl::created_at.ge(time_range.start_time));

        query = match time_range.end_time {
            Some(ending_at) => query.filter(dsl::created_at.le(ending_at)),
            None => query,
        };

        logger::debug!(query = %diesel::debug_query::<diesel::pg::Pg,_>(&query).to_string());

        db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
            query.get_results_async::<(common_enums::DisputeStatus, i64)>(conn),
            db_metrics::DatabaseOperation::Count,
        )
        .await
        .change_context(errors::DatabaseError::NotFound)
        .attach_printable_lazy(|| "Error filtering records by predicate")
    }
}


// File: crates/router/src/types/storage/kv.rs

pub use diesel_models::kv::{
    AddressUpdateMems, DBOperation, Insertable, PaymentAttemptUpdateMems, PaymentIntentUpdateMems,
    RefundUpdateMems, TypedSql, Updateable,
};


// File: crates/router/src/types/storage/callback_mapper.rs

pub use diesel_models::callback_mapper::CallbackMapper;


// File: crates/router/src/types/storage/role.rs

pub use diesel_models::role::*;


// File: crates/router/src/types/storage/payment_attempt.rs

use common_utils::types::MinorUnit;
use diesel_models::{capture::CaptureNew, enums};
use error_stack::ResultExt;
pub use hyperswitch_domain_models::payments::payment_attempt::{
    PaymentAttempt, PaymentAttemptUpdate,
};

use crate::{
    core::errors, errors::RouterResult, types::transformers::ForeignFrom, utils::OptionExt,
};
pub trait PaymentAttemptExt {
    fn make_new_capture(
        &self,
        capture_amount: MinorUnit,
        capture_status: enums::CaptureStatus,
    ) -> RouterResult<CaptureNew>;

    fn get_next_capture_id(&self) -> String;
    fn get_total_amount(&self) -> MinorUnit;
    fn get_surcharge_details(&self) -> Option<api_models::payments::RequestSurchargeDetails>;
}

impl PaymentAttemptExt for PaymentAttempt {
    #[cfg(feature = "v2")]
    fn make_new_capture(
        &self,
        capture_amount: MinorUnit,
        capture_status: enums::CaptureStatus,
    ) -> RouterResult<CaptureNew> {
        todo!()
    }

    #[cfg(feature = "v1")]
    fn make_new_capture(
        &self,
        capture_amount: MinorUnit,
        capture_status: enums::CaptureStatus,
    ) -> RouterResult<CaptureNew> {
        let capture_sequence = self.multiple_capture_count.unwrap_or_default() + 1;
        let now = common_utils::date_time::now();
        Ok(CaptureNew {
            payment_id: self.payment_id.clone(),
            merchant_id: self.merchant_id.clone(),
            capture_id: self.get_next_capture_id(),
            status: capture_status,
            amount: capture_amount,
            currency: self.currency,
            connector: self
                .connector
                .clone()
                .get_required_value("connector")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(
                    "connector field is required in payment_attempt to create a capture",
                )?,
            error_message: None,
            tax_amount: None,
            created_at: now,
            modified_at: now,
            error_code: None,
            error_reason: None,
            authorized_attempt_id: self.attempt_id.clone(),
            capture_sequence,
            connector_capture_id: None,
            connector_response_reference_id: None,
            processor_capture_data: None,
            // Below fields are deprecated. Please add any new fields above this line.
            connector_capture_data: None,
        })
    }

    #[cfg(feature = "v1")]
    fn get_next_capture_id(&self) -> String {
        let next_sequence_number = self.multiple_capture_count.unwrap_or_default() + 1;
        format!("{}_{}", self.attempt_id.clone(), next_sequence_number)
    }

    #[cfg(feature = "v2")]
    fn get_next_capture_id(&self) -> String {
        todo!()
    }

    #[cfg(feature = "v1")]
    fn get_surcharge_details(&self) -> Option<api_models::payments::RequestSurchargeDetails> {
        self.net_amount
            .get_surcharge_amount()
            .map(
                |surcharge_amount| api_models::payments::RequestSurchargeDetails {
                    surcharge_amount,
                    tax_amount: self.net_amount.get_tax_on_surcharge(),
                },
            )
    }

    #[cfg(feature = "v2")]
    fn get_surcharge_details(&self) -> Option<api_models::payments::RequestSurchargeDetails> {
        todo!()
    }

    #[cfg(feature = "v1")]
    fn get_total_amount(&self) -> MinorUnit {
        self.net_amount.get_total_amount()
    }

    #[cfg(feature = "v2")]
    fn get_total_amount(&self) -> MinorUnit {
        todo!()
    }
}

pub trait AttemptStatusExt {
    fn maps_to_intent_status(self, intent_status: enums::IntentStatus) -> bool;
}

impl AttemptStatusExt for enums::AttemptStatus {
    fn maps_to_intent_status(self, intent_status: enums::IntentStatus) -> bool {
        enums::IntentStatus::foreign_from(self) == intent_status
    }
}

#[cfg(test)]
#[cfg(all(
    feature = "v1", // Ignoring tests for v2 since they aren't actively running
    feature = "dummy_connector"
))]
mod tests {
    use hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptNew;
    use tokio::sync::oneshot;
    use uuid::Uuid;

    use crate::{
        configs::settings::Settings,
        db::StorageImpl,
        routes, services,
        types::{self, storage::enums},
    };

    async fn create_single_connection_test_transaction_pool() -> routes::AppState {
        // Set pool size to 1 and minimum idle connection size to 0
        std::env::set_var("ROUTER__MASTER_DATABASE__POOL_SIZE", "1");
        std::env::set_var("ROUTER__MASTER_DATABASE__MIN_IDLE", "0");
        std::env::set_var("ROUTER__REPLICA_DATABASE__POOL_SIZE", "1");
        std::env::set_var("ROUTER__REPLICA_DATABASE__MIN_IDLE", "0");

        let conf = Settings::new().expect("invalid settings");
        let tx: oneshot::Sender<()> = oneshot::channel().0;
        let api_client = Box::new(services::MockApiClient);
        Box::pin(routes::AppState::with_storage(
            conf,
            StorageImpl::PostgresqlTest,
            tx,
            api_client,
        ))
        .await
    }

    #[tokio::test]
    async fn test_payment_attempt_insert() {
        let state = create_single_connection_test_transaction_pool().await;
        let payment_id =
            common_utils::id_type::PaymentId::generate_test_payment_id_for_sample_data();
        let current_time = common_utils::date_time::now();
        let connector = types::Connector::DummyConnector1.to_string();
        let payment_attempt = PaymentAttemptNew {
            payment_id: payment_id.clone(),
            connector: Some(connector),
            created_at: current_time.into(),
            modified_at: current_time.into(),
            merchant_id: Default::default(),
            attempt_id: Default::default(),
            status: Default::default(),
            net_amount: Default::default(),
            currency: Default::default(),
            save_to_locker: Default::default(),
            error_message: Default::default(),
            offer_amount: Default::default(),
            payment_method_id: Default::default(),
            payment_method: Default::default(),
            capture_method: Default::default(),
            capture_on: Default::default(),
            confirm: Default::default(),
            authentication_type: Default::default(),
            last_synced: Default::default(),
            cancellation_reason: Default::default(),
            amount_to_capture: Default::default(),
            mandate_id: Default::default(),
            browser_info: Default::default(),
            payment_token: Default::default(),
            error_code: Default::default(),
            connector_metadata: Default::default(),
            payment_experience: Default::default(),
            payment_method_type: Default::default(),
            payment_method_data: Default::default(),
            business_sub_label: Default::default(),
            straight_through_algorithm: Default::default(),
            preprocessing_step_id: Default::default(),
            mandate_details: Default::default(),
            error_reason: Default::default(),
            connector_response_reference_id: Default::default(),
            multiple_capture_count: Default::default(),
            amount_capturable: Default::default(),
            updated_by: Default::default(),
            authentication_data: Default::default(),
            encoded_data: Default::default(),
            merchant_connector_id: Default::default(),
            unified_code: Default::default(),
            unified_message: Default::default(),
            external_three_ds_authentication_attempted: Default::default(),
            authentication_connector: Default::default(),
            authentication_id: Default::default(),
            mandate_data: Default::default(),
            payment_method_billing_address_id: Default::default(),
            fingerprint_id: Default::default(),
            client_source: Default::default(),
            client_version: Default::default(),
            customer_acceptance: Default::default(),
            profile_id: common_utils::generate_profile_id_of_default_length(),
            organization_id: Default::default(),
            connector_mandate_detail: Default::default(),
            request_extended_authorization: Default::default(),
            extended_authorization_applied: Default::default(),
            extended_authorization_last_applied_at: Default::default(),
            capture_before: Default::default(),
            card_discovery: Default::default(),
            processor_merchant_id: Default::default(),
            created_by: None,
            setup_future_usage_applied: Default::default(),
            routing_approach: Default::default(),
            connector_request_reference_id: Default::default(),
            network_transaction_id: Default::default(),
            network_details: Default::default(),
            is_stored_credential: None,
            authorized_amount: Default::default(),
            tokenization: Default::default(),
        };

        let store = state
            .stores
            .get(state.conf.multitenancy.get_tenant_ids().first().unwrap())
            .unwrap();
        let response = store
            .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly)
            .await
            .unwrap();
        eprintln!("{response:?}");

        assert_eq!(response.payment_id, payment_id.clone());
    }

    #[tokio::test]
    /// Example of unit test
    /// Kind of test: state-based testing
    async fn test_find_payment_attempt() {
        let state = create_single_connection_test_transaction_pool().await;
        let current_time = common_utils::date_time::now();
        let payment_id =
            common_utils::id_type::PaymentId::generate_test_payment_id_for_sample_data();
        let attempt_id = Uuid::new_v4().to_string();
        let merchant_id = common_utils::id_type::MerchantId::new_from_unix_timestamp();
        let connector = types::Connector::DummyConnector1.to_string();

        let payment_attempt = PaymentAttemptNew {
            payment_id: payment_id.clone(),
            merchant_id: merchant_id.clone(),
            connector: Some(connector),
            created_at: current_time.into(),
            modified_at: current_time.into(),
            attempt_id: attempt_id.clone(),
            status: Default::default(),
            net_amount: Default::default(),
            currency: Default::default(),
            save_to_locker: Default::default(),
            error_message: Default::default(),
            offer_amount: Default::default(),
            payment_method_id: Default::default(),
            payment_method: Default::default(),
            capture_method: Default::default(),
            capture_on: Default::default(),
            confirm: Default::default(),
            authentication_type: Default::default(),
            last_synced: Default::default(),
            cancellation_reason: Default::default(),
            amount_to_capture: Default::default(),
            mandate_id: Default::default(),
            browser_info: Default::default(),
            payment_token: Default::default(),
            error_code: Default::default(),
            connector_metadata: Default::default(),
            payment_experience: Default::default(),
            payment_method_type: Default::default(),
            payment_method_data: Default::default(),
            business_sub_label: Default::default(),
            straight_through_algorithm: Default::default(),
            preprocessing_step_id: Default::default(),
            mandate_details: Default::default(),
            error_reason: Default::default(),
            connector_response_reference_id: Default::default(),
            multiple_capture_count: Default::default(),
            amount_capturable: Default::default(),
            updated_by: Default::default(),
            authentication_data: Default::default(),
            encoded_data: Default::default(),
            merchant_connector_id: Default::default(),
            unified_code: Default::default(),
            unified_message: Default::default(),
            external_three_ds_authentication_attempted: Default::default(),
            authentication_connector: Default::default(),
            authentication_id: Default::default(),
            mandate_data: Default::default(),
            payment_method_billing_address_id: Default::default(),
            fingerprint_id: Default::default(),
            client_source: Default::default(),
            client_version: Default::default(),
            customer_acceptance: Default::default(),
            profile_id: common_utils::generate_profile_id_of_default_length(),
            organization_id: Default::default(),
            connector_mandate_detail: Default::default(),
            request_extended_authorization: Default::default(),
            extended_authorization_applied: Default::default(),
            extended_authorization_last_applied_at: Default::default(),
            capture_before: Default::default(),
            card_discovery: Default::default(),
            processor_merchant_id: Default::default(),
            created_by: None,
            setup_future_usage_applied: Default::default(),
            routing_approach: Default::default(),
            connector_request_reference_id: Default::default(),
            network_transaction_id: Default::default(),
            network_details: Default::default(),
            is_stored_credential: Default::default(),
            authorized_amount: Default::default(),
            tokenization: Default::default(),
        };
        let store = state
            .stores
            .get(state.conf.multitenancy.get_tenant_ids().first().unwrap())
            .unwrap();
        store
            .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly)
            .await
            .unwrap();

        let response = store
            .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_id,
                &merchant_id,
                &attempt_id,
                enums::MerchantStorageScheme::PostgresOnly,
            )
            .await
            .unwrap();

        eprintln!("{response:?}");

        assert_eq!(response.payment_id, payment_id);
    }

    #[tokio::test]
    /// Example of unit test
    /// Kind of test: state-based testing
    async fn test_payment_attempt_mandate_field() {
        let state = create_single_connection_test_transaction_pool().await;
        let uuid = Uuid::new_v4().to_string();
        let merchant_id =
            common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from("merchant1"))
                .unwrap();

        let payment_id =
            common_utils::id_type::PaymentId::generate_test_payment_id_for_sample_data();
        let current_time = common_utils::date_time::now();
        let connector = types::Connector::DummyConnector1.to_string();

        let payment_attempt = PaymentAttemptNew {
            payment_id: payment_id.clone(),
            merchant_id: merchant_id.clone(),
            connector: Some(connector),
            created_at: current_time.into(),
            modified_at: current_time.into(),
            mandate_id: Some("man_121212".to_string()),
            attempt_id: uuid.clone(),
            status: Default::default(),
            net_amount: Default::default(),
            currency: Default::default(),
            save_to_locker: Default::default(),
            error_message: Default::default(),
            offer_amount: Default::default(),
            payment_method_id: Default::default(),
            payment_method: Default::default(),
            capture_method: Default::default(),
            capture_on: Default::default(),
            confirm: Default::default(),
            authentication_type: Default::default(),
            last_synced: Default::default(),
            cancellation_reason: Default::default(),
            amount_to_capture: Default::default(),
            browser_info: Default::default(),
            payment_token: Default::default(),
            error_code: Default::default(),
            connector_metadata: Default::default(),
            payment_experience: Default::default(),
            payment_method_type: Default::default(),
            payment_method_data: Default::default(),
            business_sub_label: Default::default(),
            straight_through_algorithm: Default::default(),
            preprocessing_step_id: Default::default(),
            mandate_details: Default::default(),
            error_reason: Default::default(),
            connector_response_reference_id: Default::default(),
            multiple_capture_count: Default::default(),
            amount_capturable: Default::default(),
            updated_by: Default::default(),
            authentication_data: Default::default(),
            encoded_data: Default::default(),
            merchant_connector_id: Default::default(),
            unified_code: Default::default(),
            unified_message: Default::default(),
            external_three_ds_authentication_attempted: Default::default(),
            authentication_connector: Default::default(),
            authentication_id: Default::default(),
            mandate_data: Default::default(),
            payment_method_billing_address_id: Default::default(),
            fingerprint_id: Default::default(),
            client_source: Default::default(),
            client_version: Default::default(),
            customer_acceptance: Default::default(),
            profile_id: common_utils::generate_profile_id_of_default_length(),
            organization_id: Default::default(),
            connector_mandate_detail: Default::default(),
            request_extended_authorization: Default::default(),
            extended_authorization_applied: Default::default(),
            extended_authorization_last_applied_at: Default::default(),
            capture_before: Default::default(),
            card_discovery: Default::default(),
            processor_merchant_id: Default::default(),
            created_by: None,
            setup_future_usage_applied: Default::default(),
            routing_approach: Default::default(),
            connector_request_reference_id: Default::default(),
            network_transaction_id: Default::default(),
            network_details: Default::default(),
            is_stored_credential: Default::default(),
            authorized_amount: Default::default(),
            tokenization: Default::default(),
        };
        let store = state
            .stores
            .get(state.conf.multitenancy.get_tenant_ids().first().unwrap())
            .unwrap();
        store
            .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly)
            .await
            .unwrap();

        let response = store
            .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_id,
                &merchant_id,
                &uuid,
                enums::MerchantStorageScheme::PostgresOnly,
            )
            .await
            .unwrap();
        // checking it after fetch
        assert_eq!(response.mandate_id, Some("man_121212".to_string()));
    }
}


// File: crates/router/src/types/storage/events.rs

pub use diesel_models::events::{Event, EventMetadata, EventNew};


// File: crates/router/src/types/storage/merchant_account.rs

pub use diesel_models::merchant_account::{
    MerchantAccount, MerchantAccountNew, MerchantAccountUpdateInternal,
};

pub use crate::types::domain::MerchantAccountUpdate;


// File: crates/router/src/types/storage/payouts.rs

pub use diesel_models::payouts::{Payouts, PayoutsNew, PayoutsUpdate, PayoutsUpdateInternal};


// File: crates/router/src/types/storage/fraud_check.rs

pub use diesel_models::fraud_check::{
    FraudCheck, FraudCheckNew, FraudCheckUpdate, FraudCheckUpdateInternal,
};