package com.ruoyi.dj.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.dj.mqtt.client.DJICloudMQTTClient;
import com.ruoyi.dj.mqtt.cmd.CmdConstant;
import com.ruoyi.dj.mqtt.common.CommonFieldsVo;
import com.ruoyi.dj.mqtt.dto.*;
import com.ruoyi.dj.mqtt.service.TaskSchedulingService;
import com.ruoyi.dj.mqtt.utils.GatewayOperationLogUtil;
import com.ruoyi.dj.mqtt.utils.HandleResultUtil;
import com.ruoyi.dj.mqtt.utils.PayloadUtil;
import com.ruoyi.dj.mqtt.utils.StorageConfigUtil;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @auther 周志雄
 * @date 2024/7/15 14:31
 */
@Slf4j
@RestController
@Api(tags = "航线执行相关")
@RequestMapping("/dj")
public class FlightController {
    private static final String SERVICES_TOPIC = "thing/product/?/services";
    @Resource
    private DJICloudMQTTClient djiCloudMQTTClient;
    @Resource
    private IGatewayOperationLogService gatewayOperationLogService;
    @Resource
    private IDroneMissionsService droneMissionsService;
    @Resource
    private IFlightTasksService flightTasksService;
    @Resource
    private IVideoInfoService videoInfoService;
    @Resource
    private IScheduleTaskService scheduleTaskService;
    @Resource
    private TaskSchedulingService taskSchedulingService;
    @Resource
    private IMinioFileService minioFileService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 下发任务
     *
     * @return
     */
    @ApiOperation("下发任务")
    @PostMapping("/flightTaskPrepare")
    public AjaxResult flighttaskPrepare(@RequestBody FlightTaskDto flightTaskDto) {
        // 查看任务的类型
        Integer type = flightTaskDto.getType();
        // 立即执行的任务则直接下发
        if (type.equals(0)) {
            // 首先下发任务、获取最新的ID和对应的flightId
            Map<String, String> map = flightTaskPrepare(flightTaskDto);
            String flightId = map.get("flightId");
            String taskId = map.get("taskId");
            // 然后立即执行任务
            FlightTaskExecuteDto flightTaskExecuteDto = new FlightTaskExecuteDto();
            flightTaskExecuteDto.setFlightId(flightId);
            flightTaskExecuteDto.setGateway(flightTaskDto.getGateway());
            flightTaskExecuteDto.setTaskId(Long.valueOf(taskId));
            return executeTask(flightTaskExecuteDto);
        } else {
            // 定时任务、首先根据网关查询飞机的SN
            VideoInfo videoInfo = videoInfoService.selectVideoByCondition(flightTaskDto.getGateway(), "飞机");
            String sn = videoInfo.getSn();
            // 定时任务则先存储数据到数据库
            ScheduleTask scheduleTask = new ScheduleTask();
            scheduleTask.setTaskId(String.valueOf(flightTaskDto.getMissionId()));
            scheduleTask.setSn(sn);
            scheduleTask.setCreatedAt(new Date());
            scheduleTask.setExecMonth(flightTaskDto.getExecMonth());
            scheduleTask.setExecDay(flightTaskDto.getExecDay());
            scheduleTask.setExecHour(flightTaskDto.getExecHour());
            scheduleTask.setExecMinute(flightTaskDto.getExecMinute());
            scheduleTask.setExecSecond(flightTaskDto.getExecSecond());
            // 新增定时任务成功
            scheduleTaskService.insertScheduleTask(scheduleTask);
            return AjaxResult.success("任务下发成功").put("data", "任务下发成功");
        }
    }

    /**
     * 执行任务
     *
     * @return
     */
    @ApiOperation("执行任务")
    @PostMapping("/flightTaskExecute")
    public AjaxResult flightTaskExecute(@RequestBody FlightTaskExecuteDto flightTaskExecuteDto) {
        return executeTask(flightTaskExecuteDto);
    }

    /**
     * 取消任务
     *
     * @return
     */
    @ApiOperation("取消任务")
    @PostMapping("/flightTaskUndo")
    public AjaxResult flightTaskUndo(@RequestBody FlightTaskUndoDto flightTaskUndoDto) {
        String topic = SERVICES_TOPIC.replace("?", flightTaskUndoDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.FLIGHTTASK_UNDO, flightTaskUndoDto.getGateway());
        // 构造参数
        Map<String, Object> map = new HashMap<>();
        map.put("flight_ids", flightTaskUndoDto.getFlightIds());
        String payload = PayloadUtil.generatePayload(commonFieldVo, map);
        log.info("取消航线请求数据：{}", payload);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        // 记录操作日志
        GatewayOperationLogUtil.generateLog(flightTaskUndoDto.getGateway(), CmdConstant.FLIGHTTASK_UNDO, payload, response);
        return HandleResultUtil.handleResult(response, null,"取消任务成功");
    }

    /**
     * 暂停任务
     *
     * @return
     */
    @ApiOperation("暂停任务")
    @PostMapping("/flightTaskPause")
    public AjaxResult flightTaskPause(@RequestBody FlightTaskPauseDto flightTaskPauseDto) {
        String topic = SERVICES_TOPIC.replace("?", flightTaskPauseDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.FLIGHTTASK_PAUSE, flightTaskPauseDto.getGateway());
        String payload = PayloadUtil.generatePayload(commonFieldVo, null);
        log.info("暂停航线请求数据：{}", payload);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        // 记录操作日志
        GatewayOperationLogUtil.generateLog(flightTaskPauseDto.getGateway(), CmdConstant.FLIGHTTASK_PAUSE, payload, response);
        return HandleResultUtil.handleResult(response, null,"暂停任务成功");
    }

    /**
     * 航线恢复
     *
     * @return
     */
    @ApiOperation("航线恢复")
    @PostMapping("/flightTaskRecovery")
    public AjaxResult flightTaskRecovery(@RequestBody FlightTaskRecoveryDto flightTaskRecoveryDto) {
        String topic = SERVICES_TOPIC.replace("?", flightTaskRecoveryDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.FLIGHTTASK_RECOVERY, flightTaskRecoveryDto.getGateway());
        String payload = PayloadUtil.generatePayload(commonFieldVo, null);
        log.info("恢复航线请求数据：{}", payload);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        // 记录操作日志
        GatewayOperationLogUtil.generateLog(flightTaskRecoveryDto.getGateway(), CmdConstant.FLIGHTTASK_RECOVERY, payload, response);
        return HandleResultUtil.handleResult(response, null,"航线恢复成功");
    }

    /**
     * 一键返航
     *
     * @return
     */
    @ApiOperation("一键返航")
    @PostMapping("/returnHome")
    public AjaxResult returnHome(@RequestBody ReturnHomeDto returnHomeDto) {
        String topic = SERVICES_TOPIC.replace("?", returnHomeDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.RETURN_HOME, returnHomeDto.getGateway());
        String payload = PayloadUtil.generatePayload(commonFieldVo, null);
        log.info("一键返航请求数据：{}", payload);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        // 记录操作日志
        GatewayOperationLogUtil.generateLog(returnHomeDto.getGateway(), CmdConstant.RETURN_HOME, payload, response);
        return HandleResultUtil.handleResult(response, null,"一键返航成功");
    }

    /**
     * 取消返航
     *
     * @return
     */
    @ApiOperation("取消返航")
    @PostMapping("/returnHomeCancel")
    public AjaxResult returnHomeCancel(@RequestBody ReturnHomeCancelDto returnHomeCancelDto) {
        String topic = SERVICES_TOPIC.replace("?", returnHomeCancelDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.RETURN_HOME_CANCEL, returnHomeCancelDto.getGateway());
        String payload = PayloadUtil.generatePayload(commonFieldVo, null);
        log.info("取消返航请求数据：{}", payload);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        // 记录操作日志
        GatewayOperationLogUtil.generateLog(returnHomeCancelDto.getGateway(), CmdConstant.RETURN_HOME_CANCEL, payload, response);
        return HandleResultUtil.handleResult(response, null,"取消返航成功");
    }

    private Map<String, String> flightTaskPrepare(FlightTaskDto flightTaskDto) {
        HashMap<String, String> hashMap = new HashMap<>();
        String topic = SERVICES_TOPIC.replace("?", flightTaskDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.FLIGHTTASK_PREPARE, flightTaskDto.getGateway());
        String gateway = flightTaskDto.getGateway();
        // 先生成一个随机的数字字符串作为任务ID
        String flightId = String.valueOf((int) (Math.random() * 900000) + 100000);
        // 根据任务ID去查询是否存在任务信息
        DroneMissions droneMissions = droneMissionsService.selectDroneMissionsById(flightTaskDto.getMissionId());
        if (droneMissions != null) {
            log.info("{}", droneMissions);
            // 把任务对象转化为 FlightTaskParam 参数、便于构建下发任务的 JSON 参数
            FlightTaskParam flightTaskParam = getFlightTaskParam(droneMissions, flightId);
            flightTaskParam.setTaskType(Long.valueOf(flightTaskDto.getType()));
            flightTaskParam.setExecuteTime(flightTaskDto.getTimeStamp());
            Map<String, Object> map = convertDtoToMap(flightTaskParam);
            String payload = PayloadUtil.generatePayload(commonFieldVo, map);
            String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
            // 记录操作日志
            GatewayOperationLogUtil.generateLog(flightTaskDto.getGateway(), CmdConstant.FLIGHTTASK_PREPARE, payload, response);
            ObjectMapper objectMapper = new ObjectMapper();
            // 将字符串解析为 JSON 对象
            JsonNode resultNode = null;
            try {
                ObjectNode jsonNode = null;
                jsonNode = (ObjectNode) objectMapper.readTree(response);
                resultNode = jsonNode.path("data").path("result");
                Integer result = Integer.valueOf(resultNode.asText());
                if (result == 0) {
                    // 执行指令没有出错则存储下发任务数据到数据库
                    FlightTasks flightTasks = new FlightTasks();
                    // 根据网关查询飞机的SN
                    VideoInfo videoInfo = videoInfoService.selectVideoByCondition(gateway, "飞机");
                    String sn = videoInfo.getSn();
                    flightTasks.setSn(sn);
                    flightTasks.setIsExecuted(0);
                    flightTasks.setStatus("下发成功待执行");
                    flightTasks.setFlightId(flightId);
                    flightTasks.setCreatedAt(new Date());
                    flightTasks.setMessionId(flightTaskDto.getMissionId());
                    flightTasksService.insertFlightTasks(flightTasks);

                    // 返回任务ID
                    hashMap.put("flightId", flightId);
                    hashMap.put("taskId", flightTasks.getId().toString());
                }
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }

        return hashMap;
    }

    private AjaxResult executeTask(FlightTaskExecuteDto flightTaskExecuteDto) {
        String topic = SERVICES_TOPIC.replace("?", flightTaskExecuteDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.FLIGHTTASK_EXECUTE, flightTaskExecuteDto.getGateway());
        // 构造参数
        Map<String, Object> map = new HashMap<>();
        map.put("flight_id", flightTaskExecuteDto.getFlightId());
        String payload = PayloadUtil.generatePayload(commonFieldVo, map);
        log.info("执行航线请求数据：{}", payload);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        // 记录操作日志
        GatewayOperationLogUtil.generateLog(flightTaskExecuteDto.getGateway(), CmdConstant.FLIGHTTASK_EXECUTE, payload, response);
        return HandleResultUtil.handleResult(response, null,"下发航线成功");
    }

    /**
     * 根据droneMissions获取FlightTaskParam
     *
     * @param droneMissions
     * @return
     */
    private static FlightTaskParam getFlightTaskParam(DroneMissions droneMissions, String flightId) {
        FlightTaskParam flightTaskParam = new FlightTaskParam();
        // 设置计划ID
        flightTaskParam.setFlightId(flightId.toString());
        // 创建航线文件对象
        FlightTaskParam.File file = new FlightTaskParam.File();
        // 设置航线文件MD5
        file.setFingerprint(droneMissions.getFlightPathMd5());
        // 设置航线文件URL
        file.setUrl(droneMissions.getFlightPathUrl());
        // 设置文件信息
        flightTaskParam.setFile(file);
        // 设置返航高度
        flightTaskParam.setRthAltitude(Integer.valueOf(droneMissions.getReturnAltitude().toString()));
        // 设置返航高度模式
        flightTaskParam.setRthMode(Integer.valueOf(droneMissions.getReturnAltitudeMode().toString()));
        // 设置遥控器失控类型
        flightTaskParam.setOutOfControlAction(Integer.valueOf(droneMissions.getOutOfControlAction().toString()));
        // 设置航线失控动作
        flightTaskParam.setExitWaylineWhenRcLost(Integer.valueOf(droneMissions.getFlightControlAction().toString()));
        // 设置航线精度类型
        flightTaskParam.setWaylinePrecisionType(Integer.valueOf(droneMissions.getWaylinePrecisionType().toString()));
        log.info("构造航线下发的参数：{}", flightTaskParam);
        return flightTaskParam;
    }

    /**
     * 构造航线下发的参数
     *
     * @param flightTaskParam
     * @return
     */
    private Map<String, Object> convertDtoToMap(FlightTaskParam flightTaskParam) {
        Map<String, Object> dataMap = new HashMap<>();

        // 如果断点信息不为空，则加入到 Map
        if (flightTaskParam.getBreakPoint() != null) {
            dataMap.put("break_point", flightTaskParam.getBreakPoint());
        }
        // 如果执行条件不为空，则加入到 Map
        if (flightTaskParam.getExecutableConditions() != null) {
            dataMap.put("executable_conditions", flightTaskParam.getExecutableConditions());
        }
        // 如果有指定任务执行时间，则加入到 Map
        if (flightTaskParam.getExecuteTime() != null) {
            dataMap.put("execute_time", flightTaskParam.getExecuteTime());
        }
        // 如果文件信息不为空，则加入到 Map
        if (flightTaskParam.getFile() != null) {
            dataMap.put("file", flightTaskParam.getFile());
        }
        // 如果有计划 ID，则加入到 Map
        if (flightTaskParam.getFlightId() != null) {
            dataMap.put("flight_id", flightTaskParam.getFlightId());
        }
        // 如果有定义遥控器失控动作，则加入到 Map
        if (flightTaskParam.getOutOfControlAction() != null) {
            dataMap.put("out_of_control_action", flightTaskParam.getOutOfControlAction());
        }
        // 如果有就绪条件，则加入到 Map
        if (flightTaskParam.getReadyConditions() != null) {
            dataMap.put("ready_conditions", flightTaskParam.getReadyConditions());
        }
        // 如果模拟任务不为空，则加入到 Map
        if (flightTaskParam.getSimulateMission() != null) {
            dataMap.put("simulate_mission", flightTaskParam.getSimulateMission());
        }
        // 如果有返航高度，则加入到 Map
        if (flightTaskParam.getRthAltitude() != null) {
            dataMap.put("rth_altitude", flightTaskParam.getRthAltitude());
        }
        // 如果有返航高度模式，则加入到 Map
        if (flightTaskParam.getRthMode() != null) {
            dataMap.put("rth_mode", flightTaskParam.getRthMode());
        }
        // 如果有航线失控动作，则加入到 Map
        if (flightTaskParam.getExitWaylineWhenRcLost() != null) {
            dataMap.put("exit_wayline_when_rc_lost", flightTaskParam.getExitWaylineWhenRcLost());
        }
        // 如果有航线类型，则加入到 Map
        if (flightTaskParam.getTaskType() != null) {
            dataMap.put("task_type", flightTaskParam.getTaskType());
        }
        // 任务类型和航线精度类型始终加入到 Map，因为它们为基础属性
        dataMap.put("task_type", flightTaskParam.getTaskType());
        dataMap.put("wayline_precision_type", flightTaskParam.getWaylinePrecisionType());

        return dataMap;
    }

    @ApiOperation("获取任务执行文件结果")
    @PostMapping("/getFileByflightId")
    public AjaxResult getFileByflightId(@RequestBody GetFileDto getFileDto) {
        VideoInfo videoInfo = videoInfoService.selectGatewayByCondition(getFileDto.getSn());
        if (videoInfo == null) {
            log.info("未找到对应的视频设备");
            return AjaxResult.success().put("data", null);
        }
        List<Map<String, String>> files = StorageConfigUtil.getFiles(videoInfo.getGateway(), getFileDto.getFlightId());
        log.info("获取到的文件信息：{}", files);
        // 循环遍历数据
        for (Map<String, String> file : files) {
            String fileName = file.get("filename");
            String fileUrl = file.get("url");
            // 存到数据库之前需要先查看、不存在再存入
            MinioFile exist = minioFileService.selectMinioFileName(fileName);
            if (exist == null) {
                MinioFile minioFile = new MinioFile();
                minioFile.setFilename(fileName);
                minioFile.setUrl(fileUrl);
                minioFile.setFlightId(Long.valueOf(getFileDto.getFlightId()));
                minioFile.setCreatedAt(new Date());
                minioFileService.insertMinioFile(minioFile);
            }
        }
        return AjaxResult.success().put("data", files);
    }
}
