import {
    getRegionTree,
    getIndoorUnitList,
    getElectricMaterList,
    getIndoorUnitStateList,
    getMaterStateList,
    getIndoorUnitAlarmErrorHis,
    getFaultCodeInfo,
    getFaultTypeName,
} from "./feiyiClient";
import { mysqlModelMap } from "../model/sqlModelBind";
import moment from "moment";
import XLSX from "xlsx";
import path from "path";

/** 飞奕品牌编码，默认 "1"=日立 */
const FEIYI_BRAND_CODE = "1";

// ==================== 工具函数 ====================

/**
 * 获取今天的日期字符串，格式 YYYYMMDD（使用本地时区，避免时区问题）
 */
function getTodayDateStr(): string {
    return moment().format('YYYYMMDD');
}

/**
 * 从内机四段地址（如 "2-1-0-1"）提取外机地址（"2-1-0"）
 */
function getOuterAddress(indoorUnitAddressFull: string): string | null {
    if (!indoorUnitAddressFull) return null;
    const parts = indoorUnitAddressFull.split('-');
    if (parts.length >= 3) {
        return parts.slice(0, 3).join('-');
    }
    return null;
}

/**
 * 从 Excel 文件读取电表设备编号 → 外机地址 的映射表
 */
function loadMeterOuterAddrMap(): Map<string, string> {
    const map = new Map<string, string>();
    try {
        const filePath = path.resolve(__dirname, '../../res/电表对应区域.xlsx');
        const workbook = XLSX.readFile(filePath);
        const sheet = workbook.Sheets[workbook.SheetNames[0]];
        const rows: any[] = XLSX.utils.sheet_to_json(sheet);
        for (const row of rows) {
            const meterCode = row['电表设备编号'];
            const outerAddr = row['空调外机地址'];
            if (meterCode && outerAddr) {
                map.set(String(meterCode), String(outerAddr));
            }
        }
        console.log(`[工具] 电表对应区域.xlsx 读取完成，共 ${map.size} 条映射`);
    } catch (err) {
        console.error('[工具] 读取电表对应区域.xlsx 失败:', err);
    }
    return map;
}

// ==================== 分页拉取辅助函数 ====================

/**
 * 分页拉取所有数据（通用）
 * @param fetchFn 分页查询函数，入参 { page, limit, ...extraParams }，返回 { page, limit, total, rows }
 * @param extraParams 额外的查询参数，会透传每次调用
 * @param limit 每页条数，默认 100
 */
async function fetchAllPages(
    fetchFn: (page: number, limit: number, extraParams: Record<string, any>) => Promise<any>,
    extraParams: Record<string, any> = {},
    limit: number = 100,
): Promise<any[]> {
    const allRows: any[] = [];
    let page = 1;
    let hasMore = true;

    while (hasMore) {
        const result = await fetchFn(page, limit, extraParams);
        if (!result || !result.rows || result.rows.length === 0) {
            hasMore = false;
            break;
        }
        allRows.push(...result.rows);
        // 判断是否还有下一页
        if (page * limit >= result.total) {
            hasMore = false;
        } else {
            page++;
        }
    }
    return allRows;
}

// ==================== 区域集成 ====================

/**
 * 递归遍历建筑物树，提取所有房间节点（buildingType === 3）
 * 同时追溯父节点获取楼层和楼栋信息
 */
function extractRoomsFromTree(
    tree: any[],
    parentFloor: { floorId: string; floorName: string } | null = null,
    parentBuilding: { buildingId: string; buildingName: string } | null = null,
): Array<{
    roomId: string;
    roomName: string;
    floorId: string;
    floorName: string;
    buildingId: string;
    buildingName: string;
}> {
    const rooms: any[] = [];

    for (const node of tree) {
        const buildingType = node.buildingType;
        const buildingId = node.buildingId;
        const buildingName = node.buildingName;
        const children = node.children || [];

        if (buildingType === 1) {
            // 楼栋节点
            const building = { buildingId, buildingName };
            rooms.push(...extractRoomsFromTree(children, null, building));
        } else if (buildingType === 2) {
            // 楼层节点
            const floor = { floorId: buildingId, floorName: buildingName };
            rooms.push(...extractRoomsFromTree(children, floor, parentBuilding));
        } else if (buildingType === 3) {
            // 房间节点
            rooms.push({
                roomId: buildingId,
                roomName: buildingName,
                floorId: parentFloor?.floorId || '',
                floorName: parentFloor?.floorName || '',
                buildingId: parentBuilding?.buildingId || '',
                buildingName: parentBuilding?.buildingName || '',
            });
        }
    }

    return rooms;
}

/**
 * 数据集成-区域集成
 * 按最小节点（房间）平铺集成，一条数据对应一个房间
 */
export async function region() {
    console.log('[区域集成] 开始执行...');
    try {
        const regionModel = mysqlModelMap['region'];
        if (!regionModel) {
            console.error('[区域集成] region 表模型未初始化，跳过');
            return;
        }

        // 1. 获取建筑物树结构
        const treeData = await getRegionTree();
        if (!treeData || !Array.isArray(treeData)) {
            console.warn('[区域集成] 未获取到建筑物树数据');
            return;
        }

        // 2. 提取所有房间节点
        const rooms = extractRoomsFromTree(treeData);
        console.log(`[区域集成] 共提取到 ${rooms.length} 个房间节点`);

        // 3. 逐房间检查并插入
        let insertCount = 0;
        let skipCount = 0;
        for (const room of rooms) {
            // 校验是否已存在（通过 room_id）
            const existing = await regionModel.findOne({ where: { room_id: room.roomId } });
            if (existing) {
                skipCount++;
                continue;
            }

            // 插入新区域记录
            await regionModel.create({
                room_id: room.roomId,
                name: room.roomName,
                floor_id: room.floorId,
                type: room.floorName,       // type 对应楼层名称
                building_id: room.buildingId,
                groups: room.buildingName,  // groups 对应楼栋名称
                sort_order: 0,
            });
            insertCount++;
        }

        console.log(`[区域集成] 完成，新增 ${insertCount} 条，跳过 ${skipCount} 条已存在`);

        // 2. 一次性查询所有 region，后续两步共用内存数据，避免 DB 读写时序问题
        const allRegions = await regionModel.findAll({ raw: true }) as any[];

        // 3. 同步空调内机 → 反写 region.address
        const updatedRegions = await syncRegionAddressByAcDevices(regionModel, allRegions);

        // 4. 同步电表设备 region_key（复用上一步已更新 address 的内存数据）
        await syncMeterDeviceRegionKey(updatedRegions);

    } catch (err) {
        console.error('[区域集成] 执行异常:', err);
    }
}

// ==================== 区域-设备关联同步 ====================

/**
 * 通过空调内机信息反写 region 表的 address 字段（外机地址）
 * 逻辑：拉取所有空调内机 → 通过 roomId 匹配 region → 用内机地址前三位反写 region.address
 */
async function syncRegionAddressByAcDevices(regionModel: any, allRegions: any[]) {
    console.log('[同步region.address] 开始...');
    try {
        // 拉取所有空调内机
        const rows = await fetchAllPages((page, limit) => getIndoorUnitList({ page, limit }));
        console.log(`[同步region.address] 共获取 ${rows.length} 条空调内机`);

        // 构建 roomId → region 映射（使用外部传入的 allRegions）
        const roomIdToRegion = new Map<string, any>();
        for (const r of allRegions) {
            if (r.room_id) {
                roomIdToRegion.set(r.room_id, r);
            }
        }

        let updateCount = 0;
        for (const item of rows) {
            if (!item.roomId || !item.indoorUnitAddressFull) continue;

            const region = roomIdToRegion.get(item.roomId);
            if (!region) continue;

            // 用内机四段地址前三位作为外机地址
            const outerAddr = getOuterAddress(item.indoorUnitAddressFull);
            if (!outerAddr) continue;

            // address 为空或值不同时才更新
            if (region.address === outerAddr) continue;

            await regionModel.update(
                { address: outerAddr },
                { where: { id: region.id } },
            );
            // 同步更新内存中的值，后续 syncMeterDeviceRegionKey 可直接使用
            region.address = outerAddr;
            updateCount++;
        }
        console.log(`[同步region.address] 完成，更新 ${updateCount} 条`);
        return allRegions; // 返回已更新 address 的内存数据
    } catch (err) {
        console.error('[同步region.address] 执行异常:', err);
        return allRegions; // 异常时仍返回原始数据，不阻塞后续流程
    }
}

/**
 * 通过 Excel 映射 + region.address 同步电表设备的 region_key
 * 逻辑：Excel(电表设备编号→外机地址) → region.address → region.id → 更新 device.region_key
 */
async function syncMeterDeviceRegionKey(allRegions: any[]) {
    console.log('[同步电表region_key] 开始...');
    try {
        const deviceModel = mysqlModelMap['device'];
        if (!deviceModel) {
            console.error('[同步电表region_key] device 表模型未初始化，跳过');
            return;
        }

        // 1. 读取 Excel 映射：电表设备编号 → 外机地址
        const meterOuterMap = loadMeterOuterAddrMap();
        if (meterOuterMap.size === 0) {
            console.warn('[同步电表region_key] Excel 映射为空，跳过');
            return;
        }

        // 2. 使用外部传入的 allRegions 构建 address → regionId 映射（复用已更新 address 的内存数据）
        const addrToRegionId = new Map<string, number>();
        for (const r of allRegions) {
            if (r.address) {
                addrToRegionId.set(r.address, r.id);
            }
        }
        console.log(`[同步电表region_key] region.address 映射共 ${addrToRegionId.size} 条`);

        // 3. 查询所有电表设备
        const meterDevices = await deviceModel.findAll({
            where: { device_type: '电能监测' },
            raw: true,
        });
        console.log(`[同步电表region_key] 共 ${meterDevices.length} 个电表设备`);

        // 4. 逐一匹配更新
        let updateCount = 0;
        let missCount = 0;
        for (const device of meterDevices as any[]) {
            const outerAddr = meterOuterMap.get(device.device_id);
            if (!outerAddr) {
                missCount++;
                continue;
            }
            const regionId = addrToRegionId.get(outerAddr);
            if (!regionId) {
                missCount++;
                continue;
            }
            // region_key 已正确则跳过
            if (device.region_key === regionId) continue;

            await deviceModel.update(
                { region_key: regionId },
                { where: { id: device.id } },
            );
            updateCount++;
        }
        console.log(`[同步电表region_key] 完成，更新 ${updateCount} 条，未匹配 ${missCount} 条`);
    } catch (err) {
        console.error('[同步电表region_key] 执行异常:', err);
    }
}

// ==================== 设备集成 ====================

/** 空调控制参数模板（按设备数据结构.md） */
const AC_CONTROL_PARAMS = {
    power: ["on", "off"],
    mode: ["制冷", "制热", "送风", "除湿"],
    fanSpeed: ["自动", "弱", "强", "超强"],
    setTemp: "16~30",
    roomTemp: "-20~50",
    maxTemp: "20~40",
    minTemp: "-10~20",
    autoControl: ["生效", "解除"],
};

/** 电表控制参数模板（按设备数据结构.md） */
const METER_CONTROL_PARAMS = {
    current: "0~9999",
    voltage: "0~500",
    power: "0~99999",
    energy: "0~999999",
    switch: ["closed", "open"],
};

/**
 * 数据集成-设备集成
 * 包含：空调设备集成 + 电表设备集成
 */
export async function device() {
    console.log('[设备集成] 开始执行...');
    try {
        await integrateAcDevices();
        await integrateMeterDevices();
        console.log('[设备集成] 完成');
    } catch (err) {
        console.error('[设备集成] 执行异常:', err);
    }
}

/**
 * 空调设备集成
 * device_id = indoorUnitAddressFull
 * device_ad = indoorUnitId
 */
async function integrateAcDevices() {
    console.log('[空调设备集成] 开始...');
    const deviceModel = mysqlModelMap['device'];
    if (!deviceModel) {
        console.error('[空调设备集成] device 表模型未初始化，跳过');
        return;
    }

    // 查询未处理的故障设备信息
    const faultDevices = await deviceModel.findAll({
        where: { device_type: '空调', device_state: 2 },
        raw: true,
    });
    console.log(`[空调设备集成] 共 ${faultDevices.length} 个故障设备`);
    let deviceNomalIds = [];

    // 分页拉取所有内机数据
    const rows = await fetchAllPages((page, limit) => getIndoorUnitList({ page, limit }));
    console.log(`[空调设备集成] 共获取 ${rows.length} 条内机数据`);

    let insertCount = 0;
    let skipCount = 0;
    for (const item of rows) {
        const deviceId = item.indoorUnitAddressFull; // device_id 用四段地址
        if (!deviceId) {
            skipCount++;
            continue;
        }

        // 校验是否已存在
        const existing = await deviceModel.findOne({ where: { device_id: deviceId } });
        if (existing) {
            skipCount++;
            // 更新设备状态
            const newState = item.state ?? 0;
            if (existing.device_state !== newState) {
                await deviceModel.update(
                    { device_state: newState, updated_at: new Date() },
                    { where: { id: existing.id } },
                );
            }
            // 校验故障设备是否已恢复
            if (faultDevices.find(d => d.device_id === deviceId) && newState === 0) {
                console.log(`[空调设备集成] 故障设备 ${deviceId} 已恢复`);
                deviceNomalIds.push(deviceId);
            }
            continue;
        }

        let deviceType = '空调';
        let deviceName = '空调内机';

        // 查找对应的 region_key
        let regionKey = 0;
        if (item.roomId) {
            const regionModel = mysqlModelMap['region'];
            if (regionModel) {
                const region = await regionModel.findOne({ where: { room_id: item.roomId } });
                if (region) {
                    regionKey = region.id;
                    // 如果区域地址包含新风，则空调视为新风机
                    if (region.name && region.name.includes('新风')) {
                        deviceType = '新风';
                        deviceName = '新风机';
                    }
                }
            }
        }

        // 插入新设备
        await deviceModel.create({
            device_id: deviceId,
            device_ad: item.indoorUnitId || '',
            region_key: regionKey,
            device_type: deviceType,
            device_name: `${deviceName}-${deviceId}`,
            control_params: AC_CONTROL_PARAMS,
            device_state: item.state ?? 0
        });
        insertCount++;
    }
    // 根据deviceNomalIds更新故障信息为正常
    if (deviceNomalIds.length > 0) {
        const faultModel = mysqlModelMap['device_fault'];
        if (faultModel) {
            const Sequelize = require('sequelize');
            const Op = Sequelize.Op;
            const now = new Date();
            await faultModel.update(
                { status: 2, resolved_time: now, updated_at: now },
                { where: { device_id: { [Op.in]: deviceNomalIds }, status: { [Op.ne]: 2 } } },
            );
            console.log(`[空调设备集成] 批量解决 ${deviceNomalIds.length} 台设备故障`);
        }
    }

    console.log(`[空调设备集成] 完成，新增 ${insertCount} 条，跳过 ${skipCount} 条已存在`);
}

/**
 * 电表设备集成
 * device_id = gatewayCode
 * device_ad = meterId
 */
async function integrateMeterDevices() {
    console.log('[电表设备集成] 开始...');
    const deviceModel = mysqlModelMap['device'];
    if (!deviceModel) {
        console.error('[电表设备集成] device 表模型未初始化，跳过');
        return;
    }

    // 分页拉取所有电表数据
    const rows = await fetchAllPages((page, limit) => getElectricMaterList({ page, limit }));
    console.log(`[电表设备集成] 共获取 ${rows.length} 条电表数据`);

    let insertCount = 0;
    let skipCount = 0;
    for (const item of rows) {
        const deviceId = item.gatewayCode; // device_id 用 gatewayCode
        if (!deviceId) {
            skipCount++;
            continue;
        }

        // 校验是否已存在
        const existing = await deviceModel.findOne({ where: { device_id: deviceId } });
        if (existing) {
            skipCount++;
            continue;
        }

        // 插入新设备
        await deviceModel.create({
            device_id: deviceId,
            device_ad: item.meterId || '',
            region_key: 0, // 电表无房间关联
            device_type: '电能监测',
            device_name: `电表-${item.meterComId || item.meterId || ''}`,
            control_params: METER_CONTROL_PARAMS,
        });
        insertCount++;
    }

    console.log(`[电表设备集成] 完成，新增 ${insertCount} 条，跳过 ${skipCount} 条已存在`);
}

// ==================== 设备数据集成 ====================

/** 工作模式映射：1=制冷 2=制热 3=送风 4=除湿 */
const WORK_MODE_MAP: Record<number, string> = {
    1: '制冷',
    2: '制热',
    3: '送风',
    4: '除湿',
};

/** 风速映射：1=高风 2=中风 4=低风 */
const FAN_SPEED_MAP: Record<number, string> = {
    1: '超强',
    2: '强',
    4: '弱',
};

/**
 * 数据集成-空调内机、电表设备数据集成
 */
export async function deviceData() {
    console.log('[设备数据集成] 开始执行...');
    try {
        await integrateAcDeviceData();
        await integrateMeterDeviceData();
        console.log('[设备数据集成] 完成');
    } catch (err) {
        console.error('[设备数据集成] 执行异常:', err);
    }
}

/**
 * 空调设备数据集成（开关机记录）
 * 用 indoorUnitAddressFull 匹配 device 表的 device_id
 */
async function integrateAcDeviceData() {
    console.log('[空调设备数据集成] 开始...');
    const deviceModel = mysqlModelMap['device'];
    const deviceDataModel = mysqlModelMap['device_data'];
    if (!deviceModel || !deviceDataModel) {
        console.error('[空调设备数据集成] device 或 device_data 表模型未初始化，跳过');
        return;
    }

    // 1. 分页拉取当天内机开关机记录（使用 occurrenceTime 过滤当天数据）
    const todayDate = getTodayDateStr();
    const rows = await fetchAllPages(
        (page, limit, extraParams) => getIndoorUnitStateList({ page, limit, occurrenceTime: extraParams.occurrenceTime }),
        { occurrenceTime: todayDate },
    );
    console.log(`[空调设备数据集成] 共获取 ${rows.length} 条开关机记录`);
    if (rows.length === 0) {
        console.log('[空调设备数据集成] 当天无开关机记录，跳过');
        return;
    }

    // 2. 提取所有涉及的 device_id（去重），并过滤出已在 device 表中注册的设备
    const deviceIdSet = new Set<string>();
    for (const item of rows) {
        if (item.indoorUnitAddressFull) {
            deviceIdSet.add(item.indoorUnitAddressFull);
        }
    }
    const deviceIds = Array.from(deviceIdSet);

    // 3. 一次 SQL GROUP BY 查询这些设备在 device_data 表中各自的最大 device_time
    const maxTimeMap = new Map<string, Date | null>();
    if (deviceIds.length > 0) {
        const maxTimeResults = await deviceDataModel.findAll({
            attributes: [
                'device_id',
                [deviceDataModel.sequelize.fn('MAX', deviceDataModel.sequelize.col('device_time')), 'max_time'],
            ],
            where: { device_id: deviceIds },
            group: ['device_id'],
            raw: true,
        });
        for (const r of maxTimeResults as any[]) {
            maxTimeMap.set(r.device_id, r.max_time ? new Date(r.max_time) : null);
        }
    }

    // 4. 内存过滤：保留 device_time > 该设备最大时间的记录
    const toInsert: any[] = [];
    let skipCount = 0;
    for (const item of rows) {
        const deviceId = item.indoorUnitAddressFull;
        if (!deviceId) {
            skipCount++;
            continue;
        }

        // 校验设备是否已注册
        if (!deviceIdSet.has(deviceId)) {
            skipCount++;
            continue;
        }

        const itemTime = item.createTime ? new Date(item.createTime) : null;
        const maxTime = maxTimeMap.get(deviceId);

        // 新设备（无历史数据）全部保留；已有数据的设备只保留增量
        // 使用秒级时间戳比较，避免 MySQL DATETIME（秒精度）与接口毫秒时间戳精度不一致导致重复插入
        if (maxTime !== undefined && maxTime !== null && itemTime) {
            const itemSec = Math.floor(itemTime.getTime() / 1000);
            const maxSec = Math.floor(maxTime.getTime() / 1000);
            if (itemSec <= maxSec) {
                skipCount++;
                continue;
            }
        }

        // 构造数据（按设备数据结构.md 空调设备数据格式）
        const data = {
            power: item.onOff === 1 ? 'on' : 'off',
            mode: WORK_MODE_MAP[item.workMode] || '未知',
            fanSpeed: FAN_SPEED_MAP[item.fanSpeed] || '自动',
            setTemp: item.tempSet || 0,
            roomTemp: item.roomTemp || 0,
            maxTemp: item.tempSetHi || 28,   // 飞奕无数据时默认上限28℃
            minTemp: item.tempSetLo || 16,   // 飞奕无数据时默认下限16℃
            autoControl: '生效',
        };
        toInsert.push({
            device_id: deviceId,
            data: data,
            received_time: new Date(),
            device_time: itemTime,
        });

    }

    // TODO: 考虑是否需要在插入前对数据按 device_time 排序，避免数据库存储顺序混乱

    // 5. 批量插入
    if (toInsert.length > 0) {
        await deviceDataModel.bulkCreate(toInsert);
    }

    console.log(`[空调设备数据集成] 完成，新增 ${toInsert.length} 条，跳过 ${skipCount} 条`);
}

/**
 * 电表设备数据集成（抄表记录）
 * 用 gatewayCode 匹配 device 表的 device_id
 */
async function integrateMeterDeviceData() {
    console.log('[电表设备数据集成] 开始...');
    const deviceModel = mysqlModelMap['device'];
    const deviceDataModel = mysqlModelMap['device_data'];
    if (!deviceModel || !deviceDataModel) {
        console.error('[电表设备数据集成] device 或 device_data 表模型未初始化，跳过');
        return;
    }

    // 1. 分页拉取当天抄表记录（使用 occurrenceTime 过滤当天数据）
    const todayDate = getTodayDateStr();
    const rows = await fetchAllPages(
        (page, limit, extraParams) => getMaterStateList({ page, limit, occurrenceTime: extraParams.occurrenceTime }),
        { occurrenceTime: todayDate },
    );
    console.log(`[电表设备数据集成] 共获取 ${rows.length} 条抄表记录`);
    if (rows.length === 0) {
        console.log('[电表设备数据集成] 当天无抄表记录，跳过');
        return;
    }

    // 2. 提取所有涉及的 device_id（去重）
    const deviceIdSet = new Set<string>();
    for (const item of rows) {
        if (item.gatewayCode) {
            deviceIdSet.add(item.gatewayCode);
        }
    }
    const deviceIds = Array.from(deviceIdSet);

    // 3. 一次 SQL GROUP BY 查询这些设备在 device_data 表中各自的最大 device_time
    const maxTimeMap = new Map<string, Date | null>();
    if (deviceIds.length > 0) {
        const maxTimeResults = await deviceDataModel.findAll({
            attributes: [
                'device_id',
                [deviceDataModel.sequelize.fn('MAX', deviceDataModel.sequelize.col('device_time')), 'max_time'],
            ],
            where: { device_id: deviceIds },
            group: ['device_id'],
            raw: true,
        });
        for (const r of maxTimeResults as any[]) {
            maxTimeMap.set(r.device_id, r.max_time ? new Date(r.max_time) : null);
        }
    }

    // 4. 内存过滤：保留 device_time > 该设备最大时间的记录
    const toInsert: any[] = [];
    let skipCount = 0;
    for (const item of rows) {
        const deviceId = item.gatewayCode;
        if (!deviceId) {
            skipCount++;
            continue;
        }

        const itemTime = item.deviceTime ? new Date(item.deviceTime) : null;
        const maxTime = maxTimeMap.get(deviceId);

        // 新设备（无历史数据）全部保留；已有数据的设备只保留增量
        // 使用秒级时间戳比较，避免 MySQL DATETIME（秒精度）与接口毫秒时间戳精度不一致导致重复插入
        if (maxTime !== undefined && maxTime !== null && itemTime) {
            const itemSec = Math.floor(itemTime.getTime() / 1000);
            const maxSec = Math.floor(maxTime.getTime() / 1000);
            if (itemSec <= maxSec) {
                skipCount++;
                continue;
            }
        }

        // 构造数据（按设备数据结构.md 电表设备数据格式）
        const data = {
            current: item.powerConsumptionAfterTransformationRatio || 0,
            voltage: 0,
            power: item.ammeterReadingAfterTransformationRatio || 0,
            energy: item.ammeterReadingAfterTransformationRatio || 0,
            switch: 'open',
        };

        toInsert.push({
            device_id: deviceId,
            data: data,
            received_time: new Date(),
            device_time: itemTime,
        });
    }

    // TODO: 考虑是否需要在插入前对数据按 device_time 排序，避免数据库存储顺序混乱

    // 5. 批量插入
    if (toInsert.length > 0) {
        await deviceDataModel.bulkCreate(toInsert);
    }

    console.log(`[电表设备数据集成] 完成，新增 ${toInsert.length} 条，跳过 ${skipCount} 条`);
}

// ==================== 暂无对应接口的集成函数（保留空实现） ====================

/**
 * 数据集成-九合一环境设备数据集成
 */
export async function airDeviceData() {
    // 暂无对应接口 -> MQTT
}

/**
 * 数据集成-客流监测设备数据集成
 */
export async function customerDeviceData() {
    // 暂无对应接口 -> MQTT
}

// ==================== 预警工单集成（飞奕故障记录） ====================

/**
 * 数据集成-预警工单数据集成
 * 从飞奕云平台拉取最近 100 条空调内机故障记录，按 indoorUnitAlarmId 去重后写入 device_fault 表
 */
export async function alertWorkData() {
    console.log('[预警工单集成] 开始...');
    const deviceModel = mysqlModelMap['device'];
    const faultModel = mysqlModelMap['device_fault'];
    if (!deviceModel || !faultModel) {
        console.error('[预警工单集成] device 或 device_fault 模型未初始化，跳过');
        return;
    }

    try {
        // 1. 拉取最近 100 条故障记录（不再限制当天）
        const result = await getIndoorUnitAlarmErrorHis({ page: 1, limit: 100 });
        const rows: any[] = result?.rows || [];
        console.log(`[预警工单集成] 获取 ${rows.length} 条故障记录`);

        if (rows.length === 0) {
            console.log('[预警工单集成] 无故障记录，跳过');
            return;
        }

        // 2. 收集所有 indoorUnitAlarmId，批量查已有故障记录（去重）
        const alarmIds = rows.map((r: any) => r.indoorUnitAlarmId).filter(Boolean);
        if (alarmIds.length === 0) {
            console.log('[预警工单集成] 所有记录缺少 indoorUnitAlarmId，跳过');
            return;
        }

        const existingFaults = await faultModel.findAll({
            where: { fault_origin_id: alarmIds },
            attributes: ['fault_origin_id'],
            raw: true,
        });
        const existingIds = new Set((existingFaults as any[]).map((f: any) => f.fault_origin_id));

        // 3. 收集 indoorUnitAddressFull，批量查设备确认已注册
        const addrList = rows.map((r: any) => r.indoorUnitAddressFull).filter(Boolean);
        const devices = await deviceModel.findAll({
            where: { device_id: addrList },
            attributes: ['device_id'],
            raw: true,
        });
        const registeredDeviceIds = new Set((devices as any[]).map((d: any) => d.device_id));

        // 4. 收集所有故障码，批量查询故障详情（缓存去重）
        const alarmCodes = [...new Set(rows.map((r: any) => r.alarmCode).filter(Boolean))] as string[];
        const faultInfoMap = new Map<string, { errorInfo: string; scheme: string | null }>();
        if (alarmCodes.length > 0) {
            console.log(`[预警工单集成] 查询 ${alarmCodes.length} 个不同故障码的详情...`);
            const infoResults = await Promise.all(
                alarmCodes.map(async (code) => {
                    const info = await getFaultCodeInfo(FEIYI_BRAND_CODE, code);
                    return { code, info };
                })
            );
            for (const { code, info } of infoResults) {
                faultInfoMap.set(code, info);
            }
        }

        // 5. 过滤 + 组装待插入数据
        const toInsert: any[] = [];
        let skipCount = 0;
        for (const r of rows as any[]) {
            // 去重：已存在的记录跳过
            if (!r.indoorUnitAlarmId || existingIds.has(r.indoorUnitAlarmId)) {
                skipCount++;
                continue;
            }
            // 设备未注册跳过
            const deviceId = r.indoorUnitAddressFull;
            if (!deviceId || !registeredDeviceIds.has(deviceId)) {
                skipCount++;
                continue;
            }

            // 获取故障详情
            const faultInfo = faultInfoMap.get(r.alarmCode);
            const faultFound = faultInfo
                && faultInfo.errorInfo !== r.alarmCode
                && faultInfo.errorInfo !== '查询失败';
            const errorMsg = faultFound ? faultInfo.errorInfo : '未找到主内机';

            toInsert.push({
                device_id: deviceId,
                fault_type: getFaultTypeName(r.alarmType),
                fault_code: r.alarmCode || '',
                fault_description: errorMsg,
                fault_origin_id: r.indoorUnitAlarmId,
                occurred_time: r.deviceTime ? new Date(r.deviceTime) : new Date(),
                level: 2,
                status: 0,
            });
        }

        // 6. 批量写入
        if (toInsert.length > 0) {
            await faultModel.bulkCreate(toInsert);
        }

        console.log(`[预警工单集成] 完成，新增 ${toInsert.length} 条，跳过 ${skipCount} 条（去重/未注册）`);
    } catch (err) {
        console.error('[预警工单集成] 执行异常:', err);
    }
}

// ==================== 故障状态自动更新 ====================

/**
 * 故障状态自动更新
 * 故障驱动模式：拉取所有未处理/处理中的故障，其设备一次拉回故障时间之后的全部开机记录，
 * 在内存中比对，状态匹配的批量更新为"已解决"。
 * 始终发 2 条 SQL，与故障数无关；不设时间兜底，服务宕机重启后也能补全。
 */
export async function processFaultStatus() {
    console.log('[故障状态更新] 开始...');
    const faultModel = mysqlModelMap['device_fault'];
    const deviceDataModel = mysqlModelMap['device_data'];
    if (!faultModel || !deviceDataModel) {
        console.error('[故障状态更新] device_fault 或 device_data 模型未初始化，跳过');
        return;
    }

    try {
        const Sequelize = require('sequelize');
        const Op = Sequelize.Op;

        // ① 一次查出所有未处理/处理中的故障
        const activeFaults = await faultModel.findAll({
            where: { status: { [Op.in]: [0, 1] } },
            attributes: ['id', 'device_id', 'occurred_time'],
            raw: true,
        });
        console.log(`[故障状态更新] 共 ${activeFaults.length} 条活跃故障`);

        if (activeFaults.length === 0) {
            console.log('[故障状态更新] 无活跃故障，跳过');
            return;
        }

        // ② 去重 device_id + 建立每个设备的最早故障时间映射
        //    deviceFaultTimeMap: 每个设备最早的活跃故障发生时间，用于内存中精确过滤
        //    globalMinOccurredTime: 全局最早故障时间，仅作为 SQL 查询范围下限
        const deviceSet = new Set<string>();
        let globalMinOccurredTime: Date | null = null;
        const deviceFaultTimeMap = new Map<string, Date>();
        for (const f of activeFaults as any[]) {
            deviceSet.add(f.device_id);
            const t = f.occurred_time ? new Date(f.occurred_time) : null;
            if (!t) continue;
            // 全局最早时间（SQL 查询范围）
            if (!globalMinOccurredTime || t < globalMinOccurredTime) {
                globalMinOccurredTime = t;
            }
            // 每个设备的最早故障时间（内存过滤用）
            const existing = deviceFaultTimeMap.get(f.device_id);
            if (!existing || t < existing) {
                deviceFaultTimeMap.set(f.device_id, t);
            }
        }
        const deviceIds = [...deviceSet];

        // ③ 一次拉回所有相关设备的运行记录（全局最早故障时间之后）
        //    不做时间兜底，宕机重启后也能覆盖全部历史
        const deviceDataRecords = await deviceDataModel.findAll({
            where: {
                device_id: { [Op.in]: deviceIds },
                ...(globalMinOccurredTime ? { device_time: { [Op.gte]: globalMinOccurredTime } } : {}),
            },
            attributes: ['device_id', 'data', 'device_time'],
            order: [['device_id', 'ASC'], ['device_time', 'ASC']],
            raw: true,
        });
        console.log(`[故障状态更新] 共 ${deviceDataRecords.length} 条相关设备数据记录`);

        // ④ 内存过滤：只保留各设备最早故障时间之后的开机记录（power='on'）
        //    关键修复：不以全局最早时间过滤，而是用每个设备自己的最早故障时间，
        //    避免将故障发生前的开机记录纳入比对，导致永远无法判定"已解决"
        const powerOnTimesMap = new Map<string, Date[]>();
        for (const r of deviceDataRecords as any[]) {
            let power: string | undefined;
            const rawData = r.data;
            if (typeof rawData === 'string') {
                try { power = JSON.parse(rawData).power; } catch { continue; }
            } else if (rawData && typeof rawData === 'object') {
                power = rawData.power;
            }
            if (power !== 'on') continue;

            const dt = r.device_time ? new Date(r.device_time) : null;
            if (!dt) continue;

            // 只保留该设备最早故障时间之后的记录
            const deviceFaultTime = deviceFaultTimeMap.get(r.device_id);
            if (deviceFaultTime && dt <= deviceFaultTime) continue;

            if (!powerOnTimesMap.has(r.device_id)) {
                powerOnTimesMap.set(r.device_id, []);
            }
            powerOnTimesMap.get(r.device_id)!.push(dt);
        }

        // ⑤ 逐条故障比对：查找该故障发生时间之后最早的开机记录
        //    关键修复：不再拿"全量最早开机时间"与故障时间对比，
        //    而是在该设备故障后出现的开机记录中找第一条，确保 isAfter 判定正确
        const toUpdate: { id: number; resolved_time: Date }[] = [];
        for (const fault of activeFaults as any[]) {
            const powerOnTimes = powerOnTimesMap.get(fault.device_id);
            if (!powerOnTimes || powerOnTimes.length === 0) continue;

            const occurredTime = fault.occurred_time ? new Date(fault.occurred_time) : null;
            if (!occurredTime) continue;

            // 数据已按 device_time ASC 排序，找第一条在故障时间之后的记录
            const resolvedTime = powerOnTimes.find(dt => dt > occurredTime);
            if (resolvedTime) {
                toUpdate.push({ id: fault.id, resolved_time: resolvedTime });
            }
        }

        if (toUpdate.length === 0) {
            console.log('[故障状态更新] 无符合条件的故障需更新');
            return;
        }

        // ⑥ 逐条更新 status=2
        for (const item of toUpdate) {
            await faultModel.update(
                { status: 2, resolved_time: item.resolved_time, updated_at: new Date() },
                { where: { id: item.id } },
            );
        }

        console.log(`[故障状态更新] 完成，共处理 ${toUpdate.length} 条故障`);
    } catch (err) {
        console.error('[故障状态更新] 执行异常:', err);
    }
}
