package com.tuoheng.admin.service.inspection.upload;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tuoheng.admin.entity.domain.FlightData;
import com.tuoheng.admin.entity.domain.Inspection;
import com.tuoheng.admin.enums.AiAnalyseStatusEnum;
import com.tuoheng.admin.enums.AiAnalyseTypeEnum;
import com.tuoheng.admin.enums.MarkEnum;
import com.tuoheng.admin.enums.TaskStatusEnum;
import com.tuoheng.admin.mapper.FlightDataMapper;
import com.tuoheng.admin.mapper.InspectionMapper;
import com.tuoheng.admin.entity.request.inspection.UploadFlightUrlRequest;
import com.tuoheng.admin.service.flightdata.AsyncService;
import com.tuoheng.admin.service.business.IBusinessService;
import com.tuoheng.admin.service.third.dsp.IDspService;
import com.tuoheng.admin.utils.SrtDataUtil;
import com.tuoheng.common.core.config.common.CommonConfig;
import com.tuoheng.common.core.enums.HttpStatusEnum;
import com.tuoheng.common.core.enums.ServiceExceptionEnum;
import com.tuoheng.common.core.exception.ServiceException;
import com.tuoheng.common.core.utils.FileUtils;
import com.tuoheng.common.core.utils.JsonResult;
import com.tuoheng.common.core.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * 上传离线视频业务层处理
 *
 * @author wanjing
 * @team tuoheng
 * @date 2022-12-22
 */
@Slf4j
@Service
public class UploadFlightUrlService {

    @Autowired
    private InspectionMapper inspectionMapper;

    @Autowired
    private FlightDataMapper flightDataMapper;

    @Autowired
    private IBusinessService businessService;

    @Autowired
    private IDspService dspService;

    @Resource
    private AsyncService asyncService;

    /**
     * DSP服务回调接口域名
     */
    @Value("${tuoheng.dsp-callback-url:}")
    private String dspCallbackUrl;

    /**
     * 上传离线视频
     *
     * @return
     */
    public JsonResult uploadFlightUrl(UploadFlightUrlRequest request) {
        log.info("进入上传离线视频业务接口, request:{}", request);
        JsonResult result = this.check(request);
        if (0 != result.getCode()) {
            log.info("上传离线视频业务接口：校验失败：{}", result.getMsg());
            return result;
        }

        Inspection inspection = (Inspection) result.getData();

        log.info("上传离线视频业务接口, inspectionid:{}, isLive:{}, analyseStatus:{}", inspection.getId(), inspection.getIsLive(), inspection.getAnalyseStatus());

        //修改用户直播完也可以进行分析
        if (1 == inspection.getIsLive() && inspection.getAnalyseStatus() != 1) {
            log.info("上传离线视频业务接口, 修改用户直播完进行分析");
            inspection.setAnalyseStatus(1);
            inspectionMapper.updateById(inspection);
        }

        if (TaskStatusEnum.COMPLETE.getCode() != inspection.getStatus()) {
            log.info("上传离线视频业务接口, 只有飞行完成状态可以操作！");
            throw new ServiceException(ServiceExceptionEnum.TASK_NOT_OPERATION);
        }
        // 如果任务处于待分析中，响应前端，任务待分析中，不能上传视频
        if (AiAnalyseStatusEnum.WAITING.getCode() == inspection.getAnalyseStatus()
                || AiAnalyseStatusEnum.RUNNING.getCode() == inspection.getAnalyseStatus()) {
            log.info("上传离线视频业务接口, 视频分析中，请稍后再试！");
            throw new ServiceException(ServiceExceptionEnum.VIDEO_ANALYSIS_IN_PROGRESS);
        }
        if (AiAnalyseStatusEnum.SUCCESS.getCode() == inspection.getAnalyseStatus()
                || AiAnalyseStatusEnum.SUCCESS_TIMEOUT.getCode() == inspection.getAnalyseStatus()) {
            log.info("上传离线视频业务接口, 视频分析已完成！");
            throw new ServiceException(HttpStatus.BAD_REQUEST.value(), "视频分析已完成");
        }

        this.updatInspection(request, inspection);

        // 调用DSP接口
        this.callDsp(inspection.getId(), request.getVideoUrl(), inspection.getDspServiceInstId());

        log.info("上传离线视频业务接口：上传离线视频成功:{}", inspection);
        return JsonResult.success();
    }

    /**
     * 检查参数
     *
     * @param request
     * @return
     */
    private JsonResult check(UploadFlightUrlRequest request) {
        if (StringUtils.isEmpty(request.getVideoUrl()) || StringUtils.isEmpty(request.getSrtUrl())
                || StringUtils.isEmpty(request.getTaskCode())) {
            log.info("参数为空！");
            throw new ServiceException(ServiceExceptionEnum.PARAMETER_IS_NULL);
        }
        Inspection inspection = inspectionMapper.selectOne(new LambdaQueryWrapper<Inspection>()
                .eq(Inspection::getCode, request.getTaskCode())
                .eq(Inspection::getMark, MarkEnum.VALID.getCode()));
        if (ObjectUtils.isEmpty(inspection)) {
            log.info("巡检任务不存在！");
            throw new ServiceException(ServiceExceptionEnum.TASK_DOES_NOT_EXIST);
        }
        return JsonResult.success(inspection);
    }

    private void updatInspection(UploadFlightUrlRequest request, Inspection inspection) {
        Inspection inspectionUpdate = new Inspection();
        inspectionUpdate.setId(inspection.getId());
        inspectionUpdate.setAnalyseStatus(AiAnalyseStatusEnum.WAITING.getCode());
        inspectionUpdate.setAiVideoUrl("");
        inspectionUpdate.setIsLive(AiAnalyseTypeEnum.OFFLINE.getCode());
        inspectionUpdate.setProgressbar(0.0000D);
        inspectionUpdate.setHeartbeatTime(System.currentTimeMillis());
        inspectionUpdate.setTenantId(inspection.getTenantId());

        // 如果任务处于分析完成，响应前端，任务已完成，不能上传视频
        this.updateSrtUrl(request.getSrtUrl(), inspection.getId(), request.getType(), inspection.getTenantId());

        // 上传离线视频，更新任务状态为分析中
        inspectionUpdate.setSrtName(request.getSrtUrl().substring(request.getSrtUrl().lastIndexOf("/") + 1));
        inspectionUpdate.setVideoUrl(StringUtils.removeHost(request.getVideoUrl(), CommonConfig.videoURL));
        inspectionMapper.updateByPrimaryKeySelective(inspectionUpdate);
    }

    /**
     * 长传视频
     *
     * @param fileUrl
     * @param inspectionId
     * @param type
     * @param tenantId
     */
    private void updateSrtUrl(String fileUrl, String inspectionId, Integer type, String tenantId) {
        log.info("进入srt文件上传方法, fileUrl:{}, inspectionId:{}, type:{}, tenantId:{}", fileUrl, inspectionId, type, tenantId);
        List<FlightData> flightDataList = new ArrayList<>();
        try {
            String txtContent = FileUtils.txtToString(fileUrl);
            //type为1时，机型为M300
            if (type == 1) {
                //解析srt文件经纬度和时间戳
                flightDataList = SrtDataUtil.getByM300(txtContent, inspectionId, tenantId);
            } else {
                log.info("srt文件上传, type不等于1，异常");
                throw new ServiceException(ServiceExceptionEnum.PARAMETER_IS_NULL);
            }
            if (ObjectUtils.isEmpty(flightDataList)) {
                log.info("srt文件上传, 上传文件数据错误，请检查文件！");
                throw new ServiceException(ServiceExceptionEnum.FILE_DATA_IS_ERROR);
            }
            flightDataMapper.delete(new LambdaQueryWrapper<FlightData>()
                    .eq(FlightData::getInspectionId, inspectionId)
                    .eq(FlightData::getTenantId, tenantId)
                    .eq(FlightData::getMark, MarkEnum.VALID.getCode()));
            List<List> lists = this.splitList(flightDataList, 100);
            //线程池使用多线程插入数据
            for (List list : lists) {
                asyncService.flightDataSaveBatch(list);
            }
        } catch (Exception e) {
            log.error("srt文件上传异常", e);
            throw new ServiceException(ServiceExceptionEnum.FILE_DATA_IS_ERROR);
        }
    }

    /**
     * 切分list
     *
     * @param sourceList
     * @param groupSize  每组定长
     * @return
     */
    public static List<List> splitList(List sourceList, int groupSize) {
        int length = sourceList.size();
        // 计算可以分成多少组
        int num = (length + groupSize - 1) / groupSize;
        List<List> newList = new ArrayList<>(num);
        for (int i = 0; i < num; i++) {
            // 开始位置
            int fromIndex = i * groupSize;
            // 结束位置
            int toIndex = (i + 1) * groupSize < length ? (i + 1) * groupSize : length;
            newList.add(sourceList.subList(fromIndex, toIndex));
        }
        return newList;
    }

    /**
     * 调用DSP接口
     */
    private void callDsp(String inspectionId, String videoUrl, String dspServiceInstId) {
        log.info("调用DSP接口, inspectionId:{}, videoUrl:{}", inspectionId, videoUrl);

        JSONObject requestDsp = new JSONObject();
        JSONArray configList = new JSONArray();
        JSONObject instConfigMode = new JSONObject();
        instConfigMode.put("ename", "service_mode");
        instConfigMode.put("evalue", "2");
        configList.add(instConfigMode);
        JSONObject instConfigUrl = new JSONObject();
        instConfigUrl.put("ename", "callback_url");
        instConfigUrl.put("evalue", dspCallbackUrl);
        configList.add(instConfigUrl);
        requestDsp.put("serviceInstConfigList", configList);

        JSONArray reqList = new JSONArray();

        JSONObject instReqUrl = new JSONObject();
        instReqUrl.put("ename", "original_url");
        instReqUrl.put("evalue", videoUrl);
        reqList.add(instReqUrl);
        requestDsp.put("serviceInstReqList", reqList);
        JSONObject responseJson = dspService.serviceInstApplication(requestDsp, dspServiceInstId);
        if (responseJson.getIntValue("code") == 0) {
            String requestId = responseJson.getJSONObject("data").getString("requestId");
            log.info("dsp离线调用响应requestId：", requestId);
            businessService.insertBusinessUploadUrl(requestId, inspectionId);
        } else if (responseJson.getIntValue("code") == HttpStatusEnum.NO_RESOURCES.getCode()) {
            log.warn("dsp离线调用响应失败，暂时无资源可用");
            throw new ServiceException(HttpStatus.BAD_REQUEST.value(), HttpStatusEnum.NO_RESOURCES.getDescription());
        } else {
            log.error("dsp离线调用失败：", responseJson);
        }
    }
}
