// File: crates/router/src/core/payments/routing/utils.rs
// Module: router
// Public functions: 24
// Public structs: 42

use std::{
    collections::{HashMap, HashSet},
    str::FromStr,
};

use api_models::{
    open_router as or_types,
    routing::{
        self as api_routing, ComparisonType, ConnectorSelection, ConnectorVolumeSplit,
        DeRoutableConnectorChoice, MetadataValue, NumberComparison, RoutableConnectorChoice,
        RoutingEvaluateRequest, RoutingEvaluateResponse, ValueType,
    },
};
use async_trait::async_trait;
use common_enums::{RoutableConnectors, TransactionType};
use common_utils::{
    ext_traits::{BytesExt, StringExt},
    id_type,
};
use diesel_models::{enums, routing_algorithm};
use error_stack::ResultExt;
use euclid::{
    backend::BackendInput,
    frontend::{
        ast::{self},
        dir::{self, transformers::IntoDirValue},
    },
};
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use external_services::grpc_client::dynamic_routing as ir_client;
use hyperswitch_domain_models::business_profile;
use hyperswitch_interfaces::events::routing_api_logs as routing_events;
use router_env::tracing_actix_web::RequestId;
use serde::{Deserialize, Serialize};

use super::RoutingResult;
use crate::{
    core::errors,
    db::domain,
    routes::{app::SessionStateInfo, SessionState},
    services::{self, logger},
    types::transformers::ForeignInto,
};

// New Trait for handling Euclid API calls
#[async_trait]
pub trait DecisionEngineApiHandler {
    async fn send_decision_engine_request<Req, Res>(
        state: &SessionState,
        http_method: services::Method,
        path: &str,
        request_body: Option<Req>, // Option to handle GET/DELETE requests without body
        timeout: Option<u64>,
        events_wrapper: Option<RoutingEventsWrapper<Req>>,
    ) -> RoutingResult<RoutingEventsResponse<Res>>
    where
        Req: Serialize + Send + Sync + 'static + Clone,
        Res: Serialize + serde::de::DeserializeOwned + Send + 'static + std::fmt::Debug + Clone;
}

// Struct to implement the DecisionEngineApiHandler trait
pub struct EuclidApiClient;

pub struct ConfigApiClient;

pub struct SRApiClient;

pub async fn build_and_send_decision_engine_http_request<Req, Res, ErrRes>(
    state: &SessionState,
    http_method: services::Method,
    path: &str,
    request_body: Option<Req>,
    _timeout: Option<u64>,
    context_message: &str,
    events_wrapper: Option<RoutingEventsWrapper<Req>>,
) -> RoutingResult<RoutingEventsResponse<Res>>
where
    Req: Serialize + Send + Sync + 'static + Clone,
    Res: Serialize + serde::de::DeserializeOwned + std::fmt::Debug + Clone + 'static,
    ErrRes: serde::de::DeserializeOwned + std::fmt::Debug + Clone + DecisionEngineErrorsInterface,
{
    let decision_engine_base_url = &state.conf.open_router.url;
    let url = format!("{decision_engine_base_url}/{path}");
    logger::debug!(decision_engine_api_call_url = %url, decision_engine_request_path = %path, http_method = ?http_method, "decision_engine: Initiating decision_engine API call ({})", context_message);

    let mut request_builder = services::RequestBuilder::new()
        .method(http_method)
        .url(&url);

    if let Some(body_content) = request_body {
        let body = common_utils::request::RequestContent::Json(Box::new(body_content));
        request_builder = request_builder.set_body(body);
    }

    let http_request = request_builder.build();
    logger::info!(?http_request, decision_engine_request_path = %path, "decision_engine: Constructed Decision Engine API request details ({})", context_message);
    let should_parse_response = events_wrapper
        .as_ref()
        .map(|wrapper| wrapper.parse_response)
        .unwrap_or(true);

    let closure = || async {
        let response =
            services::call_connector_api(state, http_request, "Decision Engine API call")
                .await
                .change_context(errors::RoutingError::OpenRouterCallFailed)?;

        match response {
            Ok(resp) => {
                logger::debug!(
                    "decision_engine: Received response from Decision Engine API ({:?})",
                    String::from_utf8_lossy(&resp.response) // For logging
                );

                let resp = should_parse_response
                    .then(|| {
                        if std::any::TypeId::of::<Res>() == std::any::TypeId::of::<String>()
                            && resp.response.is_empty()
                        {
                            return serde_json::from_str::<Res>("\"\"").change_context(
                                errors::RoutingError::OpenRouterError(
                                    "Failed to parse empty response as String".into(),
                                ),
                            );
                        }
                        let response_type: Res = resp
                            .response
                            .parse_struct(std::any::type_name::<Res>())
                            .change_context(errors::RoutingError::OpenRouterError(
                                "Failed to parse the response from open_router".into(),
                            ))?;

                        Ok::<_, error_stack::Report<errors::RoutingError>>(response_type)
                    })
                    .transpose()?;

                logger::debug!("decision_engine_success_response: {:?}", resp);

                Ok(resp)
            }
            Err(err) => {
                logger::debug!(
                    "decision_engine: Received response from Decision Engine API ({:?})",
                    String::from_utf8_lossy(&err.response) // For logging
                );

                let err_resp: ErrRes = err
                    .response
                    .parse_struct(std::any::type_name::<ErrRes>())
                    .change_context(errors::RoutingError::OpenRouterError(
                    "Failed to parse the response from open_router".into(),
                ))?;

                logger::error!(
                    decision_engine_error_code = %err_resp.get_error_code(),
                    decision_engine_error_message = %err_resp.get_error_message(),
                    decision_engine_raw_response = ?err_resp.get_error_data(),
                );

                Err(error_stack::report!(
                    errors::RoutingError::RoutingEventsError {
                        message: err_resp.get_error_message(),
                        status_code: err.status_code,
                    }
                ))
            }
        }
    };

    let events_response = if let Some(wrapper) = events_wrapper {
        wrapper
            .construct_event_builder(
                url,
                routing_events::RoutingEngine::DecisionEngine,
                routing_events::ApiMethod::Rest(http_method),
            )?
            .trigger_event(state, closure)
            .await?
    } else {
        let resp = closure()
            .await
            .change_context(errors::RoutingError::OpenRouterCallFailed)?;

        RoutingEventsResponse::new(None, resp)
    };

    Ok(events_response)
}

#[async_trait]
impl DecisionEngineApiHandler for EuclidApiClient {
    async fn send_decision_engine_request<Req, Res>(
        state: &SessionState,
        http_method: services::Method,
        path: &str,
        request_body: Option<Req>, // Option to handle GET/DELETE requests without body
        timeout: Option<u64>,
        events_wrapper: Option<RoutingEventsWrapper<Req>>,
    ) -> RoutingResult<RoutingEventsResponse<Res>>
    where
        Req: Serialize + Send + Sync + 'static + Clone,
        Res: Serialize + serde::de::DeserializeOwned + Send + 'static + std::fmt::Debug + Clone,
    {
        let event_response = build_and_send_decision_engine_http_request::<_, _, DeErrorResponse>(
            state,
            http_method,
            path,
            request_body,
            timeout,
            "parsing response",
            events_wrapper,
        )
        .await?;

        let parsed_response =
            event_response
                .response
                .as_ref()
                .ok_or(errors::RoutingError::OpenRouterError(
                    "Response from decision engine API is empty".to_string(),
                ))?;

        logger::debug!(parsed_response = ?parsed_response, response_type = %std::any::type_name::<Res>(), euclid_request_path = %path, "decision_engine_euclid: Successfully parsed response from Euclid API");
        Ok(event_response)
    }
}

#[async_trait]
impl DecisionEngineApiHandler for ConfigApiClient {
    async fn send_decision_engine_request<Req, Res>(
        state: &SessionState,
        http_method: services::Method,
        path: &str,
        request_body: Option<Req>,
        timeout: Option<u64>,
        events_wrapper: Option<RoutingEventsWrapper<Req>>,
    ) -> RoutingResult<RoutingEventsResponse<Res>>
    where
        Req: Serialize + Send + Sync + 'static + Clone,
        Res: Serialize + serde::de::DeserializeOwned + Send + 'static + std::fmt::Debug + Clone,
    {
        let events_response = build_and_send_decision_engine_http_request::<_, _, DeErrorResponse>(
            state,
            http_method,
            path,
            request_body,
            timeout,
            "parsing response",
            events_wrapper,
        )
        .await?;

        let parsed_response =
            events_response
                .response
                .as_ref()
                .ok_or(errors::RoutingError::OpenRouterError(
                    "Response from decision engine API is empty".to_string(),
                ))?;
        logger::debug!(parsed_response = ?parsed_response, response_type = %std::any::type_name::<Res>(), decision_engine_request_path = %path, "decision_engine_config: Successfully parsed response from Decision Engine config API");
        Ok(events_response)
    }
}

#[async_trait]
impl DecisionEngineApiHandler for SRApiClient {
    async fn send_decision_engine_request<Req, Res>(
        state: &SessionState,
        http_method: services::Method,
        path: &str,
        request_body: Option<Req>,
        timeout: Option<u64>,
        events_wrapper: Option<RoutingEventsWrapper<Req>>,
    ) -> RoutingResult<RoutingEventsResponse<Res>>
    where
        Req: Serialize + Send + Sync + 'static + Clone,
        Res: Serialize + serde::de::DeserializeOwned + Send + 'static + std::fmt::Debug + Clone,
    {
        let events_response =
            build_and_send_decision_engine_http_request::<_, _, or_types::ErrorResponse>(
                state,
                http_method,
                path,
                request_body,
                timeout,
                "parsing response",
                events_wrapper,
            )
            .await?;

        let parsed_response =
            events_response
                .response
                .as_ref()
                .ok_or(errors::RoutingError::OpenRouterError(
                    "Response from decision engine API is empty".to_string(),
                ))?;
        logger::debug!(parsed_response = ?parsed_response, response_type = %std::any::type_name::<Res>(), decision_engine_request_path = %path, "decision_engine_config: Successfully parsed response from Decision Engine config API");
        Ok(events_response)
    }
}

const EUCLID_API_TIMEOUT: u64 = 5;

pub async fn perform_decision_euclid_routing(
    state: &SessionState,
    input: BackendInput,
    created_by: String,
    events_wrapper: RoutingEventsWrapper<RoutingEvaluateRequest>,
    fallback_output: Vec<RoutableConnectorChoice>,
) -> RoutingResult<RoutingEvaluateResponse> {
    logger::debug!("decision_engine_euclid: evaluate api call for euclid routing evaluation");

    let mut events_wrapper = events_wrapper;
    let fallback_output = fallback_output
        .into_iter()
        .map(|c| DeRoutableConnectorChoice {
            gateway_name: c.connector,
            gateway_id: c.merchant_connector_id,
        })
        .collect::<Vec<_>>();

    let routing_request =
        convert_backend_input_to_routing_eval(created_by, input, fallback_output)?;
    events_wrapper.set_request_body(routing_request.clone());

    let event_response = EuclidApiClient::send_decision_engine_request(
        state,
        services::Method::Post,
        "routing/evaluate",
        Some(routing_request),
        Some(EUCLID_API_TIMEOUT),
        Some(events_wrapper),
    )
    .await?;

    let euclid_response: RoutingEvaluateResponse =
        event_response
            .response
            .ok_or(errors::RoutingError::OpenRouterError(
                "Response from decision engine API is empty".to_string(),
            ))?;

    let mut routing_event =
        event_response
            .event
            .ok_or(errors::RoutingError::RoutingEventsError {
                message: "Routing event not found in EventsResponse".to_string(),
                status_code: 500,
            })?;

    routing_event.set_routing_approach(RoutingApproach::StaticRouting.to_string());
    routing_event.set_routable_connectors(euclid_response.evaluated_output.clone());
    state.event_handler.log_event(&routing_event);

    logger::debug!(decision_engine_euclid_response=?euclid_response,"decision_engine_euclid");
    logger::debug!(decision_engine_euclid_selected_connector=?euclid_response.evaluated_output,"decision_engine_euclid");
    Ok(euclid_response)
}

/// This function transforms the decision_engine response in a way that's usable for further flows:
/// It places evaluated_output connectors first, followed by remaining output connectors (no duplicates).
pub fn transform_de_output_for_router(
    de_output: Vec<ConnectorInfo>,
    de_evaluated_output: Vec<RoutableConnectorChoice>,
) -> RoutingResult<Vec<RoutableConnectorChoice>> {
    let mut seen = HashSet::new();

    // evaluated connectors on top, to ensure the fallback is based on other connectors.
    let mut ordered = Vec::with_capacity(de_output.len() + de_evaluated_output.len());
    for eval_conn in de_evaluated_output {
        if seen.insert(eval_conn.connector) {
            ordered.push(eval_conn);
        }
    }

    // Add remaining connectors from de_output (only if not already seen), for fallback
    for conn in de_output {
        let key = RoutableConnectors::from_str(&conn.gateway_name).map_err(|_| {
            errors::RoutingError::GenericConversionError {
                from: "String".to_string(),
                to: "RoutableConnectors".to_string(),
            }
        })?;
        if seen.insert(key) {
            let de_choice = DeRoutableConnectorChoice::try_from(conn)?;
            ordered.push(RoutableConnectorChoice::from(de_choice));
        }
    }
    Ok(ordered)
}

pub async fn decision_engine_routing(
    state: &SessionState,
    backend_input: BackendInput,
    business_profile: &domain::Profile,
    payment_id: String,
    merchant_fallback_config: Vec<RoutableConnectorChoice>,
) -> RoutingResult<Vec<RoutableConnectorChoice>> {
    let routing_events_wrapper = RoutingEventsWrapper::new(
        state.tenant.tenant_id.clone(),
        state.request_id,
        payment_id,
        business_profile.get_id().to_owned(),
        business_profile.merchant_id.to_owned(),
        "DecisionEngine: Euclid Static Routing".to_string(),
        None,
        true,
        false,
    );

    let de_euclid_evaluate_response = perform_decision_euclid_routing(
        state,
        backend_input.clone(),
        business_profile.get_id().get_string_repr().to_string(),
        routing_events_wrapper,
        merchant_fallback_config,
    )
    .await;

    let Ok(de_euclid_response) = de_euclid_evaluate_response else {
        logger::error!("decision_engine_euclid_evaluation_error: error in evaluation of rule");
        return Ok(Vec::default());
    };

    let de_output_connector = extract_de_output_connectors(de_euclid_response.output)
            .map_err(|e| {
                logger::error!(error=?e, "decision_engine_euclid_evaluation_error: Failed to extract connector from Output");
                e
            })?;

    transform_de_output_for_router(
            de_output_connector.clone(),
            de_euclid_response.evaluated_output.clone(),
        )
        .map_err(|e| {
            logger::error!(error=?e, "decision_engine_euclid_evaluation_error: failed to transform connector from de-output");
            e
        })
}

/// Custom deserializer for output from decision_engine, this is required as untagged enum is
/// stored but the enum requires tagged deserialization, hence deserializing it into specific
/// variants
pub fn extract_de_output_connectors(
    output_value: serde_json::Value,
) -> RoutingResult<Vec<ConnectorInfo>> {
    const SINGLE: &str = "straight_through";
    const PRIORITY: &str = "priority";
    const VOLUME_SPLIT: &str = "volume_split";
    const VOLUME_SPLIT_PRIORITY: &str = "volume_split_priority";

    let obj = output_value.as_object().ok_or_else(|| {
        logger::error!("decision_engine_euclid_error: output is not a JSON object");
        errors::RoutingError::OpenRouterError("Expected output to be a JSON object".into())
    })?;

    let type_str = obj.get("type").and_then(|v| v.as_str()).ok_or_else(|| {
        logger::error!("decision_engine_euclid_error: missing or invalid 'type' in output");
        errors::RoutingError::OpenRouterError("Missing or invalid 'type' field in output".into())
    })?;

    match type_str {
        SINGLE => {
            let connector_value = obj.get("connector").ok_or_else(|| {
                logger::error!(
                    "decision_engine_euclid_error: missing 'connector' field for type=single"
                );
                errors::RoutingError::OpenRouterError(
                    "Missing 'connector' field for single output".into(),
                )
            })?;
            let connector: ConnectorInfo = serde_json::from_value(connector_value.clone())
                .map_err(|e| {
                    logger::error!(
                        ?e,
                        "decision_engine_euclid_error: Failed to parse single connector"
                    );
                    errors::RoutingError::OpenRouterError(
                        "Failed to deserialize single connector".into(),
                    )
                })?;
            Ok(vec![connector])
        }

        PRIORITY => {
            let connectors_value = obj.get("connectors").ok_or_else(|| {
                logger::error!(
                    "decision_engine_euclid_error: missing 'connectors' field for type=priority"
                );
                errors::RoutingError::OpenRouterError(
                    "Missing 'connectors' field for priority output".into(),
                )
            })?;
            let connectors: Vec<ConnectorInfo> = serde_json::from_value(connectors_value.clone())
                .map_err(|e| {
                logger::error!(
                    ?e,
                    "decision_engine_euclid_error: Failed to parse connectors for priority"
                );
                errors::RoutingError::OpenRouterError(
                    "Failed to deserialize priority connectors".into(),
                )
            })?;
            Ok(connectors)
        }

        VOLUME_SPLIT => {
            let splits_value = obj.get("splits").ok_or_else(|| {
                logger::error!(
                    "decision_engine_euclid_error: missing 'splits' field for type=volume_split"
                );
                errors::RoutingError::OpenRouterError(
                    "Missing 'splits' field for volume_split output".into(),
                )
            })?;

            // Transform each {connector, split} into {output, split}
            let fixed_splits: Vec<_> = splits_value
                .as_array()
                .ok_or_else(|| {
                    logger::error!("decision_engine_euclid_error: 'splits' is not an array");
                    errors::RoutingError::OpenRouterError("'splits' field must be an array".into())
                })?
                .iter()
                .map(|entry| {
                    let mut entry_map = entry.as_object().cloned().ok_or_else(|| {
                        logger::error!(
                            "decision_engine_euclid_error: invalid split entry in volume_split"
                        );
                        errors::RoutingError::OpenRouterError(
                            "Invalid entry in splits array".into(),
                        )
                    })?;
                    if let Some(connector) = entry_map.remove("connector") {
                        entry_map.insert("output".to_string(), connector);
                    }
                    Ok::<_, error_stack::Report<errors::RoutingError>>(serde_json::Value::Object(
                        entry_map,
                    ))
                })
                .collect::<Result<Vec<_>, _>>()?;

            let splits: Vec<VolumeSplit<ConnectorInfo>> =
                serde_json::from_value(serde_json::Value::Array(fixed_splits)).map_err(|e| {
                    logger::error!(
                        ?e,
                        "decision_engine_euclid_error: Failed to parse volume_split"
                    );
                    errors::RoutingError::OpenRouterError(
                        "Failed to deserialize volume_split connectors".into(),
                    )
                })?;

            Ok(splits.into_iter().map(|s| s.output).collect())
        }

        VOLUME_SPLIT_PRIORITY => {
            let splits_value = obj.get("splits").ok_or_else(|| {
                logger::error!("decision_engine_euclid_error: missing 'splits' field for type=volume_split_priority");
                errors::RoutingError::OpenRouterError("Missing 'splits' field for volume_split_priority output".into())
            })?;

            // Transform each {connector: [...], split} into {output: [...], split}
            let fixed_splits: Vec<_> = splits_value
                .as_array()
                .ok_or_else(|| {
                    logger::error!("decision_engine_euclid_error: 'splits' is not an array");
                    errors::RoutingError::OpenRouterError("'splits' field must be an array".into())
                })?
                .iter()
                .map(|entry| {
                    let mut entry_map = entry.as_object().cloned().ok_or_else(|| {
                        logger::error!("decision_engine_euclid_error: invalid split entry in volume_split_priority");
                        errors::RoutingError::OpenRouterError("Invalid entry in splits array".into())
                    })?;
                    if let Some(connector) = entry_map.remove("connector") {
                        entry_map.insert("output".to_string(), connector);
                    }
                    Ok::<_, error_stack::Report<errors::RoutingError>>(serde_json::Value::Object(entry_map))
                })
                .collect::<Result<Vec<_>, _>>()?;

            let splits: Vec<VolumeSplit<Vec<ConnectorInfo>>> =
                serde_json::from_value(serde_json::Value::Array(fixed_splits)).map_err(|e| {
                    logger::error!(
                        ?e,
                        "decision_engine_euclid_error: Failed to parse volume_split_priority"
                    );
                    errors::RoutingError::OpenRouterError(
                        "Failed to deserialize volume_split_priority connectors".into(),
                    )
                })?;

            Ok(splits.into_iter().flat_map(|s| s.output).collect())
        }

        other => {
            logger::error!(type_str=%other, "decision_engine_euclid_error: unknown output type");
            Err(
                errors::RoutingError::OpenRouterError(format!("Unknown output type: {other}"))
                    .into(),
            )
        }
    }
}

pub async fn create_de_euclid_routing_algo(
    state: &SessionState,
    routing_request: &RoutingRule,
) -> RoutingResult<String> {
    logger::debug!("decision_engine_euclid: create api call for euclid routing rule creation");

    logger::debug!(decision_engine_euclid_request=?routing_request,"decision_engine_euclid");
    let events_response = EuclidApiClient::send_decision_engine_request(
        state,
        services::Method::Post,
        "routing/create",
        Some(routing_request.clone()),
        Some(EUCLID_API_TIMEOUT),
        None,
    )
    .await?;

    let euclid_response: RoutingDictionaryRecord =
        events_response
            .response
            .ok_or(errors::RoutingError::OpenRouterError(
                "Response from decision engine API is empty".to_string(),
            ))?;

    logger::debug!(decision_engine_euclid_parsed_response=?euclid_response,"decision_engine_euclid");
    Ok(euclid_response.rule_id)
}

pub async fn link_de_euclid_routing_algorithm(
    state: &SessionState,
    routing_request: ActivateRoutingConfigRequest,
) -> RoutingResult<()> {
    logger::debug!("decision_engine_euclid: link api call for euclid routing algorithm");

    EuclidApiClient::send_decision_engine_request::<_, String>(
        state,
        services::Method::Post,
        "routing/activate",
        Some(routing_request.clone()),
        Some(EUCLID_API_TIMEOUT),
        None,
    )
    .await?;

    logger::debug!(decision_engine_euclid_activated=?routing_request, "decision_engine_euclid: link_de_euclid_routing_algorithm completed");
    Ok(())
}

pub async fn list_de_euclid_routing_algorithms(
    state: &SessionState,
    routing_list_request: ListRountingAlgorithmsRequest,
) -> RoutingResult<Vec<api_routing::RoutingDictionaryRecord>> {
    logger::debug!("decision_engine_euclid: list api call for euclid routing algorithms");
    let created_by = routing_list_request.created_by;
    let events_response = EuclidApiClient::send_decision_engine_request(
        state,
        services::Method::Post,
        format!("routing/list/{created_by}").as_str(),
        None::<()>,
        Some(EUCLID_API_TIMEOUT),
        None,
    )
    .await?;

    let euclid_response: Vec<RoutingAlgorithmRecord> =
        events_response
            .response
            .ok_or(errors::RoutingError::OpenRouterError(
                "Response from decision engine API is empty".to_string(),
            ))?;

    Ok(euclid_response
        .into_iter()
        .map(routing_algorithm::RoutingProfileMetadata::from)
        .map(ForeignInto::foreign_into)
        .collect::<Vec<_>>())
}

pub async fn list_de_euclid_active_routing_algorithm(
    state: &SessionState,
    created_by: String,
) -> RoutingResult<Vec<api_routing::RoutingDictionaryRecord>> {
    logger::debug!("decision_engine_euclid: list api call for euclid active routing algorithm");
    let response: Vec<RoutingAlgorithmRecord> = EuclidApiClient::send_decision_engine_request(
        state,
        services::Method::Post,
        format!("routing/list/active/{created_by}").as_str(),
        None::<()>,
        Some(EUCLID_API_TIMEOUT),
        None,
    )
    .await?
    .response
    .ok_or(errors::RoutingError::OpenRouterError(
        "Response from decision engine API is empty".to_string(),
    ))?;

    Ok(response
        .into_iter()
        .map(|record| routing_algorithm::RoutingProfileMetadata::from(record).foreign_into())
        .collect())
}

pub fn compare_and_log_result<T: RoutingEq<T> + Serialize>(
    de_result: Vec<T>,
    result: Vec<T>,
    flow: String,
) {
    let is_equal = de_result
        .iter()
        .zip(result.iter())
        .all(|(a, b)| T::is_equal(a, b));

    let is_equal_in_length = de_result.len() == result.len();
    router_env::logger::debug!(routing_flow=?flow, is_equal=?is_equal, is_equal_length=?is_equal_in_length, de_response=?to_json_string(&de_result), hs_response=?to_json_string(&result), "decision_engine_euclid");
}

pub trait RoutingEq<T> {
    fn is_equal(a: &T, b: &T) -> bool;
}

impl RoutingEq<Self> for api_routing::RoutingDictionaryRecord {
    fn is_equal(a: &Self, b: &Self) -> bool {
        a.id == b.id
            && a.name == b.name
            && a.profile_id == b.profile_id
            && a.description == b.description
            && a.kind == b.kind
            && a.algorithm_for == b.algorithm_for
    }
}

impl RoutingEq<Self> for String {
    fn is_equal(a: &Self, b: &Self) -> bool {
        a.to_lowercase() == b.to_lowercase()
    }
}

impl RoutingEq<Self> for RoutableConnectorChoice {
    fn is_equal(a: &Self, b: &Self) -> bool {
        a.connector.eq(&b.connector)
            && a.choice_kind.eq(&b.choice_kind)
            && a.merchant_connector_id.eq(&b.merchant_connector_id)
    }
}

pub fn to_json_string<T: Serialize>(value: &T) -> String {
    serde_json::to_string(value)
        .map_err(|_| errors::RoutingError::GenericConversionError {
            from: "T".to_string(),
            to: "JsonValue".to_string(),
        })
        .unwrap_or_default()
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct ActivateRoutingConfigRequest {
    pub created_by: String,
    pub routing_algorithm_id: String,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct ListRountingAlgorithmsRequest {
    pub created_by: String,
}

// Maps Hyperswitch `BackendInput` to a `RoutingEvaluateRequest` compatible with Decision Engine
pub fn convert_backend_input_to_routing_eval(
    created_by: String,
    input: BackendInput,
    fallback_output: Vec<DeRoutableConnectorChoice>,
) -> RoutingResult<RoutingEvaluateRequest> {
    let mut params: HashMap<String, Option<ValueType>> = HashMap::new();

    // Payment
    params.insert(
        "amount".to_string(),
        Some(ValueType::Number(
            input
                .payment
                .amount
                .get_amount_as_i64()
                .try_into()
                .unwrap_or_default(),
        )),
    );
    params.insert(
        "currency".to_string(),
        Some(ValueType::EnumVariant(input.payment.currency.to_string())),
    );

    if let Some(auth_type) = input.payment.authentication_type {
        params.insert(
            "authentication_type".to_string(),
            Some(ValueType::EnumVariant(auth_type.to_string())),
        );
    }
    if let Some(bin) = input.payment.card_bin {
        params.insert("card_bin".to_string(), Some(ValueType::StrValue(bin)));
    }
    if let Some(capture_method) = input.payment.capture_method {
        params.insert(
            "capture_method".to_string(),
            Some(ValueType::EnumVariant(capture_method.to_string())),
        );
    }
    if let Some(country) = input.payment.business_country {
        params.insert(
            "business_country".to_string(),
            Some(ValueType::EnumVariant(country.to_string())),
        );
    }
    if let Some(country) = input.payment.billing_country {
        params.insert(
            "billing_country".to_string(),
            Some(ValueType::EnumVariant(country.to_string())),
        );
    }
    if let Some(label) = input.payment.business_label {
        params.insert(
            "business_label".to_string(),
            Some(ValueType::StrValue(label)),
        );
    }
    if let Some(sfu) = input.payment.setup_future_usage {
        params.insert(
            "setup_future_usage".to_string(),
            Some(ValueType::EnumVariant(sfu.to_string())),
        );
    }

    // PaymentMethod
    if let Some(pm) = input.payment_method.payment_method {
        params.insert(
            "payment_method".to_string(),
            Some(ValueType::EnumVariant(pm.to_string())),
        );
        if let Some(pmt) = input.payment_method.payment_method_type {
            match (pmt, pm).into_dir_value() {
                Ok(dv) => insert_dirvalue_param(&mut params, dv),
                Err(e) => logger::debug!(
                    ?e,
                    ?pmt,
                    ?pm,
                    "decision_engine_euclid: into_dir_value failed; skipping subset param"
                ),
            }
        }
    }
    if let Some(pmt) = input.payment_method.payment_method_type {
        params.insert(
            "payment_method_type".to_string(),
            Some(ValueType::EnumVariant(pmt.to_string())),
        );
    }
    if let Some(network) = input.payment_method.card_network {
        params.insert(
            "card_network".to_string(),
            Some(ValueType::EnumVariant(network.to_string())),
        );
    }

    // Mandate
    if let Some(pt) = input.mandate.payment_type {
        params.insert(
            "payment_type".to_string(),
            Some(ValueType::EnumVariant(pt.to_string())),
        );
    }
    if let Some(mt) = input.mandate.mandate_type {
        params.insert(
            "mandate_type".to_string(),
            Some(ValueType::EnumVariant(mt.to_string())),
        );
    }
    if let Some(mat) = input.mandate.mandate_acceptance_type {
        params.insert(
            "mandate_acceptance_type".to_string(),
            Some(ValueType::EnumVariant(mat.to_string())),
        );
    }

    // Metadata
    if let Some(meta) = input.metadata {
        for (k, v) in meta.into_iter() {
            params.insert(
                k.clone(),
                Some(ValueType::MetadataVariant(MetadataValue {
                    key: k,
                    value: v,
                })),
            );
        }
    }

    Ok(RoutingEvaluateRequest {
        created_by,
        parameters: params,
        fallback_output,
    })
}

// All the independent variants of payment method types, configured via dashboard
fn insert_dirvalue_param(params: &mut HashMap<String, Option<ValueType>>, dv: dir::DirValue) {
    match dv {
        dir::DirValue::RewardType(v) => {
            params.insert(
                "reward".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::CardType(v) => {
            params.insert(
                "card".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::PayLaterType(v) => {
            params.insert(
                "pay_later".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::WalletType(v) => {
            params.insert(
                "wallet".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::VoucherType(v) => {
            params.insert(
                "voucher".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::BankRedirectType(v) => {
            params.insert(
                "bank_redirect".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::BankDebitType(v) => {
            params.insert(
                "bank_debit".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::BankTransferType(v) => {
            params.insert(
                "bank_transfer".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::RealTimePaymentType(v) => {
            params.insert(
                "real_time_payment".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::UpiType(v) => {
            params.insert(
                "upi".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::GiftCardType(v) => {
            params.insert(
                "gift_card".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::CardRedirectType(v) => {
            params.insert(
                "card_redirect".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::OpenBankingType(v) => {
            params.insert(
                "open_banking".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::MobilePaymentType(v) => {
            params.insert(
                "mobile_payment".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        dir::DirValue::CryptoType(v) => {
            params.insert(
                "crypto".to_string(),
                Some(ValueType::EnumVariant(v.to_string())),
            );
        }
        other => {
            // all other values can be ignored for now as they don't converge with
            // payment method type
            logger::warn!(
                ?other,
                "decision_engine_euclid: unmapped dir::DirValue; add a mapping here"
            );
        }
    }
}

#[derive(Debug, Clone, serde::Deserialize)]
struct DeErrorResponse {
    code: String,
    message: String,
    data: Option<serde_json::Value>,
}

impl DecisionEngineErrorsInterface for DeErrorResponse {
    fn get_error_message(&self) -> String {
        self.message.clone()
    }

    fn get_error_code(&self) -> String {
        self.code.clone()
    }

    fn get_error_data(&self) -> Option<String> {
        self.data.as_ref().map(|data| data.to_string())
    }
}

impl DecisionEngineErrorsInterface for or_types::ErrorResponse {
    fn get_error_message(&self) -> String {
        self.error_message.clone()
    }

    fn get_error_code(&self) -> String {
        self.error_code.clone()
    }

    fn get_error_data(&self) -> Option<String> {
        Some(format!(
            "decision_engine Error: {}",
            self.error_message.clone()
        ))
    }
}

pub type Metadata = HashMap<String, serde_json::Value>;

/// Represents a single comparison condition.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Comparison {
    /// The left hand side which will always be a domain input identifier like "payment.method.cardtype"
    pub lhs: String,
    /// The comparison operator
    pub comparison: ComparisonType,
    /// The value to compare against
    pub value: ValueType,
    /// Additional metadata that the Static Analyzer and Backend does not touch.
    /// This can be used to store useful information for the frontend and is required for communication
    /// between the static analyzer and the frontend.
    // #[schema(value_type=HashMap<String, serde_json::Value>)]
    pub metadata: Metadata,
}

/// Represents all the conditions of an IF statement
/// eg:
///
/// ```text
/// payment.method = card & payment.method.cardtype = debit & payment.method.network = diners
/// ```


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

pub struct RoutingConfigRequest


// Implementation: impl CurrencyFactors
// File: crates/currency_conversion/src/types.rs
// Module: currency_conversion
// Methods: 1 total (1 public)

impl CurrencyFactors


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

pub struct GetDisputeMetricRequest


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

pub struct RefundMetricsBucketResponse


// Struct: FxExchangeRatesCacheEntry
// File: crates/router/src/utils/currency.rs
// Module: router
// Implementations: 2
// Traits: ApiEventMetric

pub struct FxExchangeRatesCacheEntry


// Struct: TsysTransactionDetails
// File: crates/hyperswitch_connectors/src/connectors/tsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TsysTransactionDetails


// Struct: GpaymentsConnectorMetaData
// File: crates/hyperswitch_connectors/src/connectors/gpayments/gpayments_types.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GpaymentsConnectorMetaData


// Struct: SantanderPix
// File: crates/hyperswitch_connectors/src/connectors/santander/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SantanderPix


// Function: find_optional_by_merchant_id_merchant_reference_id
// File: crates/diesel_models/src/query/customers.rs
// Module: diesel_models

pub fn find_optional_by_merchant_id_merchant_reference_id(
        conn: &PgPooledConn,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
    ) -> StorageResult<Option<Self>>


// Implementation: impl FromStr for for AciRefundStatus
// File: crates/hyperswitch_connectors/src/connectors/aci/transformers.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl FromStr for for AciRefundStatus


// Struct: CheckTokenStatus
// File: crates/router/src/types/payment_methods.rs
// Module: router
// Implementations: 0

pub struct CheckTokenStatus


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

impl Paypal


// Struct: SurchargeDecisionConfigReq
// File: crates/api_models/src/surcharge_decision_configs.rs
// Module: api_models
// Implementations: 1
// Traits: events::ApiEventMetric

pub struct SurchargeDecisionConfigReq


// Implementation: impl common_utils::events::ApiEventMetric
        for for RefundsGenericRequestWithResourceId
// File: crates/router/src/routes/refunds.rs
// Module: router
// Methods: 1 total (0 public)

impl common_utils::events::ApiEventMetric
        for for RefundsGenericRequestWithResourceId


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

impl api::PaymentAuthorize for for Loonio


// File: crates/router/src/workflows/dispute_list.rs
// Module: router
// Public functions: 3
// Public structs: 1

use std::ops::Deref;

use common_utils::ext_traits::{StringExt, ValueExt};
use diesel_models::process_tracker::business_status;
use error_stack::ResultExt;
use router_env::{logger, tracing::Instrument};
use scheduler::{
    consumer::{self, types::process_data, workflows::ProcessTrackerWorkflow},
    errors as sch_errors, utils as scheduler_utils,
};

use crate::{
    core::disputes,
    db::StorageInterface,
    errors,
    routes::SessionState,
    types::{api, domain, storage},
};

pub struct DisputeListWorkflow;

/// This workflow fetches disputes from the connector for a given time range
/// and creates a process tracker task for each dispute.
/// It also schedules the next dispute list sync after dispute_polling_hours.
#[async_trait::async_trait]
impl ProcessTrackerWorkflow<SessionState> for DisputeListWorkflow {
    #[cfg(feature = "v2")]
    async fn execute_workflow<'a>(
        &'a self,
        _state: &'a SessionState,
        _process: storage::ProcessTracker,
    ) -> Result<(), sch_errors::ProcessTrackerError> {
        todo!()
    }

    #[cfg(feature = "v1")]
    async fn execute_workflow<'a>(
        &'a self,
        state: &'a SessionState,
        process: storage::ProcessTracker,
    ) -> Result<(), sch_errors::ProcessTrackerError> {
        let db = &*state.store;
        let tracking_data: api::DisputeListPTData = process
            .tracking_data
            .clone()
            .parse_value("ProcessDisputePTData")?;
        let key_manager_state = &state.into();
        let key_store = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &tracking_data.merchant_id,
                &db.get_master_key().to_vec().into(),
            )
            .await?;

        let merchant_account = db
            .find_merchant_account_by_merchant_id(
                key_manager_state,
                &tracking_data.merchant_id.clone(),
                &key_store,
            )
            .await?;

        let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
            merchant_account.clone(),
            key_store.clone(),
        )));

        let business_profile = state
            .store
            .find_business_profile_by_profile_id(
                &(state).into(),
                merchant_context.get_merchant_key_store(),
                &tracking_data.profile_id,
            )
            .await?;

        if process.retry_count == 0 {
            let m_db = state.clone().store;
            let m_tracking_data = tracking_data.clone();
            let dispute_polling_interval = *business_profile
                .dispute_polling_interval
                .unwrap_or_default()
                .deref();

            tokio::spawn(
                async move {
                    schedule_next_dispute_list_task(
                        &*m_db,
                        &m_tracking_data,
                        dispute_polling_interval,
                    )
                    .await
                    .map_err(|error| {
                        logger::error!(
                            "Failed to add dispute list task to process tracker: {error}"
                        )
                    })
                }
                .in_current_span(),
            );
        };

        let response = Box::pin(disputes::fetch_disputes_from_connector(
            state.clone(),
            merchant_context,
            tracking_data.merchant_connector_id,
            hyperswitch_domain_models::router_request_types::FetchDisputesRequestData {
                created_from: tracking_data.created_from,
                created_till: tracking_data.created_till,
            },
        ))
        .await
        .attach_printable("Dispute update failed");

        if response.is_err() {
            retry_sync_task(
                db,
                tracking_data.connector_name,
                tracking_data.merchant_id,
                process,
            )
            .await?;
        } else {
            state
                .store
                .as_scheduler()
                .finish_process_with_business_status(process, business_status::COMPLETED_BY_PT)
                .await?
        }

        Ok(())
    }

    async fn error_handler<'a>(
        &'a self,
        state: &'a SessionState,
        process: storage::ProcessTracker,
        error: sch_errors::ProcessTrackerError,
    ) -> errors::CustomResult<(), sch_errors::ProcessTrackerError> {
        consumer::consumer_error_handler(state.store.as_scheduler(), process, error).await
    }
}

pub async fn get_sync_process_schedule_time(
    db: &dyn StorageInterface,
    connector: &str,
    merchant_id: &common_utils::id_type::MerchantId,
    retry_count: i32,
) -> Result<Option<time::PrimitiveDateTime>, errors::ProcessTrackerError> {
    let mapping: common_utils::errors::CustomResult<
        process_data::ConnectorPTMapping,
        errors::StorageError,
    > = db
        .find_config_by_key(&format!("pt_mapping_{connector}"))
        .await
        .map(|value| value.config)
        .and_then(|config| {
            config
                .parse_struct("ConnectorPTMapping")
                .change_context(errors::StorageError::DeserializationFailed)
        });
    let mapping = match mapping {
        Ok(x) => x,
        Err(error) => {
            logger::info!(?error, "Redis Mapping Error");
            process_data::ConnectorPTMapping::default()
        }
    };
    let time_delta = scheduler_utils::get_schedule_time(mapping, merchant_id, retry_count);

    Ok(scheduler_utils::get_time_from_delta(time_delta))
}

/// Schedule the task for retry
///
/// Returns bool which indicates whether this was the last retry or not
pub async fn retry_sync_task(
    db: &dyn StorageInterface,
    connector: String,
    merchant_id: common_utils::id_type::MerchantId,
    pt: storage::ProcessTracker,
) -> Result<bool, sch_errors::ProcessTrackerError> {
    let schedule_time: Option<time::PrimitiveDateTime> =
        get_sync_process_schedule_time(db, &connector, &merchant_id, pt.retry_count + 1).await?;

    match schedule_time {
        Some(s_time) => {
            db.as_scheduler().retry_process(pt, s_time).await?;
            Ok(false)
        }
        None => {
            db.as_scheduler()
                .finish_process_with_business_status(pt, business_status::RETRIES_EXCEEDED)
                .await?;
            Ok(true)
        }
    }
}

#[cfg(feature = "v1")]
pub async fn schedule_next_dispute_list_task(
    db: &dyn StorageInterface,
    tracking_data: &api::DisputeListPTData,
    dispute_polling_interval: i32,
) -> Result<(), errors::ProcessTrackerError> {
    let new_created_till = tracking_data
        .created_till
        .checked_add(time::Duration::hours(i64::from(dispute_polling_interval)))
        .ok_or(sch_errors::ProcessTrackerError::TypeConversionError)?;

    let fetch_request = hyperswitch_domain_models::router_request_types::FetchDisputesRequestData {
        created_from: tracking_data.created_till,
        created_till: new_created_till,
    };

    disputes::add_dispute_list_task_to_pt(
        db,
        &tracking_data.connector_name,
        tracking_data.merchant_id.clone(),
        tracking_data.merchant_connector_id.clone(),
        tracking_data.profile_id.clone(),
        fetch_request,
    )
    .await?;
    Ok(())
}


// Implementation: impl api::ExternalVault for for Vgs
// File: crates/hyperswitch_connectors/src/connectors/vgs.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ExternalVault for for Vgs


// Implementation: impl AuthNFlowType
// File: crates/hyperswitch_domain_models/src/router_request_types/authentication.rs
// Module: hyperswitch_domain_models
// Methods: 7 total (7 public)

impl AuthNFlowType


// Function: new
// File: crates/hyperswitch_connectors/src/connectors/razorpay.rs
// Module: hyperswitch_connectors

pub fn new() -> &'static Self


// Function: get_vault_operation_for_pre_network_tokenization
// File: crates/router/src/core/payments.rs
// Module: router

pub fn get_vault_operation_for_pre_network_tokenization(
    state: &SessionState,
    customer_id: id_type::CustomerId,
    card_data: &hyperswitch_domain_models::payment_method_data::Card,
) -> payments::VaultOperation


// Trait: ForeignIDRef
// File: crates/hyperswitch_domain_models/src/lib.rs
// Module: hyperswitch_domain_models

pub trait ForeignIDRef


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

pub use hyperswitch_domain_models::router_request_types::authentication::MessageCategory;
pub use hyperswitch_interfaces::api::authentication_v2::ExternalAuthenticationV2;


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

impl ConnectorValidation for for Nordea


// Function: generate_new
// File: crates/router/src/types/domain/user.rs
// Module: router

pub fn generate_new() -> Self


// File: crates/router/src/routes/subscription.rs
// Module: router
// Public functions: 2

//! Analysis for usage of Subscription in Payment flows
//!
//! Functions that are used to perform the api level configuration and retrieval
//! of various types under Subscriptions.

use actix_web::{web, HttpRequest, HttpResponse, Responder};
use api_models::subscription as subscription_types;
use hyperswitch_domain_models::errors;
use router_env::{
    tracing::{self, instrument},
    Flow,
};

use crate::{
    core::{api_locking, subscription},
    headers::X_PROFILE_ID,
    routes::AppState,
    services::{api as oss_api, authentication as auth, authorization::permissions::Permission},
    types::domain,
};

#[cfg(all(feature = "oltp", feature = "v1"))]
#[instrument(skip_all)]
pub async fn create_subscription(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<subscription_types::CreateSubscriptionRequest>,
) -> impl Responder {
    let flow = Flow::CreateSubscription;
    let profile_id = match req.headers().get(X_PROFILE_ID) {
        Some(val) => val.to_str().unwrap_or_default().to_string(),
        None => {
            return HttpResponse::BadRequest().json(
                errors::api_error_response::ApiErrorResponse::MissingRequiredField {
                    field_name: "x-profile-id",
                },
            );
        }
    };
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        move |state, auth: auth::AuthenticationData, payload, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            subscription::create_subscription(
                state,
                merchant_context,
                profile_id.clone(),
                payload.clone(),
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfileSubscriptionWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all)]
pub async fn confirm_subscription(
    state: web::Data<AppState>,
    req: HttpRequest,
    subscription_id: web::Path<common_utils::id_type::SubscriptionId>,
    json_payload: web::Json<subscription_types::ConfirmSubscriptionRequest>,
) -> impl Responder {
    let flow = Flow::ConfirmSubscription;
    let subscription_id = subscription_id.into_inner();
    let profile_id = match req.headers().get(X_PROFILE_ID) {
        Some(val) => val.to_str().unwrap_or_default().to_string(),
        None => {
            return HttpResponse::BadRequest().json(
                errors::api_error_response::ApiErrorResponse::MissingRequiredField {
                    field_name: "x-profile-id",
                },
            );
        }
    };
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, auth: auth::AuthenticationData, payload, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            subscription::confirm_subscription(
                state,
                merchant_context,
                profile_id.clone(),
                payload.clone(),
                subscription_id.clone(),
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfileSubscriptionWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


// Implementation: impl AuthInfo for for AuthenticationData
// File: crates/router/src/services/authentication.rs
// Module: router
// Methods: 1 total (0 public)

impl AuthInfo for for AuthenticationData


// Struct: GocardlessPayment
// File: crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GocardlessPayment


// Struct: MerchantConnectorCreate
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 2

pub struct MerchantConnectorCreate


// Struct: ProcessTrackerDeprecated
// File: crates/router/src/routes/app.rs
// Module: router
// Implementations: 1

pub struct ProcessTrackerDeprecated


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

impl api::ConnectorAccessToken for for Forte


// Function: render_payout_link
// File: crates/router/src/routes/payout_link.rs
// Module: router

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


// Function: payments_connector_session
// File: crates/openapi/src/routes/payments.rs
// Module: openapi

pub fn payments_connector_session()


// Struct: InjectorRequest
// File: crates/injector/src/types.rs
// Module: injector
// Implementations: 1

pub struct InjectorRequest


// Struct: OrderFulfillment
// File: crates/hyperswitch_connectors/src/connectors/riskified/transformers/api.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct OrderFulfillment


// Struct: ValidateResult
// File: crates/router/src/core/payments/operations.rs
// Module: router
// Implementations: 0

pub struct ValidateResult


// Struct: DatatransSuccessResponse
// File: crates/hyperswitch_connectors/src/connectors/datatrans/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DatatransSuccessResponse


// Function: authentication_create_core
// File: crates/router/src/core/unified_authentication_service.rs
// Module: router

pub fn authentication_create_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: AuthenticationCreateRequest,
) -> RouterResponse<AuthenticationResponse>


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

pub fn server(state: AppState) -> Scope


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

impl Aci


// Struct: CardDetails
// File: crates/hyperswitch_connectors/src/connectors/peachpayments/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CardDetails


// Struct: BankRedirectConfig
// File: crates/payment_methods/src/configs/settings.rs
// Module: payment_methods
// Implementations: 0

pub struct BankRedirectConfig


// Struct: ConsultaOperacionesReturn
// File: crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ConsultaOperacionesReturn


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

impl ConnectorSpecifications for for Plaid


// Struct: DecisionEngineGatewayWiseExtraScore
// File: crates/api_models/src/open_router.rs
// Module: api_models
// Implementations: 1

pub struct DecisionEngineGatewayWiseExtraScore


// Struct: TokenRetryInfo
// File: crates/router/src/types/storage/revenue_recovery_redis_operation.rs
// Module: router
// Implementations: 0

pub struct TokenRetryInfo


// Struct: SchedulerHealthCheckResponse
// File: crates/api_models/src/health_check.rs
// Module: api_models
// Implementations: 1
// Traits: common_utils::events::ApiEventMetric

pub struct SchedulerHealthCheckResponse


// Function: get_groups_and_resources_for_role_from_token
// File: crates/router/src/routes/user_role.rs
// Module: router

pub fn get_groups_and_resources_for_role_from_token(
    state: web::Data<AppState>,
    req: HttpRequest,
) -> HttpResponse


// Implementation: impl super::KafkaMessage for for KafkaRefundEvent
// File: crates/router/src/services/kafka/refund_event.rs
// Module: router
// Methods: 2 total (0 public)

impl super::KafkaMessage for for KafkaRefundEvent


    Success,
    #[serde(alias = "TransactionFailure")]
    TransactionFailure,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    serde::Serialize,
    serde::Deserialize,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum RelayStatus {
    Created,
    #[default]
    Pending,
    Success,
    Failure,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Hash,
    PartialEq,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    serde::Serialize,
    serde::Deserialize,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum RelayType {
    Refund,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    serde::Serialize,
    serde::Deserialize,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
pub enum FrmTransactionType {
    #[default]
    PreFrm,
    PostFrm,
}

/// The status of the mandate, which indicates whether it can be used to initiate a payment.
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    Default,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MandateStatus {
    #[default]
    Active,
    Inactive,
    Pending,
    Revoked,
}

/// Indicates the card network.
#[derive(
    Clone,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
pub enum CardNetwork {
    #[serde(alias = "VISA")]
    Visa,
    #[serde(alias = "MASTERCARD")]
    Mastercard,
    #[serde(alias = "AMERICANEXPRESS")]
    #[serde(alias = "AMEX")]
    AmericanExpress,
    JCB,
    #[serde(alias = "DINERSCLUB")]
    DinersClub,
    #[serde(alias = "DISCOVER")]
    Discover,
    #[serde(alias = "CARTESBANCAIRES")]
    CartesBancaires,
    #[serde(alias = "UNIONPAY")]
    UnionPay,
    #[serde(alias = "INTERAC")]
    Interac,
    #[serde(alias = "RUPAY")]
    RuPay,
    #[serde(alias = "MAESTRO")]
    Maestro,
    #[serde(alias = "STAR")]
    Star,
    #[serde(alias = "PULSE")]
    Pulse,
    #[serde(alias = "ACCEL")]
    Accel,
    #[serde(alias = "NYCE")]
    Nyce,
}

#[derive(
    Clone,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::EnumString,
    utoipa::ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
pub enum RegulatedName {
    #[serde(rename = "GOVERNMENT NON-EXEMPT INTERCHANGE FEE (WITH FRAUD)")]
    #[strum(serialize = "GOVERNMENT NON-EXEMPT INTERCHANGE FEE (WITH FRAUD)")]
    NonExemptWithFraud,

    #[serde(untagged)]
    #[strum(default)]
    Unknown(String),
}

#[derive(
    Clone,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::EnumString,
    utoipa::ToSchema,
    Copy,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "lowercase")]
pub enum PanOrToken {
    Pan,
    Token,
}

#[derive(
    Clone,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::EnumString,
    utoipa::ToSchema,
    Copy,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "UPPERCASE")]
#[serde(rename_all = "snake_case")]
pub enum CardType {
    Credit,
    Debit,
}

#[derive(Debug, Clone, Serialize, Deserialize, strum::EnumString, strum::Display)]
#[serde(rename_all = "snake_case")]
pub enum DecisionEngineMerchantCategoryCode {
    #[serde(rename = "merchant_category_code_0001")]
    Mcc0001,
}

impl CardNetwork {
    pub fn is_signature_network(&self) -> bool {
        match self {
            Self::Interac
            | Self::Star
            | Self::Pulse
            | Self::Accel
            | Self::Nyce
            | Self::CartesBancaires => false,

            Self::Visa
            | Self::Mastercard
            | Self::AmericanExpress
            | Self::JCB
            | Self::DinersClub
            | Self::Discover
            | Self::UnionPay
            | Self::RuPay
            | Self::Maestro => true,
        }
    }

    pub fn is_us_local_network(&self) -> bool {
        match self {
            Self::Star | Self::Pulse | Self::Accel | Self::Nyce => true,
            Self::Interac
            | Self::CartesBancaires
            | Self::Visa
            | Self::Mastercard
            | Self::AmericanExpress
            | Self::JCB
            | Self::DinersClub
            | Self::Discover
            | Self::UnionPay
            | Self::RuPay
            | Self::Maestro => false,
        }
    }
}

/// Stage of the dispute
#[derive(
    Clone,
    Copy,
    Default,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum DisputeStage {
    PreDispute,
    #[default]
    Dispute,
    PreArbitration,
    Arbitration,
    DisputeReversal,
}

/// Status of the dispute
#[derive(
    Clone,
    Debug,
    Copy,
    Default,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum DisputeStatus {
    #[default]
    DisputeOpened,
    DisputeExpired,
    DisputeAccepted,
    DisputeCancelled,
    DisputeChallenged,
    // dispute has been successfully challenged by the merchant
    DisputeWon,
    // dispute has been unsuccessfully challenged
    DisputeLost,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    strum::VariantNames,
    ToSchema,
)]
pub enum MerchantCategory {
    #[serde(rename = "Grocery Stores, Supermarkets (5411)")]
    GroceryStoresSupermarkets,
    #[serde(rename = "Lodging-Hotels, Motels, Resorts-not elsewhere classified (7011)")]
    LodgingHotelsMotelsResorts,
    #[serde(rename = "Agricultural Cooperatives (0763)")]
    AgriculturalCooperatives,
    #[serde(rename = "Attorneys, Legal Services (8111)")]
    AttorneysLegalServices,
    #[serde(rename = "Office and Commercial Furniture (5021)")]
    OfficeAndCommercialFurniture,
    #[serde(rename = "Computer Network/Information Services (4816)")]
    ComputerNetworkInformationServices,
    #[serde(rename = "Shoe Stores (5661)")]
    ShoeStores,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    strum::VariantNames,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
pub enum MerchantCategoryCode {
    #[serde(rename = "5411")]
    #[strum(serialize = "5411")]
    Mcc5411,
    #[serde(rename = "7011")]
    #[strum(serialize = "7011")]
    Mcc7011,
    #[serde(rename = "0763")]
    #[strum(serialize = "0763")]
    Mcc0763,
    #[serde(rename = "8111")]
    #[strum(serialize = "8111")]
    Mcc8111,
    #[serde(rename = "5021")]
    #[strum(serialize = "5021")]
    Mcc5021,
    #[serde(rename = "4816")]
    #[strum(serialize = "4816")]
    Mcc4816,
    #[serde(rename = "5661")]
    #[strum(serialize = "5661")]
    Mcc5661,
}

impl MerchantCategoryCode {
    pub fn to_merchant_category_name(&self) -> MerchantCategory {
        match self {
            Self::Mcc5411 => MerchantCategory::GroceryStoresSupermarkets,
            Self::Mcc7011 => MerchantCategory::LodgingHotelsMotelsResorts,
            Self::Mcc0763 => MerchantCategory::AgriculturalCooperatives,
            Self::Mcc8111 => MerchantCategory::AttorneysLegalServices,
            Self::Mcc5021 => MerchantCategory::OfficeAndCommercialFurniture,
            Self::Mcc4816 => MerchantCategory::ComputerNetworkInformationServices,
            Self::Mcc5661 => MerchantCategory::ShoeStores,
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq)]
pub struct MerchantCategoryCodeWithName {
    pub code: MerchantCategoryCode,
    pub name: MerchantCategory,
}

#[derive(
    Clone,
    Debug,
    Eq,
    Default,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::EnumString,
    utoipa::ToSchema,
    Copy
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[rustfmt::skip]
pub enum CountryAlpha2 {
    AF, AX, AL, DZ, AS, AD, AO, AI, AQ, AG, AR, AM, AW, AU, AT,
    AZ, BS, BH, BD, BB, BY, BE, BZ, BJ, BM, BT, BO, BQ, BA, BW,
    BV, BR, IO, BN, BG, BF, BI, KH, CM, CA, CV, KY, CF, TD, CL,
    CN, CX, CC, CO, KM, CG, CD, CK, CR, CI, HR, CU, CW, CY, CZ,
    DK, DJ, DM, DO, EC, EG, SV, GQ, ER, EE, ET, FK, FO, FJ, FI,
    FR, GF, PF, TF, GA, GM, GE, DE, GH, GI, GR, GL, GD, GP, GU,
    GT, GG, GN, GW, GY, HT, HM, VA, HN, HK, HU, IS, IN, ID, IR,
    IQ, IE, IM, IL, IT, JM, JP, JE, JO, KZ, KE, KI, KP, KR, KW,
    KG, LA, LV, LB, LS, LR, LY, LI, LT, LU, MO, MK, MG, MW, MY,
    MV, ML, MT, MH, MQ, MR, MU, YT, MX, FM, MD, MC, MN, ME, MS,
    MA, MZ, MM, NA, NR, NP, NL, NC, NZ, NI, NE, NG, NU, NF, MP,
    NO, OM, PK, PW, PS, PA, PG, PY, PE, PH, PN, PL, PT, PR, QA,
    RE, RO, RU, RW, BL, SH, KN, LC, MF, PM, VC, WS, SM, ST, SA,
    SN, RS, SC, SL, SG, SX, SK, SI, SB, SO, ZA, GS, SS, ES, LK,
    SD, SR, SJ, SZ, SE, CH, SY, TW, TJ, TZ, TH, TL, TG, TK, TO,
    TT, TN, TR, TM, TC, TV, UG, UA, AE, GB, UM, UY, UZ, VU,
    VE, VN, VG, VI, WF, EH, YE, ZM, ZW,
    #[default]
    US
}

#[derive(
    Clone,
    Debug,
    Copy,
    Default,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RequestIncrementalAuthorization {
    True,
    #[default]
    False,
    Default,
}

#[derive(
    Clone,
    Debug,
    Copy,
    Default,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum SplitTxnsEnabled {
    Enable,
    #[default]
    Skip,
}

#[derive(
    Clone,
    Debug,
    Copy,
    Default,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum ActiveAttemptIDType {
    AttemptsGroupID,
    #[default]
    AttemptID,
}

#[derive(Clone, Copy, Eq, Hash, PartialEq, Debug, Serialize, Deserialize, strum::Display, ToSchema,)]
#[rustfmt::skip]
pub enum CountryAlpha3 {
    AFG, ALA, ALB, DZA, ASM, AND, AGO, AIA, ATA, ATG, ARG, ARM, ABW, AUS, AUT,
    AZE, BHS, BHR, BGD, BRB, BLR, BEL, BLZ, BEN, BMU, BTN, BOL, BES, BIH, BWA,
    BVT, BRA, IOT, BRN, BGR, BFA, BDI, CPV, KHM, CMR, CAN, CYM, CAF, TCD, CHL,
    CHN, CXR, CCK, COL, COM, COG, COD, COK, CRI, CIV, HRV, CUB, CUW, CYP, CZE,
    DNK, DJI, DMA, DOM, ECU, EGY, SLV, GNQ, ERI, EST, ETH, FLK, FRO, FJI, FIN,
    FRA, GUF, PYF, ATF, GAB, GMB, GEO, DEU, GHA, GIB, GRC, GRL, GRD, GLP, GUM,
    GTM, GGY, GIN, GNB, GUY, HTI, HMD, VAT, HND, HKG, HUN, ISL, IND, IDN, IRN,
    IRQ, IRL, IMN, ISR, ITA, JAM, JPN, JEY, JOR, KAZ, KEN, KIR, PRK, KOR, KWT,
    KGZ, LAO, LVA, LBN, LSO, LBR, LBY, LIE, LTU, LUX, MAC, MKD, MDG, MWI, MYS,
    MDV, MLI, MLT, MHL, MTQ, MRT, MUS, MYT, MEX, FSM, MDA, MCO, MNG, MNE, MSR,
    MAR, MOZ, MMR, NAM, NRU, NPL, NLD, NCL, NZL, NIC, NER, NGA, NIU, NFK, MNP,
    NOR, OMN, PAK, PLW, PSE, PAN, PNG, PRY, PER, PHL, PCN, POL, PRT, PRI, QAT,
    REU, ROU, RUS, RWA, BLM, SHN, KNA, LCA, MAF, SPM, VCT, WSM, SMR, STP, SAU,
    SEN, SRB, SYC, SLE, SGP, SXM, SVK, SVN, SLB, SOM, ZAF, SGS, SSD, ESP, LKA,
    SDN, SUR, SJM, SWZ, SWE, CHE, SYR, TWN, TJK, TZA, THA, TLS, TGO, TKL, TON,
    TTO, TUN, TUR, TKM, TCA, TUV, UGA, UKR, ARE, GBR, USA, UMI, URY, UZB, VUT,
    VEN, VNM, VGB, VIR, WLF, ESH, YEM, ZMB, ZWE
}

#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Hash,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    Deserialize,
    Serialize,
    utoipa::ToSchema,
)]
pub enum Country {
    Afghanistan,
    AlandIslands,
    Albania,
    Algeria,
    AmericanSamoa,
    Andorra,
    Angola,
    Anguilla,
    Antarctica,
    AntiguaAndBarbuda,
    Argentina,
    Armenia,
    Aruba,
    Australia,
    Austria,
    Azerbaijan,
    Bahamas,
    Bahrain,
    Bangladesh,
    Barbados,
    Belarus,
    Belgium,
    Belize,
    Benin,
    Bermuda,
    Bhutan,
    BoliviaPlurinationalState,
    BonaireSintEustatiusAndSaba,
    BosniaAndHerzegovina,
    Botswana,
    BouvetIsland,
    Brazil,
    BritishIndianOceanTerritory,
    BruneiDarussalam,
    Bulgaria,
    BurkinaFaso,
    Burundi,
    CaboVerde,
    Cambodia,
    Cameroon,
    Canada,
    CaymanIslands,
    CentralAfricanRepublic,
    Chad,
    Chile,
    China,
    ChristmasIsland,
    CocosKeelingIslands,
    Colombia,
    Comoros,
    Congo,
    CongoDemocraticRepublic,
    CookIslands,
    CostaRica,
    CotedIvoire,
    Croatia,
    Cuba,
    Curacao,
    Cyprus,
    Czechia,
    Denmark,
    Djibouti,
    Dominica,
    DominicanRepublic,
    Ecuador,
    Egypt,
    ElSalvador,
    EquatorialGuinea,
    Eritrea,
    Estonia,
    Ethiopia,
    FalklandIslandsMalvinas,
    FaroeIslands,
    Fiji,
    Finland,
    France,
    FrenchGuiana,
    FrenchPolynesia,
    FrenchSouthernTerritories,
    Gabon,
    Gambia,
    Georgia,
    Germany,
    Ghana,
    Gibraltar,
    Greece,
    Greenland,
    Grenada,
    Guadeloupe,
    Guam,
    Guatemala,
    Guernsey,
    Guinea,
    GuineaBissau,
    Guyana,
    Haiti,
    HeardIslandAndMcDonaldIslands,
    HolySee,
    Honduras,
    HongKong,
    Hungary,
    Iceland,
    India,
    Indonesia,
    IranIslamicRepublic,
    Iraq,
    Ireland,
    IsleOfMan,
    Israel,
    Italy,
    Jamaica,
    Japan,
    Jersey,
    Jordan,
    Kazakhstan,
    Kenya,
    Kiribati,
    KoreaDemocraticPeoplesRepublic,
    KoreaRepublic,
    Kuwait,
    Kyrgyzstan,
    LaoPeoplesDemocraticRepublic,
    Latvia,
    Lebanon,
    Lesotho,
    Liberia,
    Libya,
    Liechtenstein,
    Lithuania,
    Luxembourg,
    Macao,
    MacedoniaTheFormerYugoslavRepublic,
    Madagascar,
    Malawi,
    Malaysia,
    Maldives,
    Mali,
    Malta,
    MarshallIslands,
    Martinique,
    Mauritania,
    Mauritius,
    Mayotte,
    Mexico,
    MicronesiaFederatedStates,
    MoldovaRepublic,
    Monaco,
    Mongolia,
    Montenegro,
    Montserrat,
    Morocco,
    Mozambique,
    Myanmar,
    Namibia,
    Nauru,
    Nepal,
    Netherlands,
    NewCaledonia,
    NewZealand,
    Nicaragua,
    Niger,
    Nigeria,
    Niue,
    NorfolkIsland,
    NorthernMarianaIslands,
    Norway,
    Oman,
    Pakistan,
    Palau,
    PalestineState,
    Panama,
    PapuaNewGuinea,
    Paraguay,
    Peru,
    Philippines,
    Pitcairn,
    Poland,
    Portugal,
    PuertoRico,
    Qatar,
    Reunion,
    Romania,
    RussianFederation,
    Rwanda,
    SaintBarthelemy,
    SaintHelenaAscensionAndTristandaCunha,
    SaintKittsAndNevis,
    SaintLucia,
    SaintMartinFrenchpart,
    SaintPierreAndMiquelon,
    SaintVincentAndTheGrenadines,
    Samoa,
    SanMarino,
    SaoTomeAndPrincipe,
    SaudiArabia,
    Senegal,
    Serbia,
    Seychelles,
    SierraLeone,
    Singapore,
    SintMaartenDutchpart,
    Slovakia,
    Slovenia,
    SolomonIslands,
    Somalia,
    SouthAfrica,
    SouthGeorgiaAndTheSouthSandwichIslands,
    SouthSudan,
    Spain,
    SriLanka,
    Sudan,
    Suriname,
    SvalbardAndJanMayen,
    Swaziland,
    Sweden,
    Switzerland,
    SyrianArabRepublic,
    TaiwanProvinceOfChina,
    Tajikistan,
    TanzaniaUnitedRepublic,
    Thailand,
    TimorLeste,
    Togo,
    Tokelau,
    Tonga,
    TrinidadAndTobago,
    Tunisia,
    Turkey,
    Turkmenistan,
    TurksAndCaicosIslands,
    Tuvalu,
    Uganda,
    Ukraine,
    UnitedArabEmirates,
    UnitedKingdomOfGreatBritainAndNorthernIreland,
    UnitedStatesOfAmerica,
    UnitedStatesMinorOutlyingIslands,
    Uruguay,
    Uzbekistan,
    Vanuatu,
    VenezuelaBolivarianRepublic,
    Vietnam,
    VirginIslandsBritish,
    VirginIslandsUS,
    WallisAndFutuna,
    WesternSahara,
    Yemen,
    Zambia,
    Zimbabwe,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    Default,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum FileUploadProvider {
    #[default]
    Router,
    Stripe,
    Checkout,
    Worldpayvantiv,
}

#[derive(
    Debug, Clone, PartialEq, Eq, Serialize, Deserialize, strum::Display, strum::EnumString,
)]
pub enum UsStatesAbbreviation {
    AL,
    AK,
    AS,
    AZ,
    AR,
    CA,
    CO,
    CT,
    DE,
    DC,
    FM,
    FL,
    GA,
    GU,
    HI,
    ID,
    IL,
    IN,
    IA,
    KS,
    KY,
    LA,
    ME,
    MH,
    MD,
    MA,
    MI,
    MN,
    MS,
    MO,
    MT,
    NE,
    NV,
    NH,
    NJ,
    NM,
    NY,
    NC,
    ND,
    MP,
    OH,
    OK,
    OR,
    PW,
    PA,
    PR,
    RI,
    SC,
    SD,
    TN,
    TX,
    UT,
    VT,
    VI,
    VA,
    WA,
    WV,
    WI,
    WY,
}

#[derive(
    Debug, Clone, PartialEq, Eq, Serialize, Deserialize, strum::Display, strum::EnumString,
)]
pub enum AustraliaStatesAbbreviation {
    ACT,
    NT,
    NSW,
    QLD,
    SA,
    TAS,
    VIC,
    WA,
}

#[derive(
    Debug, Clone, PartialEq, Eq, Serialize, Deserialize, strum::Display, strum::EnumString,
)]
pub enum JapanStatesAbbreviation {
    #[strum(serialize = "23")]
    Aichi,
    #[strum(serialize = "05")]
    Akita,
    #[strum(serialize = "02")]
    Aomori,
    #[strum(serialize = "38")]
    Ehime,
    #[strum(serialize = "21")]
    Gifu,
    #[strum(serialize = "10")]
    Gunma,
    #[strum(serialize = "34")]
    Hiroshima,
    #[strum(serialize = "01")]
    Hokkaido,
    #[strum(serialize = "18")]
    Fukui,
    #[strum(serialize = "40")]
    Fukuoka,
    #[strum(serialize = "07")]
    Fukushima,
    #[strum(serialize = "28")]
    Hyogo,
    #[strum(serialize = "08")]
    Ibaraki,
    #[strum(serialize = "17")]
    Ishikawa,
    #[strum(serialize = "03")]
    Iwate,
    #[strum(serialize = "37")]
    Kagawa,
    #[strum(serialize = "46")]
    Kagoshima,
    #[strum(serialize = "14")]
    Kanagawa,
    #[strum(serialize = "39")]
    Kochi,
    #[strum(serialize = "43")]
    Kumamoto,
    #[strum(serialize = "26")]
    Kyoto,
    #[strum(serialize = "24")]
    Mie,
    #[strum(serialize = "04")]
    Miyagi,
    #[strum(serialize = "45")]
    Miyazaki,
    #[strum(serialize = "20")]
    Nagano,
    #[strum(serialize = "42")]
    Nagasaki,
    #[strum(serialize = "29")]
    Nara,
    #[strum(serialize = "15")]
    Niigata,
    #[strum(serialize = "44")]
    Oita,
    #[strum(serialize = "33")]
    Okayama,
    #[strum(serialize = "47")]
    Okinawa,
    #[strum(serialize = "27")]
    Osaka,
    #[strum(serialize = "41")]
    Saga,
    #[strum(serialize = "11")]
    Saitama,
    #[strum(serialize = "25")]
    Shiga,
    #[strum(serialize = "32")]
    Shimane,
    #[strum(serialize = "22")]
    Shizuoka,
    #[strum(serialize = "12")]
    Chiba,
    #[strum(serialize = "36")]
    Tokusima,
    #[strum(serialize = "13")]
    Tokyo,
    #[strum(serialize = "09")]
    Tochigi,
    #[strum(serialize = "31")]
    Tottori,
    #[strum(serialize = "16")]
    Toyama,
    #[strum(serialize = "30")]
    Wakayama,
    #[strum(serialize = "06")]
    Yamagata,
    #[strum(serialize = "35")]
    Yamaguchi,
    #[strum(serialize = "19")]
    Yamanashi,
}

#[derive(
    Debug, Clone, PartialEq, Eq, Serialize, Deserialize, strum::Display, strum::EnumString,
)]
pub enum NewZealandStatesAbbreviation {
    #[strum(serialize = "AUK")]
    Auckland,
    #[strum(serialize = "BOP")]
    BayOfPlenty,
    #[strum(serialize = "CAN")]
    Canterbury,
    #[strum(serialize = "GIS")]
    Gisborne,
    #[strum(serialize = "HKB")]
    HawkesBay,
    #[strum(serialize = "MWT")]
    ManawatūWhanganui,
    #[strum(serialize = "MBH")]
    Marlborough,
    #[strum(serialize = "NSN")]
    Nelson,
    #[strum(serialize = "NTL")]
    Northland,
    #[strum(serialize = "OTA")]
    Otago,
    #[strum(serialize = "STL")]
    Southland,
    #[strum(serialize = "TKI")]
    Taranaki,
    #[strum(serialize = "TAS")]
    Tasman,
    #[strum(serialize = "WKO")]
    Waikato,
    #[strum(serialize = "CIT")]
    ChathamIslandsTerritory,
    #[strum(serialize = "WGN")]
    GreaterWellington,
    #[strum(serialize = "WTC")]
    WestCoast,
}

#[derive(
    Debug, Clone, PartialEq, Eq, Serialize, Deserialize, strum::Display, strum::EnumString,
)]
pub enum SingaporeStatesAbbreviation {
    #[strum(serialize = "01")]
    CentralSingapore,
    #[strum(serialize = "02")]
    NorthEast,
    #[strum(serialize = "03")]
    NorthWest,
    #[strum(serialize = "04")]
    SouthEast,
    #[strum(serialize = "05")]
    SouthWest,
}

#[derive(
    Debug, Clone, PartialEq, Eq, Serialize, Deserialize, strum::Display, strum::EnumString,
)]
pub enum ThailandStatesAbbreviation {
    #[strum(serialize = "37")]
    AmnatCharoen,
    #[strum(serialize = "15")]
    AngThong,
    #[strum(serialize = "10")]
    Bangkok,
    #[strum(serialize = "38")]
    BuengKan,
    #[strum(serialize = "31")]
    BuriRam,
    #[strum(serialize = "24")]
    Chachoengsao,
    #[strum(serialize = "18")]
    ChaiNat,
    #[strum(serialize = "36")]
    Chaiyaphum,
    #[strum(serialize = "22")]
    Chanthaburi,
    #[strum(serialize = "57")]
    ChiangRai,
    #[strum(serialize = "50")]
    ChiangMai,
    #[strum(serialize = "20")]
    ChonBuri,
    #[strum(serialize = "86")]
    Chumphon,
    #[strum(serialize = "46")]
    Kalasin,
    #[strum(serialize = "62")]
    KamphaengPhet,
    #[strum(serialize = "71")]
    Kanchanaburi,
    #[strum(serialize = "40")]
    KhonKaen,
    #[strum(serialize = "81")]
    Krabi,
    #[strum(serialize = "52")]
    Lampang,
    #[strum(serialize = "51")]
    Lamphun,
    #[strum(serialize = "42")]
    Loei,
    #[strum(serialize = "16")]
    LopBuri,
    #[strum(serialize = "58")]
    MaeHongSon,
    #[strum(serialize = "44")]
    MahaSarakham,
    #[strum(serialize = "49")]
    Mukdahan,
    #[strum(serialize = "26")]
    NakhonNayok,
    #[strum(serialize = "73")]
    NakhonPathom,
    #[strum(serialize = "48")]
    NakhonPhanom,
    #[strum(serialize = "30")]
    NakhonRatchasima,
    #[strum(serialize = "60")]
    NakhonSawan,
    #[strum(serialize = "80")]
    NakhonSiThammarat,
    #[strum(serialize = "55")]
    Nan,
    #[strum(serialize = "96")]
    Narathiwat,
    #[strum(serialize = "39")]
    NongBuaLamPhu,
    #[strum(serialize = "43")]
    NongKhai,
    #[strum(serialize = "12")]
    Nonthaburi,
    #[strum(serialize = "13")]
    PathumThani,
    #[strum(serialize = "94")]
    Pattani,
    #[strum(serialize = "82")]
    Phangnga,
    #[strum(serialize = "93")]
    Phatthalung,
    #[strum(serialize = "56")]
    Phayao,
    #[strum(serialize = "S")]
    Phatthaya,
    #[strum(serialize = "67")]
    Phetchabun,
    #[strum(serialize = "76")]
    Phetchaburi,
    #[strum(serialize = "66")]
    Phichit,
    #[strum(serialize = "65")]
    Phitsanulok,
    #[strum(serialize = "54")]
    Phrae,
    #[strum(serialize = "14")]
    PhraNakhonSiAyutthaya,
    #[strum(serialize = "83")]
    Phuket,
    #[strum(serialize = "25")]
    PrachinBuri,
    #[strum(serialize = "77")]
    PrachuapKhiriKhan,
    #[strum(serialize = "85")]
    Ranong,
    #[strum(serialize = "70")]
    Ratchaburi,
    #[strum(serialize = "21")]
    Rayong,
    #[strum(serialize = "45")]
    RoiEt,
    #[strum(serialize = "27")]
    SaKaeo,
    #[strum(serialize = "47")]
    SakonNakhon,
    #[strum(serialize = "11")]
    SamutPrakan,
    #[strum(serialize = "74")]
    SamutSakhon,
    #[strum(serialize = "75")]
    SamutSongkhram,
    #[strum(serialize = "19")]
    Saraburi,
    #[strum(serialize = "91")]
    Satun,
    #[strum(serialize = "33")]
    SiSaKet,
    #[strum(serialize = "17")]
    SingBuri,
    #[strum(serialize = "90")]
    Songkhla,
    #[strum(serialize = "64")]
    Sukhothai,
    #[strum(serialize = "72")]
    SuphanBuri,


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

impl api::PaymentSession for for Braintree


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

impl api::PaymentToken for for Bambora


// Struct: DwollaAmount
// File: crates/hyperswitch_connectors/src/connectors/dwolla/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DwollaAmount


// Implementation: impl GetPaymentMethodType for for CryptoData
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Methods: 1 total (0 public)

impl GetPaymentMethodType for for CryptoData


// Implementation: impl PaymentMethodRecord
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Methods: 3 total (0 public)

impl PaymentMethodRecord


// Struct: PazeConsumer
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PazeConsumer


// Struct: DashboardMetadataUpdateInternal
// File: crates/diesel_models/src/user/dashboard_metadata.rs
// Module: diesel_models
// Implementations: 0

pub struct DashboardMetadataUpdateInternal


// Struct: RedsysRouterData
// File: crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RedsysRouterData<T>


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

impl Responder


// Struct: Nomupay
// File: crates/hyperswitch_connectors/src/connectors/nomupay.rs
// Module: hyperswitch_connectors
// Implementations: 27
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, ConnectorCommon, ConnectorValidation, ConnectorRedirectResponse, api::Payouts, api::PayoutCancel, api::PayoutCreate, api::PayoutEligibility, api::PayoutFulfill, api::PayoutQuote, api::PayoutRecipient, api::PayoutRecipientAccount, api::PayoutSync, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Nomupay

api::Payment api::PaymentSession api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::PaymentToken ConnectorCommon ConnectorValidation ConnectorRedirectResponse api::Payouts api::PayoutCancel api::PayoutCreate api::PayoutEligibility api::PayoutFulfill api::PayoutQuote api::PayoutRecipient api::PayoutRecipientAccount api::PayoutSync webhooks::IncomingWebhook ConnectorSpecifications


// Function: from_merchant_name
// File: crates/common_utils/src/id_type/merchant.rs
// Module: common_utils

// Documentation: Create a Merchant id from MerchantName

pub fn from_merchant_name(merchant_name: MerchantName) -> Self


// Implementation: impl super::payments::distribution::PaymentDistributionAnalytics for for SqlxClient
// File: crates/analytics/src/sqlx.rs
// Module: analytics
// Methods: 0 total (0 public)

impl super::payments::distribution::PaymentDistributionAnalytics for for SqlxClient