package com.cico.nest.lcdp.boot.core.qxsd.application.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSONArray;
import com.cico.nest.lcdp.boot.core.qxsd.adapter.web.request.LocusDataRequest;
import com.cico.nest.lcdp.boot.core.qxsd.application.service.HistoryService;
import com.cico.nest.lcdp.boot.core.qxsd.domain.event.EventType;
import com.cico.nest.lcdp.boot.core.qxsd.domain.gateway.HistoryGateway;
import com.cico.nest.lcdp.boot.core.qxsd.domain.vehicle.HistoryTunnelVehicle;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.common.result.XlwResultData;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.common.utils.EventUtil;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.database.EventHisMapper;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.database.TunnelDetailMapper;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.database.dataobject.EventDetailDO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.database.dataobject.EventHisDO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.database.dataobject.TunnelDetailDO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.database.dataobject.TunnelDeviceDO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.http.hk.HkVideoMapper;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.http.xlw.dto.XlwHolographicTimeHistoryDTO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.http.xlw.dto.XlwPageDTO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.http.xlw.dto.XlwPassingProfileDTO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.http.xlw.dto.XlwPassingProfileSearchDTO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.http.xlw.dto.XlwRemainDetailDTO;
import com.cico.nest.lcdp.boot.core.qxsd.infrastructure.gatewayimpl.http.xlw.feign.XlwClientFeign;
import com.cico.nest.lcdp.boot.core.system.dal.dataobject.dict.DictDataDO;
import com.cico.nest.lcdp.boot.core.system.dal.mysql.dict.DictDataMapper;
import com.cico.nest.lcdp.framework.common.util.date.DateUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
import org.apache.commons.lang3.StringUtils;
import org.simpleframework.xml.Path;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zws
 * @date 2024/8/20
 */

@Service
@Slf4j
public class HistoryServiceImpl implements HistoryService {

    @Resource
    private HistoryGateway historyGateway;
    @Resource
    private EventHisMapper eventHisMapper;
    @Resource
    private DictDataMapper dictDataMapper;
    @Resource
    private XlwClientFeign xlwClientFeign;
    @Value("${spring.profiles.active}")
    private String activeProfile;
    @Value("${qxsd.video.eventHisSavePath}")
    private String eventHisSaveVideoPath;
    /**
     * 事件完成时保存事件历史数据
     * v1 事件完成后被调用，his表做新增动作
     * v2 事件未完成或者完成都会被调用，未完成时保存卡口，及事件图片，完成时调用如果保存卡口 或者 事件图片 为空，再保存一次
     *    有可能第一次的执行还没结束，第二次又进来了，
     * @param eventDetailDO 事件信息
     */
    @Override
    public void saveEventHistoryData(EventDetailDO eventDetailDO) {
        // 为0需要更新其他信息
        boolean isEventEnd = Objects.equals(eventDetailDO.getThirdEventStatus(), 0);
        log.info("保存事件历史表,eventId:{},开始,三方事件状态是否结束={},",eventDetailDO.getId(),isEventEnd?"结束":"未结束");

        EventHisDO eventHisDO = new EventHisDO();
        eventHisDO.setEventId(eventDetailDO.getId());
        EventHisDO mysqlEventHisDO = eventHisMapper.selectByEventId(eventDetailDO.getId());
        if(Objects.nonNull(mysqlEventHisDO)){
            eventHisDO = mysqlEventHisDO;
        }else{
            eventHisMapper.insertOrUpdate(eventHisDO);
        }

        // 获取事件车辆进出时间
        this.setProfileSearch(eventDetailDO, eventHisDO);

        // 事件图片 不存在 更新 事件图片 字段
        if( CollectionUtils.isEmpty(eventHisDO.getEventImagesPath()) ){
            this.getEventImage( eventDetailDO, eventHisDO);
            log.info("保存事件历史表,eventId:{},【事件图片】完成",eventDetailDO.getId());
        }

        // 卡口图片 不存在 更新 卡口图片 字段 因为为空需去里面判断
        this.getBayonetImage( eventDetailDO, eventHisDO);
        log.info("保存事件历史表,eventId:{},【卡口图片】完成",eventDetailDO.getId());
        this.saveEventHis(eventHisDO);

        // 事件完成 && 字段为空 更新 轨迹数据 字段
        if( isEventEnd && CollectionUtils.isEmpty(eventHisDO.getHisVehicleInfo()) ){
            //轨迹数据
            this.getHisVehicleInfo(eventDetailDO, eventHisDO);
            log.info("保存事件历史表,eventId:{},【轨迹数据】完成",eventDetailDO.getId());
        }

        // 事件完成 && 字段为空  || 存储是 包含"error" 更新 历史视频数据 字段
        if( StringUtils.isEmpty(eventHisDO.getVideoPath()) || eventHisDO.getVideoPath().indexOf("error")>-1 ){
            //历史视频数据 非 localhost 环境会下载，本地就不测试了 todo
            if(!"localhost".equals(activeProfile)){
                this.getVideoPathV2( eventDetailDO, eventHisDO);
                log.info("保存事件历史表,eventId:{},【历史视频数据】完成",eventDetailDO.getId());
            }else{
                log.info("保存事件历史表,eventId:{},【历史视频数据】localhost环境不进行下载",eventDetailDO.getId());
            }
        }

        this.saveEventHis(eventHisDO);
    }
    private void saveEventHis(EventHisDO eventHisDO){
        try {
            eventHisMapper.insertOrUpdate(eventHisDO);
            log.info("保存事件历史表,eventId:{},结束",eventHisDO.getEventId());
        } catch (Exception e) {
            log.error("保存事件历史表,eventId:{},保存事件历史数据异常",eventHisDO.getEventId(),e);
        }
    }

    private void getEventImage(EventDetailDO eventDetailDO,EventHisDO eventHisDO) {
        //保存事件图片
        try {
            List<String> eventImagesPathList = eventDetailDO.getEventImagesPath();
            List<String> eventImagesSavePath = new ArrayList<>();
            for(String urlPath : eventImagesPathList){
                if(urlPath.startsWith("/")){
                    String saveUrlPath = "";
                    try{
                        saveUrlPath = historyGateway.saveEventImage(urlPath);
                    }catch (Throwable e){
                        saveUrlPath = "error获取事件图片失败";
                        log.error("获取事件图片失败",e);
                    }
                    eventImagesSavePath.add(saveUrlPath);
                }
            }
            eventHisDO.setEventImagesPath(eventImagesSavePath);
        } catch (Exception e) {

        }
    }

    private void setProfileSearch(EventDetailDO eventDetailDO,EventHisDO eventHisDO) {
        if(StringUtils.isEmpty(eventDetailDO.getTrackId())){
            log.error("获取事件入口时间,eventId:{},缺少trackId",eventDetailDO.getId());
            return;
        }
        EventHisDO.EventHisExpandInfo x = EventHisDO.getEventHisExpandInfo(eventHisDO.getExpandInfo(),eventDetailDO.getEventCarPlate().get(0));
        x.setEventId(eventDetailDO.getId());
        x.setEventStartTime(DateUtil.format(eventDetailDO.getEventStartTime(),"yyyy-MM-dd HH:mm:ss"));
        if(StringUtils.isNotEmpty(x.getVehicleInTunnelTime()) && StringUtils.isNotEmpty(x.getVehicleOutTunnelTime())){
            log.info("获取事件入口时间,eventId:{},数据已经完整，不需要重新获取",eventDetailDO.getId());
            return;
        }else if(StringUtils.isNotEmpty(x.getVehicleInTunnelTime()) && StringUtils.isEmpty(x.getVehicleOutTunnelTime())){
            // 入口不为空，出口为空，说明临时轨迹详情查询以前获取过
            this.getPassingProfile(eventDetailDO,x);
        }
        if(StringUtils.isEmpty(x.getVehicleInTunnelTime()) || StringUtils.isEmpty(x.getVehicleOutTunnelTime()) ) {
            //临时轨迹详情查询 先获取入口时间，如果不存在再获取5.1
            this.getRemainDetail(eventDetailDO,x);
        }
        eventHisDO.setExpandInfo(EventHisDO.setEventHisExpandInfo(eventHisDO.getExpandInfo(),x));
    }

    private void getRemainDetail(EventDetailDO eventDetailDO,EventHisDO.EventHisExpandInfo x){
        XlwResultData<XlwPassingProfileDTO> resultData = xlwClientFeign.getRemainDetail(new XlwRemainDetailDTO().setSecondTrackTempId(eventDetailDO.getTrackId()));
        if(Objects.nonNull(resultData) && Objects.nonNull(resultData.getData())){
            XlwPassingProfileDTO passingProfile = resultData.getData();
            log.info("调用临时轨迹详情查询接口,eventId:{},trackId:{},入口开始时间:{}",eventDetailDO.getId(),eventDetailDO.getTrackId(),passingProfile.getFirstTime());
            // 保存拓展对象
            x.setVehicleInTunnelTime(passingProfile.getFirstTime());
            //x.setVehicleOutTunnelTime(passingProfile.getLastTime());这个不准
        }else{
            log.info("调用临时轨迹详情查询接口,eventId:{},trackId:{},返回对象为空",eventDetailDO.getId(),eventDetailDO.getTrackId());
            this.getPassingProfile(eventDetailDO, x);
        }
    }
    private void getPassingProfile(EventDetailDO eventDetailDO,EventHisDO.EventHisExpandInfo x){
        //通过 5.1 获取通行记录列表数据 trackId 查询车辆的入口开始时间
        XlwPassingProfileSearchDTO searchDTO = new XlwPassingProfileSearchDTO();
        searchDTO.setPage(1);
        searchDTO.setPageSize(1);
        searchDTO.setTunnelId(eventDetailDO.getXlwTunnelId());
        searchDTO.setPassProfileId(eventDetailDO.getTrackId());
        XlwResultData<XlwPageDTO<XlwPassingProfileDTO>> resultData = xlwClientFeign.getPassingProfile(searchDTO);
        if(Objects.nonNull(resultData) && resultData.getData().getRows().size()>0){
            XlwPassingProfileDTO passingProfile = resultData.getData().getRows().get(0);
            log.info("调用5.1接口,eventId:{},trackId:{},入口开始时间:{},结束时间:{},",eventDetailDO.getId(),eventDetailDO.getTrackId(),passingProfile.getFirstTime(),passingProfile.getLastTime());
            // 保存拓展对象
            x.setVehicleInTunnelTime(passingProfile.getFirstTime());
            x.setVehicleOutTunnelTime(passingProfile.getLastTime());
        }else{
            log.info("调用5.1接口,eventId:{},trackId:{},返回对象为空",eventDetailDO.getId(),eventDetailDO.getTrackId());
        }
    }



    /**
     * 获取历史车辆轨迹
     * @param eventDetailDO
     * @param eventHisDO
     */
    private void getHisVehicleInfo(EventDetailDO eventDetailDO,EventHisDO eventHisDO) {
        //获取轨迹数据
        try{
            EventHisDO.EventHisExpandInfo x = EventHisDO.getEventHisExpandInfo(eventHisDO.getExpandInfo(),eventDetailDO.getEventCarPlate().get(0));
            if(StringUtils.isEmpty(x.getVehicleInTunnelTime()) || StringUtils.isEmpty(x.getVehicleOutTunnelTime()) ) {
                log.error("获取历史车辆轨迹,eventId:{},trackId:{},入口开始时间或结束时间为空",eventDetailDO.getId(),eventDetailDO.getTrackId());
                return;
            }
            // 交通事故类才获取历史车辆轨迹
            List<Integer> eventTypeList = EventType.getTypeStatistics().get("交通事故").getTypes();
            if(!eventTypeList.contains(eventDetailDO.getEventType())){
                log.info("获取历史车辆轨迹,eventId:{},eventType:{},不在获取范围内",eventDetailDO.getId(),eventDetailDO.getEventType());
                return;
            }
            LocusDataRequest locusDataRequest = new LocusDataRequest().setTunnelId(eventDetailDO.getXlwTunnelId())
                    .setStartTime(x .getVehicleInTunnelTime()).setEndTime(x.getVehicleOutTunnelTime());
            List<HistoryTunnelVehicle> historyTunnelVehicleList = historyGateway.getVehicleInfo( locusDataRequest );
            eventHisDO.setHisVehicleInfo(JSONArray.from(historyTunnelVehicleList));
        }catch (Throwable e){
            eventHisDO.setHisVehicleInfo(JSONArray.from(new ArrayList<>()));
            log.error("获取轨迹数据失败",e);
        }
    }

//    private void getVideoPath(EventDetailDO eventDetailDO,EventHisDO eventHisDO) {
//        //获取历史视频数据
//        try{
//            if(Objects.isNull(eventDetailDO.getEventStartTime()) || Objects.isNull(eventDetailDO.getEventEndTime())){
//                log.info("获取历史视频,eventId:{},时间不完整,不进行下载视频数据操作",eventDetailDO.getId());
//                return;
//            }
//            Date eventStartTime = eventDetailDO.getEventStartTime();
//            Date eventEndTime = eventDetailDO.getEventEndTime();
//            long diffSecond = DateUtil.between(eventStartTime,eventEndTime, DateUnit.SECOND);
//            if(Objects.nonNull(eventDetailDO.getDeviceMilestone())){
//                String beginTime = DateUtil.format(DateUtil.offsetSecond(eventStartTime,-6),"yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
//                String endTime = DateUtil.format(eventEndTime,"yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
//                if(diffSecond > 24){
//                    endTime = DateUtil.format(DateUtil.offsetSecond(eventStartTime,24),"yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
//                    log.info("getVideoPath,时长:{}>24s,重新设置结束时间:{}",diffSecond,endTime);
//                }
//                log.info("获取历史视频,eventId:{},开始时间：{}，结束时间：{},时长:{}",eventDetailDO.getId(),beginTime,endTime,diffSecond);
//                int direction = 100700;
//                if("下行".equals(eventDetailDO.getDirection())){
//                    direction = 100701;
//                }
//                TunnelDeviceDO tunnelDeviceDO = historyGateway.getDevice(eventDetailDO.getBaseTunnelCode()
//                        ,eventDetailDO.getHoleId()
//                        ,Integer.valueOf(eventDetailDO.getDeviceMilestone())
//                        ,direction);
//                if(Objects.isNull(tunnelDeviceDO)){
//                    log.error("未找到对应摄像头，tunnelCode:{},holeId:{},deviceMilestone:{},direction:{}", eventDetailDO.getBaseTunnelCode(), eventDetailDO.getHoleId(), Integer.valueOf(eventDetailDO.getDeviceMilestone()), direction);
//                    eventHisDO.setVideoPath("error未找到对应摄像头,事件桩号:" + eventDetailDO.getDeviceMilestone());
//                    return;
//                }
//                //保存
//                eventHisDO.setCameraIndexCode(tunnelDeviceDO.getCameraIndexCode());
//                String videoUrl = historyGateway.saveVideo(tunnelDeviceDO.getCameraIndexCode()
//                        ,beginTime
//                        ,endTime
//                        ,eventDetailDO.getId() + ".mp4");
//                eventHisDO.setVideoPath("/" + videoUrl);
//                // 保存拓展对象
//                EventHisDO.EventHisExpandInfo x = EventHisDO.getEventHisExpandInfo(eventHisDO.getExpandInfo(),eventDetailDO.getEventCarPlate().get(0));
//                x.setDeviceId(tunnelDeviceDO.getId());
//                x.setDeviceMilestone(tunnelDeviceDO.getMileStart());
//                x.setEventMilestone(eventDetailDO.getDeviceMilestone());
//                eventHisDO.setExpandInfo(EventHisDO.setEventHisExpandInfo(eventHisDO.getExpandInfo(),x));
//            }
//        }catch (Throwable e){
//            eventHisDO.setVideoPath("error获取历史视频失败");
//            log.error("获取历史视频失败",e);
//        }
//    }

    /**
     * 只根据事件开始时间 进行视频切片
     * 视频开始时间=事件开始时间 + 字典配置开始时间的偏移量
     * 视频结束时间=事件开始时间 + 字典配置结束时间的偏移量
     * @param eventDetailDO
     * @param eventHisDO
     */
    private void getVideoPathV2(EventDetailDO eventDetailDO,EventHisDO eventHisDO) {
        long startTimeDetail = System.currentTimeMillis();
        //获取历史视频数据
        try{
            if(Objects.isNull(eventDetailDO.getEventStartTime())){
                log.info("获取历史视频,eventId:{},时间不完整,不进行下载视频数据操作",eventDetailDO.getId());
                return;
            }
            // 保存拓展对象
            EventHisDO.EventHisExpandInfo x = EventHisDO.getEventHisExpandInfo(eventHisDO.getExpandInfo(),eventDetailDO.getEventCarPlate().get(0));
            // todo 接入字典项配置

            int videoOffsetStart = this.getVideoOffset("hk_config", "videoOffsetStart",x.getVideoOffsetStart());
            int videoOffsetEnd = this.getVideoOffset("hk_config", "videoOffsetEnd",x.getVideoOffsetEnd());
            Date eventStartTime = eventDetailDO.getEventStartTime();
            if(Objects.nonNull(eventDetailDO.getDeviceMilestone())){
                String beginTime = DateUtil.format(DateUtil.offsetSecond(eventStartTime,videoOffsetStart),"yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
                String endTime = DateUtil.format(DateUtil.offsetSecond(eventStartTime,videoOffsetEnd),"yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
                log.info("获取历史视频,eventId:{},开始时间：{}，结束时间：{},时长:{}",eventDetailDO.getId(),beginTime,endTime,videoOffsetEnd- videoOffsetStart);
                int direction = 100700;
                if("下行".equals(eventDetailDO.getDirection())){
                    direction = 100701;
                }
                TunnelDeviceDO tunnelDeviceDO = historyGateway.getDevice(eventDetailDO.getBaseTunnelCode()
                        ,eventDetailDO.getHoleId()
                        ,Integer.valueOf(eventDetailDO.getDeviceMilestone())
                        ,direction);
                if(Objects.isNull(tunnelDeviceDO)){
                    log.error("未找到对应摄像头，tunnelCode:{},holeId:{},deviceMilestone:{},direction:{}", eventDetailDO.getBaseTunnelCode(), eventDetailDO.getHoleId(), Integer.valueOf(eventDetailDO.getDeviceMilestone()), direction);
                    eventHisDO.setVideoPath("error未找到对应摄像头,事件桩号:" + eventDetailDO.getDeviceMilestone());
                    return;
                }
                //保存
                eventHisDO.setCameraIndexCode(tunnelDeviceDO.getCameraIndexCode());
                String rtspUrl = historyGateway.getVideoRtspUrl(tunnelDeviceDO.getCameraIndexCode()
                        ,beginTime
                        ,endTime);
                String outputFile = eventDetailDO.getId() + ".mp4";
                try {
                    historyGateway.videoPuller(rtspUrl,eventHisSaveVideoPath + "/" + outputFile);
                } catch (Exception e) {
                    outputFile =  "error保存回放视频流失败";
                }
                 eventHisDO.setVideoPath("/" + outputFile);
                long endTimeDetail = System.currentTimeMillis();
                log.info("获取历史视频,eventId:{},保存视频,耗时:{}毫秒",eventDetailDO.getId(),endTimeDetail - startTimeDetail);

                x.setDeviceId(tunnelDeviceDO.getId());
                x.setDeviceMilestone(tunnelDeviceDO.getMileStart());
                x.setEventMilestone(eventDetailDO.getDeviceMilestone());
                x.setVideoTime(videoOffsetEnd- videoOffsetStart);
                x.setVideoOffsetStart(videoOffsetStart);
                x.setVideoOffsetEnd(videoOffsetEnd);
                x.setVideoTimeConsuming((endTimeDetail - startTimeDetail)/1000+"s");
                x.setVideoRtspUrl(rtspUrl);
                x.setVideoBeginTime(beginTime);
                x.setVideoEndTime(endTime);
                eventHisDO.setExpandInfo(EventHisDO.setEventHisExpandInfo(eventHisDO.getExpandInfo(),x));
            }
        }catch (Throwable e){
            eventHisDO.setVideoPath("error获取历史视频失败");
            log.error("获取历史视频失败",e);
        }
    }

    private int getVideoOffset(String dictType, String label,int defaultValue){
        DictDataDO dictDataDO =  dictDataMapper.selectByDictTypeAndLabel(dictType,label);
        if(Objects.nonNull(dictDataDO)){
            return Integer.parseInt(dictDataDO.getValue());
        }
        return defaultValue;
    }

    private void getBayonetImage(EventDetailDO eventDetailDO,EventHisDO eventHisDO) {
        Map<String,List<String>> map = new HashMap<>(8);
        if(CollectionUtils.isNotEmpty(eventHisDO.getBayonetImage())){
            for(Object o : eventHisDO.getBayonetImage()){
                Map<String,List<String>> tempMap = (Map<String,List<String>>)o;
                map.putAll(tempMap);
            }
        }
        //获取卡口图片
        try {
            List<String> carPlateList = eventDetailDO.getEventCarPlate();
            JSONArray jsonArray = new JSONArray();
            for(String carPlate : carPlateList){
                // 卡口图片存在，并且集合>0
                if(map.containsKey(carPlate) && CollectionUtils.isNotEmpty(map.get(carPlate))){
                    jsonArray.add(Map.of(carPlate,map.get(carPlate)));
                }else{
                    try{
                        if(EventUtil.CAR_PLATE_ERROR_LIST.contains(carPlate)){
                            log.info("保存事件历史表,eventId:{},【卡口图片】carPlate={},定义为无卡口信息不进行查询",eventDetailDO.getId(),carPlate);
                            jsonArray.add(Map.of(carPlate,new ArrayList()));
                        }else{
                            EventHisDO.EventHisExpandInfo eventHisExpandInfo = historyGateway.saveBayonetImage(carPlate,eventDetailDO.getEventStartTime());
                            if(Objects.nonNull(eventHisExpandInfo)){
                                EventHisDO.EventHisExpandInfo x = EventHisDO.getEventHisExpandInfo(eventHisDO.getExpandInfo(),carPlate);
                                x.setPlateImage(eventHisExpandInfo.getPlateImage());
                                x.setHeadImage(eventHisExpandInfo.getHeadImage());
                                x.setSmallSideImage(eventHisExpandInfo.getSmallSideImage());
                                eventHisDO.setExpandInfo(EventHisDO.setEventHisExpandInfo(eventHisDO.getExpandInfo(),x));
                                jsonArray.add(Map.of(carPlate,Arrays.asList(eventHisExpandInfo.getPlateImage(),eventHisExpandInfo.getHeadImage())));
                            }
                        }
                    }catch (Throwable e){
                        log.error("获取卡口图片失败",e);
                    }
                }
            }
            eventHisDO.setBayonetImage(jsonArray);
        } catch (Exception e) {

        }

    }



}
