import { selectOneDataByParam, selectDataListByParam } from "../data/findData";
import { TABLENAME } from "../config/dbEnum";
import { getMySqlMs } from "../tools/systemTools";
import { BizError } from "../util/bizError";
import { ERRORENUM } from "../config/errorEnum";
import { addData } from "../data/addData";
import { updateManyData } from "../data/updateData";
import { controlAcByEnvironment } from "./running";

// ======= 模块级缓存（低频变化的数据） =======
let deviceTreeCache: { data: any; timestamp: number } | null = null;
const CACHE_TTL = 5 * 60 * 1000; // 5 分钟
const RECORD_LIMIT = 200;         // 每天每设备最多取最近 200 条


/**
 * 注册或更新设备信息
 * @param deviceId 设备唯一标识（必填）
 * @param roomId 房间编号（必填），对应 region 表的 room_id
 * @param deviceType 设备类型（必填），如“灯光”“空调”
 * @param deviceName 设备名称（可选），如“主照明灯”
 * @param controlParams 设备支持的参数定义（可选），JSON对象
 * @returns {Promise<{success: boolean}>}
 * @throws 如果 regionKey 对应的区域不存在，则抛出错误
 */
export async function registerOrUpdateDevice( deviceId: string, roomId: string, deviceType: string, deviceName?: string | null, controlParams?: any ) {
    // 校验区域是否存在
    let region = await selectOneDataByParam(TABLENAME.区域表, { room_id: roomId });
    if (!region.data) {
        throw new BizError(ERRORENUM.参数错误, `区域编号 ${roomId} 不存在`);
    }

    let regionKey = region.data.id;

    let exist = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
    let now = new Date().toISOString().slice(0, 19).replace('T', ' ');

    if (Object.keys(exist.data).length) {
        await updateManyData(TABLENAME.设备表, { device_id: deviceId }, {
            region_key: regionKey,
            device_type: deviceType,
            device_name: deviceName,
            control_params: controlParams || null,
            updated_at: now,
        });
    } else {
        await addData(TABLENAME.设备表, {
            device_id: deviceId,
            region_key: regionKey,
            device_type: deviceType,
            device_name: deviceName,
            control_params: controlParams || null,
            created_at: now,
            updated_at: now,
        });
    }
    return { success: true };
}


/**
 * 处理设备商推送的数据
 * @param deviceId 设备唯一标识（必填）
 * @param data 设备上报的JSON数据（必填），例如：{ "power": "on", "brightness": 80 }
 * @param deviceTime 设备本地时间（可选），格式：YYYY-MM-DD HH:MM:SS
 * @returns {Promise<{recordId: number}>} 返回新插入记录的ID
 * @throws 如果设备未在 device 表中注册，则抛出错误（需先调用注册接口）
 */
export async function handleDevicePush(deviceId: string, data: any, deviceTime?: string) {
    if (!deviceId || !data) {
        throw new BizError(ERRORENUM.参数错误, "deviceId和data不能为空");
    }

    // 检查设备是否存在，不存在则抛出错误（要求先注册）
    let device = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
    if (!device.data) {
        throw new BizError(ERRORENUM.未找到数据, `设备 ${deviceId} 未注册，请先调用注册接口`);
    }

    let nowMs = getMySqlMs();
    let receivedTime = formatTime(new Date(nowMs));
    
    await addData(TABLENAME.设备数据表, {
        device_id: deviceId,
        data: data,
        received_time: receivedTime,
        device_time: deviceTime ? formatTime(deviceTime) : null,
        created_at: receivedTime,
    });

    // 根据regionKey查询区域表，若包含文创，则禁用空调控制
    const regionRow = await selectOneDataByParam(
        TABLENAME.区域表, { id: device.data.region_key },
        ["name"]
    );
    // 调用空调控制（非文创区域才允许自动控温）
    // if (regionRow?.data && !regionRow.data.name?.includes("文创")) {
    // 根据设备上报的数据，联动控制空调设备
    let regionName = regionRow?.data && !regionRow.data.name ? regionRow.data.name : "";
    await controlAcByEnvironment(deviceId, data, regionName);
    // }

    return { success: true };
}

/**
 * 处理设备商推送的故障数据
 * @param deviceId 设备唯一标识（必填）
 * @param faultType 故障类型（必填），如“离线”“数据异常”“硬件故障”
 * @param faultLevel 故障等级
 * @param faultDescription 故障详细描述（可选）
 * @param occurredTime 故障发生时间（可选），格式：YYYY-MM-DD HH:MM:SS，不传则使用服务器时间
 * @returns {Promise<{faultId: number}>} 返回新插入故障记录的ID
 * @throws 如果设备未注册，则抛出错误
 */
export async function handleDeviceFaultPush( deviceId: string, faultType: string, faultLevel: string, faultDescription?: string, occurredTime?: string ) {
    if (!deviceId || !faultType) {
        throw new BizError(ERRORENUM.参数错误, "deviceId 和 faultType 不能为空");
    }

    // 检查设备是否存在
    let device = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
    if (!device.data) {
        throw new BizError(ERRORENUM.未找到数据, `设备 ${deviceId} 未注册，请先调用注册接口`);
    }

    let nowMs = getMySqlMs();
    let nowStr = new Date(nowMs).toISOString().slice(0, 19).replace('T', ' ');
    let faultTime = occurredTime
        ? new Date(occurredTime).toISOString().slice(0, 19).replace('T', ' ')
        : nowStr;

    let insertResult: any = await addData(TABLENAME.设备故障表, {
        device_id: deviceId,
        fault_type: faultType,
        fault_description: faultDescription || null,
        occurred_time: faultTime,
        level: faultLevel || '1', // 默认一级故障
        status: 0, // 0=未处理
        resolved_time: null,
        created_at: nowStr,
        updated_at: nowStr,
    });

    return { faultId: insertResult.insertId, success: true };
}

/**
 * 设备类型中文到英文Key的映射
 */
let deviceTypeKeyMap: { [key: string]: string } = {
    "空调": "hvac",
    "新风": "freshAir",
    "人体传感": "occupancySensor",
    "客流传感器": "occupancySensor",
    "环境监测": "airSensor",
    "三相电表": "electricityMeter",
    "电表": "electricityMeter",
    "电能监测": "electricityMeter"
};

/**
 * 指标定义配置
 */
const INDICATOR_CONFIG = [
    { key: "co2", name: "二氧化碳", unit: "ppm", min: 0, max: 1000 },
    { key: "pm10", name: "PM10", unit: "μg/m³", min: 0, max: 150 },
    { key: "pm2_5", name: "PM2.5", unit: "μg/m³", min: 0, max: 75 },
    { key: "hcho", name: "甲醛", unit: "mg/m³", min: 0, max: 0.08 },
    { key: "tvoc", name: "TVOC", unit: "mg/m³", min: 0, max: 0.5 },
    { key: "temperature", name: "温度", unit: "℃", min: 18, max: 26 },
    { key: "humidity", name: "湿度", unit: "%", min: 40, max: 60 },
    { key: "pressure", name: "气压", unit: "hPa", min: null, max: null },
    { key: "light_level", name: "光照度", unit: "lux", min: null, max: null },
    { key: "pir", name: "人体感应", unit: "", min: null, max: null },
    { key: "o3", name: "臭氧", unit: "mg/m³", min: 0, max: 0.1 },
];

/**
 * 质量等级阈值（基于 GB/T 18883-2022 / RESET / WELL 标准）
 * 优/良/中/差 四档，区间均为闭区间 [min, max]
 */
const QUALITY_THRESHOLDS: { [key: string]: { you: [number, number]; liang: [number, number]; zhong: [number, number] } } = {
    co2:         { you: [0, 800],     liang: [800, 1000],  zhong: [1000, 1500] }, // ppm
    pm10:        { you: [0, 50],      liang: [50, 150],    zhong: [150, 250]   }, // μg/m³
    pm2_5:       { you: [0, 35],      liang: [35, 75],     zhong: [75, 150]    }, // μg/m³
    hcho:        { you: [0, 0.03],    liang: [0.03, 0.08], zhong: [0.08, 0.10] }, // mg/m³
    tvoc:        { you: [0, 0.3],     liang: [0.3, 0.6],   zhong: [0.6, 1.0]   }, // mg/m³
    // humidity 不在此表中，由 getQuality() 内联双向判断处理（偏低/偏高各方向独立降级）
    o3:          { you: [0, 0.05],    liang: [0.05, 0.1],  zhong: [0.1, 0.16]  }, // mg/m³
    light_level: { you: [300, 500],   liang: [100, 300],   zhong: [50, 100]    }, // lux — 通用办公/室内场景，>500 过于明亮也会判差
};

/**
 * 根据数据值判断质量等级（优/良/中/差）
 * - 温度：根据当前月份自动判定夏/冬季模式
 * - 湿度：双向判断（低于 40 或高于 60 降级）
 * - 气压：双向判断（以 1013hPa 为中心，优:1010~1020, 良:1000~1010|1020~1030, 中:990~1000|1030~1040, 差:<990|>1040）
 * - 光照度：通用室内办公标准，查阈值表（优:300~500, 良:100~300, 中:50~100, 差:<50 或 >500）
 */
function getQuality(key: string, value: number): string | null {
    if (value === null || value === undefined) return null;

    // 人体感应：特殊处理
    if (key === "pir") return value === 1 ? "有人" : "无感应";

    // 温度：季节模式
    if (key === "temperature") return getTemperatureQuality(value);

    // 湿度：双向判断
    if (key === "humidity") {
        if (value >= 40 && value <= 60) return "优";
        if ((value >= 30 && value < 40) || (value > 60 && value <= 70)) return "良";
        if ((value >= 20 && value < 30) || (value > 70 && value <= 80)) return "中";
        return "差"; // <20 或 >80
    }

    // 气压：双向判断（以 1013hPa 标准大气压为中心，偏低/偏高各方向独立降级）
    if (key === "pressure") {
        if (value >= 1010 && value <= 1020) return "优";
        if ((value >= 1000 && value < 1010) || (value > 1020 && value <= 1030)) return "良";
        if ((value >= 990 && value < 1000) || (value > 1030 && value <= 1040)) return "中";
        return "差"; // <990 或 >1040
    }

    // 其他指标：查阈值表
    const t = QUALITY_THRESHOLDS[key];
    if (!t) return null;

    if (value >= t.you[0] && value <= t.you[1]) return "优";
    if (value >= t.liang[0] && value <= t.liang[1]) return "良";
    if (value >= t.zhong[0] && value <= t.zhong[1]) return "中";
    return "差"; // 超出 zhong 区间上限
}

/**
 * 温度质量等级（夏/冬季模式，根据当前月份自动判定）
 * 设计逻辑：温度控制是一个收敛系统，20℃/24℃ 是收敛目标。
 * 良为收敛带（可接受偏移），中为漂移区间（需关注），差为失控（需告警）。
 *
 * 夏季（3-11月）：
 *   优 = 24℃（精确收敛点）
 *   良 = 22~26℃（收敛带）
 *   中 = 18~22℃ 或 26~32℃（漂移区间）
 *   差 = <18℃ 或 >32℃（失控，可能设备故障或极端天气）
 *
 * 冬季（12-2月）：
 *   优 = 20℃（精确收敛点）
 *   良 = 18~21℃（收敛带）
 *   中 = 14~18℃ 或 21~27℃（漂移区间）
 *   差 = <14℃ 或 >27℃（失控）
 */
function getTemperatureQuality(value: number): string {
    const month = new Date().getMonth() + 1; // 1-12
    const isWinter = month === 12 || month <= 2;

    if (isWinter) {
        if (value === 20) return "优";
        if (value >= 18 && value <= 21) return "良";
        if (value >= 14 && value <= 27) return "中";  // 14~18 和 21~27（优/良已在前两步返回）
        return "差"; // <14 或 >27
    } else {
        // 夏季（3-11月）
        if (value === 24) return "优";
        if (value >= 22 && value <= 26) return "良";
        if (value >= 18 && value <= 32) return "中";  // 18~22 和 26~32（优/良已在前两步返回）
        return "差"; // <18 或 >32
    }
}

/**
 * 格式化为本地时间：yyyy-MM-dd HH:mm:ss（替代 toISOString，避免 UTC 时区偏移）
 */
function formatTime(date: Date | string): string {
    if (!date) return null;
    const d = new Date(date);
    const y = d.getFullYear();
    const mo = String(d.getMonth() + 1).padStart(2, '0');
    const day = String(d.getDate()).padStart(2, '0');
    const h = String(d.getHours()).padStart(2, '0');
    const mi = String(d.getMinutes()).padStart(2, '0');
    const s = String(d.getSeconds()).padStart(2, '0');
    return `${y}-${mo}-${day} ${h}:${mi}:${s}`;
}

/**
 * 获取当天零点的时间字符串
 */
function getTodayStart(): string {
    const now = new Date();
    now.setHours(0, 0, 0, 0);
    return formatTime(now);
}

/**
 * 获取最近一小时起始的时间字符串
 */
function getLastHourStart(): string {
    const now = new Date();
    now.setHours(now.getHours() - 1);
    return formatTime(now);
}

/**
 * 获取当天最近的 device_data 记录（按设备测量时间倒序，限量返回）
 */
async function getTodayDeviceData(deviceId: string, limit: number = RECORD_LIMIT): Promise<any[]> {
    const hourAgo = getLastHourStart();
    const params: any = {
        device_id: deviceId,
        device_time: { "%gte%": hourAgo },
        "%orderDesc%": "device_time",
    };
    if (limit) params["%limit%"] = limit;
    const result = await selectDataListByParam(TABLENAME.设备数据表, params);
    return result.data || [];
}

/**
 * 构建 indicators 数组（基于当天最新数据）
 */
function buildIndicators(allLatestData: { [deviceId: string]: any }): any[] {
    // 收集所有设备的最新数据
    const allData = Object.values(allLatestData).filter(d => d && d.data);
    if (allData.length === 0) {
        return INDICATOR_CONFIG.map(config => ({
            key: config.key,
            name: config.name,
            unit: config.unit,
            current_value: 0,
            min: config.min,
            max: config.max,
            quality: null,
        }));
    }

    return INDICATOR_CONFIG.map(config => {
        // 收集所有设备该指标的数值
        const values = allData
            .map(d => d.data[config.key])
            .filter(v => v !== undefined && v !== null && typeof v === 'number');

        let currentValue: number = 0;
        if (values.length > 0) {
            const sum = values.reduce((acc, v) => acc + v, 0);
            currentValue = Math.round((sum / values.length) * 100) / 100; // 保留两位小数
        }

        return {
            key: config.key,
            name: config.name,
            unit: config.unit,
            current_value: currentValue,
            min: config.min,
            max: config.max,
            quality: getQuality(config.key, currentValue),
        };
    });
}

/**
 * 构建 records 数据
 */
function buildRecords(deviceDataMap: { [deviceId: string]: any[] }) {
    const allRows: any[] = [];
    for (const deviceId in deviceDataMap) {
        for (const record of deviceDataMap[deviceId]) {
            const rawData = record.data || {};
            const row: any = {
                device_id: deviceId,
                device_name: record.device_name || deviceId,
                device_time: record.device_time ? formatTime(record.device_time) : formatTime(record.created_at),
            };
            INDICATOR_CONFIG.forEach(c => {
                row[c.key] = rawData[c.key] !== undefined ? rawData[c.key] : null;
            });
            allRows.push(row);
        }
    }
    // 按时间倒序
    allRows.sort((a, b) => (b.device_time || "").localeCompare(a.device_time || ""));

    // titleList
    const titleList = ["设备编号", "设备名", "时间", ...INDICATOR_CONFIG.map(c => c.name)];

    // dataList：将 row 转为数组按 titleList 顺序
    const dataList = allRows.map(row => [
        row.device_id,
        row.device_name,
        row.device_time,
        ...INDICATOR_CONFIG.map(c => row[c.key]),
    ]);

    return { titleList, dataList };
}

/**
 * 获取环境设备信息
 * @param regionGroup 
 * @param regionType 
 * @param regionKey 
 * @param deviceId 
 */
/**
 * 获取环境监测设备，按 馆 → 层 → 设备 三级树形结构返回
 * 响应格式为：[{"key":"A馆","value":"A馆","sub":[{"key":"一层","value":"一层","sub":[{"key":"24E124710E467239","value":"沙盘厅_cgq5","sub":[]}]}]}]
 */
export async function getEnvironmentDevices() {
    // 内存缓存：设备树结构变化低频，5 分钟内直接复用
    if (deviceTreeCache && (Date.now() - deviceTreeCache.timestamp) < CACHE_TTL) {
        return deviceTreeCache.data;
    }

    // 1. 查询所有环境监测设备
    const deviceResult = await selectDataListByParam(TABLENAME.设备表, {
        device_type: "环境监测"
    }, ["device_id", "device_name", "region_key"]);

    const devices = deviceResult.data || [];
    if (devices.length === 0) {
        return [];
    }

    // 2. 收集所有 region_key，查询对应区域信息
    const regionKeys = [...new Set(devices.map((d: any) => d.region_key).filter(Boolean))];
    if (regionKeys.length === 0) {
        return [];
    }

    const regionResult = await selectDataListByParam(TABLENAME.区域表, {
        id: { "%in%": regionKeys },
        "%orderAsc%": "sort_order",
    }, ["id", "name", "type", "groups"]);

    const regions = regionResult.data || [];
    // 构建 regionKey → region 的映射
    const regionMap: { [id: number]: any } = {};
    regions.forEach((r: any) => {
        regionMap[r.id] = r;
    });

    // 3. 构建三级树：groups(馆) → type(层) → device
    // 用于跟踪已创建的 group/type 节点，避免重复
    const groupMap: { [group: string]: { key: string; value: string; typeMap: { [type: string]: { key: string; value: string; sub: any[] } } } } = {};

    devices.forEach((dev: any) => {
        const region = regionMap[dev.region_key];
        if (!region) return;

        const groupName = region.groups;
        const typeName = region.type;
        const roomName = region.name;

        // 确保 group 节点存在
        if (!groupMap[groupName]) {
            groupMap[groupName] = { key: groupName, value: groupName, typeMap: {} };
        }

        // 确保 type 节点存在
        if (!groupMap[groupName].typeMap[typeName]) {
            groupMap[groupName].typeMap[typeName] = { key: typeName, value: typeName, sub: [] };
        }

        // 添加设备到 type 下
        groupMap[groupName].typeMap[typeName].sub.push({
            key: dev.device_id,
            value: roomName+"_"+dev.device_name,
            sub: []
        });
    });

    // 4. 组装最终的树形数组
    const result: any[] = [];
    // 按 group 名称排序，保证稳定顺序
    const sortedGroups = Object.keys(groupMap).sort();
    sortedGroups.forEach(groupName => {
        const group = groupMap[groupName];
        const subArr: any[] = [];
        // 按 type 名称排序
        const sortedTypes = Object.keys(group.typeMap).sort();
        sortedTypes.forEach(typeName => {
            subArr.push(group.typeMap[typeName]);
        });
        result.push({
            key: group.key,
            value: group.value,
            sub: subArr
        });
    });

    deviceTreeCache = { data: result, timestamp: Date.now() };
    return result;
}

/**
 * 获取环境监测数据
 * - 传入 deviceId 时：查询指定设备的环境数据
 * - 传入 regionKey 时：查询该区域下所有环境监测设备的数据
 * - 传入 regionGroup + regionType 时：查询该楼栋+楼层下所有环境监测设备的数据
 * @param regionGroup 楼栋（如 "A馆"）
 * @param regionType 楼层（如 "1F"）
 * @param regionKey 区域ID
 * @param deviceIds 设备ID数组，精确查询指定设备
 * @returns
 */
export async function getEnvironmentData(regionGroup: string, regionType: string, regionKey: number, deviceId: string[]) {
    // ====== 假数据（调试用） ======
    // return {
    //     indicators: [
    //         { key: "co2", name: "二氧化碳", unit: "ppm", current_value: 450, min: 0, max: 1000, quality: "优秀" },
    //         { key: "pm10", name: "PM10", unit: "μg/m³", current_value: 30, min: 0, max: 150, quality: "优秀" },
    //         { key: "pm2_5", name: "PM2.5", unit: "μg/m³", current_value: 15, min: 0, max: 75, quality: "优秀" },
    //         { key: "hcho", name: "甲醛", unit: "mg/m³", current_value: 0.02, min: 0, max: 0.08, quality: "优秀" },
    //         { key: "tvoc", name: "TVOC", unit: "mg/m³", current_value: 0.3, min: 0, max: 0.5, quality: "优秀" },
    //         { key: "temperature", name: "温度", unit: "℃", current_value: 25, min: 18, max: 26, quality: "优秀" },
    //         { key: "humidity", name: "湿度", unit: "%", current_value: 60, min: 40, max: 60, quality: "优秀" },
    //         { key: "pressure", name: "气压", unit: "hPa", current_value: 1013, min: null, max: null, quality: null },
    //         { key: "light_level", name: "光照度", unit: "lux", current_value: 300, min: null, max: null, quality: null },
    //         { key: "pir", name: "人体感应", unit: "", current_value: 0, min: null, max: null, quality: "无感应" },
    //         { key: "o3", name: "臭氧", unit: "mg/m³", current_value: 0.04, min: 0, max: 0.1, quality: "优秀" },
    //     ],
    //     records: {
    //         titleList: ["设备", "时间", "二氧化碳", "PM10", "PM2.5", "甲醛", "TVOC", "温度", "湿度", "气压", "光照度", "PIR", "臭氧"],
    //         dataList: [
    //             ["沙盘厅_cgq2", "2025-06-18 10:00:00", 450, 30, 15, 0.02, 0.3, 25, 60, 1013, 300, 0, 0.04],
    //             ["辉煌厅_cgq5", "2025-06-18 10:30:00", 460, 32, 16, 0.02, 0.28, 25.5, 58, 1012, 320, 1, 0.04],
    //         ],
    //     },
    //     areaInfo: {
    //         building: "A馆",
    //         floor: "1F",
    //         room: "101室",
    //         devices: [{ device_id: "ENV-001", device_name: "环境监测仪1号" }],
    //     },
    // };

    let devices: any[] = [];

    // 1. 如果传了 deviceId，直接查这些设备
    if (deviceId && deviceId.length > 0) {
        const deviceResult = await selectDataListByParam(TABLENAME.设备表, {
            device_id: {'%in%': deviceId},
            device_type: "环境监测",
        });
        devices = deviceResult.data || [];
    } else {
        // 2. 确定区域查询条件
        const regionWhere: any = {};
        if (regionKey) {
            regionWhere.id = regionKey;
        } else {
            if (regionGroup) regionWhere.groups = regionGroup;
            if (regionType) regionWhere.type = regionType;
        }

        // 3. 查询区域
        const regionResult = await selectDataListByParam(TABLENAME.区域表, regionWhere);
        const regions = regionResult.data || [];
        if (regions.length === 0) {
            return { indicators: [], records: { titleList: [], dataList: [] }, areaInfo: null };
        }

        // 4. 查询区域下的环境监测设备
        const regionKeys = regions.map((r: any) => r.id);
        const deviceResult = await selectDataListByParam(TABLENAME.设备表, {
            region_key: { "%in%": regionKeys },
            device_type: "环境监测",
        });
        devices = deviceResult.data || [];
    }

    if (devices.length === 0) {
        return { indicators: [], records: { titleList: [], dataList: [] }, areaInfo: null };
    }

    // 查询设备所属区域，构建 regionKey -> 区域信息 的映射
    const deviceRegionKeys = [...new Set(devices.map((d: any) => d.region_key).filter(Boolean))];
    const regionNameMap: { [key: number]: string } = {};
    const regionInfoMap: { [key: number]: any } = {};
    if (deviceRegionKeys.length > 0) {
        const regionResult = await selectDataListByParam(TABLENAME.区域表, {
            id: { "%in%": deviceRegionKeys },
            "%orderAsc%": "sort_order",
        }, ["id", "name", "type", "groups"]);
        (regionResult.data || []).forEach((r: any) => {
            regionNameMap[r.id] = r.name || '';
            regionInfoMap[r.id] = r;
        });
    }

    // 5. 并行获取每个设备当天的数据（取代串行 for...of，N 个设备只需 1 次 DB 往返时间）
    const deviceDataMap: { [deviceId: string]: any[] } = {};
    const allLatestData: { [deviceId: string]: any } = {};

    await Promise.all(devices.map(async (device) => {
        const devId = device.device_id;
        const records = await getTodayDeviceData(devId);
        const regionName = regionNameMap[device.region_key] || '';
        records.forEach((r: any) => {
            r.device_name = regionName + '_' + (device.device_name || devId);
        });
        deviceDataMap[devId] = records;
        // 降序查询，第一条即为最新
        if (records.length > 0) {
            allLatestData[devId] = records[0];
        }
    }));

    // 6. 构建 indicators
    const indicators = buildIndicators(allLatestData);

    // 7. 构建 records
    const records = buildRecords(deviceDataMap);

    // 8. 构建 areaInfo（复用已查询的区域信息）
    let building = "";
    let floor = "";
    let room = "";
    const regionValues = Object.values(regionInfoMap);
    if (regionValues.length > 0) {
        const firstRegion: any = regionValues[0];
        building = firstRegion.groups || "";
        floor = firstRegion.type || "";
        room = firstRegion.name || "";
    }
    const areaInfo = {
        building,
        floor,
        room,
        devices: devices.map((d: any) => ({
            device_id: d.device_id,
            device_name: d.device_name || d.device_id,
        })),
    };

    return { indicators, records, areaInfo };
}

/**
 * CO2 线性超标描述（仅一般/差级别调用）
 * 偏高:1000~1500  超标:1500~2500  严重超标:>2500
 */
function getCo2ExceedLabel(co2: number): string | null {
    if (co2 > 2500) return '二氧化碳严重超标';
    if (co2 > 1500) return '二氧化碳超标';
    if (co2 >= 1000) return '二氧化碳偏高';
    return null;
}

/**
 * PM2.5 线性超标描述（仅一般/差级别调用）
 * 偏高:50~75  超标:75~95  严重超标:>95
 */
function getPm25ExceedLabel(pm25: number): string | null {
    if (pm25 > 95) return 'PM2.5严重超标';
    if (pm25 > 75) return 'PM2.5超标';
    if (pm25 > 50) return 'PM2.5偏高';
    return null;
}

/**
 * 运行分析 - 环境质量弹窗
 * 按区域查询环境监测设备最新数据，返回超标区域（一般/差）。
 * 质量等级与 todayEnvironmental 一致，优秀/良好不返回。
 */
export async function getEnvQualityPop(regionGroup?: string, regionType?: string) {
    // 1. 查询区域
    const regionWhere: any = {};
    if (regionGroup) regionWhere.groups = regionGroup;
    if (regionType) regionWhere.type = regionType;
    const regionResult = await selectDataListByParam(TABLENAME.区域表, { ...regionWhere, "%orderAsc%": "sort_order" }, ["id", "name", "type", "groups"]);
    const regions = regionResult.data || [];
    if (regions.length === 0) return { total: 0, dataList: [] };

    const regionMap: { [id: number]: any } = {};
    regions.forEach((r: any) => { regionMap[r.id] = r; });
    const regionKeys = regions.map((r: any) => r.id);

    // 2. 查询环境监测设备
    const deviceResult = await selectDataListByParam(TABLENAME.设备表, {
        region_key: { "%in%": regionKeys },
        device_type: "环境监测"
    }, ["device_id", "region_key"]);
    const devices = deviceResult.data || [];

    // 3. 按 region_key 分组
    const regionDevices: { [rk: number]: string[] } = {};
    devices.forEach((d: any) => {
        if (!regionDevices[d.region_key]) regionDevices[d.region_key] = [];
        regionDevices[d.region_key].push(d.device_id);
    });

    const dataList: any[] = [];

    const todayStart = getTodayStart();

    // 4. 并行处理有设备的区域
    await Promise.all(Object.keys(regionDevices).map(async (rkStr) => {
        const rk = Number(rkStr);
        const deviceIds = regionDevices[rk];
        const region = regionMap[rk];
        if (!region || deviceIds.length === 0) return;

        // 仅查询今天的数据，按 device_time 降序，每个设备最多取一条
        const dataResult = await selectDataListByParam(TABLENAME.设备数据表, {
            device_id: { "%in%": deviceIds },
            device_time: { "%gte%": todayStart },
            "%orderDesc%": "device_time",
            "%limit%": deviceIds.length
        }, ["device_id", "data"]);
        const records = dataResult.data || [];
        if (records.length === 0) return;

        // 每个设备取最新一条，算均值
        const deviceLatest: { [devId: string]: any } = {};
        records.forEach((r: any) => {
            if (!deviceLatest[r.device_id]) deviceLatest[r.device_id] = r.data;
        });
        const dataArr = Object.values(deviceLatest);
        const co2Avg = Math.round(dataArr.reduce((s, d: any) => s + (d.co2 ?? 0), 0) / dataArr.length);
        const pm25Avg = Math.round(dataArr.reduce((s, d: any) => s + (d.pm2_5 ?? 0), 0) / dataArr.length);

        // 质量等级判定（与 todayEnvironmental 一致）
        let quality: string;
        if (co2Avg < 800 && pm25Avg <= 35) {
            quality = '优秀';
        } else if (co2Avg < 1000 && pm25Avg <= 45) {
            quality = '优秀';
        } else if (co2Avg <= 1000 && pm25Avg <= 50) {
            quality = '良好';
        } else if (co2Avg > 1000 && co2Avg < 1500 && pm25Avg > 50 && pm25Avg < 75) {
            quality = '一般';
        } else {
            quality = '差';
        }

        // 仅返回一般/差
        if (quality === '优秀' || quality === '良好') return;

        // 构建超标内容
        const exceedItems: string[] = [];
        const co2Label = getCo2ExceedLabel(co2Avg);
        const pm25Label = getPm25ExceedLabel(pm25Avg);
        if (co2Label) exceedItems.push(co2Label);
        if (pm25Label) exceedItems.push(pm25Label);
        const qualityContent = exceedItems.length > 0
            ? `环境质量${quality}：${exceedItems.join('、')}`
            : `环境质量${quality}`;

        dataList.push({
            indexs: 0,
            regionName: [region.groups, region.type, region.name].filter(Boolean).join('_'),
            co2: co2Avg,
            pm25: pm25Avg,
            qualityContent
        });
    }));

    // 5. 排序并分配序号
    dataList.sort((a, b) => a.regionName.localeCompare(b.regionName, 'zh'));
    dataList.forEach((item, i) => { item.indexs = i + 1; });

    return { total: dataList.length, dataList };
}