package com.ruoyi.dj.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
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.config.DJIMqttConfig;
import com.ruoyi.dj.mqtt.domain.*;
import com.ruoyi.dj.mqtt.dto.DroneControlDto;
import com.ruoyi.dj.mqtt.dto.DroneEmergencyStop;
import com.ruoyi.dj.mqtt.utils.*;
import com.ruoyi.system.domain.GatewayOperationLog;
import com.ruoyi.system.domain.OneClickTakeoff;
import com.ruoyi.system.domain.VideoInfo;
import com.ruoyi.system.service.IGatewayOperationLogService;
import com.ruoyi.system.service.IOneClickTakeoffService;
import com.ruoyi.system.service.IVideoInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
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.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * @auther 周志雄
 * @date 2024/7/25 9:51
 */


@Slf4j
@Api(tags = "指令飞行")
@RestController
@RequestMapping("/dj/cmdFly")
public class CmdFlyController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    private static final String SERVICES_TOPIC = "thing/product/?/services";
    private static final String DRC_SERVICES_TOPIC = "thing/product/?/drc/down";
    @Resource
    private DJICloudMQTTClient djiCloudMQTTClient;
    @Autowired
    private DJIMqttConfig djiMqttConfig;
    @Autowired
    private Environment environment;
    @Resource
    private IOneClickTakeoffService oneClickTakeoffService;
    @Resource
    private IVideoInfoService videoInfoService;
    @Resource
    private IGatewayOperationLogService gatewayOperationLogService;

    Map<String, String> map = new HashMap<>();


    @PostConstruct
    public void init() {
        map.put("flight_authority_grab", "飞行控制权抢夺");
        map.put("payload_authority_grab", "负载控制权抢夺");
        map.put("drc_mode_enter", "进入指令飞行控制模式");
        map.put("drc_mode_close", "退出指令飞行控制模式");
        map.put("takeoff_to_point", "一键起飞");
        map.put("fly_to_point", "飞向目标点");
        map.put("fly_to_point", "飞向目标点");
        map.put("camera_mode_switch", "切换相机模式成功");
        map.put("camera_photo_take", "开始拍照成功");
        map.put("camera_photo_stop", "停止拍照成功");
        map.put("camera_recording_start", "开始录像成功");
        map.put("camera_recording_stop", "结束录像成功");
        map.put("camera_screen_drag", "画面拖动控制成功");
        map.put("camera_aim", "居中成功");
        map.put("camera_focal_length_set", "变焦成功");
    }

    // 定义一个标志变量，用来控制心跳线程是否继续执行
    private volatile boolean keepHeartBeatRunning = true;

    /**
     * 删除 Redis 中所有以 drc: 开头的键
     */
    public void deleteAllWithPrefix(String prefix) {
        Set<String> keys = redisTemplate.keys(prefix + "*");
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 无参数的指令飞行
     *
     * @param cmdFlyServiceDto
     * @return
     */
    @ApiOperation("无参数的指令飞行")
    @PostMapping("/noDataFlight")
    public AjaxResult noParameterFlight(@RequestBody CmdFlyServiceDto cmdFlyServiceDto) {


        String msg = map.get(cmdFlyServiceDto.getMethod());
        // 退出指令模式
        if ("drc_mode_exit".equals(cmdFlyServiceDto.getMethod())) {
            // 更新标志位，通知心跳线程停止
            keepHeartBeatRunning = false;
            // 删除和这个网关有关的任何内容
            String prefix = "drc:" + cmdFlyServiceDto.getGateway();
            deleteAllWithPrefix(prefix);
        }
        String gateway = cmdFlyServiceDto.getGateway();
        String topic = SERVICES_TOPIC.replace("?", cmdFlyServiceDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(cmdFlyServiceDto.getMethod(), cmdFlyServiceDto.getGateway());
        String payload = PayloadUtil.generatePayload(commonFieldVo, null);
        log.info("无参数的指令发送数据：" + payload);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        AjaxResult ajaxResult = HandleResultUtil.handleResult(response, null, msg);
        // 记录日志
        GatewayOperationLogUtil.generateLog(gateway, commonFieldVo.getMethod(), payload, response);
        return ajaxResult;
    }

    /**
     * 有参数的指令飞行
     *
     * @param cmdFlyDataServiceDto
     * @return
     */
    @ApiOperation("有参数的指令飞行")
    @PostMapping("/hasDataFlight")
    public AjaxResult payloadAuthorityGrab(@RequestBody CmdFlyDataServiceDto cmdFlyDataServiceDto) {
        String msg = map.get(cmdFlyDataServiceDto.getMethod());
        // 如果是一键起飞
        if (cmdFlyDataServiceDto.getMethod().equals("takeoff_to_point")) {
            Map<String, Object> params = cmdFlyDataServiceDto.getParams();
            // 生成随机的七位字符串
            String flightId = String.valueOf(new Random().nextInt(8999999) + 1000000);
            params.put("flight_id", flightId);
            // 查询飞机的序列号
            VideoInfo videoInfo = videoInfoService.selectVideoByCondition(cmdFlyDataServiceDto.getGateway(), "飞机");
            // 生成数据到数据库一键起飞记录中
            OneClickTakeoff oneClickTakeoff = new OneClickTakeoff();
            oneClickTakeoff.setFlightId(Long.valueOf(flightId));
            oneClickTakeoff.setDroneSerialNumber(videoInfo.getSn());
            oneClickTakeoff.setTakeoffTime(new Date());
            oneClickTakeoffService.insertOneClickTakeoff(oneClickTakeoff);
        }
        String gateway = cmdFlyDataServiceDto.getGateway();
        String topic = SERVICES_TOPIC.replace("?", cmdFlyDataServiceDto.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(cmdFlyDataServiceDto.getMethod(), cmdFlyDataServiceDto.getGateway());
        Map<String, Object> params = cmdFlyDataServiceDto.getParams();
        String payload = PayloadUtil.generatePayload(commonFieldVo, params);
        log.info("参数:{}", payload);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        AjaxResult ajaxResult = HandleResultUtil.handleResult(response, null, msg);
        // 记录日志
        GatewayOperationLogUtil.generateLog(gateway, commonFieldVo.getMethod(), payload, response);
        return ajaxResult;
    }

    /**
     * 进入指令飞行控制模式
     *
     * @param drcModeEnter
     * @return
     */
    @ApiOperation("进入指令飞行控制模式")
    @PostMapping("/drcModeEnter")
    public AjaxResult drcModeEnter(@RequestBody DrcModeEnter drcModeEnter) {
        String gateway = drcModeEnter.getGateway();
        String topic = SERVICES_TOPIC.replace("?", drcModeEnter.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.DRC_MODE_ENTER, drcModeEnter.getGateway());
        Map<String, Object> mqttMap = new HashMap<>();
        mqttMap.put("address", environment.getProperty("cmd.host"));
        // 设置 clientId
        mqttMap.put("client_id", "cmd" + drcModeEnter.getGateway());
        mqttMap.put("enable_tls", false);
        mqttMap.put("expire_time", 7200);
        mqttMap.put("password", environment.getProperty("cmd.password"));
        mqttMap.put("username", environment.getProperty("cmd.username"));
        Map<String, Object> params = new HashMap<>();
        params.put("hsi_frequency", 1);
        params.put("mqtt_broker", mqttMap);
        params.put("osd_frequency", 10);
        String payload = PayloadUtil.generatePayload(commonFieldVo, params);
        String response = djiCloudMQTTClient.publishMessage(topic, payload, commonFieldVo);
        AjaxResult ajaxResult = HandleResultUtil.handleResult(response, null, "进入指令飞行控制模式");
        log.error("进入指令控制模式:{}", response);
        GatewayOperationLogUtil.generateLog(gateway, commonFieldVo.getMethod(), payload, response);

        // 启动心跳线程
        CompletableFuture.runAsync(() -> {
            while (keepHeartBeatRunning) {  // 循环发送心跳，直到标志位为false
                HeartBeatVo heartBeatVo = new HeartBeatVo();
                heartBeatVo.setGateway(gateway);
                AjaxResult heart = heart(heartBeatVo);
                if (heart.isSuccess()) {
                    try {
                        Thread.sleep(2000); // 休眠2秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Thread.currentThread().interrupt();  // 中断线程
                    }
                }
            }
        });
        return ajaxResult;
    }


    /**
     * DRC-飞行控制
     *
     * @param drcModeEnter
     * @return
     */
    /**
     * DRC-飞行控制
     *
     * @param droneControlDto
     * @return
     */
    @ApiOperation("DRC-飞行控制")
    @PostMapping("/droneControl")
    public AjaxResult droneControl(@RequestBody DroneControlDto droneControlDto) {
        long seq;
        // 获取请求参数的字符串标识
        String droneControlDtoString = droneControlDto.toString();
        // 将参数的字符串标识 MD5 作为 Redis 的 Key
        String md5 = Md5Util.getMD5(droneControlDtoString);
        log.error(md5);
        String key = "drc:" + droneControlDto.getGateway() + ":" + md5;
        // 检查 Redis 中是否已存在该 key，如果不存在则初始化为 0
        Boolean keyExists = redisTemplate.hasKey(key);
        if (!keyExists) {
            // 如果 Redis 中没有该 key，手动设置为 0
            redisTemplate.opsForValue().set(key, "0");
            seq = 0;
        } else {
            // 使用 Redis 原子性操作 INCRBY 增加值，如果键不存在则初始化为 0，存在则加 1
            seq = redisTemplate.opsForValue().increment(key, 1);
        }
        // 设置 key 为 seq
        redisTemplate.opsForValue().set(key, String.valueOf(seq));
        // 生成需要发布的消息
        String gateway = droneControlDto.getGateway();
        String topic = DRC_SERVICES_TOPIC.replace("?", gateway);
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.DRONE_CONTROL, gateway);
        // 构建参数
        Map<String, Object> params = new HashMap<>();
        params.put("seq", seq);
        params.put("x", droneControlDto.getX());
        params.put("y", droneControlDto.getY());
        params.put("h", droneControlDto.getH());
        params.put("w", droneControlDto.getW());
        // 生成消息内容
        String payload = PayloadUtil.generatePayload(commonFieldVo, params);
        List<String> list = new ArrayList<>();
        list.add("gateway");
        list.add("tid");
        list.add("bid");
        try {
            payload = JsonKeyRemover.removeKeysFromJson(list, payload);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        GatewayOperationLog gatewayOperationLog = new GatewayOperationLog();
        gatewayOperationLog.setGatewayId(gateway);
        gatewayOperationLog.setSendParams(payload);
        gatewayOperationLog.setOperationTime(new Date());
        gatewayOperationLogService.insertGatewayOperationLog(gatewayOperationLog);

        log.info("操作飞行参数:{}", payload);
        // 异步发布 MQTT 消息、接口直接先返回
        final String finalTopic = topic; // 声明为 final
        final String finalPayload = payload; // 声明为 final
        CompletableFuture.runAsync(() -> {
            djiCloudMQTTClient.publishMessageWithDrc(finalTopic, finalPayload, commonFieldVo);
        });

        return AjaxResult.success();
    }


    /**
     * DRC-飞行器急停
     *
     * @param droneEmergencyStop
     * @return
     */
    @ApiOperation("DRC-飞行器急停")
    @PostMapping("/droneEmergencyStop")
    public AjaxResult droneEmergencyStop(@RequestBody DroneEmergencyStop droneEmergencyStop) {
        String gateway = droneEmergencyStop.getGateway();
        String topic = DRC_SERVICES_TOPIC.replace("?", droneEmergencyStop.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.DRONE_EMERGENCY_STOP, droneEmergencyStop.getGateway());

        String payload = PayloadUtil.generatePayload(commonFieldVo, null);
        List<String> list = new ArrayList<>();
        list.add("gateway");
        list.add("tid");
        list.add("bid");
        try {
            payload = JsonKeyRemover.removeKeysFromJson(list, payload);
            log.error("急停发送数据:{}", payload);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        String response = djiCloudMQTTClient.publishMessageWithDrc(topic, payload, commonFieldVo);
        AjaxResult ajaxResult = HandleResultUtil.handleResult(response, null, "飞行器急停成功");

        // 记录日志
        GatewayOperationLogUtil.generateLog(gateway, commonFieldVo.getMethod(), payload, response);
        return ajaxResult;
    }

    @NotNull
    private AjaxResult heart(HeartBeatVo heartBeatVo) {
        String key = "drc:" + heartBeatVo.getGateway() + ":" + "heart";

        // 使用 Redis 的 INCR 命令进行原子性递增
        Long seq = redisTemplate.opsForValue().increment(key, 1);  // 如果没有该 key，会默认设置为 0 然后加 1

        // 生成 topic 和 commonFieldVo
        String gateway = heartBeatVo.getGateway();
        String topic = DRC_SERVICES_TOPIC.replace("?", heartBeatVo.getGateway());
        CommonFieldsVo commonFieldVo = CommonFieldsVo.createCommonFieldVo(CmdConstant.HEART_BEAT, heartBeatVo.getGateway());

        // 构建请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("timestamp", System.currentTimeMillis());

        String payload = PayloadUtil.generatePayload(commonFieldVo, params);
        List<String> keysToRemove = new ArrayList<>();
        keysToRemove.add("gateway");
        keysToRemove.add("tid");
        keysToRemove.add("bid");

        try {
            // 移除不必要的键并添加 seq 字段
            payload = JsonKeyRemover.removeKeysFromJson(keysToRemove, payload);
            payload = JsonKeyRemover.addKeyToRoot(payload, "seq", seq.intValue());
            log.error("心跳发送内容: {}", payload);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }

        // 发布消息到 MQTT
        DrcVo drcVo = new DrcVo();
        drcVo.setMethod(CmdConstant.HEART_BEAT);
        String response = djiCloudMQTTClient.publishMessageWithDrc(topic, payload, commonFieldVo);

        // 记录日志
        GatewayOperationLogUtil.generateLog(gateway, commonFieldVo.getMethod(), payload, response);

        // 返回响应
        return AjaxResult.success("心跳发送成功", response);
    }
}
