package com.tuoheng.admin.service.accident.verify;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tuoheng.admin.constant.SystemConstant;
import com.tuoheng.admin.entity.Accident;
import com.tuoheng.admin.entity.Inspection;
import com.tuoheng.admin.entity.InspectionFile;
import com.tuoheng.admin.entity.User;
import com.tuoheng.admin.enums.*;
import com.tuoheng.admin.mapper.AccidentMapper;
import com.tuoheng.admin.mapper.InspectionFileMapper;
import com.tuoheng.admin.mapper.InspectionMapper;
import com.tuoheng.admin.request.accident.AccidentVerifyRequest;
import com.tuoheng.admin.utils.CurrentUserUtil;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 事故核实业务层处理
 *
 * @author wanjing
 * @team tuoheng
 * @date 2023-03-15
 */
@Slf4j
@Service
public class AccidentVerifyService {

    @Autowired
    private AccidentMapper accidentMapper;

    @Autowired
    private InspectionMapper inspectionMapper;

    @Autowired
    private InspectionFileMapper inspectionFileMapper;

    /**
     * 1、向机场起降平台推送指令，无人机前往事故发生地点，即获取拍摄到事故照片时所处的经纬度，并在事故地点上方悬停
     * 2、修改该事故状态为处理中
     *
     * @param request
     * @return
     */
    @Transactional
    public JsonResult verify(AccidentVerifyRequest request) {
//        log.info("进入事故核实业务");
        User user = CurrentUserUtil.getUserInfo();
        String userId = user.getId();
        String tenantId = user.getTenantId();
        JsonResult result = this.check(tenantId, request);
        if (0 != result.getCode()) {
            log.info("事故核实业务：校验失败：{}", result.getMsg());
            return result;
        }
        Accident accident = (Accident) result.getData();

        Inspection oldInspection = this.getInspection(accident);

        // 无人机前往事故发生地点，事故地点上方悬停
        this.callAirpor(accident, oldInspection);

        // 修改原巡检任务状态为已完成
        oldInspection.setStatus(InspectionStatusEnum.FLIGHT_COMPLETED.getCode());
        inspectionMapper.updateById(oldInspection);

        // 创建应急任务
        this.createInspection(userId, oldInspection);

        this.updateAccident(accident.getId(), userId);

        return JsonResult.success();
    }

    /**
     * 检查参数
     *
     * @param tenantId
     * @param request
     * @return
     */
    private JsonResult check(String tenantId, AccidentVerifyRequest request) {
        if (StringUtils.isEmpty(request.getAccidentId())) {
            throw new ServiceException("事故ID为空");
        }
        Accident accident = accidentMapper.selectOne(new LambdaQueryWrapper<Accident>()
                .eq(Accident::getTenantId, tenantId)
                .eq(Accident::getId, request.getAccidentId())
                .eq(Accident::getMark, MarkEnum.VALID.getCode()));
        if (ObjectUtil.isNull(accident)) {
            throw new ServiceException("事故不存在");
        }
        if (AccidentStatusEnum.UNTREATED.getCode() != accident.getStatus()) {
            throw new ServiceException("该事故已处理，不能在核实");
        }

        Inspection inspection = inspectionMapper.selectOne(new LambdaQueryWrapper<Inspection>()
                .eq(Inspection::getEmergencyDataInspectionId, accident.getInspectionId())
                .eq(Inspection::getMark, MarkEnum.VALID.getCode()));
        if (ObjectUtil.isNotNull(inspection)) {
            throw new ServiceException("该事故关联的任务已被处理，不能在核实");
        }
        return JsonResult.success(accident);
    }

    /**
     * 获取巡检任务
     *
     * @param accident
     * @return
     */
    private Inspection getInspection(Accident accident) {
        Inspection inspection = inspectionMapper.selectOne(new LambdaQueryWrapper<Inspection>()
                .eq(Inspection::getId, accident.getInspectionId())
                .eq(Inspection::getMark, MarkEnum.VALID.getCode()));
        if (InspectionStatusEnum.FLIGHT_COMPLETED.getCode() == inspection.getStatus()) {
            log.info("该任务已完成，不能在核实：inspectionId={}， status={}", inspection.getId(), inspection.getStatus());
            throw new ServiceException("该任务已完成，不能在核实");
        }
        if (InspectionStatusEnum.FLIGHT_FAILED.getCode() == inspection.getStatus()) {
            log.info("该任务已失败，不能在核实：inspectionId={}， status={}", inspection.getId(), inspection.getStatus());
            throw new ServiceException("该任务已失败，不能在核实");
        }
        return inspection;
    }

    /**
     * 创建应急任务
     *
     * @param userid
     * @param oldInspection
     * @return
     */
    private Inspection createInspection(String userid, Inspection oldInspection) {
        Inspection inspection = new Inspection();
        String code = DateUtils.generateCode("XJRW");
        ;
        inspection.setCode(code);
        inspection.setName("应急任务" + code);
        inspection.setDeptId(oldInspection.getDeptId());
        inspection.setTenantId(oldInspection.getTenantId());
        inspection.setType(2);
        inspection.setStatus(InspectionStatusEnum.IN_FLIGHT.getCode()); // 设置应急任务状态为飞行中
        inspection.setInspectionType(InspectionTypeEnum.AIRPORT.getCode());
        inspection.setAirportId(oldInspection.getAirportId());
        inspection.setAirportName(oldInspection.getAirportName());
        inspection.setInspectionLine(0);
        inspection.setInspectionLineName("");
        inspection.setExecutionStartTime(DateUtils.now());
        inspection.setCreateUser(userid);
        inspection.setCreateTime(DateUtils.now());
        inspection.setEmergencyDataInspectionId(oldInspection.getId());
        inspection.setRoadId("");
        inspection.setRoadName("");
        inspection.setSectionId("");
        inspection.setSectionName("");
        inspection.setIsLive(1);
        Integer count = inspectionMapper.insert(inspection);
        if (count <= 0) {
            log.info("创建应急任务失败");
            throw new ServiceException("创建应急任务失败");
        }
        return inspection;
    }

    /**
     * 调用机场平台，执行指点飞行
     *
     * @param accident
     * @param inspection
     */
    private void callAirpor(Accident accident, Inspection inspection) {
        InspectionFile inspectionFile = inspectionFileMapper.selectOne(new LambdaQueryWrapper<InspectionFile>()
                .eq(InspectionFile::getId, accident.getInspectionFileId()));
        String url = CommonConfig.airportURL + SystemConstant.API_AIRPORT_DRONE_CONTROL;
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("taskId", inspection.getId());
        jsonObject.put("airportId", inspection.getAirportId());
        jsonObject.put("zalt", "100");
        jsonObject.put("zlon", inspectionFile.getLongitude());
        jsonObject.put("zlat", inspectionFile.getLatitude());

        log.info("调用机场平台，无人机执行定点飞行: url:{}", url);
        log.info("调用机场平台，无人机执行定点飞行: jsonObject:{}", jsonObject);

        String airPortStr = HttpUtils.doSend(url, jsonObject, null, "POST");
        if (StringUtils.isEmpty(airPortStr)) {
            log.info("调用机场平台，无人机执行定点飞行：返回数据为空，飞行失败，jsonObject：{}", jsonObject);
            throw new ServiceException("机场平台返回数据为空，飞行失败");
        }

        JsonResult jsonResult = JacksonUtil.json2pojo(airPortStr, JsonResult.class);
        if (0 != jsonResult.getCode()) {
            log.info("调用机场平台，无人机执行定点飞行：飞行失败，jsonResult：{}", jsonResult.getMsg());
            throw new ServiceException("机场平台返回，飞行失败");
        }
    }

    /**
     * 修改预警记录信息
     *
     * @param id
     * @param userId
     */
    private void updateAccident(String id, String userId) {
        Accident accidentUpdate = new Accident();
        accidentUpdate.setId(id);
        accidentUpdate.setStatus(AccidentStatusEnum.PROCESSING.getCode());
        accidentUpdate.setUpdateUser(userId);
        accidentUpdate.setUpdateTime(DateUtils.now());
        accidentUpdate.setVerificationTime(DateUtils.now());
        Integer count = accidentMapper.updateById(accidentUpdate);
        if (count <= 0) {
            log.info("事故核实，修改预警信息失败");
        }
    }
}
