// File: crates/router/src/core/user_role.rs

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::ParentGroupDescription>> {
    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::ParentGroupDescription {
                    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 merchant_name_map = state
        .store
        .list_multiple_merchant_accounts(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(merchant_id, master_key)
                .await
                .change_context(UserErrors::InternalServerError)?;

            let business_profile = state
                .store
                .find_business_profile_by_profile_id(&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)
}


// File: crates/router/src/core/files.rs

pub mod helpers;

use api_models::files;
use error_stack::ResultExt;

use super::errors::{self, RouterResponse};
use crate::{
    consts,
    routes::SessionState,
    services::ApplicationResponse,
    types::{api, domain},
};

pub async fn files_create_core(
    state: SessionState,
    platform: domain::Platform,
    create_file_request: api::CreateFileRequest,
) -> RouterResponse<files::CreateFileResponse> {
    helpers::validate_file_upload(&state, platform.clone(), create_file_request.clone()).await?;
    let file_id = common_utils::generate_id(consts::ID_LENGTH, "file");
    let file_key = format!(
        "{}/{}",
        platform
            .get_processor()
            .get_account()
            .get_id()
            .get_string_repr(),
        file_id
    );
    let file_new: diesel_models::FileMetadataNew = diesel_models::file::FileMetadataNew {
        file_id: file_id.clone(),
        merchant_id: platform.get_processor().get_account().get_id().clone(),
        file_name: create_file_request.file_name.clone(),
        file_size: create_file_request.file_size,
        file_type: create_file_request.file_type.to_string(),
        provider_file_id: None,
        file_upload_provider: None,
        available: false,
        connector_label: None,
        profile_id: None,
        merchant_connector_id: None,
    };

    let file_metadata_object = state
        .store
        .insert_file_metadata(file_new)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to insert file_metadata")?;
    let (provider_file_id, file_upload_provider, profile_id, merchant_connector_id) = Box::pin(
        helpers::upload_and_get_provider_provider_file_id_profile_id(
            &state,
            &platform,
            &create_file_request,
            file_key.clone(),
        ),
    )
    .await?;

    // Update file metadata
    let update_file_metadata = diesel_models::file::FileMetadataUpdate::Update {
        provider_file_id: Some(provider_file_id),
        file_upload_provider: Some(file_upload_provider),
        available: true,
        profile_id,
        merchant_connector_id,
    };
    state
        .store
        .as_ref()
        .update_file_metadata(file_metadata_object, update_file_metadata)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!("Unable to update file_metadata with file_id: {file_id}")
        })?;
    Ok(ApplicationResponse::Json(files::CreateFileResponse {
        file_id,
    }))
}

pub async fn files_delete_core(
    state: SessionState,
    platform: domain::Platform,
    req: api::FileId,
) -> RouterResponse<serde_json::Value> {
    helpers::delete_file_using_file_id(&state, req.file_id.clone(), &platform).await?;
    state
        .store
        .as_ref()
        .delete_file_metadata_by_merchant_id_file_id(
            platform.get_processor().get_account().get_id(),
            &req.file_id,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to delete file_metadata")?;
    Ok(ApplicationResponse::StatusOk)
}

pub async fn files_retrieve_core(
    state: SessionState,
    platform: domain::Platform,
    req: api::FileRetrieveRequest,
) -> RouterResponse<serde_json::Value> {
    let file_metadata_object = state
        .store
        .as_ref()
        .find_file_metadata_by_merchant_id_file_id(
            platform.get_processor().get_account().get_id(),
            &req.file_id,
        )
        .await
        .change_context(errors::ApiErrorResponse::FileNotFound)
        .attach_printable("Unable to retrieve file_metadata")?;
    let file_info = helpers::retrieve_file_and_provider_file_id_from_file_id(
        &state,
        Some(req.file_id),
        req.dispute_id,
        &platform,
        api::FileDataRequired::Required,
    )
    .await?;
    let content_type = file_metadata_object
        .file_type
        .parse::<mime::Mime>()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to parse file content type")?;
    Ok(ApplicationResponse::FileData((
        file_info
            .file_data
            .ok_or(errors::ApiErrorResponse::FileNotAvailable)
            .attach_printable("File data not found")?,
        content_type,
    )))
}


// File: crates/router/src/core/metrics.rs

use router_env::{counter_metric, global_meter};

global_meter!(GLOBAL_METER, "ROUTER_API");

counter_metric!(INCOMING_DISPUTE_WEBHOOK_METRIC, GLOBAL_METER); // No. of incoming dispute webhooks
counter_metric!(
    INCOMING_DISPUTE_WEBHOOK_SIGNATURE_FAILURE_METRIC,
    GLOBAL_METER
); // No. of incoming dispute webhooks for which signature verification failed
counter_metric!(
    INCOMING_DISPUTE_WEBHOOK_VALIDATION_FAILURE_METRIC,
    GLOBAL_METER
); // No. of incoming dispute webhooks for which validation failed
counter_metric!(INCOMING_DISPUTE_WEBHOOK_NEW_RECORD_METRIC, GLOBAL_METER); // No. of incoming dispute webhooks for which new record is created in our db
counter_metric!(INCOMING_DISPUTE_WEBHOOK_UPDATE_RECORD_METRIC, GLOBAL_METER); // No. of incoming dispute webhooks for which we have updated the details to existing record
counter_metric!(
    INCOMING_DISPUTE_WEBHOOK_MERCHANT_NOTIFIED_METRIC,
    GLOBAL_METER
); // No. of incoming dispute webhooks which are notified to merchant
counter_metric!(
    ACCEPT_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC,
    GLOBAL_METER
); //No. of status validation failures while accepting a dispute
counter_metric!(
    EVIDENCE_SUBMISSION_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC,
    GLOBAL_METER
); //No. of status validation failures while submitting evidence for a dispute
   //No. of status validation failures while attaching evidence for a dispute
counter_metric!(
    ATTACH_EVIDENCE_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC,
    GLOBAL_METER
);

counter_metric!(INCOMING_PAYOUT_WEBHOOK_METRIC, GLOBAL_METER); // No. of incoming payout webhooks
counter_metric!(
    INCOMING_PAYOUT_WEBHOOK_SIGNATURE_FAILURE_METRIC,
    GLOBAL_METER
); // No. of incoming payout webhooks for which signature verification failed

counter_metric!(WEBHOOK_INCOMING_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_INCOMING_FILTERED_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_SOURCE_VERIFIED_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_OUTGOING_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_OUTGOING_RECEIVED_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_OUTGOING_NOT_RECEIVED_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_PAYMENT_NOT_FOUND, GLOBAL_METER);
counter_metric!(
    WEBHOOK_EVENT_TYPE_IDENTIFICATION_FAILURE_COUNT,
    GLOBAL_METER
);
counter_metric!(WEBHOOK_FLOW_FAILED_BUT_ACKNOWLEDGED, GLOBAL_METER);

counter_metric!(ROUTING_CREATE_REQUEST_RECEIVED, GLOBAL_METER);
counter_metric!(ROUTING_CREATE_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_MERCHANT_DICTIONARY_RETRIEVE, GLOBAL_METER);
counter_metric!(
    ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE,
    GLOBAL_METER
);
counter_metric!(ROUTING_LINK_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_LINK_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_RETRIEVE_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_RETRIEVE_DEFAULT_CONFIG, GLOBAL_METER);
counter_metric!(
    ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE,
    GLOBAL_METER
);
counter_metric!(ROUTING_RETRIEVE_LINK_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_UNLINK_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_UPDATE_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_UPDATE_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_UPDATE_CONFIG_FOR_PROFILE, GLOBAL_METER);
counter_metric!(
    ROUTING_UPDATE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE,
    GLOBAL_METER
);
counter_metric!(ROUTING_RETRIEVE_CONFIG_FOR_PROFILE, GLOBAL_METER);
counter_metric!(
    ROUTING_RETRIEVE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE,
    GLOBAL_METER
);
counter_metric!(DYNAMIC_SUCCESS_BASED_ROUTING, GLOBAL_METER);
counter_metric!(DYNAMIC_CONTRACT_BASED_ROUTING, GLOBAL_METER);

#[cfg(feature = "partial-auth")]
counter_metric!(PARTIAL_AUTH_FAILURE, GLOBAL_METER);

counter_metric!(API_KEY_REQUEST_INITIATED, GLOBAL_METER);
counter_metric!(API_KEY_REQUEST_COMPLETED, GLOBAL_METER);