package com.usky.cdi.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.usky.cdi.domain.BaseAlarm;
import com.usky.cdi.domain.BaseBuildFacility;
import com.usky.cdi.mapper.BaseAlarmMapper;
import com.usky.cdi.mapper.BaseBuildFacilityMapper;
import com.usky.cdi.service.CdiDeliveryLogService;
import com.usky.cdi.service.enums.MqttTopics;
import com.usky.cdi.service.mqtt.MqttConnectionTool;
import com.usky.cdi.service.util.SnowflakeIdGenerator;
import com.usky.cdi.service.vo.alarm.AlarmMessageVO;
import com.usky.cdi.service.enums.AlarmType;
import com.usky.common.security.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 多租户定时任务服务
 *
 * @author fu
 * @since 2025-12-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AlarmDataSyncService {

    @Value("${snowflake.worker-id:3}")
    private long workerId;

    @Value("${snowflake.data-center-id:3}")
    private long dataCenterId;

    private static final String PEACETIME = "peacetime";
    private static final String WARTIME = "wartime";
    private static final String ALARM_DELIVERY_KEY_PREFIX = "alarm:delivery:";
    private final MqttConnectionTool mqttConnectionTool;
    private final SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private BaseAlarmMapper baseAlarmMapper;
    @Autowired
    private BaseBuildFacilityMapper baseBuildFacilityMapper;
    @Autowired
    private CdiDeliveryLogService cdiDeliveryLogService;

    private SnowflakeIdGenerator idGenerator;

    @PostConstruct
    public void init() {
        this.idGenerator = new SnowflakeIdGenerator(workerId, dataCenterId);
    }

    /**
     * 获取当前时间
     */
    private String getCurrentTime() {
        return timeFormat.format(new Date());
    }

    /**
     * 生成数据包ID
     */
    private Long generateDataPacketID() {
        return idGenerator.nextPacketId();
    }

    @Async
    public void synchronizeAlarmData(Integer tenantId, Long engineeringId, String username, String password, String status) {
        log.info("租户：{}的人防告警数据推送定时任务开始执行，平战时状态：{}", tenantId, PEACETIME.equals(status) ? "平时" : "战时");

        LocalDateTime now = LocalDateTime.now();
        long startTime = System.currentTimeMillis();
        long endTime;
        log.info("开始时间:{}", getCurrentTime());

        // 1.查询 base_alarm 表中的告警数据，筛选条件为 tenant_id = tenantId
        LambdaQueryWrapper<BaseAlarm> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BaseAlarm::getTenantId, tenantId)
                .eq(BaseAlarm::getAlarmGrade, 1);
        List<BaseAlarm> alarmList = baseAlarmMapper.selectList(queryWrapper);

        if (alarmList.isEmpty()) {
            log.warn("租户{}没有新的告警数据，任务结束", tenantId);
            return;
        }
        int size = alarmList.size();
        log.info("查询到租户{}的告警数据总数：{}", tenantId, size);

        // 批量查询建筑设施数据，构建设备ID到设施的映射
        Map<String, BaseBuildFacility> facilityMap = buildFacilityMap(alarmList, tenantId);

        // 记录每一条告警数据
        for (BaseAlarm alarm : alarmList) {
            log.info("告警数据：ID={}, 设备ID={}, 告警类型={}, 告警时间={}, 处理状态={}, 是否误报={}",
                    alarm.getId(), alarm.getDeviceId(), alarm.getAlarmType(),
                    alarm.getAlarmTime(), alarm.getHandleStatus(), alarm.getAlarmFalse());
        }

        int successCount = 0;
        int failureCount = 0;
        String topic = MqttTopics.Alarm.MESSAGE.getTopic();
        String desc = MqttTopics.Alarm.MESSAGE.getDesc();

        String userName = "自动同步";
        try {
            userName = SecurityUtils.getUsername();
        } catch (Exception e) {
            log.error("定时任务无法获取用户名，使用默认‘自动同步’", e);
        }

        try {
            // 2.创建MQTT连接
            mqttConnectionTool.connectOrRefresh(username, password);

            // 3.遍历告警数据，转换为AlarmMessageVO并发送
            for (BaseAlarm alarm : alarmList) {
                try {
                    // 根据告警类型映射关系转换
                    String alarmType = convertAlarmType(alarm.getAlarmType());
                    if (alarmType == null) {
                        log.warn("不支持的告警类型：{}，跳过处理", alarm.getAlarmType());
                        failureCount++;
                        continue;
                    }

                    // 转换告警状态
                    Integer alarmStatus = convertAlarmStatus(alarm.getHandleStatus(), alarm.getAlarmFalse(), status);

                    // 构建AlarmMessageVO对象
                    AlarmMessageVO<Integer> alarmMessageVO = new AlarmMessageVO<>();
                    alarmMessageVO.setDataPacketID(generateDataPacketID());
                    alarmMessageVO.setEngineeringID(engineeringId);
                    alarmMessageVO.setAlarmID(alarm.getId());
                    // 固定值1，人防工程级物联平台自动发现
                    alarmMessageVO.setAlarmSource(1);
                    alarmMessageVO.setSensorID(alarm.getDeviceId() != null ? Integer.valueOf(alarm.getDeviceId()) : null);
                    alarmMessageVO.setAlarmType(alarmType);
                    alarmMessageVO.setAlarmStatus(alarmStatus);

                    // 设置告警时间：新增告警取 alarm_type 字段，更新数据则取 handle_time 字段
                    String alarmTime = alarm.getHandleTime() != null ?
                            alarm.getHandleTime().toString() : alarm.getAlarmTime().toString();
                    alarmMessageVO.setAlarmUpdateTime(alarmTime);

                    // 设置监测对象告警描述和编号
                    alarmMessageVO.setAlarmDesc(alarm.getAlarmAttribute());
                    alarmMessageVO.setMonitorObjNo(facilityMap.get(alarm.getDeviceId()).getFacilityNum());

                    // 设置上报时间为当前时间
                    alarmMessageVO.setPublishTime(getCurrentTime());

                    // 设置告警数据字段
                    alarmMessageVO.setSensorValue(alarm.getAlarmData());

                    // 根据告警类型填充unitName、thresholding、lineNo字段
                    populateAlarmFields(alarmMessageVO, alarm, tenantId, facilityMap);

                    // 从Redis获取缓存的投递状态，检查是否有变更
                    String deliveryKey = alarm.getDeviceId() + "_" + alarm.getId();
                    String cachedRecord = (String) stringRedisTemplate.opsForHash().get(ALARM_DELIVERY_KEY_PREFIX + tenantId, deliveryKey);
                    if (cachedRecord != null && !hasAlarmChanged(alarm, cachedRecord, status)) {
                        log.info("告警数据无变化，跳过推送：设备ID={}, 告警ID={}", alarm.getDeviceId(), alarm.getId());
                        continue;
                    }

                    // 4.将AlarmMessageVO转换为JSON字符串并发送MQTT消息
                    String jsonMessage = JSON.toJSONString(alarmMessageVO);
                    MqttConnectionTool.MqttGateway gateway = mqttConnectionTool.connectOrRefresh(username, password);
                    gateway.sendToMqtt(topic, jsonMessage);

                    successCount++;

                    // 更新Redis中的投递记录
                    saveDeliveryRecordToRedis(alarm, alarmStatus, status, tenantId);

                    log.info("成功发送告警消息：设备ID={}, 告警类型={}, 消息内容={}",
                            alarm.getDeviceId(), alarm.getAlarmType(), jsonMessage);

                } catch (Exception e) {
                    failureCount++;
                    log.error("发送告警消息失败：设备ID={}, 告警类型={}, 错误信息={}",
                            alarm.getDeviceId(), alarm.getAlarmType(), e.getMessage(), e);
                }
            }

            // 6.打印统计信息
            log.info("租户{}的告警数据推送任务完成", tenantId);
            log.info("告警数据总数：{}, 成功推送：{}, 失败：{}", size, successCount, failureCount);
            endTime = System.currentTimeMillis();

            cdiDeliveryLogService.saveLog(topic, desc, 5, tenantId, engineeringId, now, startTime, endTime, size,
                    successCount, failureCount, size - successCount - failureCount, 1, userName);
        } catch (Exception e) {
            log.error("租户{}的告警数据推送定时任务执行失败：{}", tenantId, e.getMessage(), e);
        } finally {
            endTime = System.currentTimeMillis();
            log.info("结束时间:{}, 耗时:{}ms", getCurrentTime(), endTime - startTime);

            cdiDeliveryLogService.saveLog(topic, desc, 5, tenantId, engineeringId, now, startTime, endTime, size,
                    successCount, failureCount, size - successCount - failureCount, 0, userName);
        }
    }

    /**
     * 将base_alarm的alarm_type转换为AlarmMessageVO的alarmType
     */
    private String convertAlarmType(String alarmType) {
        AlarmType type = AlarmType.fromOriginalCode(alarmType);
        return type != null ? type.getMappedCode() : null;
    }

    /**
     * 根据平时/战时状态转换告警状态
     * 平时：handle_status=0则alarmStatus=1，handle_status=1则alarmStatus=4，alarm_false=1则alarmStatus=2
     * 战时：handle_status=0则alarmStatus=1，handle_status=1则alarmStatus=3，alarm_false=1则alarmStatus=5
     */
    private Integer convertAlarmStatus(Integer handleStatus, Integer alarmFalse, String status) {
        // 如果是误报，优先返回对应的误报状态
        if (alarmFalse != null && alarmFalse == 1) {
            // 平时误报=2，战时误报=5
            return PEACETIME.equals(status) ? 2 : 5;
        }

        // 根据处理状态和平战时状态返回对应状态
        if (handleStatus != null) {
            if (handleStatus == 0) {
                // 未处理/待处置
                return 1;
            } else if (handleStatus == 1) {
                // 平时已办结=4，战时已处置=3
                return PEACETIME.equals(status) ? 4 : 3;
            }
        }

        // 默认返回未处理状态
        return 1;
    }

    /**
     * 批量查询建筑设施数据，构建设备ID到设施的映射
     */
    private Map<String, BaseBuildFacility> buildFacilityMap(List<BaseAlarm> alarmList, Integer tenantId) {
        Map<String, BaseBuildFacility> facilityMap = new HashMap<>();

        // 收集所有设备ID
        List<String> deviceIds = alarmList.stream()
                .map(BaseAlarm::getDeviceId)
                .filter(id -> id != null && !id.trim().isEmpty())
                .distinct()
                .collect(Collectors.toList());

        if (deviceIds.isEmpty()) {
            return facilityMap;
        }

        try {
            LambdaQueryWrapper<BaseBuildFacility> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(BaseBuildFacility::getDeviceId, deviceIds)
                    .eq(BaseBuildFacility::getTenantId, tenantId)
                    .eq(BaseBuildFacility::getDeleteFlag, 0);

            List<BaseBuildFacility> facilities = baseBuildFacilityMapper.selectList(queryWrapper);

            for (BaseBuildFacility facility : facilities) {
                if (facility.getDeviceId() != null) {
                    facilityMap.put(facility.getDeviceId(), facility);
                }
            }

            log.info("批量查询建筑设施数据完成：设备ID数量={}, 匹配到设施数量={}", deviceIds.size(), facilityMap.size());
        } catch (Exception e) {
            log.warn("批量查询BaseBuildFacility失败：{}", e.getMessage());
        }

        return facilityMap;
    }

    /**
     * 将投递记录保存到Redis Hash中
     * 使用Hash结构：key=alarm:delivery:{tenantId}, field=deviceId_alarmId, value=JSON格式的投递状态
     */
    private void saveDeliveryRecordToRedis(BaseAlarm alarm, Integer currentAlarmStatus, String status, Integer tenantId) {
        try {
            String deliveryKey = ALARM_DELIVERY_KEY_PREFIX + tenantId;
            String fieldKey = alarm.getDeviceId() + "_" + alarm.getId();

            Map<String, String> recordMap = new HashMap<>();
            recordMap.put("alarmStatus", String.valueOf(currentAlarmStatus));
            recordMap.put("handleStatus", String.valueOf(alarm.getHandleStatus()));
            recordMap.put("alarmFalse", String.valueOf(alarm.getAlarmFalse()));
            recordMap.put("handleTime", alarm.getHandleTime() != null ? alarm.getHandleTime().toString() : "");
            recordMap.put("handleBy", alarm.getHandleBy() != null ? alarm.getHandleBy() : "");
            recordMap.put("deliveryTime", LocalDateTime.now().toString());

            stringRedisTemplate.opsForHash().put(deliveryKey, fieldKey, JSON.toJSONString(recordMap));

            log.debug("保存投递记录到Redis：key={}, field={}", deliveryKey, fieldKey);
        } catch (Exception e) {
            log.warn("保存投递记录到Redis失败：告警ID={}, 错误={}", alarm.getId(), e.getMessage());
        }
    }

    /**
     * 检查告警数据是否有变更
     * 比较handleStatus、alarmFalse、handleTime、handleBy是否发生变化
     */
    private boolean hasAlarmChanged(BaseAlarm alarm, String cachedRecord, String status) {
        try {
            Map<String, String> cachedMap = JSON.parseObject(cachedRecord, Map.class);

            Integer currentAlarmStatus = convertAlarmStatus(alarm.getHandleStatus(), alarm.getAlarmFalse(), status);
            Integer cachedAlarmStatus = parseInteger(cachedMap.get("alarmStatus"));
            Integer cachedHandleStatus = parseInteger(cachedMap.get("handleStatus"));
            Integer cachedAlarmFalse = parseInteger(cachedMap.get("alarmFalse"));

            if (!Objects.equals(currentAlarmStatus, cachedAlarmStatus)) {
                return true;
            }
            if (!Objects.equals(alarm.getHandleStatus(), cachedHandleStatus)) {
                return true;
            }
            if (!Objects.equals(alarm.getAlarmFalse(), cachedAlarmFalse)) {
                return true;
            }

            String cachedHandleTime = cachedMap.get("handleTime");
            String currentHandleTime = alarm.getHandleTime() != null ? alarm.getHandleTime().toString() : "";
            if (!Objects.equals(currentHandleTime, cachedHandleTime)) {
                return true;
            }

            String cachedHandleBy = cachedMap.get("handleBy");
            String currentHandleBy = alarm.getHandleBy() != null ? alarm.getHandleBy() : "";
            if (!Objects.equals(currentHandleBy, cachedHandleBy)) {
                return true;
            }

            return false;
        } catch (Exception e) {
            log.warn("解析缓存的投递记录失败：告警ID={}, 错误={}", alarm.getId(), e.getMessage());
            return true;
        }
    }

    private Integer parseInteger(String value) {
        if (value == null || value.trim().isEmpty()) {
            return null;
        }
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * 根据告警类型填充unitName、thresholding、lineNo字段
     */
    private void populateAlarmFields(AlarmMessageVO<Integer> alarmMessageVO, BaseAlarm alarm, Integer tenantId,
                                     Map<String, BaseBuildFacility> facilityMap) {
        String originalAlarmType = alarm.getAlarmType();
        AlarmType alarmTypeEnum = AlarmType.fromOriginalCode(originalAlarmType);

        try {
            // 根据AlarmMessageVO字段注释中的规则填充字段
            if (alarmTypeEnum != null && alarmTypeEnum.isStructureMonitoringType()) {
                // 倾斜/位移/裂缝告警类型 - 设置unitName
                populateUnitName(alarmMessageVO, alarm, tenantId, facilityMap);
            }
            if (alarmTypeEnum != null && alarmTypeEnum.isThresholdAlarmType()) {
                // 温湿度、气体浓度告警类型 - 设置thresholding
                populateThresholding(alarmMessageVO, alarm);
            }
            if (alarmTypeEnum != null && alarmTypeEnum.isLineNoAlarmType()) {
                // 电气相关告警类型 - 设置lineNo
                populateLineNo(alarmMessageVO, alarm);
            }
        } catch (Exception e) {
            log.warn("填充告警字段时发生异常：告警类型={}, 设备ID={}, 错误信息={}",
                    originalAlarmType, alarm.getDeviceId(), e.getMessage());
        }
    }

    /**
     * 填充unitName字段（倾斜才用）
     * 对于711、712、713类型，实现与BaseBuildFacility表中facilityDesc字段的映射
     */
    private void populateUnitName(AlarmMessageVO<Integer> alarmMessageVO, BaseAlarm alarm, Integer tenantId,
                                  Map<String, BaseBuildFacility> facilityMap) {
        String originalAlarmType = alarm.getAlarmType();
        AlarmType alarmTypeEnum = AlarmType.fromOriginalCode(originalAlarmType);

        if (alarmTypeEnum != null && alarmTypeEnum.isStructureMonitoringType()) {
            // 从Map中获取建筑设施信息
            BaseBuildFacility facility = facilityMap.get(alarm.getDeviceId());
            if (facility != null && facility.getFacilityDesc() != null) {
                alarmMessageVO.setUnitName(facility.getFacilityDesc());
                log.debug("成功映射unitName：设备ID={}, facilityDesc={}", alarm.getDeviceId(), facility.getFacilityDesc());
            } else {
                // 如果没有找到对应的facilityDesc，设置默认值
                String defaultUnitName = alarmTypeEnum.getDefaultUnitName();
                alarmMessageVO.setUnitName(defaultUnitName);
                log.debug("未找到facilityDesc，使用默认值：设备ID={}, 默认值={}", alarm.getDeviceId(), defaultUnitName);
            }
        } else {
            // 其他情况，设置默认值
            String defaultUnitName = alarmTypeEnum != null ? alarmTypeEnum.getDefaultUnitName() : "监测单元";
            alarmMessageVO.setUnitName(defaultUnitName);
        }
    }

    /**
     * 填充thresholding字段（氧气、温湿度、一氧化碳、二氧化碳、剩余电流、供电电缆温度、电流偏大专用）
     */
    private void populateThresholding(AlarmMessageVO<Integer> alarmMessageVO, BaseAlarm alarm) {
        // try {
        //     // 从告警数据中解析阈值，如果解析失败则设置默认值
        //     Float threshold = parseThresholdFromAlarmData(alarm.getAlarmData(), alarm.getAlarmType());
        //     alarmMessageVO.setThresholding(threshold);
        // } catch (Exception e) {
        // 解析失败时设置合理的默认值
        Float defaultThreshold = getDefaultThreshold(alarm.getAlarmType());
        alarmMessageVO.setThresholding(defaultThreshold);
        // log.warn("解析阈值失败，使用默认值：设备ID={}, 告警类型={}, 错误={}",
        //         alarm.getDeviceId(), alarm.getAlarmType(), e.getMessage());
        // }
    }

    /**
     * 从告警数据中解析阈值
     */
    private Float parseThresholdFromAlarmData(String alarmData, String alarmType) {
        if (alarmData == null || alarmData.trim().isEmpty()) {
            return getDefaultThreshold(alarmType);
        }

        try {
            // 尝试从告警数据中提取数值
            String cleanedData = alarmData.replaceAll("[^\\d.]", "");
            if (!cleanedData.isEmpty()) {
                float parsedValue = Float.parseFloat(cleanedData);

                // 添加边界检查，防止异常值
                if (isValidThreshold(parsedValue, alarmType)) {
                    return parsedValue;
                } else {
                    log.debug("阈值超出合理范围，使用默认值：alarmData={}, alarmType={}, value={}",
                            alarmData, alarmType, parsedValue);
                }
            }
        } catch (NumberFormatException e) {
            log.debug("无法从告警数据中解析数值：alarmData={}", alarmData);
        }

        return getDefaultThreshold(alarmType);
    }

    /**
     * 验证阈值是否在合理范围内
     */
    private boolean isValidThreshold(float value, String alarmType) {
        AlarmType type = AlarmType.fromOriginalCode(alarmType);
        return type == null || type.isValidThreshold(value);
    }

    /**
     * 获取thresholding的默认值
     */
    private Float getDefaultThreshold(String alarmType) {
        AlarmType type = AlarmType.fromOriginalCode(alarmType);
        return type != null ? type.getDefaultThreshold() : 0.0f;
    }

    /**
     * 填充lineNo字段（剩余电流、供电电缆温度、电流偏大专用）
     */
    private void populateLineNo(AlarmMessageVO<Integer> alarmMessageVO, BaseAlarm alarm) {
        // try {
        //     // 从设备ID或告警数据中解析线路编号
        //     Integer lineNo = parseLineNoFromDeviceOrData(alarm.getDeviceId(), alarm.getAlarmData());
        //     alarmMessageVO.setLineNo(lineNo);
        // } catch (Exception e) {
        // 解析失败时设置默认值
        alarmMessageVO.setLineNo(1); // 默认线路1
        // log.warn("解析线路编号失败，使用默认值：设备ID={}, 错误={}", alarm.getDeviceId(), e.getMessage());
        // }
    }

    /**
     * 从设备ID或告警数据中解析线路编号
     */
    // private Integer parseLineNoFromDeviceOrData(String deviceId, String alarmData) {
    //     // 首先尝试从设备ID中解析
    //     if (deviceId != null && !deviceId.trim().isEmpty()) {
    //         // 假设设备ID格式可能包含线路信息，例如：DEV001_L1, DEV002_L2等
    //         if (deviceId.contains("_L")) {
    //             try {
    //                 String linePart = deviceId.substring(deviceId.lastIndexOf("_L") + 2);
    //                 int lineNo = Integer.parseInt(linePart);
    //                 if (isValidLineNo(lineNo)) {
    //                     return lineNo;
    //                 } else {
    //                     log.debug("线路编号超出范围，使用默认值：deviceId={}, lineNo={}", deviceId, lineNo);
    //                 }
    //             } catch (NumberFormatException e) {
    //                 log.debug("无法从设备ID中解析线路编号：deviceId={}", deviceId);
    //             }
    //         }
    //
    //         // 或者尝试从设备ID末尾提取数字作为线路编号
    //         String numbers = deviceId.replaceAll("[^\\d]", "");
    //         if (!numbers.isEmpty()) {
    //             try {
    //                 int lineNo = Integer.parseInt(numbers);
    //                 if (isValidLineNo(lineNo)) {
    //                     return lineNo;
    //                 } else {
    //                     log.debug("从设备ID提取的线路编号超出范围，使用默认值：deviceId={}, lineNo={}", deviceId, lineNo);
    //                 }
    //             } catch (NumberFormatException e) {
    //                 log.debug("无法从设备ID中提取有效线路编号：deviceId={}", deviceId);
    //             }
    //         }
    //     }
    //
    //     // 然后尝试从告警数据中解析
    //     if (alarmData != null && !alarmData.trim().isEmpty()) {
    //         String numbers = alarmData.replaceAll("[^\\d]", "");
    //         if (!numbers.isEmpty()) {
    //             try {
    //                 int lineNo = Integer.parseInt(numbers);
    //                 if (isValidLineNo(lineNo)) {
    //                     return lineNo;
    //                 } else {
    //                     log.debug("从告警数据中解析的线路编号超出范围，使用默认值：alarmData={}, lineNo={}", alarmData, lineNo);
    //                 }
    //             } catch (NumberFormatException e) {
    //                 log.debug("无法从告警数据中解析线路编号：alarmData={}", alarmData);
    //             }
    //         }
    //     }
    //
    //     // 默认返回线路1
    //     return 1;
    // }

    /**
     * 验证线路编号是否在有效范围内
     */
    private boolean isValidLineNo(int lineNo) {
        return lineNo >= 1 && lineNo <= 20; // 限制在1-20范围内，符合实际情况
    }
}