// 引入lodash库，它提供了很多实用的函数来方便地处理JavaScript中的数据，例如对数组、对象进行操作等，后续代码中会多次使用到它提供的相关功能。
var _ = require('lodash');
// 引入Node.js的path模块，主要用于处理文件路径相关的操作，像拼接、解析文件路径等，这里用于准确地引入自定义的 `dao/DAO` 模块所在的文件位置，该模块大概率用于数据库操作相关的功能封装。
var path = require("path");
// 引入自定义的 `DAO` 模块，通过拼接当前工作目录（`process.cwd()`）和相对路径的方式找到并引入该模块，这个模块应该封装了通用的数据访问操作方法，例如查询、插入、更新等操作，具体操作的数据表等信息会在调用相应方法时传入。
var dao = require(path.join(process.cwd(), "dao/DAO"));

// 定义 `reportOne` 函数，该函数主要用于获取并处理 `ReportOneModel` 相关的报表数据，最终将处理好的数据以特定格式通过回调函数返回给调用者。
function reportOne(cb) {
    // 调用 `dao` 模块的 `list` 方法来获取 `ReportOneModel` 对应的数据，这里第一个参数 `"ReportOneModel"` 可能是表示要查询的数据模型名称，对应数据库中的具体数据表，
    // 第二个参数 `null` 可能表示查询条件（这里暂时没有传递具体查询条件，可能是获取该表的所有数据的意思），第三个参数是一个回调函数，用于处理查询操作完成后的结果情况。
    dao.list("ReportOneModel", null, function (err, result) {
        // 如果在获取报表数据的过程中出现错误（`err` 不为 `null`），比如数据库查询失败（可能是连接问题、权限不足、表不存在等原因），则直接通过回调函数 `cb` 返回错误信息，表示获取报表数据失败，让调用者知晓操作出现问题及原因。
        if (err) return cb("获取报表数据失败");

        // 创建一个空对象 `areaKeyResult`，用于后续按照区域（area）和日期（date）来存储和整理报表数据，以方便构建最终需要返回的格式化数据结构。
        var areaKeyResult = {};

        // 使用 `lodash` 的 `union` 和 `map` 函数来获取报表数据中所有不重复的区域信息（`rp1_area` 字段），将其整理为一个数组并赋值给 `areaKeys`。
        // `_.map` 函数用于从 `result` 数组中的每个对象提取 `rp1_area` 字段的值，形成一个新的数组，然后 `_.union` 函数对这个新数组进行去重操作，得到唯一的区域值数组。
        var areaKeys = _.union(_.map(result, "rp1_area"));

        // 使用 `lodash` 的 `union` 函数结合一个匿名函数来获取报表数据中所有不重复的日期信息（经过格式化后的日期字符串），并赋值给 `dateKeys`。
        // 匿名函数内先将日期对象（`rp1_date` 字段）格式化为 `年-月-日` 的字符串形式，然后返回该字符串，`_.union` 函数对这些格式化后的日期字符串进行去重操作，得到唯一的日期值数组。
        var dateKeys = _.union(_.map(result, function (record) {
            str = record["rp1_date"].getFullYear() + "-" + (record["rp1_date"].getMonth() + 1) + "-" + record["rp1_date"].getDate();
            console.log(str);
            return str;
        }));

        // 遍历获取到的报表数据 `result`，通过索引 `idx` 来访问每个数据记录对象，进行后续的数据整理操作，将数据按照区域和日期的维度进行重新组织。
        for (var idx in result) {
            var record = result[idx];
            var dateKey = record["rp1_date"].getFullYear() + "-" + (record["rp1_date"].getMonth() + 1) + "-" + record["rp1_date"].getDate();
            // 如果 `areaKeyResult` 对象中还不存在当前记录的区域键（`record["rp1_area"]`），则创建一个以该区域为键的空对象，用于后续存储该区域下不同日期的数据。
            if (!areaKeyResult[record["rp1_area"]]) {
                areaKeyResult[record["rp1_area"]] = {};
            }
            // 将当前记录按照日期键（`dateKey`）存储到对应的区域对象下，这样就构建了一个以区域为一级键，日期为二级键，对应报表记录为值的嵌套对象结构，方便后续按区域和日期查找数据。
            areaKeyResult[record["rp1_area"]][dateKey] = record;
        }

        // 格式输出
        // 创建一个空数组 `series`，用于存储最终要返回的数据格式中的系列（series）信息，每个元素代表不同区域的数据系列，在图表展示等场景中通常对应不同的线条或图形。
        var series = [];

        // 使用 `lodash` 的 `forEach` 函数遍历 `areaKeys` 数组，对于每个区域键（`areaKey`）进行以下操作，构建每个区域对应的图表数据系列信息。
        _(areaKeys).forEach(function (areaKey) {
            var data = [];

            // 使用 `lodash` 的 `forEach` 函数遍历 `dateKeys` 数组，对于每个日期键（`dateKey`）进行以下操作，构建当前区域在不同日期下的数据点信息。
            _(dateKeys).forEach(function (dateKey) {
                console.log("areaKey:" + areaKey + "," + "dateKey:" + dateKey);
                // 如果在 `areaKeyResult` 中根据当前区域键（`areaKey`）和日期键（`dateKey`）能找到对应的报表记录，则将该记录中的用户数量（`rp1_user_count` 字段）添加到 `data` 数组中，
                // 表示该区域在该日期下的用户数量数据点；如果找不到对应的记录，则添加 `0`，表示该日期下该区域没有相应的数据（可能是缺失或未统计等情况）。
                if (areaKeyResult[areaKey][dateKey]) {
                    data.push(areaKeyResult[areaKey][dateKey]["rp1_user_count"]);
                } else {
                    data.push(0);
                }
            })

            // 将构建好的当前区域的数据系列信息（包含区域名称、图表类型、样式以及数据点数组等）添加到 `series` 数组中，完成一个区域的数据系列构建。
            series.push({
                name: areaKey,
                type: 'line',
                stack: '总量',
                areaStyle: { normal: {} },
                data: data
            })
        });

        // 构建最终要返回的数据对象，包含图例（legend）、y轴（yAxis）、x轴（xAxis）以及系列（series）等信息，符合常见的图表数据格式要求，用于在前端图表库等场景中进行展示。
        data = {
            legend: {
                data: areaKeys
            },
            yAxis: [
                {
                    type: 'value'
                }
            ],
            xAxis: [
                {
                    data: dateKeys
                }
            ],
            series: series
        };

        // 将整理好的最终数据对象通过回调函数 `cb` 返回给调用者，表示 `reportOne` 函数成功获取并处理好了报表数据，调用者可以根据这个数据格式进行后续的操作，比如传递给前端进行图表渲染展示等。
        cb(null, data);
    });
}

// 定义 `reportTwo` 函数，该函数用于获取并处理 `ReportTwoModel` 相关的报表数据，将数据按照日期进行整理后通过回调函数返回给调用者。
function reportTwo(cb) {
    // 调用 `dao` 模块的 `list` 方法来获取 `ReportTwoModel` 对应的数据，参数含义与 `reportOne` 函数中调用时类似，第一个参数指定要查询的数据表对应的模型名称，第二个参数为查询条件（这里是 `null`，获取全部数据），第三个参数处理查询结果。
    dao.list("ReportTwoModel", null, function (err, result) {
        // 如果在获取报表数据过程中出现错误（`err` 不为 `null`），比如数据库查询失败，就通过回调函数 `cb` 返回错误信息，表示获取报表数据失败，让调用者知晓操作出现问题及原因。
        if (err) return cb("获取报表数据失败");

        // 创建一个空对象 `dateKeyResult`，用于按照日期来存储和整理报表数据，后续会将相同日期的数据记录存储在以日期为键的数组中。
        var dateKeyResult = {};

        // 遍历获取到的报表数据 `result`，通过索引 `idx` 来访问每个数据记录对象，进行后续的数据整理操作，将数据按照日期维度进行重新组织。
        for (var idx in result) {
            var record = result[idx];
            var dateKey = record["rp2_date"].getFullYear() + "-" + (record["rp2_date"].getMonth() + 1) + "-" + record["rp2_date"].getDate();
            // 如果 `dateKeyResult` 对象中还不存在当前记录的日期键（`dateKey`），则创建一个以该日期为键的空数组，用于存储该日期下的所有报表记录。
            if (!dateKeyResult[dateKey]) {
                dateKeyResult[dateKey] = [];
            }
            // 将当前报表记录添加到对应日期键的数组中，这样就构建了一个以日期为键，对应日期下所有报表记录为值的对象结构，方便后续按日期查找和处理数据。
            dateKeyResult[dateKey].push(record);
        }

        // 将整理好的按照日期分组的报表数据对象通过回调函数 `cb` 返回给调用者，表示 `reportTwo` 函数成功获取并整理好了报表数据，调用者可以根据这个数据结构进行后续的操作，比如进一步统计分析每个日期下的数据情况等。
        cb(null, dateKeyResult);
    });
}

// 定义 `reportThree` 函数，目前该函数为空，可能后续会用于实现获取和处理第三种报表数据相关的逻辑，暂时没有具体功能实现代码。
function reportThree(cb) {

}

// 定义 `reportFour` 函数，目前该函数为空，可能后续会用于实现获取和处理第四种报表数据相关的逻辑，暂时没有具体功能实现代码。
function reportFour(cb) {

}

// 对外暴露 `reports` 函数，根据传入的报表类型ID（`typeid`）来调用相应的报表处理函数（如 `reportOne`、`reportTwo` 等），并将处理结果通过回调函数返回给调用者，若类型ID不合法则返回错误信息。
module.exports.reports = function (typeid, cb) {
    console.log(typeid);
    // 将传入的报表类型ID（`typeid`）转换为整数类型，然后通过 `switch` 语句根据不同的类型值来执行相应的操作，调用对应的报表处理函数来获取和处理报表数据。
    switch (parseInt(typeid)) {
        case 1:
            // 如果类型ID为1，则调用 `reportOne` 函数来获取和处理第一种报表数据，`reportOne` 函数内部会进行数据库查询、数据整理等操作，最终返回处理好的数据，
            // 这里通过回调函数接收 `reportOne` 函数返回的数据，如果出现错误（`err` 不为 `null`），则直接将错误信息通过外层的回调函数 `cb` 返回给调用者；
            // 如果没有错误，则将 `reportOne` 函数返回的处理后的数据通过 `cb` 返回给调用者。
            reportOne(function (err, result) {
                if (err) return cb(err);
                cb(null, result);
            });
            break;
        case 2:
            // 如果类型ID为2，则调用 `reportTwo` 函数来获取和处理第二种报表数据，处理逻辑与 `reportOne` 类似，根据 `reportTwo` 函数的执行结果通过回调函数 `cb` 返回相应的信息给调用者。
            reportTwo(function (err, result) {
                if (err) return cb(err);
                cb(null, result);
            });
            break;
        case 3:
            // 如果类型ID为3，则调用 `reportThree` 函数来获取和处理第三种报表数据，目前该函数为空，后续可补充具体逻辑来实现相应功能并通过回调函数返回结果给调用者。
            reportThree(function (err, result) {
                if (err) return cb(err);
                cb(null, result);
            });
            break;
        case 4:
            // 如果类型ID为4，则调用 `reportFour` 函数来获取和处理第四种报表数据，目前该函数为空，后续可补充具体逻辑来实现相应功能并通过回调函数返回结果给调用者。
            reportFour(function (err, result) {
                if (err) return cb(err);
                cb(null, result);
            });
            break;
        default:
            // 如果传入的报表类型ID不属于1 - 4的范围，则通过回调函数 `cb` 返回错误信息，表示类型出错，告知调用者传入的报表类型ID不符合要求。
            cb("类型出错");
            break;
    }
}
//这段代码整体构建了一个报表数据获取与处理的模块，根据不同的报表类型 ID，调用相应的内部函数来获
//取特定报表模型的数据，并进行整理和格式化，最终通过回调函数将处理好的数据返回给调用者，方便
//在其他模块中进一步使用这些报表数据进行展示、分析等操作，适用于有报表相关业务需求的应用开发场景。