// 引入lodash库，用于处理各种数据结构，提供了如对象操作、数组处理、排序等很多实用的工具函数，在后续代码中会频繁使用到。
var _ = require('lodash');
// 引入Node.js的path模块，主要用于处理文件路径相关操作，通过 `path.join` 方法拼接当前工作目录（`process.cwd()`）和相对路径，来准确引入自定义的 `dao/DAO` 和 `dao/PermissionAPIDAO` 模块所在的路径。
var path = require("path");
// 引入自定义的 `DAO` 模块，该模块应该封装了通用的数据访问操作方法，例如查询、获取单条记录等操作，对应不同的数据模型（如 `RoleModel` 等）与数据库进行交互，执行相关的数据操作。
var dao = require(path.join(process.cwd(), "dao/DAO"));
// 引入自定义的 `PermissionAPIDAO` 模块，推测这个模块主要用于权限相关数据的访问操作，比如获取权限列表等功能，同样是与数据库进行交互来获取权限数据，为后续构建菜单数据提供基础信息。
var permissionAPIDAO = require(path.join(process.cwd(), "dao/PermissionAPIDAO"));

/**
 * 获取左侧菜单数据
 * 此函数用于根据用户信息获取对应的左侧菜单数据，通过回调函数将获取到的菜单数据（成功时）或错误信息（失败时）返回给调用者，以用于前端页面展示菜单等相关业务操作。
 * 
 * @param  {Function} cb 回调函数，用于接收获取左侧菜单数据操作的结果，若成功获取到菜单数据，则传递菜单数据对象作为参数；若出现错误情况（如无权限、数据库查询失败等），则传递相应的错误信息字符串作为参数。
 */
module.exports.getLeftMenus = function (userInfo, cb) {
    // 首先验证传入的用户信息（`userInfo`）是否存在，如果不存在则说明无法确定用户身份及权限等情况，直接通过回调函数 `cb` 返回错误信息，表示无权限访问，终止后续操作。
    if (!userInfo) return cb("无权限访问");

    // 定义一个内部函数 `authFn`，用于根据角色ID（`rid`）以及角色已有的权限信息（`keyRolePermissions`）来获取并整理权限数据，构建成菜单结构形式的数据，最终通过回调函数 `cb` 返回处理后的结果。
    var authFn = function (rid, keyRolePermissions, cb) {
        // 调用 `permissionAPIDAO` 模块的 `list` 方法来获取所有的权限数据，该方法内部大概率会执行数据库查询操作，从数据库中获取权限相关的记录信息，它是一个异步操作，通过回调函数来处理查询结果。
        permissionAPIDAO.list(function (err, permissions) {
            // 如果在获取权限数据的过程中出现错误（`err` 不为 `null`），比如数据库查询失败（可能是连接问题、权限不足、表不存在等原因），则直接通过回调函数 `cb` 返回错误信息，表示获取权限数据失败，让调用者知晓操作出现问题及原因。
            if (err) return cb("获取权限数据失败");

            // 使用 `lodash` 的 `keyBy` 函数，将获取到的权限数据（`permissions`）按照权限ID（`ps_id`）进行转换，生成一个以权限ID为键，对应权限详细信息为值的对象结构，
            // 方便后续通过权限ID快速查找对应的权限详情，在构建菜单数据结构以及判断权限关联等操作中能更高效地获取所需信息。
            var keyPermissions = _.keyBy(permissions, 'ps_id');

            // 创建一个空对象 `rootPermissionsResult`，用于存储最终整理好的菜单数据结构，它将以一级菜单权限的权限ID为键，对应权限的详细信息（包含子菜单信息等）为值进行存储，作为菜单结构的顶层节点。
            var rootPermissionsResult = {};

            // 处理一级菜单
            // 遍历获取到的权限数据数组 `permissions`，通过索引 `idx` 来访问每个权限信息对象，针对每个权限进行相关处理，构建一级菜单对应的权限信息结构并添加到 `rootPermissionsResult` 对象中。
            for (idx in permissions) {
                permission = permissions[idx];
                // 如果当前权限的层级（`ps_level`）为0，表示它是一级菜单权限，进行以下操作来构建一级菜单数据结构。
                if (permission.ps_level == 0) {
                    // 如果角色ID（`rid`）不等于0，意味着不是特殊的默认角色（可能是普通用户角色等情况），此时需要进一步判断当前权限是否在该角色已有的权限列表中（通过 `keyRolePermissions` 对象来检查），
                    // 如果当前权限不在角色权限列表中（`!keyRolePermissions[permission.ps_id]`），则使用 `continue` 跳过本次循环，不将该权限添加到一级菜单数据结构中，因为该角色没有此权限对应的菜单显示权限。
                    if (rid!= 0) {
                        if (!keyRolePermissions[permission.ps_id]) continue;
                    }
                    // 如果满足添加条件（角色ID为0或者权限在角色权限列表中），则将当前一级菜单权限的相关信息按照特定结构添加到 `rootPermissionsResult` 对象中，
                    // 包括权限ID、权限名称（`authName`）、对应的接口路径（`ps_api_path`，可能用于前端路由跳转等操作）、初始化一个空的子菜单数组（`children`）以及权限的显示顺序（`order`，用于后续菜单排序）。
                    rootPermissionsResult[permission.ps_id] = {
                        "id": permission.ps_id,
                        "authName": permission.ps_name,
                        "path": permission.ps_api_path,
                        "children": [],
                        "order": permission.ps_api_order
                    };
                }
            }

            // 处理二级菜单
            // 再次遍历权限数据数组 `permissions`，同样对每个权限进行检查和相关处理，这次是构建二级菜单对应的权限信息结构，并关联到对应的一级菜单权限下，完善菜单数据的层级结构。
            for (idx in permissions) {
                permission = permissions[idx];
                if (permission.ps_level == 1) {
                    // 如果角色ID（`rid`）不等于0，同样需要判断当前二级菜单权限是否在该角色已有的权限列表中（通过 `keyRolePermissions` 对象来检查），
                    // 如果不在角色权限列表中，则使用 `continue` 跳过本次循环，不处理该权限，因为角色没有此权限对应的菜单显示权限。
                    if (rid!= 0) {
                        if (!keyRolePermissions[permission.ps_id]) continue;
                    }
                    // 根据当前二级菜单权限的父级权限ID（`ps_pid`），从 `rootPermissionsResult` 中获取对应的一级菜单权限结果对象，后续将把当前二级菜单权限添加到这个一级菜单权限的子菜单列表中。
                    parentPermissionResult = rootPermissionsResult[permission.ps_pid];
                    if (parentPermissionResult) {
                        // 将当前二级菜单权限的相关信息按照特定结构添加到对应的一级菜单权限结果对象的子菜单数组（`children`）中，建立起二级菜单与一级菜单的层级关系，
                        // 包括权限ID、权限名称、对应的接口路径、初始化一个空的子菜单数组（用于后续可能存在的三级菜单等继续添加）以及权限的显示顺序。
                        parentPermissionResult.children.push({
                            "id": permission.ps_id,
                            "authName": permission.ps_name,
                            "path": permission.ps_api_path,
                            "children": [],
                            "order": permission.ps_api_order
                        });
                    }
                }
            }

            // 排序
            // 首先使用 `_.values` 获取 `rootPermissionsResult` 对象中的值（即整理好的菜单数据结构，包含一级菜单及其子菜单信息），形成一个数组 `result`，方便后续进行排序操作。
            result = _.values(rootPermissionsResult);
            // 使用 `_.sortBy` 函数按照每个菜单对象中的 `order` 属性（权限的显示顺序）对 `result` 数组进行排序，使得一级菜单按照指定的顺序排列，提升菜单展示的合理性和美观性。
            result = _.sortBy(result, "order");
            // 遍历排序后的一级菜单数组 `result`，对每个一级菜单下的子菜单数组（`subresult.children`）同样使用 `_.sortBy` 函数按照 `order` 属性进行排序，
            // 保证二级菜单在各自的一级菜单下也按照指定顺序排列，进一步完善菜单数据的有序性，方便前端按照顺序展示菜单。
            for (idx in result) {
                subresult = result[idx];
                subresult.children = _.sortBy(subresult.children, "order");
            }

            // 将整理好并排序后的菜单数据结构通过回调函数 `cb` 返回给调用者，表示成功获取并处理好了左侧菜单数据，调用者可以根据这个数据结构进行后续的操作，
            // 比如将菜单数据传递给前端框架进行菜单渲染展示等业务操作。
            cb(null, result);
        });
    }

    // 获取用户信息中的角色ID（`rid`），用于后续判断用户角色情况以及相应的权限处理，角色ID是区分不同用户角色、确定权限范围的重要标识。
    rid = userInfo.rid;
    // 如果角色ID（`rid`）等于0，可能表示特殊的默认角色（例如超级管理员等拥有全部权限的角色），直接调用 `authFn` 函数，传入角色ID以及 `null`（因为默认角色无需再额外判断权限是否在角色权限列表中），
    // 让 `authFn` 函数去获取并整理所有权限数据构建菜单结构，最终通过回调函数 `cb` 返回结果。
    if (rid == 0) {
        authFn(rid, null, cb);
    } else {
        // 如果角色ID不等于0，说明是普通用户角色等情况，需要先获取该角色对应的详细信息，以确定其拥有的权限列表，进而根据权限来构建菜单数据结构。
        // 调用 `dao` 模块的 `show` 方法来获取指定角色ID（`userInfo.rid`）对应的角色信息，该方法内部会执行数据库查询操作，查找对应角色的记录，它是一个异步操作，通过回调函数来处理查询结果。
        dao.show("RoleModel", userInfo.rid, function (err, role) {
            // 如果在获取角色信息的过程中出现错误（`err` 不为 `null`），或者查询结果没有获取到对应的角色（`!role`），说明可能出现权限问题（比如角色不存在、无权限查询角色信息等情况），
            // 则直接通过回调函数 `cb` 返回错误信息，表示无权限访问，终止后续操作，告知调用者无法获取菜单数据及原因。
            if (err ||!role) return cb("无权限访问");

            // 将获取到的角色对象中的权限ID字符串（`ps_ids`，可能是以逗号分隔的权限ID列表）进行分割，得到一个权限ID数组 `rolePermissions`，方便后续判断每个权限是否属于该角色。
            rolePermissions = role.ps_ids.split(",");
            // 创建一个空对象 `keyRolePermissions`，用于以权限ID为键，值为 `true` 的形式来存储该角色拥有的权限信息，方便后续快速判断某个权限是否在该角色的权限范围内。
            keyRolePermissions = {};
            // 遍历权限ID数组 `rolePermissions`，将每个权限ID作为键添加到 `keyRolePermissions` 对象中，并将对应的值设置为 `true`，构建角色权限的快速查找结构。
            for (idx in rolePermissions) {
                keyRolePermissions[rolePermissions[idx]] = true;
            }

            // 调用 `authFn` 函数，传入角色ID（`rid`）以及构建好的角色权限查找对象（`keyRolePermissions`），让 `authFn` 函数根据该角色的权限情况去获取并整理权限数据，构建菜单结构，最终通过回调函数 `cb` 返回结果。
            authFn(rid, keyRolePermissions, cb);

        })
    }
}
//这段代码整体实现了根据用户角色信息获取对应的左侧菜单数据的功能，通过对不同角色（如超级管理员
//和普通用户角色）的权限判断和处理，构建出具有层级结构且有序排列的菜单数据，适用于权限管理与前
//端菜单展示相关的应用开发场景，能够根据用户权限灵活展示不同的菜单内容。