package com.sz.admin.monitor.task;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.sz.admin.monitor.enums.AlarmReportEnums;
import com.sz.admin.monitor.mapper.CameraAlarmMapper;
import com.sz.admin.monitor.mapper.CameraMapper;
import com.sz.admin.monitor.mapper.NvrMapper;
import com.sz.admin.monitor.pojo.dto.edgebox.BoxAlarmReportDto;
import com.sz.admin.monitor.pojo.po.*;
import com.sz.admin.monitor.pojo.vo.camerasnapshot.CameraSnapshotVO;
import com.sz.admin.monitor.sdk.ManageNVR;
import com.sz.admin.monitor.service.*;
import com.sz.admin.monitor.utils.*;
import com.sz.admin.system.pojo.dto.sysmessage.Message;
import com.sz.admin.system.service.SysMessageService;
import com.sz.core.common.entity.SocketMessage;
import com.sz.core.common.entity.TransferMessage;
import com.sz.core.common.enums.MessageTransferScopeEnum;
import com.sz.core.common.enums.SocketChannelEnum;
import com.sz.core.util.JsonUtils;
import com.sz.redis.WebsocketRedisService;
import com.sz.security.core.util.LoginUtils;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

/**
 * ClassName: ScheduleTask
 * Package: com.sz.admin.monitor.task
 * Description:  周期任务类
 */
@Component
@Slf4j
public class ScheduledTask {
    @Resource
    private PresetService presetService;
    @Resource
    private CameraService cameraService;
    @Resource
    private ManageNVR manageNVR;
    @Resource
    private NvrMapper nvrMapper;
    @Resource
    private CameraSnapshotService cameraSnapshotService;
    @Resource
    private CameraAlarmMapper cameraAlarmMapper;
    @Resource
    private WebsocketRedisService websocketRedisService;
    @Resource
    private  SysMessageService sysMessageService;
    @Resource
    private ForwardService forwardService;
    @Resource
    private BlurDetectionService   blurDetectionService;
    @Resource(name = "cameraInspectionExecutor")
    private Executor cameraInspectionExecutor;

   // @Scheduled(cron = "0/5 * * * * ?")
    public void test()
    {
        SocketMessage bean = new SocketMessage();
        Map<String, Object> data = new HashMap<>();
        data.put("title", "移位报警");
        data.put("content", "摄像头[" +33 + "]发生异常，请及时处理！");
        bean.setData(JSON.toJSONString(data));
        bean.setChannel(SocketChannelEnum.MESSAGE);
        bean.setScope(MessageTransferScopeEnum.SOCKET_CLIENT);
        TransferMessage msg = new TransferMessage();
        msg.setMessage(bean);
        msg.setFromUser("system");
        msg.setToPushAll(true);
        websocketRedisService.sendServiceToWs(msg);
    }
    // 定期模糊检测
   // @Scheduled(cron = "0/20 * * * * ?")
    public void executeFuzzyDetection() {
        log.info("执行模糊检测");
        // 遍历模糊检测表
        List<BlurDetection> blurDetectionList = blurDetectionService.list();
        // 过滤掉关闭的状态
        blurDetectionList = blurDetectionList.stream().filter(blurDetection -> blurDetection.getEnable() == 1).toList();
        if (blurDetectionList.isEmpty()) {
            log.info("无启用的模糊检测任务");
            return;
        }
        int count = 0;
        for (BlurDetection blurDetection : blurDetectionList) {
            Long cameraId = blurDetection.getCameraId();
            try {
                Camera camera = cameraService.getById(cameraId);
                // 获取基准图
                String imagePath1 = camera.getHomeImagePath();
                // 检查基准图是否存在
                boolean isExists = true;
                if (imagePath1 == null || imagePath1.trim().isEmpty()) {
                    log.warn("摄像头[{}] 未设置基准图路径", cameraId);
                    isExists = false;
                } else {
                    File file1 = new File(imagePath1);
                    if (!file1.exists() || !file1.isFile()) {
                        log.error("基准图文件物理路径不存在：{}", imagePath1);
                        isExists = false;
                    }
                }
                // 抓取当前图
                String filename = ImageNameUtils.generateFileName("CAPTURE", cameraId);
                String imagePath2 = manageNVR.capturePic(cameraId.intValue(), filename);
                if (imagePath2 == null || imagePath2.isEmpty()) {
                    log.error("抓拍图片失败，摄像头 ID: {}", cameraId);
                    continue;
                }
                // 检查抓拍图是否存在
                File file2 = new File(imagePath2);
                if (!file2.exists() || !file2.isFile()) {
                    log.error("抓拍图不存在：{}", imagePath2);
                    continue;
                }
                count++;
                // 进行单一背景检测
//                BackgroundDetector.DetectResult result = BackgroundDetector.detectBackground(imagePath2);
//                if("1".equals(result.value))
//                {
//                    // 当前摄像头面对的是白墙，直接进行报警
//                    handleAnalysisResult(blurDetection, imagePath2, result);
//                    continue;
//                }
                // 进行模糊图片对比
                BlurDetectorV4.DetectResult detectResult = null;
                if (isExists) {
                    // 基准图存在，使用双图对比功能
                    // 检查是否对着墙壁或者特征不明显的地方
                    BlurrinessComparator.DetectResult result = BlurrinessComparator.compareBlurriness(imagePath1, imagePath2);
                    if (result.value.equals("1")) {
                        // 当前摄像头面对的是白墙，直接进行报警
                        handleAnalysisResult(blurDetection, imagePath2, result);
                        continue;
                    }
                    detectResult = BlurDetectorV4.doubleDetectBlur(imagePath1, imagePath2);
                } else {
                    // 基准图不存在，使用单图对比功能
                    detectResult = BlurDetectorV4.detectBlur(imagePath2);
                }
                log.info("模糊检测结果：{}", detectResult);
                handleAnalysisResult(blurDetection, imagePath1, imagePath2, detectResult, isExists);
                log.info("模糊检测完成，共检测{}个摄像头", count);
            } catch (Exception e) {
                log.error("摄像头 ID [{}] 模糊检测过程中发生未知异常", cameraId, e);
            }

        }
    }
    //Result{type='bpmh', value='1', code='2000', desc='模糊 score=39.82', conf=1.0, score=39.82, ratio=0.00}
    public record DetectResult(String desc,double score) {
    }
    // 分析背景检测结果并且报警
    private void handleAnalysisResult(BlurDetection blurDetection, String imagePath, BlurrinessComparator.DetectResult result) throws IOException {
        Camera camera = cameraService.getById(blurDetection.getCameraId());
        CameraAlarm cameraAlarm = new CameraAlarm();
        cameraAlarm.setCameraId(camera.getId());
        Nvr nvr = nvrMapper.selectOneById(camera.getNvrId());
        cameraAlarm.setAlarmType(6);
        cameraAlarm.setAlarmAreaId(nvr.getStationId());
        cameraAlarm.setChannelId(camera.getChannelId());
        cameraAlarm.setCameraNo(camera.getCameraNo());
        cameraAlarm.setCameraName(camera.getName());
        cameraAlarm.setCaptureImage(imagePath);
        DetectResult backgroundResult = new DetectResult(result.desc, result.score);
        cameraAlarm.setAlgoResult(JsonUtils.toJsonString(backgroundResult));
        cameraAlarm.setStatus(0);
        cameraAlarmMapper.insert(cameraAlarm);
        SocketMessage bean = new SocketMessage();
        Map<String, Object> data = new HashMap<>();
        data.put("title", "摄像头报警");
        data.put("content", "摄像头[" + camera.getId() + "]发生模糊，请及时处理！");
        bean.setData(JSON.toJSONString(data));
        bean.setChannel(SocketChannelEnum.MESSAGE);
        bean.setScope(MessageTransferScopeEnum.SOCKET_CLIENT);
        TransferMessage msg = new TransferMessage();
        msg.setMessage(bean);
        msg.setFromUser("system");
        msg.setToPushAll(true);
        websocketRedisService.sendServiceToWs(msg);
        BoxAlarmReportDto reportDto = new BoxAlarmReportDto();
        // 获取时间字符串
        String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.parse(timeStr, timeFormatter);
        // 转成时间戳（毫秒）
        long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
        reportDto.setAlarmTime(timestamp);
        reportDto.setAlarmTypeDesc(AlarmReportEnums.BLUR_ALARM.getAlarmDescription());
        byte[] CaptureFileContent = Files.readAllBytes(Paths.get(imagePath));
        reportDto.setAlarmImage(Base64.getEncoder().encodeToString(CaptureFileContent));
        reportDto.setCameraName(camera.getName());
        reportDto.setAlarmType(AlarmReportEnums.BLUR_ALARM.getAlarmCode());
        reportDto.setCameraId(camera.getId());
        reportDto.setCameraNo(camera.getCameraNo());
        try {
            forwardService.enrichAndForward(blurDetection.getUrl(),reportDto);
        }catch (Exception e)
        {
            log.error("模糊报警上报失败，摄像头 ID: {}, 相机编号：{}",
                camera.getId(), camera.getCameraNo(), e);
        }

    }
    // 分析模糊算法并且报警
    private void handleAnalysisResult(BlurDetection blurDetection, String imagePath1, String imagePath2, BlurDetectorV4.DetectResult detectResult, boolean isExists) throws IOException {
        if ("1".equals(detectResult.value)) {
            // 发生了模糊，需要保存报警信息等人工处理
            Camera camera = cameraService.getById(blurDetection.getCameraId());
            CameraAlarm cameraAlarm = new CameraAlarm();
            cameraAlarm.setCameraId(camera.getId());
            // 设置告警区域
            // 根据摄像头查询它对应的nvr
            Nvr nvr = nvrMapper.selectOneById(camera.getNvrId());
            // 设置告警区域
            cameraAlarm.setAlarmType(6);
            cameraAlarm.setAlarmAreaId(nvr.getStationId());
            cameraAlarm.setChannelId(camera.getChannelId());
            cameraAlarm.setCameraNo(camera.getCameraNo());
            cameraAlarm.setCameraName(camera.getName());
            cameraAlarm.setBaseImage(imagePath1);
            cameraAlarm.setCaptureImage(imagePath2);
            // 算法的结果
            DetectResult result = new DetectResult(detectResult.desc, detectResult.score);
            cameraAlarm.setAlgoResult(JsonUtils.toJsonString(result));
            // 状态为未处理
            cameraAlarm.setStatus(0);
            cameraAlarmMapper.insert(cameraAlarm);
           // log.info("生成模糊报警: 通道{}, 模糊信息: {}", camera.getChannelId(), detectResult.getDescription());
            // 向前端主动推送消息
            SocketMessage bean = new SocketMessage();
            Map<String, Object> data = new HashMap<>();
            data.put("title", "摄像头报警");
            data.put("content", "摄像头[" + camera.getId() + "]发生模糊，请及时处理！");
            bean.setData(JSON.toJSONString(data));
            bean.setChannel(SocketChannelEnum.MESSAGE);
            bean.setScope(MessageTransferScopeEnum.SOCKET_CLIENT);
            TransferMessage msg = new TransferMessage();
            msg.setMessage(bean);
            msg.setFromUser("system");
            msg.setToPushAll(true);
            websocketRedisService.sendServiceToWs(msg);
            // 统一处理报警信息后，将摄像头信息+报警信息上报给别人服务器
            BoxAlarmReportDto reportDto = new BoxAlarmReportDto();
            // 获取时间字符串
            String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime localDateTime = LocalDateTime.parse(timeStr, timeFormatter);
            // 转成时间戳（毫秒）
            long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
            reportDto.setAlarmTime(timestamp);
            reportDto.setAlarmTypeDesc(AlarmReportEnums.BLUR_ALARM.getAlarmDescription());
            if(isExists && imagePath1 !=null) {
                byte[] BaseFileContent = Files.readAllBytes(Paths.get(imagePath1));
                reportDto.setBaseImage(Base64.getEncoder().encodeToString(BaseFileContent));
            }
            byte[] CaptureFileContent = Files.readAllBytes(Paths.get(imagePath2));
            reportDto.setAlarmImage(Base64.getEncoder().encodeToString(CaptureFileContent));
            reportDto.setCameraName(camera.getName());
            reportDto.setAlarmType(AlarmReportEnums.BLUR_ALARM.getAlarmCode());
            reportDto.setCameraId(camera.getId());
            reportDto.setCameraNo(camera.getCameraNo());
            try {
                forwardService.enrichAndForward(blurDetection.getUrl(),reportDto);
            }catch (Exception e)
            {
                log.error("模糊报警上报失败，摄像头 ID: {}, 相机编号：{}",
                    camera.getId(), camera.getCameraNo(), e);
            }
        }
    }
    // 定期检查
    @Scheduled(cron = "0/20 * * * * ?")
    public void executeInspection() throws InterruptedException {
        // 从摄像头表中获取设置了抓图对比和在线的摄像头
        List<Camera> cameraList = cameraService.selectListByInspection();
        // 然后根据摄像头id查询设置为标准位的预置位，
        List<Long> cameraIds = cameraList.stream().map(Camera::getId).toList();
        if(CollectionUtil.isEmpty(cameraIds))
        {
            // 如果为空，则表示没有摄像头开启巡检功能，直接返回
            return;
        }
        List<Preset> presetList = presetService.selectByCameraIds(cameraIds);
        for (Preset preset : presetList) {
            CompletableFuture.runAsync(() -> {
                // 调用具体的单次检测逻辑
                processSinglePreset(preset);
            }, cameraInspectionExecutor).exceptionally(e -> {
                // 捕获线程池级别或执行过程中的严重异常
                log.error("预置位[{}]异步巡检任务执行失败", preset.getPresetId(), e);
                return null;
            });
        }

    }
    private void processSinglePreset(Preset preset) {
        Long cameraId = preset.getCameraId();
        try {
            boolean ok = manageNVR.ptzPresets(cameraId.intValue(), 39, String.valueOf(preset.getPresetId()), preset.getPresetName());
            // 这里的 sleep 现在是在独立的异步线程中执行的，不会阻塞主线程！
            Thread.sleep(5000);
            if (ok) {
                String filename = ImageNameUtils.generateFileName("CAPTURE", cameraId);
                String image2 = manageNVR.capturePic(cameraId.intValue(), filename);
                //String image2="D:\\work\\images\\CAPTURE_258_20260331104152476_de86220b.jpg";
                // 抓图合法性校验
                if (image2 == null || image2.isBlank()) {
                    log.error("摄像机[{}] 抓拍失败，无法获取当前图片", cameraId);
                    return; // 结束当前预置位的处理
                }
                CameraSnapshotVO cameraSnapshotVO = cameraSnapshotService.selectByCameraIdAndPresetIndex(cameraId, preset.getPresetId());
                if (cameraSnapshotVO == null || cameraSnapshotVO.getImagePath() == null || cameraSnapshotVO.getImagePath().isBlank()) {
                    log.warn("摄像机[{}] 未设置预置位基准图，无法对比", cameraId);
                    return;
                }
                String image1 = cameraSnapshotVO.getImagePath();
                Map<String, Object> map = RobustImageMatcherUtil.calculateOffset(image1, image2);
                log.info("摄像机[{}] 预置位[{}] 对比结果：{}", cameraId, preset.getPresetId(), map);
                if (map != null && map.containsKey("value")) {
                    handleAnalysisResult(preset, image1, image2, map);
                }
            }
        } catch (InterruptedException ie) {
            log.error("摄像机[{}] 等待云台转动被中断", cameraId);
            Thread.currentThread().interrupt();
        } catch (Exception e) {
            log.error("摄像机[{}] 预置位[{}] 巡检过程中发生业务异常", cameraId, preset.getPresetId(), e);
        }
    }

    //判断移位算法结果并报警
    private void handleAnalysisResult(Preset preset, String imagePath, String image2, Map<String, Object> map) throws IOException {
        int value = (int) map.get("value");
        if (value == 1) {
            // 发生了偏移，需要保存报警信息等人工处理
            Camera camera = cameraService.getById(preset.getCameraId());
            CameraAlarm cameraAlarm = new CameraAlarm();
            cameraAlarm.setCameraId(camera.getId());
            // 设置告警区域
            // 根据摄像头查询它对应的nvr
            Nvr nvr = nvrMapper.selectOneById(camera.getNvrId());
            // 设置告警区域
            cameraAlarm.setAlarmAreaId(nvr.getStationId());
            cameraAlarm.setChannelId(camera.getChannelId());
            cameraAlarm.setCameraNo(camera.getCameraNo());
            cameraAlarm.setCameraName(camera.getName());
            cameraAlarm.setPresetIndex(preset.getPresetId());
            // 标记为移位报警
            cameraAlarm.setAlarmType(1);
            cameraAlarm.setBaseImage(imagePath);
            cameraAlarm.setCaptureImage(image2);
            // 算法的结果
            cameraAlarm.setAlgoResult((String) map.get("description"));
            // 状态为未处理
            cameraAlarm.setStatus(0);
            cameraAlarmMapper.insert(cameraAlarm);
            log.info("生成移位报警: 通道{}, 偏移信息: {}", preset.getChannelId(), map.get("description"));
            // 向前端主动推送消息
            SocketMessage bean = new SocketMessage();
            Map<String, Object> data = new HashMap<>();
            data.put("title", "摄像头报警");
            data.put("content", "摄像头[" + preset.getCameraId() + "]发生移位，请及时处理！");
            bean.setData(JSON.toJSONString(data));
            bean.setChannel(SocketChannelEnum.MESSAGE);
            bean.setScope(MessageTransferScopeEnum.SOCKET_CLIENT);
            TransferMessage msg = new TransferMessage();
            msg.setMessage(bean);
            msg.setFromUser("system");
            msg.setToPushAll(true);
            websocketRedisService.sendServiceToWs(msg);
            // 统一处理报警信息后，将摄像头信息+报警信息上报给别人服务器
            BoxAlarmReportDto reportDto = new BoxAlarmReportDto();
            // 获取时间字符串
            String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime localDateTime = LocalDateTime.parse(timeStr, timeFormatter);
            // 转成时间戳（毫秒）
            long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
            reportDto.setAlarmTime(timestamp);
            reportDto.setAlarmTypeDesc(AlarmReportEnums.DISPLACEMENT_ALARM.getAlarmDescription());
            byte[] BaseFileContent = Files.readAllBytes(Paths.get(imagePath));
            reportDto.setBaseImage(Base64.getEncoder().encodeToString(BaseFileContent));
            byte[] CaptureFileContent = Files.readAllBytes(Paths.get(image2));
            reportDto.setAlarmImage(Base64.getEncoder().encodeToString(CaptureFileContent));
            reportDto.setCameraName(camera.getName());
            reportDto.setCameraId(camera.getId());
            reportDto.setAlarmType(AlarmReportEnums.DISPLACEMENT_ALARM.getAlarmCode());
            reportDto.setCameraNo(camera.getCameraNo());
            try {
                forwardService.enrichAndForward(preset.getPresetUrl(),reportDto);
            }catch (Exception e)
            {
                log.error("移位报警上报失败，摄像头 ID: {}, 相机编号：{}",
                    camera.getId(), camera.getCameraNo(), e);
            }
        }
    }

}
