package com.tuoheng.admin.service.third.dsp;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tuoheng.admin.entity.*;
import com.tuoheng.admin.enums.*;
import com.tuoheng.admin.mapper.*;
import com.tuoheng.admin.request.third.DspCallbackRequest;
import com.tuoheng.admin.request.third.QuestionFile;
import com.tuoheng.admin.service.accident.IAccidentService;
import com.tuoheng.admin.utils.GaodeUtil;
import com.tuoheng.common.core.config.common.CommonConfig;
import com.tuoheng.common.core.exception.ServiceException;
import com.tuoheng.common.core.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * DSP回调 服务实现类
 *
 * @author WangHaoran
 * @since 2021-09-01
 */
@Slf4j
@Service
public class DspCallbackServiceImpl implements IDspCallbackService {

    @Autowired
    private BusinessMapper businessMapper;

    @Autowired
    private InspectionMapper inspectionMapper;

    @Autowired
    private InspectionFileMapper inspectionFileMapper;

    @Autowired
    private FlightDataMapper flightDataMapper;

    @Autowired
    private QuestionTypeMapper questionTypeMapper;

    @Autowired
    private RoadInformationMapper roadInformationMapper;

    @Autowired
    private IAccidentService accidentService;

    /**
     * 保存DSP回调数据
     *
     * @param requestId          请求id
     * @param dspCallbackRequest 回调请求体
     * @return
     */
    @Override
    public JsonResult saveCallbackData(String requestId, DspCallbackRequest dspCallbackRequest) {
        log.info("DSP服务回调地址接收成功, requestId：{}, callbackRequest:{}", requestId, JacksonUtil.obj2StringPretty(dspCallbackRequest));
        log.info("以下为打印的回调参数");
        log.info("requestId为：{}", requestId);
        log.info(JSON.toJSONString(dspCallbackRequest));

        // 查询业务表数据
        Business business = businessMapper.selectOne(new LambdaQueryWrapper<Business>()
                .eq(Business::getMsgId, requestId)
                .eq(Business::getType, 1)
                .last("limit 1"));
        if (StringUtils.isNull(business)) {
            log.info("业务关系数据不存在，requestId：{}", requestId);
            throw new ServiceException(0, "业务数据不存在");
        }
        log.info("业务关系数据, business：{}", business);

        // 巡检任务ID
        String inspectionId = business.getTypeId();

        // 查询任务
        Inspection inspection = inspectionMapper.selectOne(new LambdaQueryWrapper<Inspection>()
                .eq(Inspection::getId, inspectionId)
                .eq(Inspection::getTenantId, business.getTenantId()));
        if (ObjectUtil.isNull(inspection)) {
            log.info("任务不存在, inspectionId={}", inspectionId);
            throw new ServiceException(0, "巡检任务不存在");
        }
        log.info("巡检任务查询成功，分析状态为：{}", dspCallbackRequest.getAnalyseStatus());
        // 分析状态处理: 5：waiting(等待)、10：running(分析中)、15：success(分析完成)、20：timeout(成功超时)、25：failed(分析失败)
        if (dspCallbackRequest.getAnalyseStatus().equals(5)) {
            // 等待
            inspection.setAnalyseStatus(2);
        } else if (dspCallbackRequest.getAnalyseStatus().equals(10)) {
            // 分析中
            inspection.setAnalyseStatus(3);
        } else if (dspCallbackRequest.getAnalyseStatus().equals(15)) {
            // 分析完成
            inspection.setAnalyseStatus(4);
        } else if (dspCallbackRequest.getAnalyseStatus().equals(20)) {
            // 成功超时
            inspection.setAnalyseStatus(5);
        } else if (dspCallbackRequest.getAnalyseStatus().equals(25)) {
            // 分析失败
            inspection.setAnalyseStatus(6);
        }
        // 分析进度
        log.info("巡检任务查询成功，分析进度为：{}", dspCallbackRequest.getProgress());
        inspection.setProgressbar(dspCallbackRequest.getProgress());
        // 视频处理
        if (dspCallbackRequest.getType().equals(1)) {
            // 实时直播
            log.info("实时直播视频处理");
            log.info("实时直播视频，原视频地址：{}", dspCallbackRequest.getVideoUrl());
            log.info("实时直播视频，分析后视频地址：{}", dspCallbackRequest.getAiVideoUrl());
            if (StringUtils.isNotBlank(dspCallbackRequest.getVideoUrl())) {
                inspection.setVideoUrl(StringUtils.removeHost(dspCallbackRequest.getVideoUrl(), CommonConfig.videoURL));
            }
            if (StringUtils.isNotBlank(dspCallbackRequest.getAiVideoUrl())) {
                inspection.setAiVideoUrl(StringUtils.removeHost(dspCallbackRequest.getAiVideoUrl(), CommonConfig.videoURL));
            }
        } else if (dspCallbackRequest.getType().equals(2)) {
            // 离线识别
            log.info("离线检测视频处理");
            log.info("离线检测视频，分析后视频地址：{}", dspCallbackRequest.getAiVideoUrl());
            if (StringUtils.isNotBlank(dspCallbackRequest.getAiVideoUrl())) {
                inspection.setAiVideoUrl(StringUtils.removeHost(dspCallbackRequest.getAiVideoUrl(), CommonConfig.videoURL));
            }
        }
        inspection.setUpdateTime(DateUtils.now());
        inspectionMapper.updateByPrimaryKey(inspection);

        //获取当前任务对应的部门id
        String deptId = inspection.getDeptId();

        log.info("巡检任务状态更新成功");

        log.info("巡检任务分析问题图片处理开始，此处图片数量为：{}张", StringUtils.isNull(dspCallbackRequest.getQuestionFiles()) ? 0 : dspCallbackRequest.getQuestionFiles().size());

        // 分析处理图片并入库
        log.info("以下为本地问题图片信息：");
        log.info(JSON.toJSONString(dspCallbackRequest.getQuestionFiles()));
        List<QuestionFile> questionFiles = dspCallbackRequest.getQuestionFiles();
        if (!CollectionUtils.isEmpty(questionFiles)) {
            log.info("问题图片处理开始, 图片数量：{}", questionFiles.size());
            log.info("问题图片处理开始, inspectionId:{}, isLive：{}", inspection.getId(), inspection.getIsLive());
            // 坐标
            boolean online_condition = AiAnalyseTypeEnum.ONLINE.getCode() == inspection.getIsLive();
            boolean offline_condition = AiAnalyseTypeEnum.OFFLINE.getCode() == inspection.getIsLive();
            log.info("在线：{}, 离线：{}", online_condition, offline_condition);
            List<FlightData> flightDataList = flightDataMapper.selectList(new LambdaQueryWrapper<FlightData>()
                    .eq(FlightData::getInspectionId, business.getTypeId())
                    .notLike(online_condition, FlightData::getLng, "E-")
                    .notLike(online_condition, FlightData::getLng, "0.0")
                    .notLike(online_condition, FlightData::getLat, "E-")
                    .notLike(online_condition, FlightData::getLat, "0.0")
                    .orderByDesc(FlightData::getTimestamp));
            if (!CollectionUtils.isEmpty(flightDataList)) {
                log.info("飞行坐标大小：{}", flightDataList.size());
            }
            List<InspectionFile> thirstyQuestionFiles = questionFiles.stream().map(questionFile -> {
                // 遍历创建巡检图片信息
                InspectionFile inspectionFile = new InspectionFile();
                inspectionFile.setId(RandomUtil.randomString(36));
                // 对象属性拷贝
                BeanUtils.copyProperties(questionFile, inspectionFile);
                //问题编号
                inspectionFile.setQuestionCode(questionFile.getQuestionCode());
                //问题id
                //根据questionCode查询问题类型表出对应的id值
                if (StringUtils.isNotEmpty(questionFile.getQuestionCode())) {
                    QuestionType questionType = questionTypeMapper.selectOne(Wrappers.<QuestionType>lambdaQuery()
                            .eq(QuestionType::getCode, questionFile.getQuestionCode())
                            .eq(QuestionType::getMark, MarkEnum.VALID.getCode()));
                    if (ObjectUtil.isNotEmpty(questionType)) {
                        inspectionFile.setQuestionId(questionType.getId());
                    } else {
                        log.info("该问题类型不存在，questionCode={}", questionFile.getQuestionCode());
                    }
                }
                // 问题名称
                inspectionFile.setQuestionName(questionFile.getQuestionName());
                inspectionFile.setTenantId(inspection.getTenantId());
                inspectionFile.setInspectionId(inspectionId);
                // 文件类型
                inspectionFile.setFileType(FileTypeEnum.IMAGE.getCode());
                // 文件编码
                inspectionFile.setFileCode(inspectionFile.getFileCode());
                // 文件名称
                inspectionFile.setFileName(inspectionFile.getFileName());
                //图片大小，单位MB，保留两位小数，目前没有获取图片大小的字段
                inspectionFile.setFileSize(0.00);
                // 原始图片
                String fileOriginalUrl = questionFile.getFileOriginalUrl();
                if (StringUtils.isNotEmpty(fileOriginalUrl) && fileOriginalUrl.contains(CommonConfig.imageURL)) {
                    fileOriginalUrl = fileOriginalUrl.replaceAll(CommonConfig.imageURL, "");
                }
                inspectionFile.setFileOriginal(fileOriginalUrl);
                // 标记图片
                String fileMarkerUrl = questionFile.getFileMarkerUrl();
                if (StringUtils.isNotEmpty(fileMarkerUrl) && fileMarkerUrl.contains(CommonConfig.imageURL)) {
                    fileMarkerUrl = fileMarkerUrl.replaceAll(CommonConfig.imageURL, "");
                }
                inspectionFile.setFileImage(fileMarkerUrl);

                // 问题图片审核状态
                inspectionFile.setStatus(InspectionFileStatusEnum.WAIT_CONFIRMED.getCode());
                // 问题图片来源
                inspectionFile.setSource(SourceEnum.AI.getCode());
                inspectionFile.setCreateUser(business.getCreateUser()); // 设置默认用户
                inspectionFile.setCreateTime(DateUtils.now());

                if (offline_condition) {
                    // 离线分析处理
                    log.info("离线坐标分析处理...");
                    // 离线获取图片坐标信息
                    String frame = StringUtils.subString(questionFile.getFileName(), "_frame-", "_type");
                    String startFrame = frame.substring(0, frame.indexOf("-"));
                    //统一按照25帧计算
                    int second = Integer.parseInt(startFrame) / 25;

                    // 解决离线视频分析的问题都是一个坐标问题
//                    long shootTime = inspection.getExecutionStartTime().getTime() + second;
                    long shootTime = Long.parseLong(flightDataList.get(flightDataList.size() - 1).getTimestamp()) + second * 1000;
                    FlightData flightData_off = getFlightDataByTime(flightDataList, shootTime);
                    if (ObjectUtil.isNotNull(flightData_off)) {
                        inspectionFile.setLongitude(flightData_off.getLng());
                        inspectionFile.setLatitude(flightData_off.getLat());
                    }
                    JSONObject gaodeCoordinateOff = GaodeUtil.getGaodeCoordinate(flightData_off.getLng(), flightData_off.getLat());
                    if (ObjectUtil.isNotNull(gaodeCoordinateOff)) {
                        inspectionFile.setGaodeLongitude(gaodeCoordinateOff.getString("longitude"));
                        inspectionFile.setGaodeLatitude(gaodeCoordinateOff.getString("latitude"));
                    }
                    String gaodeAddressOff = GaodeUtil.getGaodeAddress(gaodeCoordinateOff.getString("longitude"), gaodeCoordinateOff.getString("latitude"));
                    if (ObjectUtil.isNotNull(gaodeAddressOff)) {
                        inspectionFile.setLocation(gaodeAddressOff);
                        inspectionFile.setGaodeAddress(gaodeAddressOff);
                    }
                }
                if (online_condition) {
                    // 实时直播处理
                    log.info("实时直播坐标分析处理...");
//                    long time = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, questionFile.getAnalyseTime()).getTime();
                    FlightData flightData_live = getFlightDataByTime(flightDataList, questionFile.getAnalyseTime().getTime());
                    log.info("实时坐标：{}", JacksonUtil.obj2StringPretty(flightData_live));
                    if (ObjectUtil.isNotNull(flightData_live)) {
                        inspectionFile.setLatitude(flightData_live.getLat());
                        inspectionFile.setLongitude(flightData_live.getLng());
                        inspectionFile.setGaodeLatitude(flightData_live.getLat());
                        inspectionFile.setGaodeLongitude(flightData_live.getLng());
                    }
                    String gaodeAddress_live = GaodeUtil.getGaodeAddress(flightData_live.getLng(), flightData_live.getLat());
                    if (ObjectUtil.isNotNull(gaodeAddress_live)) {
                        inspectionFile.setLocation(gaodeAddress_live);
                        inspectionFile.setGaodeAddress(gaodeAddress_live);
                    }
                }

                Map<String, QuestionType> questionTypeMap = this.getStringQuestionTypeMap();
                QuestionType questionType = questionTypeMap.get(questionFile.getQuestionCode());
                log.info("问题对应的code={}",questionFile.getQuestionCode());
                log.info("问题对应的问题类型questionType={}",questionType);
                if(0 == questionType.getIsShow()){
                    inspectionFile.setMark(MarkEnum.NOTVALID.getCode());
                    log.info("修改问题mark值mark={}",inspectionFile.getMark());
                }else {
                    inspectionFile.setMark(MarkEnum.VALID.getCode());
                }
                return inspectionFile;
            }).collect(Collectors.toList());

            log.info("批量插入问题图片数据");
            CommonUtils.batchOperate((x) -> inspectionFileMapper.addBatch(x), thirstyQuestionFiles, 1000);


            log.info("调用saveAccidentData方法保存应急记录数据...");

            Boolean flag = this.saveAccidentData(thirstyQuestionFiles);

            log.info("预警数据入库结束,{}", flag);

        }
        return JsonResult.success();
    }

    /**
     * 应急记录数据入库
     *
     * @param inspectionFileList
     * @return
     */
    private Boolean saveAccidentData(List<InspectionFile> inspectionFileList) {
        log.info("调用保存应急数据方法,应急数据入库开始....");
        List<Accident> accidentList = new ArrayList<>();
        Map<String, QuestionType> questionTypeMap = getStringQuestionTypeMap();
        for (InspectionFile inspectionFile : inspectionFileList) {
            QuestionType questionType = questionTypeMap.get(inspectionFile.getQuestionCode());
            if (0 == questionType.getAccidentQuestion()) {
                // 非事故问题，不用添加到事故表
                continue;
            }
            Accident accident = new Accident();
            //问题类型为应急类型时添加数据
            accident.setTenantId(inspectionFile.getTenantId());
            accident.setInspectionId(inspectionFile.getInspectionId());
            accident.setInspectionFileId(inspectionFile.getId());
            //应急任务id
            accident.setAccidentInspectionId(inspectionFile.getInspectionId());
            //根据巡检任务查询对应的公路id
            Inspection inspection = inspectionMapper.selectById(inspectionFile.getInspectionId());
            if (ObjectUtils.isNotEmpty(inspection)) {
                accident.setRoadId(inspection.getRoadId());
                accident.setSectionId(inspection.getSectionId());
                accident.setSectionName(inspection.getSectionName());

                //根据巡检任务类型判断当前应急事故类型flag设值
                if (2 == inspection.getType()) {
                    accident.setFlag(FlagEnum.ACCIDENT.getCode());
                } else {
                    // 该巡检任务存在应急任务，将后面发现的预警直接设置为不展示
                    Inspection emergencyInspection = inspectionMapper.selectOne(new LambdaQueryWrapper<Inspection>()
                            .eq(Inspection::getEmergencyDataInspectionId, inspectionFile.getInspectionId())
                            .eq(Inspection::getMark, MarkEnum.VALID.getCode()));
                    if (ObjectUtils.isNotEmpty(emergencyInspection)) {
                        accident.setFlag(FlagEnum.ACCIDENT.getCode());
                    } else {
                        accident.setFlag(FlagEnum.INSPECTION_ACCIDENT.getCode());
                    }
                }
                String deptId = inspection.getDeptId();
                accident.setDeptId(deptId);
            }

            //公路代号
            RoadInformation roadInformation = roadInformationMapper.selectById(inspection.getRoadId());
            if (ObjectUtils.isNotEmpty(roadInformation)) {
                accident.setRoadCode(roadInformation.getCode());
            }
            accident.setQuestionId(inspectionFile.getQuestionId());
            accident.setQuestionCode(inspectionFile.getQuestionCode());
            accident.setQuestionName(inspectionFile.getQuestionName());
            accident.setIsCasualties(IsCasualtiesEnum.IS_NO_CASUALTIES.getCode());
            accident.setIsDrivingSafety(IsDrivingSafetyEnum.IS_NO_DRIVING_SAFETY.getCode());
            accident.setIsFire(IsFireEnum.IS_NO_FIRE.getCode());
            accident.setLongitude(inspectionFile.getLongitude());
            accident.setLatitude(inspectionFile.getLatitude());
            accident.setUavReturn(UavReturnEnum.UAV_RETURN_IS_NOT.getCode());
            accident.setStatus(AccidentStatusEnum.UNTREATED.getCode());
            accident.setCreateUser(inspectionFile.getCreateUser());
            accident.setCreateTime(inspectionFile.getCreateTime());
            accidentList.add(accident);
        }
        return accidentService.saveBatch(accidentList);
    }

    /**
     * 根据问题类型封装成map
     * @return
     */
    private Map<String, QuestionType> getStringQuestionTypeMap() {
        List<QuestionType> questionTypeList = questionTypeMapper.selectList(Wrappers.<QuestionType>lambdaQuery()
                .eq(QuestionType::getMark, MarkEnum.VALID.getCode()));
        Map<String, QuestionType> questionTypeMap = new HashMap<>();
        for (QuestionType type : questionTypeList) {
            questionTypeMap.put(type.getCode(), type);
        }
        return questionTypeMap;
    }

    /**
     * 获取飞行坐标
     *
     * @param flightDataList 飞行坐标集合
     * @param time           当前时间
     * @return 当前飞行对象
     */
    private FlightData getFlightDataByTime(List<FlightData> flightDataList, long time) {
        if (CollectionUtils.isEmpty(flightDataList)) {
            throw new ServiceException(HttpStatus.BAD_REQUEST.value(), "获取无人机飞行坐标失败！");
        }
        List<FlightData> flightData = flightDataList.stream().filter(data -> {
            try {
                return Long.valueOf(data.getTimestamp()) <= time;
            } catch (Exception e) {
                log.error("获取飞行数据异常：", e);
                log.error("批量插入问题图片数据:{}", JacksonUtil.obj2StringPretty(data));
                return false;
            }
        }).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(flightData)) {
            throw new ServiceException(HttpStatus.BAD_REQUEST.value(), "获取无人机飞行坐标失败！");
        }
        return flightData.get(0);
    }

}
