// File: crates/router/src/core/user_role.rs
// Module: router
// Public functions: 9

use std::{
    collections::{HashMap, HashSet},
    sync::LazyLock,
};

use api_models::{
    user as user_api,
    user_role::{self as user_role_api, role as role_api},
};
use diesel_models::{
    enums::{UserRoleVersion, UserStatus},
    organization::OrganizationBridge,
    user_role::UserRoleUpdate,
};
use error_stack::{report, ResultExt};
use masking::Secret;

use crate::{
    core::errors::{StorageErrorExt, UserErrors, UserResponse},
    db::user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload},
    routes::{app::ReqState, SessionState},
    services::{
        authentication as auth,
        authorization::{
            info,
            permission_groups::{ParentGroupExt, PermissionGroupExt},
            roles,
        },
        ApplicationResponse,
    },
    types::domain,
    utils,
};
pub mod role;
use common_enums::{EntityType, ParentGroup, PermissionGroup};
use strum::IntoEnumIterator;

// TODO: To be deprecated
pub async fn get_authorization_info_with_groups(
    _state: SessionState,
) -> UserResponse<user_role_api::AuthorizationInfoResponse> {
    Ok(ApplicationResponse::Json(
        user_role_api::AuthorizationInfoResponse(
            info::get_group_authorization_info()
                .ok_or(UserErrors::InternalServerError)
                .attach_printable("No visible groups found")?
                .into_iter()
                .map(user_role_api::AuthorizationInfo::Group)
                .collect(),
        ),
    ))
}

pub async fn get_authorization_info_with_group_tag(
) -> UserResponse<user_role_api::AuthorizationInfoResponse> {
    static GROUPS_WITH_PARENT_TAGS: LazyLock<Vec<user_role_api::ParentInfo>> =
        LazyLock::new(|| {
            PermissionGroup::iter()
                .map(|group| (group.parent(), group))
                .fold(
                    HashMap::new(),
                    |mut acc: HashMap<ParentGroup, Vec<PermissionGroup>>, (key, value)| {
                        acc.entry(key).or_default().push(value);
                        acc
                    },
                )
                .into_iter()
                .filter_map(|(name, value)| {
                    Some(user_role_api::ParentInfo {
                        name: name.clone(),
                        description: info::get_parent_group_description(name)?,
                        groups: value,
                    })
                })
                .collect()
        });

    Ok(ApplicationResponse::Json(
        user_role_api::AuthorizationInfoResponse(
            GROUPS_WITH_PARENT_TAGS
                .iter()
                .cloned()
                .map(user_role_api::AuthorizationInfo::GroupWithTag)
                .collect(),
        ),
    ))
}

pub async fn get_parent_group_info(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    request: role_api::GetParentGroupsInfoQueryParams,
) -> UserResponse<Vec<role_api::ParentGroupInfo>> {
    let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .to_not_found_response(UserErrors::InvalidRoleId)?;

    let entity_type = request
        .entity_type
        .unwrap_or_else(|| role_info.get_entity_type());

    if role_info.get_entity_type() < entity_type {
        return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
            "Invalid operation, requestor entity type = {} cannot access entity type = {}",
            role_info.get_entity_type(),
            entity_type
        ));
    }

    let parent_groups =
        ParentGroup::get_descriptions_for_groups(entity_type, PermissionGroup::iter().collect())
            .unwrap_or_default()
            .into_iter()
            .map(|(parent_group, description)| role_api::ParentGroupInfo {
                name: parent_group.clone(),
                description,
                scopes: PermissionGroup::iter()
                    .filter_map(|group| (group.parent() == parent_group).then_some(group.scope()))
                    // TODO: Remove this hashset conversion when merchant access
                    // and organization access groups are removed
                    .collect::<HashSet<_>>()
                    .into_iter()
                    .collect(),
            })
            .collect::<Vec<_>>();

    Ok(ApplicationResponse::Json(parent_groups))
}

pub async fn update_user_role(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    req: user_role_api::UpdateUserRoleRequest,
    _req_state: ReqState,
) -> UserResponse<()> {
    let role_info = roles::RoleInfo::from_role_id_in_lineage(
        &state,
        &req.role_id,
        &user_from_token.merchant_id,
        &user_from_token.org_id,
        &user_from_token.profile_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .to_not_found_response(UserErrors::InvalidRoleId)?;

    if !role_info.is_updatable() {
        return Err(report!(UserErrors::InvalidRoleOperation))
            .attach_printable(format!("User role cannot be updated to {}", req.role_id));
    }

    let user_to_be_updated =
        utils::user::get_user_from_db_by_email(&state, domain::UserEmail::try_from(req.email)?)
            .await
            .to_not_found_response(UserErrors::InvalidRoleOperation)
            .attach_printable("User not found in our records".to_string())?;

    if user_from_token.user_id == user_to_be_updated.get_user_id() {
        return Err(report!(UserErrors::InvalidRoleOperation))
            .attach_printable("User Changing their own role");
    }

    let updator_role = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .change_context(UserErrors::InternalServerError)?;

    let mut is_updated = false;

    let v2_user_role_to_be_updated = match state
        .global_store
        .find_user_role_by_user_id_and_lineage(
            user_to_be_updated.get_user_id(),
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
            &user_from_token.org_id,
            &user_from_token.merchant_id,
            &user_from_token.profile_id,
            UserRoleVersion::V2,
        )
        .await
    {
        Ok(user_role) => Some(user_role),
        Err(e) => {
            if e.current_context().is_db_not_found() {
                None
            } else {
                return Err(UserErrors::InternalServerError.into());
            }
        }
    };

    if let Some(user_role) = v2_user_role_to_be_updated {
        let role_to_be_updated = roles::RoleInfo::from_role_id_org_id_tenant_id(
            &state,
            &user_role.role_id,
            &user_from_token.org_id,
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
        )
        .await
        .change_context(UserErrors::InternalServerError)?;

        if !role_to_be_updated.is_updatable() {
            return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
                "User role cannot be updated from {}",
                role_to_be_updated.get_role_id()
            ));
        }

        if role_info.get_entity_type() != role_to_be_updated.get_entity_type() {
            return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
                "Upgrade and downgrade of roles is not allowed, user_entity_type = {} req_entity_type = {}",
                role_to_be_updated.get_entity_type(),
                role_info.get_entity_type(),
            ));
        }

        if updator_role.get_entity_type() < role_to_be_updated.get_entity_type() {
            return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
                "Invalid operation, update requestor = {} cannot update target = {}",
                updator_role.get_entity_type(),
                role_to_be_updated.get_entity_type()
            ));
        }

        state
            .global_store
            .update_user_role_by_user_id_and_lineage(
                user_to_be_updated.get_user_id(),
                user_from_token
                    .tenant_id
                    .as_ref()
                    .unwrap_or(&state.tenant.tenant_id),
                &user_from_token.org_id,
                Some(&user_from_token.merchant_id),
                Some(&user_from_token.profile_id),
                UserRoleUpdate::UpdateRole {
                    role_id: req.role_id.clone(),
                    modified_by: user_from_token.user_id.clone(),
                },
                UserRoleVersion::V2,
            )
            .await
            .change_context(UserErrors::InternalServerError)?;

        is_updated = true;
    }

    let v1_user_role_to_be_updated = match state
        .global_store
        .find_user_role_by_user_id_and_lineage(
            user_to_be_updated.get_user_id(),
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
            &user_from_token.org_id,
            &user_from_token.merchant_id,
            &user_from_token.profile_id,
            UserRoleVersion::V1,
        )
        .await
    {
        Ok(user_role) => Some(user_role),
        Err(e) => {
            if e.current_context().is_db_not_found() {
                None
            } else {
                return Err(UserErrors::InternalServerError.into());
            }
        }
    };

    if let Some(user_role) = v1_user_role_to_be_updated {
        let role_to_be_updated = roles::RoleInfo::from_role_id_org_id_tenant_id(
            &state,
            &user_role.role_id,
            &user_from_token.org_id,
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
        )
        .await
        .change_context(UserErrors::InternalServerError)?;

        if !role_to_be_updated.is_updatable() {
            return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
                "User role cannot be updated from {}",
                role_to_be_updated.get_role_id()
            ));
        }

        if role_info.get_entity_type() != role_to_be_updated.get_entity_type() {
            return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
                "Upgrade and downgrade of roles is not allowed, user_entity_type = {} req_entity_type = {}",
                role_to_be_updated.get_entity_type(),
                role_info.get_entity_type(),
            ));
        }

        if updator_role.get_entity_type() < role_to_be_updated.get_entity_type() {
            return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
                "Invalid operation, update requestor = {} cannot update target = {}",
                updator_role.get_entity_type(),
                role_to_be_updated.get_entity_type()
            ));
        }

        state
            .global_store
            .update_user_role_by_user_id_and_lineage(
                user_to_be_updated.get_user_id(),
                user_from_token
                    .tenant_id
                    .as_ref()
                    .unwrap_or(&state.tenant.tenant_id),
                &user_from_token.org_id,
                Some(&user_from_token.merchant_id),
                Some(&user_from_token.profile_id),
                UserRoleUpdate::UpdateRole {
                    role_id: req.role_id.clone(),
                    modified_by: user_from_token.user_id,
                },
                UserRoleVersion::V1,
            )
            .await
            .change_context(UserErrors::InternalServerError)?;

        is_updated = true;
    }

    if !is_updated {
        return Err(report!(UserErrors::InvalidRoleOperation))
            .attach_printable("User with given email is not found in the organization")?;
    }

    auth::blacklist::insert_user_in_blacklist(&state, user_to_be_updated.get_user_id()).await?;

    Ok(ApplicationResponse::StatusOk)
}

pub async fn accept_invitations_v2(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    req: user_role_api::AcceptInvitationsV2Request,
) -> UserResponse<()> {
    let lineages = futures::future::try_join_all(req.into_iter().map(|entity| {
        utils::user_role::get_lineage_for_user_id_and_entity_for_accepting_invite(
            &state,
            &user_from_token.user_id,
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
            entity.entity_id,
            entity.entity_type,
        )
    }))
    .await?
    .into_iter()
    .flatten()
    .collect::<Vec<_>>();

    let update_results = futures::future::join_all(lineages.iter().map(
        |(org_id, merchant_id, profile_id)| async {
            let (update_v1_result, update_v2_result) =
                utils::user_role::update_v1_and_v2_user_roles_in_db(
                    &state,
                    user_from_token.user_id.as_str(),
                    user_from_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                    org_id,
                    merchant_id.as_ref(),
                    profile_id.as_ref(),
                    UserRoleUpdate::UpdateStatus {
                        status: UserStatus::Active,
                        modified_by: user_from_token.user_id.clone(),
                    },
                )
                .await;

            if update_v1_result.is_err_and(|err| !err.current_context().is_db_not_found())
                || update_v2_result.is_err_and(|err| !err.current_context().is_db_not_found())
            {
                Err(report!(UserErrors::InternalServerError))
            } else {
                Ok(())
            }
        },
    ))
    .await;

    if update_results.is_empty() || update_results.iter().all(Result::is_err) {
        return Err(UserErrors::MerchantIdNotFound.into());
    }

    Ok(ApplicationResponse::StatusOk)
}

pub async fn accept_invitations_pre_auth(
    state: SessionState,
    user_token: auth::UserFromSinglePurposeToken,
    req: user_role_api::AcceptInvitationsPreAuthRequest,
) -> UserResponse<user_api::TokenResponse> {
    let lineages = futures::future::try_join_all(req.into_iter().map(|entity| {
        utils::user_role::get_lineage_for_user_id_and_entity_for_accepting_invite(
            &state,
            &user_token.user_id,
            user_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
            entity.entity_id,
            entity.entity_type,
        )
    }))
    .await?
    .into_iter()
    .flatten()
    .collect::<Vec<_>>();

    let update_results = futures::future::join_all(lineages.iter().map(
        |(org_id, merchant_id, profile_id)| async {
            let (update_v1_result, update_v2_result) =
                utils::user_role::update_v1_and_v2_user_roles_in_db(
                    &state,
                    user_token.user_id.as_str(),
                    user_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                    org_id,
                    merchant_id.as_ref(),
                    profile_id.as_ref(),
                    UserRoleUpdate::UpdateStatus {
                        status: UserStatus::Active,
                        modified_by: user_token.user_id.clone(),
                    },
                )
                .await;

            if update_v1_result.is_err_and(|err| !err.current_context().is_db_not_found())
                || update_v2_result.is_err_and(|err| !err.current_context().is_db_not_found())
            {
                Err(report!(UserErrors::InternalServerError))
            } else {
                Ok(())
            }
        },
    ))
    .await;

    if update_results.is_empty() || update_results.iter().all(Result::is_err) {
        return Err(UserErrors::MerchantIdNotFound.into());
    }

    let user_from_db: domain::UserFromStorage = state
        .global_store
        .find_user_by_id(user_token.user_id.as_str())
        .await
        .change_context(UserErrors::InternalServerError)?
        .into();

    let current_flow =
        domain::CurrentFlow::new(user_token, domain::SPTFlow::MerchantSelect.into())?;
    let next_flow = current_flow.next(user_from_db.clone(), &state).await?;

    let token = next_flow.get_token(&state).await?;

    let response = user_api::TokenResponse {
        token: token.clone(),
        token_type: next_flow.get_flow().into(),
    };
    auth::cookies::set_cookie_response(response, token)
}

pub async fn delete_user_role(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    request: user_role_api::DeleteUserRoleRequest,
    _req_state: ReqState,
) -> UserResponse<()> {
    let user_from_db: domain::UserFromStorage = state
        .global_store
        .find_user_by_email(&domain::UserEmail::from_pii_email(request.email)?)
        .await
        .map_err(|e| {
            if e.current_context().is_db_not_found() {
                e.change_context(UserErrors::InvalidRoleOperation)
                    .attach_printable("User not found in our records")
            } else {
                e.change_context(UserErrors::InternalServerError)
            }
        })?
        .into();

    if user_from_db.get_user_id() == user_from_token.user_id {
        return Err(report!(UserErrors::InvalidDeleteOperation))
            .attach_printable("User deleting himself");
    }

    let deletion_requestor_role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .change_context(UserErrors::InternalServerError)?;

    let mut user_role_deleted_flag = false;

    // Find in V2
    let user_role_v2 = match state
        .global_store
        .find_user_role_by_user_id_and_lineage(
            user_from_db.get_user_id(),
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
            &user_from_token.org_id,
            &user_from_token.merchant_id,
            &user_from_token.profile_id,
            UserRoleVersion::V2,
        )
        .await
    {
        Ok(user_role) => Some(user_role),
        Err(e) => {
            if e.current_context().is_db_not_found() {
                None
            } else {
                return Err(UserErrors::InternalServerError.into());
            }
        }
    };

    if let Some(role_to_be_deleted) = user_role_v2 {
        let target_role_info = roles::RoleInfo::from_role_id_in_lineage(
            &state,
            &role_to_be_deleted.role_id,
            &user_from_token.merchant_id,
            &user_from_token.org_id,
            &user_from_token.profile_id,
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
        )
        .await
        .change_context(UserErrors::InternalServerError)?;

        if !target_role_info.is_deletable() {
            return Err(report!(UserErrors::InvalidDeleteOperation)).attach_printable(format!(
                "Invalid operation, role_id = {} is not deletable",
                role_to_be_deleted.role_id
            ));
        }

        if deletion_requestor_role_info.get_entity_type() < target_role_info.get_entity_type() {
            return Err(report!(UserErrors::InvalidDeleteOperation)).attach_printable(format!(
                "Invalid operation, deletion requestor = {} cannot delete target = {}",
                deletion_requestor_role_info.get_entity_type(),
                target_role_info.get_entity_type()
            ));
        }

        user_role_deleted_flag = true;
        state
            .global_store
            .delete_user_role_by_user_id_and_lineage(
                user_from_db.get_user_id(),
                user_from_token
                    .tenant_id
                    .as_ref()
                    .unwrap_or(&state.tenant.tenant_id),
                &user_from_token.org_id,
                &user_from_token.merchant_id,
                &user_from_token.profile_id,
                UserRoleVersion::V2,
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Error while deleting user role")?;
    }

    // Find in V1
    let user_role_v1 = match state
        .global_store
        .find_user_role_by_user_id_and_lineage(
            user_from_db.get_user_id(),
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
            &user_from_token.org_id,
            &user_from_token.merchant_id,
            &user_from_token.profile_id,
            UserRoleVersion::V1,
        )
        .await
    {
        Ok(user_role) => Some(user_role),
        Err(e) => {
            if e.current_context().is_db_not_found() {
                None
            } else {
                return Err(UserErrors::InternalServerError.into());
            }
        }
    };

    if let Some(role_to_be_deleted) = user_role_v1 {
        let target_role_info = roles::RoleInfo::from_role_id_in_lineage(
            &state,
            &role_to_be_deleted.role_id,
            &user_from_token.merchant_id,
            &user_from_token.org_id,
            &user_from_token.profile_id,
            user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
        )
        .await
        .change_context(UserErrors::InternalServerError)?;

        if !target_role_info.is_deletable() {
            return Err(report!(UserErrors::InvalidDeleteOperation)).attach_printable(format!(
                "Invalid operation, role_id = {} is not deletable",
                role_to_be_deleted.role_id
            ));
        }

        if deletion_requestor_role_info.get_entity_type() < target_role_info.get_entity_type() {
            return Err(report!(UserErrors::InvalidDeleteOperation)).attach_printable(format!(
                "Invalid operation, deletion requestor = {} cannot delete target = {}",
                deletion_requestor_role_info.get_entity_type(),
                target_role_info.get_entity_type()
            ));
        }

        user_role_deleted_flag = true;
        state
            .global_store
            .delete_user_role_by_user_id_and_lineage(
                user_from_db.get_user_id(),
                user_from_token
                    .tenant_id
                    .as_ref()
                    .unwrap_or(&state.tenant.tenant_id),
                &user_from_token.org_id,
                &user_from_token.merchant_id,
                &user_from_token.profile_id,
                UserRoleVersion::V1,
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Error while deleting user role")?;
    }

    if !user_role_deleted_flag {
        return Err(report!(UserErrors::InvalidDeleteOperation))
            .attach_printable("User is not associated with the merchant");
    }

    // Check if user has any more role associations
    let remaining_roles = state
        .global_store
        .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
            user_id: user_from_db.get_user_id(),
            tenant_id: user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),

            org_id: None,
            merchant_id: None,
            profile_id: None,
            entity_id: None,
            version: None,
            status: None,
            limit: None,
        })
        .await
        .change_context(UserErrors::InternalServerError)?;

    // If user has no more role associated with him then deleting user
    if remaining_roles.is_empty() {
        state
            .global_store
            .delete_user_by_user_id(user_from_db.get_user_id())
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Error while deleting user entry")?;
    }

    auth::blacklist::insert_user_in_blacklist(&state, user_from_db.get_user_id()).await?;
    Ok(ApplicationResponse::StatusOk)
}

pub async fn list_users_in_lineage(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    request: user_role_api::ListUsersInEntityRequest,
) -> UserResponse<Vec<user_role_api::ListUsersInEntityResponse>> {
    let requestor_role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .change_context(UserErrors::InternalServerError)?;

    let user_roles_set: HashSet<_> = match utils::user_role::get_min_entity(
        requestor_role_info.get_entity_type(),
        request.entity_type,
    )? {
        EntityType::Tenant => {
            let mut org_users = utils::user_role::fetch_user_roles_by_payload(
                &state,
                ListUserRolesByOrgIdPayload {
                    user_id: None,
                    tenant_id: user_from_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                    org_id: &user_from_token.org_id,
                    merchant_id: None,
                    profile_id: None,
                    version: None,
                    limit: None,
                },
                request.entity_type,
            )
            .await?;

            // Fetch tenant user
            let tenant_user = state
                .global_store
                .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                    user_id: &user_from_token.user_id,
                    tenant_id: user_from_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                    org_id: None,
                    merchant_id: None,
                    profile_id: None,
                    entity_id: None,
                    version: None,
                    status: None,
                    limit: None,
                })
                .await
                .change_context(UserErrors::InternalServerError)?;

            org_users.extend(tenant_user);
            org_users
        }
        EntityType::Organization => {
            utils::user_role::fetch_user_roles_by_payload(
                &state,
                ListUserRolesByOrgIdPayload {
                    user_id: None,
                    tenant_id: user_from_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                    org_id: &user_from_token.org_id,
                    merchant_id: None,
                    profile_id: None,
                    version: None,
                    limit: None,
                },
                request.entity_type,
            )
            .await?
        }
        EntityType::Merchant => {
            utils::user_role::fetch_user_roles_by_payload(
                &state,
                ListUserRolesByOrgIdPayload {
                    user_id: None,
                    tenant_id: user_from_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                    org_id: &user_from_token.org_id,
                    merchant_id: Some(&user_from_token.merchant_id),
                    profile_id: None,
                    version: None,
                    limit: None,
                },
                request.entity_type,
            )
            .await?
        }
        EntityType::Profile => {
            utils::user_role::fetch_user_roles_by_payload(
                &state,
                ListUserRolesByOrgIdPayload {
                    user_id: None,
                    tenant_id: user_from_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                    org_id: &user_from_token.org_id,
                    merchant_id: Some(&user_from_token.merchant_id),
                    profile_id: Some(&user_from_token.profile_id),
                    version: None,
                    limit: None,
                },
                request.entity_type,
            )
            .await?
        }
    };

    // This filtering is needed because for org level users in V1, merchant_id is present.
    // Due to this, we get org level users in merchant level users list.
    let user_roles_set = user_roles_set
        .into_iter()
        .filter_map(|user_role| {
            let (_entity_id, entity_type) = user_role.get_entity_id_and_type()?;
            (entity_type <= requestor_role_info.get_entity_type()).then_some(user_role)
        })
        .collect::<HashSet<_>>();

    let mut email_map = state
        .global_store
        .find_users_by_user_ids(
            user_roles_set
                .iter()
                .map(|user_role| user_role.user_id.clone())
                .collect(),
        )
        .await
        .change_context(UserErrors::InternalServerError)?
        .into_iter()
        .map(|user| (user.user_id.clone(), user.email))
        .collect::<HashMap<_, _>>();

    let role_info_map =
        futures::future::try_join_all(user_roles_set.iter().map(|user_role| async {
            roles::RoleInfo::from_role_id_org_id_tenant_id(
                &state,
                &user_role.role_id,
                &user_from_token.org_id,
                user_from_token
                    .tenant_id
                    .as_ref()
                    .unwrap_or(&state.tenant.tenant_id),
            )
            .await
            .map(|role_info| {
                (
                    user_role.role_id.clone(),
                    user_role_api::role::MinimalRoleInfo {
                        role_id: user_role.role_id.clone(),
                        role_name: role_info.get_role_name().to_string(),
                    },
                )
            })
        }))
        .await
        .change_context(UserErrors::InternalServerError)?
        .into_iter()
        .collect::<HashMap<_, _>>();

    let user_role_map = user_roles_set
        .into_iter()
        .fold(HashMap::new(), |mut map, user_role| {
            map.entry(user_role.user_id)
                .or_insert(Vec::with_capacity(1))
                .push(user_role.role_id);
            map
        });

    Ok(ApplicationResponse::Json(
        user_role_map
            .into_iter()
            .map(|(user_id, role_id_vec)| {
                Ok::<_, error_stack::Report<UserErrors>>(user_role_api::ListUsersInEntityResponse {
                    email: email_map
                        .remove(&user_id)
                        .ok_or(UserErrors::InternalServerError)?,
                    roles: role_id_vec
                        .into_iter()
                        .map(|role_id| {
                            role_info_map
                                .get(&role_id)
                                .cloned()
                                .ok_or(UserErrors::InternalServerError)
                        })
                        .collect::<Result<Vec<_>, _>>()?,
                })
            })
            .collect::<Result<Vec<_>, _>>()?,
    ))
}

pub async fn list_invitations_for_user(
    state: SessionState,
    user_from_token: auth::UserIdFromAuth,
) -> UserResponse<Vec<user_role_api::ListInvitationForUserResponse>> {
    let user_roles = state
        .global_store
        .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
            user_id: &user_from_token.user_id,
            tenant_id: user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
            org_id: None,
            merchant_id: None,
            profile_id: None,
            entity_id: None,
            version: None,
            status: Some(UserStatus::InvitationSent),
            limit: None,
        })
        .await
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Failed to list user roles by user id and invitation sent")?
        .into_iter()
        .collect::<HashSet<_>>();

    let (org_ids, merchant_ids, profile_ids_with_merchant_ids) = user_roles.iter().try_fold(
        (Vec::new(), Vec::new(), Vec::new()),
        |(mut org_ids, mut merchant_ids, mut profile_ids_with_merchant_ids), user_role| {
            let (_, entity_type) = user_role
                .get_entity_id_and_type()
                .ok_or(UserErrors::InternalServerError)
                .attach_printable("Failed to compute entity id and type")?;

            match entity_type {
                EntityType::Tenant => {
                    return Err(report!(UserErrors::InternalServerError))
                        .attach_printable("Tenant roles are not allowed for this operation");
                }
                EntityType::Organization => org_ids.push(
                    user_role
                        .org_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)?,
                ),
                EntityType::Merchant => merchant_ids.push(
                    user_role
                        .merchant_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)?,
                ),
                EntityType::Profile => profile_ids_with_merchant_ids.push((
                    user_role
                        .profile_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)?,
                    user_role
                        .merchant_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)?,
                )),
            }

            Ok::<_, error_stack::Report<UserErrors>>((
                org_ids,
                merchant_ids,
                profile_ids_with_merchant_ids,
            ))
        },
    )?;

    let org_name_map = futures::future::try_join_all(org_ids.into_iter().map(|org_id| async {
        let org_name = state
            .accounts_store
            .find_organization_by_org_id(&org_id)
            .await
            .change_context(UserErrors::InternalServerError)?
            .get_organization_name()
            .map(Secret::new);

        Ok::<_, error_stack::Report<UserErrors>>((org_id, org_name))
    }))
    .await?
    .into_iter()
    .collect::<HashMap<_, _>>();

    let key_manager_state = &(&state).into();

    let merchant_name_map = state
        .store
        .list_multiple_merchant_accounts(key_manager_state, merchant_ids)
        .await
        .change_context(UserErrors::InternalServerError)?
        .into_iter()
        .map(|merchant| {
            (
                merchant.get_id().clone(),
                merchant
                    .merchant_name
                    .map(|encryptable_name| encryptable_name.into_inner()),
            )
        })
        .collect::<HashMap<_, _>>();

    let master_key = &state.store.get_master_key().to_vec().into();

    let profile_name_map = futures::future::try_join_all(profile_ids_with_merchant_ids.iter().map(
        |(profile_id, merchant_id)| async {
            let merchant_key_store = state
                .store
                .get_merchant_key_store_by_merchant_id(key_manager_state, merchant_id, master_key)
                .await
                .change_context(UserErrors::InternalServerError)?;

            let business_profile = state
                .store
                .find_business_profile_by_profile_id(
                    key_manager_state,
                    &merchant_key_store,
                    profile_id,
                )
                .await
                .change_context(UserErrors::InternalServerError)?;

            Ok::<_, error_stack::Report<UserErrors>>((
                profile_id.clone(),
                Secret::new(business_profile.profile_name),
            ))
        },
    ))
    .await?
    .into_iter()
    .collect::<HashMap<_, _>>();

    user_roles
        .into_iter()
        .map(|user_role| {
            let (entity_id, entity_type) = user_role
                .get_entity_id_and_type()
                .ok_or(UserErrors::InternalServerError)
                .attach_printable("Failed to compute entity id and type")?;

            let entity_name = match entity_type {
                EntityType::Tenant => {
                    return Err(report!(UserErrors::InternalServerError))
                        .attach_printable("Tenant roles are not allowed for this operation");
                }
                EntityType::Organization => user_role
                    .org_id
                    .as_ref()
                    .and_then(|org_id| org_name_map.get(org_id).cloned())
                    .ok_or(UserErrors::InternalServerError)?,
                EntityType::Merchant => user_role
                    .merchant_id
                    .as_ref()
                    .and_then(|merchant_id| merchant_name_map.get(merchant_id).cloned())
                    .ok_or(UserErrors::InternalServerError)?,
                EntityType::Profile => user_role
                    .profile_id
                    .as_ref()
                    .map(|profile_id| profile_name_map.get(profile_id).cloned())
                    .ok_or(UserErrors::InternalServerError)?,
            };

            Ok(user_role_api::ListInvitationForUserResponse {
                entity_id,
                entity_type,
                entity_name,
                role_id: user_role.role_id,
            })
        })
        .collect::<Result<Vec<_>, _>>()
        .map(ApplicationResponse::Json)
}


// Struct: BamboraapacErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/bamboraapac/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BamboraapacErrorResponse


// Function: get_apple_pay_wallet_data
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models

pub fn get_apple_pay_wallet_data(&self) -> Option<&ApplePayWalletData>


// Function: connector_events_core
// File: crates/analytics/src/connector_events/core.rs
// Module: analytics

pub fn connector_events_core(
    pool: &AnalyticsProvider,
    req: ConnectorEventsRequest,
    merchant_id: &common_utils::id_type::MerchantId,
) -> AnalyticsResult<Vec<ConnectorEventsResult>>


// Function: new
// File: crates/router/src/core/payments/routing/utils.rs
// Module: router

pub fn new(event: Option<routing_events::RoutingEvent>, response: Option<Res>) -> Self


// Struct: MollieRefundRequest
// File: crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MollieRefundRequest


// Struct: PaysafePaymentMethodDetails
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 1

pub struct PaysafePaymentMethodDetails


// Struct: FileMetadataUpdateInternal
// File: crates/diesel_models/src/file.rs
// Module: diesel_models
// Implementations: 0

pub struct FileMetadataUpdateInternal


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

pub struct RoutingConfigRequest


// File: crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
// Module: hyperswitch_domain_models
// Public functions: 46
// Public structs: 11

#[cfg(all(feature = "v1", feature = "olap"))]
use api_models::enums::Connector;
use common_enums as storage_enums;
#[cfg(feature = "v2")]
use common_types::payments as common_payments_types;
#[cfg(feature = "v1")]
use common_types::primitive_wrappers::{
    ExtendedAuthorizationAppliedBool, OvercaptureEnabledBool, RequestExtendedAuthorizationBool,
};
#[cfg(feature = "v2")]
use common_utils::{
    crypto::Encryptable, encryption::Encryption, ext_traits::Encode,
    types::keymanager::ToEncryptable,
};
use common_utils::{
    errors::{CustomResult, ValidationError},
    ext_traits::{OptionExt, ValueExt},
    id_type, pii,
    types::{
        keymanager::{self, KeyManagerState},
        ConnectorTransactionId, ConnectorTransactionIdTrait, CreatedBy, MinorUnit,
    },
};
#[cfg(feature = "v1")]
use diesel_models::{
    ConnectorMandateReferenceId, NetworkDetails, PaymentAttemptUpdate as DieselPaymentAttemptUpdate,
};
use diesel_models::{
    PaymentAttempt as DieselPaymentAttempt, PaymentAttemptNew as DieselPaymentAttemptNew,
};
#[cfg(feature = "v2")]
use diesel_models::{
    PaymentAttemptFeatureMetadata as DieselPaymentAttemptFeatureMetadata,
    PaymentAttemptRecoveryData as DieselPassiveChurnRecoveryData,
};
use error_stack::ResultExt;
#[cfg(feature = "v2")]
use masking::PeekInterface;
use masking::Secret;
#[cfg(feature = "v1")]
use router_env::logger;
#[cfg(feature = "v2")]
use rustc_hash::FxHashMap;
#[cfg(feature = "v1")]
use serde::Deserialize;
use serde::Serialize;
#[cfg(feature = "v2")]
use serde_json::Value;
use time::PrimitiveDateTime;

#[cfg(all(feature = "v1", feature = "olap"))]
use super::PaymentIntent;
#[cfg(feature = "v2")]
use crate::{
    address::Address,
    consts,
    merchant_key_store::MerchantKeyStore,
    router_response_types,
    type_encryption::{crypto_operation, CryptoOperation},
};
use crate::{behaviour, errors, ForeignIDRef};
#[cfg(feature = "v1")]
use crate::{
    mandates::{MandateDataType, MandateDetails},
    router_request_types,
};

#[async_trait::async_trait]
pub trait PaymentAttemptInterface {
    type Error;
    #[cfg(feature = "v1")]
    async fn insert_payment_attempt(
        &self,
        payment_attempt: PaymentAttemptNew,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v2")]
    async fn insert_payment_attempt(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        payment_attempt: PaymentAttempt,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v1")]
    async fn update_payment_attempt_with_attempt_id(
        &self,
        this: PaymentAttempt,
        payment_attempt: PaymentAttemptUpdate,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v2")]
    async fn update_payment_attempt(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        this: PaymentAttempt,
        payment_attempt: PaymentAttemptUpdate,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
        &self,
        connector_transaction_id: &ConnectorTransactionId,
        payment_id: &id_type::PaymentId,
        merchant_id: &id_type::MerchantId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id(
        &self,
        payment_id: &id_type::PaymentId,
        merchant_id: &id_type::MerchantId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id(
        &self,
        payment_id: &id_type::PaymentId,
        merchant_id: &id_type::MerchantId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v2")]
    async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        payment_id: &id_type::GlobalPaymentId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_merchant_id_connector_txn_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_txn_id: &str,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v2")]
    async fn find_payment_attempt_by_profile_id_connector_transaction_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        profile_id: &id_type::ProfileId,
        connector_transaction_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id(
        &self,
        payment_id: &id_type::PaymentId,
        merchant_id: &id_type::MerchantId,
        attempt_id: &str,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_attempt_id_merchant_id(
        &self,
        attempt_id: &str,
        merchant_id: &id_type::MerchantId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v2")]
    async fn find_payment_attempt_by_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        attempt_id: &id_type::GlobalAttemptId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v2")]
    async fn find_payment_attempts_by_payment_intent_id(
        &self,
        state: &KeyManagerState,
        payment_id: &id_type::GlobalPaymentId,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<PaymentAttempt>, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_preprocessing_id_merchant_id(
        &self,
        preprocessing_id: &str,
        merchant_id: &id_type::MerchantId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_attempts_by_merchant_id_payment_id(
        &self,
        merchant_id: &id_type::MerchantId,
        payment_id: &id_type::PaymentId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<PaymentAttempt>, Self::Error>;

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_filters_for_payments(
        &self,
        pi: &[PaymentIntent],
        merchant_id: &id_type::MerchantId,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentListFilters, Self::Error>;

    #[cfg(all(feature = "v1", feature = "olap"))]
    #[allow(clippy::too_many_arguments)]
    async fn get_total_count_of_filtered_payment_attempts(
        &self,
        merchant_id: &id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<Connector>>,
        payment_method: Option<Vec<storage_enums::PaymentMethod>>,
        payment_method_type: Option<Vec<storage_enums::PaymentMethodType>>,
        authentication_type: Option<Vec<storage_enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<storage_enums::CardNetwork>>,
        card_discovery: Option<Vec<storage_enums::CardDiscovery>>,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<i64, Self::Error>;

    #[cfg(all(feature = "v2", feature = "olap"))]
    #[allow(clippy::too_many_arguments)]
    async fn get_total_count_of_filtered_payment_attempts(
        &self,
        merchant_id: &id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<api_models::enums::Connector>>,
        payment_method_type: Option<Vec<storage_enums::PaymentMethod>>,
        payment_method_subtype: Option<Vec<storage_enums::PaymentMethodType>>,
        authentication_type: Option<Vec<storage_enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<storage_enums::CardNetwork>>,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<i64, Self::Error>;
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub struct AttemptAmountDetails {
    /// The total amount for this payment attempt. This includes all the surcharge and tax amounts.
    net_amount: MinorUnit,
    /// The amount that has to be captured,
    amount_to_capture: Option<MinorUnit>,
    /// Surcharge amount for the payment attempt.
    /// This is either derived by surcharge rules, or sent by the merchant
    surcharge_amount: Option<MinorUnit>,
    /// Tax amount for the payment attempt
    /// This is either derived by surcharge rules, or sent by the merchant
    tax_on_surcharge: Option<MinorUnit>,
    /// The total amount that can be captured for this payment attempt.
    amount_capturable: MinorUnit,
    /// Shipping cost for the payment attempt.
    shipping_cost: Option<MinorUnit>,
    /// Tax amount for the order.
    /// This is either derived by calling an external tax processor, or sent by the merchant
    order_tax_amount: Option<MinorUnit>,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub struct AttemptAmountDetailsSetter {
    /// The total amount for this payment attempt. This includes all the surcharge and tax amounts.
    pub net_amount: MinorUnit,
    /// The amount that has to be captured,
    pub amount_to_capture: Option<MinorUnit>,
    /// Surcharge amount for the payment attempt.
    /// This is either derived by surcharge rules, or sent by the merchant
    pub surcharge_amount: Option<MinorUnit>,
    /// Tax amount for the payment attempt
    /// This is either derived by surcharge rules, or sent by the merchant
    pub tax_on_surcharge: Option<MinorUnit>,
    /// The total amount that can be captured for this payment attempt.
    pub amount_capturable: MinorUnit,
    /// Shipping cost for the payment attempt.
    pub shipping_cost: Option<MinorUnit>,
    /// Tax amount for the order.
    /// This is either derived by calling an external tax processor, or sent by the merchant
    pub order_tax_amount: Option<MinorUnit>,
}

/// Set the fields of amount details, since the fields are not public
impl From<AttemptAmountDetailsSetter> for AttemptAmountDetails {
    fn from(setter: AttemptAmountDetailsSetter) -> Self {
        Self {
            net_amount: setter.net_amount,
            amount_to_capture: setter.amount_to_capture,
            surcharge_amount: setter.surcharge_amount,
            tax_on_surcharge: setter.tax_on_surcharge,
            amount_capturable: setter.amount_capturable,
            shipping_cost: setter.shipping_cost,
            order_tax_amount: setter.order_tax_amount,
        }
    }
}

impl AttemptAmountDetails {
    pub fn get_net_amount(&self) -> MinorUnit {
        self.net_amount
    }

    pub fn get_amount_to_capture(&self) -> Option<MinorUnit> {
        self.amount_to_capture
    }

    pub fn get_surcharge_amount(&self) -> Option<MinorUnit> {
        self.surcharge_amount
    }

    pub fn get_tax_on_surcharge(&self) -> Option<MinorUnit> {
        self.tax_on_surcharge
    }

    pub fn get_amount_capturable(&self) -> MinorUnit {
        self.amount_capturable
    }

    pub fn get_shipping_cost(&self) -> Option<MinorUnit> {
        self.shipping_cost
    }

    pub fn get_order_tax_amount(&self) -> Option<MinorUnit> {
        self.order_tax_amount
    }

    pub fn set_amount_to_capture(&mut self, amount_to_capture: MinorUnit) {
        self.amount_to_capture = Some(amount_to_capture);
    }

    /// Validate the amount to capture that is sent in the request
    pub fn validate_amount_to_capture(
        &self,
        request_amount_to_capture: MinorUnit,
    ) -> Result<(), ValidationError> {
        common_utils::fp_utils::when(request_amount_to_capture > self.get_net_amount(), || {
            Err(ValidationError::IncorrectValueProvided {
                field_name: "amount_to_capture",
            })
        })
    }
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub struct ErrorDetails {
    /// The error code that was returned by the connector.
    /// This is a mandatory field. This is used to lookup the global status map record for unified code and retries
    pub code: String,
    /// The error message that was returned by the connector.
    /// This is a mandatory field. This is used to lookup the global status map record for unified message and retries
    pub message: String,
    /// The detailed error reason that was returned by the connector.
    pub reason: Option<String>,
    /// The unified code that is generated by the application based on the global status map record.
    /// This can be relied upon for common error code across all connectors
    pub unified_code: Option<String>,
    /// The unified message that is generated by the application based on the global status map record.
    /// This can be relied upon for common error code across all connectors
    /// If there is translation available, message will be translated to the requested language
    pub unified_message: Option<String>,
    /// This field can be returned for both approved and refused Mastercard payments.
    /// This code provides additional information about the type of transaction or the reason why the payment failed.
    /// If the payment failed, the network advice code gives guidance on if and when you can retry the payment.
    pub network_advice_code: Option<String>,
    /// For card errors resulting from a card issuer decline, a brand specific 2, 3, or 4 digit code which indicates the reason the authorization failed.
    pub network_decline_code: Option<String>,
    /// A string indicating how to proceed with an network error if payment gateway provide one. This is used to understand the network error code better.
    pub network_error_message: Option<String>,
}

#[cfg(feature = "v2")]
impl From<ErrorDetails> for api_models::payments::RecordAttemptErrorDetails {
    fn from(error_details: ErrorDetails) -> Self {
        Self {
            code: error_details.code,
            message: error_details.message,
            network_decline_code: error_details.network_decline_code,
            network_advice_code: error_details.network_advice_code,
            network_error_message: error_details.network_error_message,
        }
    }
}

/// Domain model for the payment attempt.
/// Few fields which are related are grouped together for better readability and understandability.
/// These fields will be flattened and stored in the database in individual columns
#[cfg(feature = "v2")]
#[derive(Clone, Debug, PartialEq, serde::Serialize, router_derive::ToEncryption)]
pub struct PaymentAttempt {
    /// Payment id for the payment attempt
    pub payment_id: id_type::GlobalPaymentId,
    /// Merchant id for the payment attempt
    pub merchant_id: id_type::MerchantId,
    /// Group id for the payment attempt
    pub attempts_group_id: Option<String>,
    /// Amount details for the payment attempt
    pub amount_details: AttemptAmountDetails,
    /// Status of the payment attempt. This is the status that is updated by the connector.
    /// The intent status is updated by the AttemptStatus.
    pub status: storage_enums::AttemptStatus,
    /// Name of the connector that was used for the payment attempt. The connector is either decided by
    /// either running the routing algorithm or by straight through processing request.
    /// This will be updated before calling the connector
    // TODO: use connector enum, this should be done in v1 as well as a part of moving to domain types wherever possible
    pub connector: Option<String>,
    /// Error details in case the payment attempt failed
    pub error: Option<ErrorDetails>,
    /// The authentication type that was requested for the payment attempt.
    /// This authentication type maybe decided by step up 3ds or by running the decision engine.
    pub authentication_type: storage_enums::AuthenticationType,
    /// The time at which the payment attempt was created
    pub created_at: PrimitiveDateTime,
    /// The time at which the payment attempt was last modified
    pub modified_at: PrimitiveDateTime,
    pub last_synced: Option<PrimitiveDateTime>,
    /// The reason for the cancellation of the payment attempt. Some connectors will have strict rules regarding the values this can have
    /// Cancellation reason will be validated at the connector level when building the request
    pub cancellation_reason: Option<String>,
    /// Browser information required for 3DS authentication
    pub browser_info: Option<common_utils::types::BrowserInformation>,
    /// Payment token is the token used for temporary use in case the payment method is stored in vault
    pub payment_token: Option<String>,
    /// Metadata that is returned by the connector.
    pub connector_metadata: Option<pii::SecretSerdeValue>,
    pub payment_experience: Option<storage_enums::PaymentExperience>,
    /// The insensitive data of the payment method data is stored here
    pub payment_method_data: Option<pii::SecretSerdeValue>,
    /// The result of the routing algorithm.
    /// This will store the list of connectors and other related information that was used to route the payment.
    // TODO: change this to type instead of serde_json::Value
    pub routing_result: Option<Value>,
    pub preprocessing_step_id: Option<String>,
    /// Number of captures that have happened for the payment attempt
    pub multiple_capture_count: Option<i16>,
    /// A reference to the payment at connector side. This is returned by the connector
    pub connector_response_reference_id: Option<String>,
    /// Whether the payment was updated by postgres or redis
    pub updated_by: String,
    /// The authentication data which is used for external authentication
    pub redirection_data: Option<router_response_types::RedirectForm>,
    pub encoded_data: Option<Secret<String>>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    /// Whether external 3DS authentication was attempted for this payment.
    /// This is based on the configuration of the merchant in the business profile
    pub external_three_ds_authentication_attempted: Option<bool>,
    /// The connector that was used for external authentication
    pub authentication_connector: Option<String>,
    /// The foreign key reference to the authentication details
    pub authentication_id: Option<id_type::AuthenticationId>,
    pub fingerprint_id: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub customer_acceptance: Option<Secret<common_payments_types::CustomerAcceptance>>,
    /// The profile id for the payment attempt. This will be derived from payment intent.
    pub profile_id: id_type::ProfileId,
    /// The organization id for the payment attempt. This will be derived from payment intent.
    pub organization_id: id_type::OrganizationId,
    /// Payment method type for the payment attempt
    pub payment_method_type: storage_enums::PaymentMethod,
    /// Foreig key reference of Payment method id in case the payment instrument was stored
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,
    /// The reference to the payment at the connector side
    pub connector_payment_id: Option<String>,
    /// The payment method subtype for the payment attempt.
    pub payment_method_subtype: storage_enums::PaymentMethodType,
    /// The authentication type that was applied for the payment attempt.
    pub authentication_applied: Option<common_enums::AuthenticationType>,
    /// A reference to the payment at connector side. This is returned by the connector
    pub external_reference_id: Option<String>,
    /// The billing address for the payment method
    #[encrypt(ty = Value)]
    pub payment_method_billing_address: Option<Encryptable<Address>>,
    /// The global identifier for the payment attempt
    pub id: id_type::GlobalAttemptId,
    /// Connector token information that can be used to make payments directly by the merchant.
    pub connector_token_details: Option<diesel_models::ConnectorTokenDetails>,
    /// Indicates the method by which a card is discovered during a payment
    pub card_discovery: Option<common_enums::CardDiscovery>,
    /// Split payment data
    pub charges: Option<common_types::payments::ConnectorChargeResponseData>,
    /// Additional data that might be required by hyperswitch, to enable some specific features.
    pub feature_metadata: Option<PaymentAttemptFeatureMetadata>,
    /// merchant who owns the credentials of the processor, i.e. processor owner
    pub processor_merchant_id: id_type::MerchantId,
    /// merchantwho invoked the resource based api (identifier) and through what source (Api, Jwt(Dashboard))
    pub created_by: Option<CreatedBy>,
    pub connector_request_reference_id: Option<String>,
    pub network_transaction_id: Option<String>,
}

impl PaymentAttempt {
    #[cfg(feature = "v1")]
    pub fn get_payment_method(&self) -> Option<storage_enums::PaymentMethod> {
        self.payment_method
    }

    #[cfg(feature = "v2")]
    pub fn get_payment_method(&self) -> Option<storage_enums::PaymentMethod> {
        // TODO: check if we can fix this
        Some(self.payment_method_type)
    }

    #[cfg(feature = "v1")]
    pub fn get_payment_method_type(&self) -> Option<storage_enums::PaymentMethodType> {
        self.payment_method_type
    }

    #[cfg(feature = "v2")]
    pub fn get_payment_method_type(&self) -> Option<storage_enums::PaymentMethodType> {
        // TODO: check if we can fix this
        Some(self.payment_method_subtype)
    }

    #[cfg(feature = "v1")]
    pub fn get_id(&self) -> &str {
        &self.attempt_id
    }

    #[cfg(feature = "v2")]
    pub fn get_id(&self) -> &id_type::GlobalAttemptId {
        &self.id
    }

    #[cfg(feature = "v1")]
    pub fn get_connector_payment_id(&self) -> Option<&str> {
        self.connector_transaction_id.as_deref()
    }

    #[cfg(feature = "v2")]
    pub fn get_connector_payment_id(&self) -> Option<&str> {
        self.connector_payment_id.as_deref()
    }

    /// Construct the domain model from the ConfirmIntentRequest and PaymentIntent
    #[cfg(feature = "v2")]
    pub async fn create_domain_model(
        payment_intent: &super::PaymentIntent,
        cell_id: id_type::CellId,
        storage_scheme: storage_enums::MerchantStorageScheme,
        request: &api_models::payments::PaymentsConfirmIntentRequest,
        encrypted_data: DecryptedPaymentAttempt,
    ) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
        let id = id_type::GlobalAttemptId::generate(&cell_id);
        let intent_amount_details = payment_intent.amount_details.clone();

        let attempt_amount_details = intent_amount_details.create_attempt_amount_details(request);

        let now = common_utils::date_time::now();

        let payment_method_billing_address = encrypted_data
            .payment_method_billing_address
            .as_ref()
            .map(|data| {
                data.clone()
                    .deserialize_inner_value(|value| value.parse_value("Address"))
            })
            .transpose()
            .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to decode billing address")?;

        let connector_token = Some(diesel_models::ConnectorTokenDetails {
            connector_mandate_id: None,
            connector_token_request_reference_id: Some(common_utils::generate_id_with_len(
                consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH,
            )),
        });

        let authentication_type = payment_intent.authentication_type.unwrap_or_default();

        Ok(Self {
            payment_id: payment_intent.id.clone(),
            merchant_id: payment_intent.merchant_id.clone(),
            attempts_group_id: None,
            amount_details: attempt_amount_details,
            status: common_enums::AttemptStatus::Started,
            // This will be decided by the routing algorithm and updated in update trackers
            // right before calling the connector
            connector: None,
            authentication_type,
            created_at: now,
            modified_at: now,
            last_synced: None,
            cancellation_reason: None,
            browser_info: request.browser_info.clone(),
            payment_token: request.payment_token.clone(),
            connector_metadata: None,
            payment_experience: None,
            payment_method_data: None,
            routing_result: None,
            preprocessing_step_id: None,
            multiple_capture_count: None,
            connector_response_reference_id: None,
            updated_by: storage_scheme.to_string(),
            redirection_data: None,
            encoded_data: None,
            merchant_connector_id: None,
            external_three_ds_authentication_attempted: None,
            authentication_connector: None,
            authentication_id: None,
            fingerprint_id: None,
            charges: None,
            client_source: None,
            client_version: None,
            customer_acceptance: request.customer_acceptance.clone().map(Secret::new),
            profile_id: payment_intent.profile_id.clone(),
            organization_id: payment_intent.organization_id.clone(),
            payment_method_type: request.payment_method_type,
            payment_method_id: request.payment_method_id.clone(),
            connector_payment_id: None,
            payment_method_subtype: request.payment_method_subtype,
            authentication_applied: None,
            external_reference_id: None,
            payment_method_billing_address,
            error: None,
            connector_token_details: connector_token,
            id,
            card_discovery: None,
            feature_metadata: None,
            processor_merchant_id: payment_intent.merchant_id.clone(),
            created_by: None,
            connector_request_reference_id: None,
            network_transaction_id: None,
        })
    }

    #[cfg(feature = "v2")]
    pub async fn proxy_create_domain_model(
        payment_intent: &super::PaymentIntent,
        cell_id: id_type::CellId,
        storage_scheme: storage_enums::MerchantStorageScheme,
        request: &api_models::payments::ProxyPaymentsRequest,
        encrypted_data: DecryptedPaymentAttempt,
    ) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
        let id = id_type::GlobalAttemptId::generate(&cell_id);
        let intent_amount_details = payment_intent.amount_details.clone();

        let attempt_amount_details =
            intent_amount_details.proxy_create_attempt_amount_details(request);

        let now = common_utils::date_time::now();
        let payment_method_billing_address = encrypted_data
            .payment_method_billing_address
            .as_ref()
            .map(|data| {
                data.clone()
                    .deserialize_inner_value(|value| value.parse_value("Address"))
            })
            .transpose()
            .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to decode billing address")?;
        let connector_token = Some(diesel_models::ConnectorTokenDetails {
            connector_mandate_id: None,
            connector_token_request_reference_id: Some(common_utils::generate_id_with_len(
                consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH,
            )),
        });
        let payment_method_type_data = payment_intent.get_payment_method_type();

        let payment_method_subtype_data = payment_intent.get_payment_method_sub_type();
        let authentication_type = payment_intent.authentication_type.unwrap_or_default();
        Ok(Self {
            payment_id: payment_intent.id.clone(),
            merchant_id: payment_intent.merchant_id.clone(),
            attempts_group_id: None,
            amount_details: attempt_amount_details,
            status: common_enums::AttemptStatus::Started,
            connector: Some(request.connector.clone()),
            authentication_type,
            created_at: now,
            modified_at: now,
            last_synced: None,
            cancellation_reason: None,
            browser_info: request.browser_info.clone(),
            payment_token: None,
            connector_metadata: None,
            payment_experience: None,
            payment_method_data: None,
            routing_result: None,
            preprocessing_step_id: None,
            multiple_capture_count: None,
            connector_response_reference_id: None,
            updated_by: storage_scheme.to_string(),
            redirection_data: None,
            encoded_data: None,
            merchant_connector_id: Some(request.merchant_connector_id.clone()),
            external_three_ds_authentication_attempted: None,
            authentication_connector: None,
            authentication_id: None,
            fingerprint_id: None,
            charges: None,
            client_source: None,
            client_version: None,
            customer_acceptance: None,
            profile_id: payment_intent.profile_id.clone(),
            organization_id: payment_intent.organization_id.clone(),
            payment_method_type: payment_method_type_data
                .unwrap_or(common_enums::PaymentMethod::Card),
            payment_method_id: None,
            connector_payment_id: None,
            payment_method_subtype: payment_method_subtype_data
                .unwrap_or(common_enums::PaymentMethodType::Credit),
            authentication_applied: None,
            external_reference_id: None,
            payment_method_billing_address,
            error: None,
            connector_token_details: connector_token,
            feature_metadata: None,
            id,
            card_discovery: None,
            processor_merchant_id: payment_intent.merchant_id.clone(),
            created_by: None,
            connector_request_reference_id: None,
            network_transaction_id: None,
        })
    }

    #[cfg(feature = "v2")]
    pub async fn external_vault_proxy_create_domain_model(
        payment_intent: &super::PaymentIntent,
        cell_id: id_type::CellId,
        storage_scheme: storage_enums::MerchantStorageScheme,
        request: &api_models::payments::ExternalVaultProxyPaymentsRequest,
        encrypted_data: DecryptedPaymentAttempt,
    ) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
        let id = id_type::GlobalAttemptId::generate(&cell_id);
        let intent_amount_details = payment_intent.amount_details.clone();
        let attempt_amount_details = AttemptAmountDetails {
            net_amount: intent_amount_details.order_amount,
            amount_to_capture: None,
            surcharge_amount: None,
            tax_on_surcharge: None,
            amount_capturable: intent_amount_details.order_amount,
            shipping_cost: None,
            order_tax_amount: None,
        };

        let now = common_utils::date_time::now();
        let payment_method_billing_address = encrypted_data
            .payment_method_billing_address
            .as_ref()
            .map(|data| {
                data.clone()
                    .deserialize_inner_value(|value| value.parse_value("Address"))
            })
            .transpose()
            .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to decode billing address")?;
        let connector_token = Some(diesel_models::ConnectorTokenDetails {
            connector_mandate_id: None,
            connector_token_request_reference_id: Some(common_utils::generate_id_with_len(
                consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH,
            )),
        });
        let payment_method_type_data = payment_intent.get_payment_method_type();

        let payment_method_subtype_data = payment_intent.get_payment_method_sub_type();
        let authentication_type = payment_intent.authentication_type.unwrap_or_default();
        Ok(Self {
            payment_id: payment_intent.id.clone(),
            merchant_id: payment_intent.merchant_id.clone(),
            attempts_group_id: None,
            amount_details: attempt_amount_details,
            status: common_enums::AttemptStatus::Started,
            connector: None,
            authentication_type,
            created_at: now,
            modified_at: now,
            last_synced: None,
            cancellation_reason: None,
            browser_info: request.browser_info.clone(),
            payment_token: request.payment_token.clone(),
            connector_metadata: None,
            payment_experience: None,
            payment_method_data: None,
            routing_result: None,
            preprocessing_step_id: None,
            multiple_capture_count: None,
            connector_response_reference_id: None,
            updated_by: storage_scheme.to_string(),
            redirection_data: None,
            encoded_data: None,
            merchant_connector_id: None,
            external_three_ds_authentication_attempted: None,
            authentication_connector: None,
            authentication_id: None,
            fingerprint_id: None,
            charges: None,
            client_source: None,
            client_version: None,
            customer_acceptance: request.customer_acceptance.clone().map(Secret::new),
            profile_id: payment_intent.profile_id.clone(),
            organization_id: payment_intent.organization_id.clone(),
            payment_method_type: payment_method_type_data
                .unwrap_or(common_enums::PaymentMethod::Card),
            payment_method_id: request.payment_method_id.clone(),
            connector_payment_id: None,
            payment_method_subtype: payment_method_subtype_data
                .unwrap_or(common_enums::PaymentMethodType::Credit),
            authentication_applied: None,
            external_reference_id: None,
            payment_method_billing_address,
            error: None,
            connector_token_details: connector_token,
            feature_metadata: None,
            id,
            card_discovery: None,
            processor_merchant_id: payment_intent.merchant_id.clone(),
            created_by: None,
            connector_request_reference_id: None,
            network_transaction_id: None,
        })
    }

    /// Construct the domain model from the ConfirmIntentRequest and PaymentIntent
    #[cfg(feature = "v2")]
    pub async fn create_domain_model_using_record_request(
        payment_intent: &super::PaymentIntent,
        cell_id: id_type::CellId,
        storage_scheme: storage_enums::MerchantStorageScheme,
        request: &api_models::payments::PaymentsAttemptRecordRequest,
        encrypted_data: DecryptedPaymentAttempt,
    ) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
        let id = id_type::GlobalAttemptId::generate(&cell_id);

        let amount_details = AttemptAmountDetailsSetter::from(&request.amount_details);

        let now = common_utils::date_time::now();
        // we consume transaction_created_at from webhook request, if it is not present we take store current time as transaction_created_at.
        let transaction_created_at = request
            .transaction_created_at
            .unwrap_or(common_utils::date_time::now());

        // This function is called in the record attempt flow, which tells us that this is a payment attempt created by an external system.
        let feature_metadata = PaymentAttemptFeatureMetadata {
            revenue_recovery: Some({
                PaymentAttemptRevenueRecoveryData {
                    attempt_triggered_by: request.triggered_by,
                    charge_id: request.feature_metadata.as_ref().and_then(|metadata| {
                        metadata
                            .revenue_recovery
                            .as_ref()
                            .and_then(|data| data.charge_id.clone())
                    }),
                }
            }),
        };

        let payment_method_data = request
            .payment_method_data
            .as_ref()
            .map(|data| data.payment_method_data.clone().encode_to_value())
            .transpose()
            .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to decode additional payment method data")?
            .map(pii::SecretSerdeValue::new);

        let payment_method_billing_address = encrypted_data
            .payment_method_billing_address
            .as_ref()
            .map(|data| {
                data.clone()
                    .deserialize_inner_value(|value| value.parse_value("Address"))
            })
            .transpose()
            .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to decode billing address")?;
        let error = request.error.as_ref().map(ErrorDetails::from);
        let connector_payment_id = request
            .connector_transaction_id
            .as_ref()
            .map(|txn_id| txn_id.get_id().clone());
        let connector = request.connector.map(|connector| connector.to_string());
        let connector_request_reference_id = payment_intent
            .merchant_reference_id
            .as_ref()
            .map(|id| id.get_string_repr().to_owned());
        Ok(Self {
            payment_id: payment_intent.id.clone(),
            merchant_id: payment_intent.merchant_id.clone(),
            attempts_group_id: None,
            amount_details: AttemptAmountDetails::from(amount_details),
            status: request.status,
            connector,
            authentication_type: storage_enums::AuthenticationType::NoThreeDs,
            created_at: transaction_created_at,
            modified_at: now,
            last_synced: None,
            cancellation_reason: None,
            browser_info: None,
            payment_token: None,


// Function: payouts_list_core
// File: crates/router/src/core/payouts.rs
// Module: router

pub fn payouts_list_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<id_type::ProfileId>>,
    constraints: payouts::PayoutListConstraints,
) -> RouterResponse<payouts::PayoutListResponse>


// Implementation: impl PayoutAttemptInterface for for KafkaStore
// File: crates/router/src/db/kafka_store.rs
// Module: router
// Methods: 6 total (0 public)

impl PayoutAttemptInterface for for KafkaStore


// Function: new_with_implicit_entries
// File: crates/router_env/src/logger/formatter.rs
// Module: router_env

// Documentation: Construct of `FormattingLayer with implicit default entries.

pub fn new_with_implicit_entries(
        service: &str,
        dst_writer: W,
        default_fields: HashMap<String, Value>,
        formatter: F,
    ) -> error_stack::Result<Self, ConfigError>


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

impl api::PaymentVoid for for Worldpayxml


// Struct: Shift4WebhookObjectId
// File: crates/hyperswitch_connectors/src/connectors/shift4/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Shift4WebhookObjectId


// Struct: Evidence
// File: crates/hyperswitch_domain_models/src/router_flow_types/dispute.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct Evidence


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

// Documentation: Routing Algorithm specific to merchants

pub struct MerchantRoutingAlgorithm


// Struct: BraintreeMeta
// File: crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BraintreeMeta


// Struct: SquareWebhookData
// File: crates/hyperswitch_connectors/src/connectors/square/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SquareWebhookData


// Struct: GetnetWebhookNotificationResponse
// File: crates/hyperswitch_connectors/src/connectors/getnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GetnetWebhookNotificationResponse


// Struct: NovalnetPaymentsResponseTransactionData
// File: crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 1

pub struct NovalnetPaymentsResponseTransactionData


// Struct: ExtendedData
// File: crates/hyperswitch_connectors/src/connectors/powertranz/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ExtendedData


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

pub struct IndonesianBankTransferDetails


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

impl api::PaymentSession for for Zen


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

pub struct TsysAuthType


// File: crates/router_env/src/env.rs
// Module: router_env
// Public functions: 3

//! Information about the current environment.

use std::path::PathBuf;

use serde::{Deserialize, Serialize};

/// Environment variables accessed by the application. This module aims to be the source of truth
/// containing all environment variable that the application accesses.
pub mod vars {
    /// Parent directory where `Cargo.toml` is stored.
    pub const CARGO_MANIFEST_DIR: &str = "CARGO_MANIFEST_DIR";

    /// Environment variable that sets development/sandbox/production environment.
    pub const RUN_ENV: &str = "RUN_ENV";

    /// Directory of config TOML files. Default is `config`.
    pub const CONFIG_DIR: &str = "CONFIG_DIR";
}

/// Current environment.
#[derive(
    Debug, Default, Deserialize, Serialize, Clone, Copy, strum::Display, strum::EnumString,
)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum Env {
    /// Development environment.
    #[default]
    Development,
    /// Sandbox environment.
    Sandbox,
    /// Production environment.
    Production,
}

/// Name of current environment. Either "development", "sandbox" or "production".
pub fn which() -> Env {
    #[cfg(debug_assertions)]
    let default_env = Env::Development;
    #[cfg(not(debug_assertions))]
    let default_env = Env::Production;

    std::env::var(vars::RUN_ENV).map_or_else(|_| default_env, |v| v.parse().unwrap_or(default_env))
}

/// Three letter (lowercase) prefix corresponding to the current environment.
/// Either `dev`, `snd` or `prd`.
pub fn prefix_for_env() -> &'static str {
    match which() {
        Env::Development => "dev",
        Env::Sandbox => "snd",
        Env::Production => "prd",
    }
}

/// Path to the root directory of the cargo workspace.
/// It is recommended that this be used by the application as the base path to build other paths
/// such as configuration and logs directories.
pub fn workspace_path() -> PathBuf {
    if let Ok(manifest_dir) = std::env::var(vars::CARGO_MANIFEST_DIR) {
        let mut path = PathBuf::from(manifest_dir);
        path.pop();
        path.pop();
        path
    } else {
        PathBuf::from(".")
    }
}

/// Version of the crate containing the following information:
///
/// - The latest git tag. If tags are not present in the repository, the short commit hash is used
///   instead.
/// - Short hash of the latest git commit.
/// - Timestamp of the latest git commit.
///
/// Example: `0.1.0-abcd012-2038-01-19T03:14:08Z`.
#[cfg(feature = "vergen")]
#[macro_export]
macro_rules! version {
    () => {
        concat!(
            env!("VERGEN_GIT_DESCRIBE"),
            "-",
            env!("VERGEN_GIT_SHA"),
            "-",
            env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
        )
    };
}

/// A string uniquely identifying the application build.
///
/// Consists of a combination of:
/// - Version defined in the crate file
/// - Timestamp of commit
/// - Hash of the commit
/// - Version of rust compiler
/// - Target triple
///
/// Example: `0.1.0-f5f383e-2022-09-04T11:39:37Z-1.63.0-x86_64-unknown-linux-gnu`
#[cfg(feature = "vergen")]
#[macro_export]
macro_rules! build {
    () => {
        concat!(
            env!("CARGO_PKG_VERSION"),
            "-",
            env!("VERGEN_GIT_SHA"),
            "-",
            env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
            "-",
            env!("VERGEN_RUSTC_SEMVER"),
            "-",
            $crate::profile!(),
            "-",
            env!("VERGEN_CARGO_TARGET_TRIPLE"),
        )
    };
}

/// Short hash of the current commit.
///
/// Example: `f5f383e`.
#[cfg(feature = "vergen")]
#[macro_export]
macro_rules! commit {
    () => {
        env!("VERGEN_GIT_SHA")
    };
}

// /// Information about the platform on which service was built, including:
// /// - Information about OS
// /// - Information about CPU
// ///
// /// Example: ``.
// #[macro_export]
// macro_rules! platform {
//     (
//     ) => {
//         concat!(
//             env!("VERGEN_SYSINFO_OS_VERSION"),
//             " - ",
//             env!("VERGEN_SYSINFO_CPU_BRAND"),
//         )
//     };
// }

/// Service name deduced from name of the binary.
/// This macro must be called within binaries only.
///
/// Example: `router`.
#[macro_export]
macro_rules! service_name {
    () => {
        env!("CARGO_BIN_NAME")
    };
}

/// Build profile, either debug or release.
///
/// Example: `release`.
#[macro_export]
macro_rules! profile {
    () => {
        env!("CARGO_PROFILE")
    };
}

/// The latest git tag. If tags are not present in the repository, the short commit hash is used
/// instead. Refer to the [`git describe`](https://git-scm.com/docs/git-describe) documentation for
/// more details.
#[macro_export]
macro_rules! git_tag {
    () => {
        env!("VERGEN_GIT_DESCRIBE")
    };
}


// Struct: ExtendedCardInfoConfig
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 0

pub struct ExtendedCardInfoConfig


// Function: refund_to_refund_core_workflow_model
// File: crates/router/src/core/refunds.rs
// Module: router

pub fn refund_to_refund_core_workflow_model(
    refund: &diesel_refund::Refund,
) -> diesel_refund::RefundCoreWorkflow


// Function: list_enabled_by_profile_id
// File: crates/diesel_models/src/query/merchant_connector_account.rs
// Module: diesel_models

pub fn list_enabled_by_profile_id(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        connector_type: common_enums::ConnectorType,
    ) -> StorageResult<Vec<Self>>


// Implementation: impl webhooks::IncomingWebhook for for Getnet
// File: crates/hyperswitch_connectors/src/connectors/getnet.rs
// Module: hyperswitch_connectors
// Methods: 7 total (0 public)

impl webhooks::IncomingWebhook for for Getnet


// Struct: TrustpayMandatoryParams
// File: crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TrustpayMandatoryParams


// Struct: GatewayStatusMap
// File: crates/hyperswitch_domain_models/src/gsm.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct GatewayStatusMap


// Implementation: impl ConfigNew
// File: crates/diesel_models/src/query/configs.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl ConfigNew


// File: crates/hyperswitch_constraint_graph/src/lib.rs
// Module: hyperswitch_constraint_graph

pub mod builder;
mod dense_map;
pub mod error;
pub mod graph;
pub mod types;

pub use builder::ConstraintGraphBuilder;
pub use error::{AnalysisTrace, GraphError};
pub use graph::ConstraintGraph;
#[cfg(feature = "viz")]
pub use types::NodeViz;
pub use types::{
    CheckingContext, CycleCheck, DomainId, DomainIdentifier, Edge, EdgeId, KeyNode, Memoization,
    Node, NodeId, NodeValue, Relation, Strength, ValueNode,
};