package com.multictrl.modules.business.handler;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.multictrl.common.config.MinioConfig;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.utils.*;
import com.multictrl.modules.business.dao.HmsDao;
import com.multictrl.modules.business.dto.MediaFileDTO;
import com.multictrl.modules.business.entity.HmsEntity;
import com.multictrl.modules.business.entity.RemoteLogEntity;
import com.multictrl.modules.business.influxdb.FlightLog;
import com.multictrl.modules.business.service.CommandService;
import com.multictrl.modules.business.service.InfluxService;
import com.multictrl.modules.business.service.MediaFileService;
import com.multictrl.modules.business.service.MqttPushService;
import com.multictrl.modules.business.service.RemoteLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * events事件消息处理
 *
 * @author Sdy
 * @since 1.0.0 2026/4/27
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class EventsHandler implements MessageHandler {
    private final MqttPushService mqttPushService;
    private final CommandService commandService;
    private final MediaFileService mediaFileService;
    private final RemoteLogService remoteLogService;
    private final MinioConfig minioConfig;
    private final HmsDao hmsDao;
    private final InfluxService influxService;

    @Override
    public void handleMessage(String topic, String payload, String gateway) {
//        log.debug("events --> topic: {}, payload: {}", topic, payload);
        JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
        if (message != null) {
            //不能写在这里，有airsense_warning(航班告警)，data为list，无需回复
//            JSONObject data = message.getJSONObject(BusinessConstant.DATA);
            Integer needReply = message.getInt(BusinessConstant.NEED_REPLY);
            if (needReply != null && needReply == 1) {
                log.debug("events --> 需要回复，topic: {}, payload: {}", topic, message);
                JSONObject data;
                Object dataObject = message.get(BusinessConstant.DATA);
                if (dataObject instanceof JSONObject) {
                    data = (JSONObject) dataObject;
                } else /*if(dataObject instanceof JSONArray)*/ {
                    data = new JSONObject();
                    message.set(BusinessConstant.DATA, data);
                }
                data.clear();
                data.set("result", 0);
                message.remove(BusinessConstant.NEED_REPLY);
                message.remove(BusinessConstant.GATEWAY);
                mqttPushService.pushMessageByClient1(topic + BusinessConstant._REPLY, message.toString());
                log.debug("events --> 回复成功，topic: {}, payload: {}", topic + BusinessConstant._REPLY, message);
            }
            message = JsonUtils.parseObject(payload, JSONObject.class);
            if (message == null) {
                return;
            }
            String method = message.getStr(BusinessConstant.METHOD);
            if (BusinessConstant.FLIGHTTASK_PROGRESS.equals(method)) {//航线任务进度上报
                log.debug("events --> 航线任务进度上报: {}", payload);
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                //缓存起来，定频发送给web端
                CacheUtils.set(BusinessConstant.FLIGHT_TASK_PROGRESS + gateway, payload);
                if (data != null && data.containsKey("output")) {
                    JSONObject output = data.getJSONObject("output");
                    if (output.containsKey("status")) {
                        CacheUtils.set(BusinessConstant.FLIGHT_TASK_PROGRESS_STATUS + gateway, output.getStr("status"));
                    }
                    if (output.containsKey("ext")) {
                        JSONObject ext = output.getJSONObject("ext");
                        CacheUtils.set(BusinessConstant.FLIGHT_TASK_PROGRESS_FLIGHT_ID + gateway, ext.getStr("flight_id"));
                    }
                }
                //飞行日志
                saveFlightTaskProgressLog(message, data, gateway);
            } else if (BusinessConstant.TAKEOFF_TO_POINT_PROGRESS.equals(method)) {
                log.debug("events --> 一键起飞结果上报: {}", payload);
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                String status = data.getStr("status");
                if ("wayline_ok".equals(status)) {
                    log.debug("收到一键起飞到点通知：{}争夺控制", gateway);
                    commandService.seizingControl(gateway);
                }
            } else if (BusinessConstant.FLY_TO_POINT_PROGRESS.equals(method)) {
                log.debug("events --> flyto执行结果上报: {}", payload);
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                String status = data.getStr("status");
                if ("wayline_ok".equals(status)) {
                    log.debug("收到flyto到点通知：{}争夺控制", gateway);
                    commandService.seizingControl(gateway);
                }
            } else if (BusinessConstant.FILE_UPLOAD_CALLBACK.equals(method)) {
                log.debug("events --> 文件上传上报: {}", payload);
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                MediaFileDTO mediaFile = new MediaFileDTO();
                if (data.containsKey("file")) {
                    JSONObject file = data.getJSONObject("file");
                    if (file == null) {
                        return;
                    }
                    String name = file.getStr("name");
                    if (!name.contains(".jpeg") && !name.contains(".mp4")) {//不接收MRK NAV OBS RTK数据包
                        return;
                    }
                    mediaFile.setName(name);
                    mediaFile.setPath(file.getStr("path"));
                    JSONObject ext = file.getJSONObject("ext");
                    if (ext != null) {
                        mediaFile.setTaskId(ext.getStr("flight_id"));
                        mediaFile.setOriginal(ext.getBool("is_original"));
                        mediaFile.setPayloadModelKey(ext.getStr("payload_model_key"));
                        mediaFile.setDroneModelKey(ext.getStr("drone_model_key"));
                    }
                    JSONObject metadata = file.getJSONObject("metadata");
                    if (metadata != null) {
                        mediaFile.setAbsoluteAltitude(metadata.getStr("absolute_altitude"));
                        mediaFile.setGimbalYawDegree(metadata.getStr("gimbal_yaw_degree"));
                        if (metadata.containsKey("shoot_position")) {
                            mediaFile.setLatitude(metadata.getJSONObject("shoot_position").getStr("lat"));
                            mediaFile.setLongitude(metadata.getJSONObject("shoot_position").getStr("lng"));
                        }
                        mediaFile.setRelativeAltitude(metadata.getStr("relative_altitude"));
                        String createdTime = metadata.getStr("created_time");
                        if (StrUtil.isNotBlank(createdTime)) {
                            OffsetDateTime offsetDateTime = OffsetDateTime.parse(createdTime);
                            mediaFile.setCreatedTime(Date.from(offsetDateTime.toInstant()));
                        }
                    }

                    if (file.containsKey("object_key")) {
                        mediaFile.setObjectKey(file.getStr("object_key"));
                        String filePath = minioConfig.getOther().getDataPath() + "/" + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + mediaFile.getObjectKey();
                        long time = 0;
                        if (mediaFile.getObjectKey().contains(".jpeg")) {
                            //获取文件的subFileType,用来区分是正常的图片还是全景图(pano是全景图)
                            mediaFile.setSubFileType(Utils.getImageWindowsXpKeywords(filePath));
                            time = Utils.getImageTime(filePath);
                        } else if (mediaFile.getObjectKey().contains(".mp4")) {
                            time = Utils.getVideoTime(filePath);
                            mediaFile.setSubFileType("video");
                            //使用ffmpeg从视频中取一帧转换为图片，作为视频的封面
                            FfmpegUtils.generateVideoCover(filePath, filePath.replace(".mp4", "_cover.jpeg"));
                        }
                        log.info("time:{}", time);
                        if (time > 0) {
                            mediaFile.setCreatedTime(new Date(time));
                        }
                    }
                }
                mediaFile.setDockSn(gateway);
                mediaFileService.save(mediaFile);
            } else if (BusinessConstant.HMS.equals(method)) {
//                log.debug("events --> hms上报: {}", payload);
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                String dockSn = message.getStr(BusinessConstant.GATEWAY);
                if (data != null) {
                    JSONArray list = data.getJSONArray("list");
                    if (list != null) {
                        List<HmsEntity> hmsList = new ArrayList<>();
                        for (Object object : list) {
                            JSONObject hms = (JSONObject) object;
                            HmsEntity hmsEntity = new HmsEntity();
                            hmsEntity.setGateway(gateway);
                            hmsEntity.setDockSn(dockSn);
                            hmsEntity.setDeviceType(hms.getStr("device_type"));
                            hmsEntity.setCode(hms.getStr("code"));
                            //是否为及时性的 {"0":"否","1":"是"}
                            hmsEntity.setImminent(hms.getInt("imminent") == 1 ? "是" : "否");
                            //是否飞行 {"0":"在地上","1":"在天上"}
                            hmsEntity.setInTheSky(hms.getInt("in_the_sky") == 1 ? "在天上" : "在地上");
                            //告警等级 {"0":"通知","1":"提醒","2":"警告"}
                            hmsEntity.setLevel(hms.getInt("level"));
                            //事件模块 {"0":"飞行任务","1":"设备管理","2":"媒体","3":"hms"}
                            int module = hms.getInt("module");
                            hmsEntity.setModule(module == 0 ? "飞行任务" : module == 1 ? "设备管理" : module == 2 ? "媒体" : "hms");
                            hmsEntity.setReportDate(new Date(message.getLong("timestamp")));
                            hmsList.add(hmsEntity);
                        }
                        hmsDao.insert(hmsList);
                        //飞行日志
                        for (HmsEntity hmsEntity : hmsList) {
                            FlightLog flightLog = new FlightLog();
                            flightLog.setDockSn(hmsEntity.getDockSn());
                            flightLog.setTime(hmsEntity.getReportDate().toInstant());
                            flightLog.setMethod("健康告警\n" + BusinessConstant.HMS);
                            flightLog.setLevel(hmsEntity.getLevel());
                            flightLog.setMessage(
                                    "模块: " + hmsEntity.getModule() + "\n" +
                                            "告警码: " + hmsEntity.getCode() + "\n" +
                                            "告警信息: " + HmsUtils.getHmsText(hmsEntity.getCode()) + "\n" +
                                            "设备类型: " + hmsEntity.getDeviceType() + "\n" +
                                            "即时性: " + hmsEntity.getImminent() + "\n" +
                                            "飞行状态: " + hmsEntity.getInTheSky());
                            influxService.addRecord(flightLog);
                        }
                    }
                }
            } else if (BusinessConstant.FILEUPLOAD_PROGRESS.equals(method)) {
                log.debug("events --> 远程日志上报: {}", payload);
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                if (!data.containsKey("output") || !data.getJSONObject("output").containsKey("ext") ||
                        !data.getJSONObject("output").getJSONObject("ext").containsKey("files")) {
                    log.debug("无文件上传进度");
                    return;
                }
                JSONArray files = data.getJSONObject("output").getJSONObject("ext").getJSONArray("files");
                for (int i = 0; i < files.size(); i++) {
                    JSONObject file = files.getJSONObject(i);
                    JSONObject progress = file.getJSONObject("progress");
                    if (progress == null) {
                        log.debug("无文件上传进度,{},跳过", file);
                        continue;
                    }
                    String key = file.getStr("key");
                    String[] keys = key.split("/");
                    if (keys.length < 3) {
                        log.debug("文件路径错误,{},跳过", key);
                        continue;
                    }
                    RemoteLogEntity remoteLogEntity = remoteLogService.getDao().selectOne(
                            new QueryWrapper<RemoteLogEntity>().eq("dock_sn", keys[0])
                                    .eq("module", keys[1])
                                    .eq("filename", keys[2]));
                    if (remoteLogEntity == null) {
                        log.debug("无此文件,{},跳过", key);
                        continue;
                    }
                    remoteLogEntity.setStatus(progress.getStr("status"));
                    remoteLogEntity.setCurrentStep(progress.getInt("current_step", -1));
                    remoteLogEntity.setTotalStep(progress.getInt("total_step", -1));
                    int uploadRate = progress.getInt("upload_rate", 0);
                    if (uploadRate > 0) {
                        remoteLogEntity.setUploadRate(uploadRate);
                    }
                    remoteLogEntity.setSize(file.getLong("size") != null ? file.getLong("size") : -1);
                    remoteLogEntity.setCode(progress.getInt("result", -1));
                    if ("ok".equals(progress.getStr("status"))) {
                        remoteLogEntity.setPath(key);
                    }
                    remoteLogService.getDao().updateById(remoteLogEntity);
                }
            } else if (BusinessConstant.OTA_PROGRESS.equals(method)) {
                log.debug("events --> 固件升级进度: {}", payload);
            } else if (BusinessConstant.FLIGHTTASK_READY.equals(method)) {
                log.debug("events --> 航线任务准备就绪: {}", payload);
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                if (data != null) {
                    JSONArray flightIds = data.getJSONArray("flight_ids");
                    //飞行日志
                    FlightLog flightLog = new FlightLog();
                    flightLog.setDockSn(gateway);
                    flightLog.setLevel(0);
                    flightLog.setMethod("任务准备就绪\n" + BusinessConstant.FLIGHTTASK_READY);
                    flightLog.setTime(Instant.ofEpochMilli(message.getLong("timestamp")));
                    flightLog.setMessage("航线任务准备就绪:" + flightIds.toString());
                    influxService.addRecord(flightLog);
                }
            } else if (BusinessConstant.RETURN_HOME_INFO.equals(method)) {
                log.debug("events --> 返航信息: {}", payload);
                //飞行日志 飞机一起飞就会上报返航信息，所以需要先判断飞机返航 才能保存返航信息防止信息错乱
                Object modeCode = CacheUtils.get(BusinessConstant.UAV_MODE_CODE + gateway);
                if (modeCode instanceof Integer && (Integer) modeCode == 9) {
                    JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                    if (data != null) {
                        FlightLog flightLog = new FlightLog();
                        flightLog.setDockSn(gateway);
                        flightLog.setTime(Instant.ofEpochMilli(message.getLong("timestamp")));
                        flightLog.setMethod("返航信息\n" + BusinessConstant.RETURN_HOME_INFO);
                        flightLog.setLevel(0);

                        int lastPointType = data.getInt("last_point_type", -1);
                        String lastPointTypeCn = lastPointType == 0 ? "轨迹最后一个点在返航点的上空"
                                : lastPointType == 1 ? "轨迹最后一个点不在返航点上空" : lastPointType == -1 ? "飞机降落完成" : String.valueOf(lastPointType);

                        StringBuilder msg = new StringBuilder();
                        msg.append("任务ID: ").append(data.getStr("flight_id", "")).append("\n");
                        msg.append("最后轨迹点类型: ").append(lastPointTypeCn).append("\n");
                        if (data.containsKey("home_dock_sn")) {
                            msg.append("Home点机场SN: ").append(data.getStr("home_dock_sn")).append("\n");
                        }
                        JSONArray points = data.getJSONArray("planned_path_points");
                        if (points != null && !points.isEmpty()) {
                            msg.append("规划轨迹点(").append(points.size()).append("个):\n");
                            for (int i = 0; i < points.size(); i++) {
                                JSONObject point = points.getJSONObject(i);
                                msg.append(i + 1).append(". 纬度: ").append(point.getDouble("latitude", 0d))
                                        .append(" 经度: ").append(point.getDouble("longitude", 0d))
                                        .append(" 高度: ").append(point.getDouble("height", 0d)).append("m\n");
                            }
                        }
                        JSONArray homeInfo = data.getJSONArray("multi_dock_home_info");
                        if (homeInfo != null && !homeInfo.isEmpty()) {
                            msg.append("蛙跳任务返航信息:\n");
                            for (int i = 0; i < homeInfo.size(); i++) {
                                JSONObject info = homeInfo.getJSONObject(i);
                                int planStatus = info.getInt("plan_status", -1);
                                String planStatusCn = planStatus == 0 ? "规划失败或正在规划中"
                                        : planStatus == 1 ? "规划路径不可达"
                                        : planStatus == 2 ? "规划路径因电量不可达"
                                        : planStatus == 3 ? "目标可达" : String.valueOf(planStatus);
                                msg.append("\t机场SN: ").append(info.getStr("sn", "")).append("\n");
                                msg.append("\t路径规划状态: ").append(planStatusCn).append("\n");
                                msg.append("\t预估电量消耗: ").append(info.getInt("estimated_battery_consumption", 0)).append("%\n");
                                msg.append("\tHome点距离: ").append(info.getDouble("home_distance", 0d)).append("m\n");
                            }
                        }
                        flightLog.setMessage(msg.toString());
                        influxService.addRecord(flightLog);
                    }
                }
            } else if (BusinessConstant.DEVICE_EXIT_HOMING_NOTIFY.equals(method)) {
                log.debug("events --> 设备返航退出状态通知: {}", payload);
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                //飞行日志
                if (data != null) {
                    Long timestamp = message.getLong("timestamp");
                    String dockSn = data.getStr("sn");
                    String action = data.getInt("action") == 0 ? "退出返航退出状态" : "进入返航退出状态";
                    Integer reason = data.getInt("reason");
                    String reasonCn = switch (reason) {
                        case 0 -> "操纵杆油门添加";
                        case 1 -> "操纵杆间距添加";
                        case 2 -> "行为树初始化失败";
                        case 3 -> "被障碍物包围";
                        case 4 -> "触发限飞限制";
                        case 5 -> "障碍物距离太近";
                        case 6 -> "无 GPS 信号";
                        case 7 -> "GPS 和 VIO 位置输出标志为 false";
                        case 8 -> "GPS 和 VIO 融合位置误差太大";
                        case 9 -> "短距离回溯";
                        case 10 -> "近距离触发返航";
                        default -> reason + "";
                    };
                    FlightLog flightLog = new FlightLog();
                    flightLog.setDockSn(dockSn);
                    flightLog.setLevel(0);
                    flightLog.setTime(Instant.ofEpochMilli(timestamp));
                    flightLog.setMethod("设备返航退出状态通知\n" + BusinessConstant.DEVICE_EXIT_HOMING_NOTIFY);
                    flightLog.setMessage(
                            "消息类型: " + action + "\n" +
                                    "退出返航原因: " + reasonCn
                    );
                }
            } else if (BusinessConstant.IN_FLIGHT_WAYLINE_PROGRESS.equals(method)) {
                log.debug("events --> 空中下发航线状态上报: {}", payload);
                String inFlightWaylineId = message.getStr("in_flight_wayline_id");
                Integer percent = null;
                JSONObject progress = message.getJSONObject("progress");
                if (progress != null) {
                    percent = progress.getInt("percent");
                }
                Integer status = message.getInt("status");
                String statusCn = switch (status) {
                    case 1 -> "上传文件中";
                    case 2 -> "文件上传成功";
                    case 3 -> "执行任务中";
                    case 4 -> "任务暂停";
                    case 5 -> "任务取消";
                    case 6 -> "任务成功";
                    case 7 -> "任务失败";
                    case 8 -> "任务超时";
                    default -> status + "";
                };
                Integer result = message.getInt("result");
                Integer wayPointIndex = message.getInt("way_point_index");
                FlightLog flightLog = new FlightLog();
                flightLog.setDockSn(gateway);
                flightLog.setLevel(0);
                flightLog.setMethod("空中下发航线状态上报\n" + BusinessConstant.IN_FLIGHT_WAYLINE_PROGRESS);
                flightLog.setMessage(
                        "航线任务ID: " + inFlightWaylineId + "\n" +
                                "任务进度: " + percent + "\n" +
                                "任务状态: " + statusCn + "\n" +
                                "错误原因码: " + result + "\n" +
                                "当前航点索引: " + wayPointIndex
                );
                influxService.addRecord(flightLog);
            } else if (BusinessConstant.OBSTACLE_AVOIDANCE_NOTIFY.equals(method)) {
                log.debug("events --> 避障记录上报事件通知: {}", payload);
                //飞行日志
                JSONObject data = message.getJSONObject(BusinessConstant.DATA);
                if (data != null) {
                    String waylineUuid = data.getStr("wayline_uuid", "");
                    String flightId = data.getStr("flight_id", "");
                    boolean isFinalReport = data.getBool("is_final_report", false);
                    JSONArray obstacles = data.getJSONArray("obstacles");
                    StringBuilder msg = new StringBuilder();
//                    msg.append("航线ID: ").append(waylineUuid).append("\n");
                    msg.append("任务ID: ").append(flightId).append("\n");
                    if (obstacles != null && !obstacles.isEmpty()) {
                        msg.append("避障记录(").append(obstacles.size()).append("条):\n");
                        for (int i = 0; i < obstacles.size(); i++) {
                            JSONObject obstacle = obstacles.getJSONObject(i);
                            int type = obstacle.getInt("type", -1);
                            String typeCn = switch (type) {
                                case 0 -> "绕行开始（飞控映射到航线上）";
                                case 1 -> "绕行结束（飞控映射到航线上）";
                                case 2 -> "避障刹停（真实物理坐标）";
                                default -> String.valueOf(type);
                            };
                            msg.append(i + 1).append(". 障碍物ID: ").append(obstacle.getInt("id", -1))
                                    .append(" 类型: ").append(typeCn)
                                    .append(" 时间戳: ").append(obstacle.getLong("timestamp", 0L))
                                    .append("\n   纬度: ").append(obstacle.getDouble("latitude", 0d))
                                    .append(" 经度: ").append(obstacle.getDouble("longitude", 0d))
                                    .append(" 高度: ").append(obstacle.getDouble("height", 0d)).append("m")
                                    .append("\n   航段ID: ").append(obstacle.getInt("wayline_id", -1))
                                    .append(" 航点索引: ").append(obstacle.getInt("waypoint_index", -1))
                                    .append("\n");
                        }
                    }
                    msg.append("是否最后一条: ").append(isFinalReport ? "是" : "否");
                    FlightLog flightLog = new FlightLog();
                    flightLog.setDockSn(gateway);
                    flightLog.setLevel(0);
                    flightLog.setMethod("避障记录上报\n" + BusinessConstant.OBSTACLE_AVOIDANCE_NOTIFY);
                    flightLog.setTime(Instant.ofEpochMilli(message.getLong("timestamp")));
                    flightLog.setMessage(msg.toString());
                    influxService.addRecord(flightLog);
                }
            }
        } else {
            log.debug("events --> payload解析失败，解析后为null");
        }
    }

    /**
     * 保存航线任务进度飞行日志
     */
    private void saveFlightTaskProgressLog(JSONObject message, JSONObject data, String gateway) {
        if (data == null || !data.containsKey("output")) {
            return;
        }
        JSONObject output = data.getJSONObject("output");
        FlightLog flightLog = new FlightLog();
        flightLog.setDockSn(gateway);
        flightLog.setTime(Instant.ofEpochMilli(message.getLong("timestamp")));
        flightLog.setMethod("航线任务进度\n" + BusinessConstant.FLIGHTTASK_PROGRESS);
        flightLog.setLevel(0);

        String status = output.getStr("status", "");
        String statusCn = switch (status) {
            case "canceled" -> "取消或终止";
            case "failed" -> "失败";
            case "in_progress" -> "执行中";
            case "ok" -> "执行成功";
            case "partially_done" -> "部分完成";
            case "paused" -> "暂停";
            case "rejected" -> "拒绝";
            case "sent" -> "已下发";
            case "timeout" -> "超时";
            default -> status;
        };
        int currentStep = -1, percent = -1;
        if (output.containsKey("progress")) {
            JSONObject progress = output.getJSONObject("progress");
            currentStep = progress.getInt("current_step", -1);
            percent = progress.getInt("percent", -1);
        }
        String[] stepCn = {
                "初始状态", "启动前检查，飞行器是否在执行航线中", "启动前检查，机场是否退出工作模式",
                "启动前检查，航线执行中", "启动前检查，返航中", "航线执行进入准备状态，开始等待任务下发",
                "机场进入工作状态", "进入开机检查准备工作和开盖准备工作", "图传远程对频",
                "等待飞行系统准备就绪，推送连接建立", "等待 RTK 源监听有值上报",
                "检查 RTK 源是否是机场源，如果不是要重新设置", "等待飞行控制权通知",
                "机场无控制权，抢夺飞行器控制权", "自定义飞行区一致性检查", "离线地图一致性检查",
                "获取最新 KMZ URL", "下载 KMZ", "KMZ 上传中", "染色配置",
                "飞行器起飞参数设置，备降点设置，起飞高度设置，染色设置",
                "飞行器 flyto 起飞参数设置", "起飞机场检查降落机场准备状态", "Home 点设置",
                "触发执行航线", "航线执行中", "进入返航的检查准备工作", "飞行器降落机场",
                "降落以后的关盖", "机场退出工作模式", "机场异常恢复", "机场上传飞行系统日志",
                "相机录像状态检查", "获取媒体文件数量", "机场起飞开盖的异常恢复", "通知任务结果",
                "日志列表拉取 - 飞行器列表", "日志列表拉取 - 拉取机场列表",
                "日志列表拉取 - 上传日志列表结果", "日志拉取-拉取飞行器日志",
                "日志拉取-拉取机场日志", "日志拉取-压缩飞行器日志", "日志拉取-压缩机场日志",
                "日志拉取-上传飞行器日志", "日志拉取-上传机场日志", "日志拉取-通知结果",
                "自定义飞行区文件更新准备中", "自定义飞行区更新中", "离线地图更新准备中", "离线地图更新中"
        };
        String stepStr;
        if (currentStep > 6000) {
            stepStr = currentStep == 65533 ? "结束后等待服务回包" : currentStep == 65534 ? "无具体状态" : "UNKNOWN";
        } else {
            stepStr = currentStep >= 0 && currentStep < stepCn.length ? stepCn[currentStep] : String.valueOf(currentStep);
        }

        StringBuilder msg = new StringBuilder();
//        msg.append("任务状态: ").append(statusCn).append("\n");
//        msg.append("执行步骤: ").append(stepStr).append("\n");
//        msg.append("进度: ").append(percent).append("%\n");
        if (output.containsKey("ext")) {
            JSONObject ext = output.getJSONObject("ext");
            int missionState = ext.getInt("wayline_mission_state", -1);
            String[] missionStateCn = {"断连", "不支持该航点", "航线准备状态,可上传文件,可执行已有文件", "航线文件上传中",
                    "触发开始命令，飞行器触发读航线等逻辑，还未开始任务，处于准备状态", "进入航线，到第一个航点", "航线执行",
                    "航线中断，触发条件：1.用户主动暂停 2.飞控异常", "航线恢复", "航线停止"};
//            msg.append("航线ID: ").append(ext.getInt("wayline_id", -1)).append("\n");
            msg.append("任务ID: ").append(ext.getStr("flight_id", "")).append("\n");
            String trackId = ext.getStr("track_id", "");
            if (StrUtil.isNotBlank(trackId)) {
                msg.append("航迹ID: ").append(trackId).append("\n");
            }
            msg.append("任务状态: ").append(statusCn).append("\n");
            msg.append("航线状态: ").append(missionState >= 0 && missionState < missionStateCn.length ? missionStateCn[missionState] : missionState).append("\n");
            msg.append("当前航点: ").append(ext.getInt("current_waypoint_index", -1)).append("\n");
            msg.append("进度: ").append("\n");
            msg.append("\t执行步骤: ").append(stepStr).append("\n");
            msg.append("\t进度值: ").append(percent).append("%\n");
            if (!"in_progress".equals(status)) {
                msg.append("媒体文件数: ").append(ext.getInt("media_count", 0)).append("\n");
            }
            if (ext.containsKey("break_point")) {
                appendBreakPointInfo(msg, ext.getJSONObject("break_point"));
            }
        }
        flightLog.setMessage(msg.toString());
        influxService.addRecord(flightLog);
    }

    /**
     * 拼接断点信息
     */
    private void appendBreakPointInfo(StringBuilder msg, JSONObject bp) {
        int bpState = bp.getInt("state", -1);
        int breakReason = bp.getInt("break_reason", 0);
        msg.append("断点信息:\n");
        msg.append("\t断点序号: ").append(bp.getInt("index", -1)).append("\n");
        msg.append("\t断点状态: ").append(bpState == 0 ? "在航段上" : bpState == 1 ? "在航点上" : bpState).append("\n");
        msg.append("\t航段进度: ").append(bp.getDouble("progress", 0d)).append("\n");
//        msg.append("\t航线ID: ").append(bp.getInt("wayline_id", -1)).append("\n");
        msg.append("\t中断原因: ").append(getBreakReasonCn(breakReason)).append("\n");
        msg.append("\t纬度: ").append(bp.getDouble("latitude", 0d)).append("\n");
        msg.append("\t经度: ").append(bp.getDouble("longitude", 0d)).append("\n");
        msg.append("\t高度: ").append(bp.getDouble("height", 0d)).append("m\n");
        msg.append("\t偏航角: ").append(bp.getDouble("attitude_head", 0d));
    }

    /**
     * 中断原因枚举转中文
     */
    private static String getBreakReasonCn(int breakReason) {
        return switch (breakReason) {
            case 0 -> "无异常";
            case 1 -> "Mission ID 不存在，该航线任务未执行";
            case 2 -> "不常见错误，建议联系技术支持";
            case 4 -> "请求开始/恢复航线任务时，航线文件加载出错，请重新尝试上传文件开始或联系技术支持";
            case 5 -> "请求查询断点信息时，查询断点文件失败。请求恢复航线任务时，解析断点类型失败";
            case 6 -> "请求开始/结束航线任务时，cmd参数有误，协议请求的指令有误。请求恢复航线任务时，解析断点类型失败";
            case 7 -> "请求开始/恢复航线任务时，解析 wpmz 文件超时，请重试";
            case 257 -> "航线已经开始，不能再次开始";
            case 258 -> "此状态下无法中断航线，只允许在航线执行状态时暂停航线";
            case 259 -> "航线未开始，不能结束航线";
            case 261 -> "飞行任务冲突，无法获取飞行器控制权，不允许在降落和返航中开始航线";
            case 262 -> "该状态下无法恢复航线，只允许在航线暂停状态时恢复航线";
            case 513 -> "飞行器超过限高高度";
            case 514 -> "飞行器超过限远距离";
            case 515 -> "航线穿过限飞区，机场无法执行飞行任务";
            case 516 -> "飞行器触发限低";
            case 517 -> "飞行器触发避障";
            case 518 -> "RTK 信号差";
            case 519 -> "接近禁飞区边界";
            case 521 -> "超过机场限飞区限高";
            case 522 -> "航线请求起飞失败";
            case 523 -> "起飞任务执行失败";
            case 524 -> "请求航线任务失败";
            case 526 -> "请求航线 RTK 收敛任务失败";
            case 527 -> "航线 RTK 收敛任务运行失败";
            case 769 -> "GPS 信号弱";
            case 770 -> "遥控器档位不在 N 档";
            case 771 -> "返航点未刷新";
            case 772 -> "当前电量过低无法开始任务";
            case 773 -> "低电量返航导致航线中断";
            case 775 -> "遥控器与飞行器失联";
            case 778 -> "飞行器在地面起桨，不允许开始航线";
            case 779 -> "实时仿地过程中，相机状态异常（如过亮，过暗，两侧亮度不一致）";
            case 780 -> "实时仿地用户设置的仿地高度不合法（大于 200 m 或者小于 30 m）";
            case 781 -> "实时仿地过程中全局地图计算出错";
            case 784 -> "大风返航导致航线中断";
            case 1281 -> "用户退出";
            case 1282 -> "用户中断";
            case 1283 -> "用户触发返航";
            case 1539 -> "开始信息（航点 index 或者 progress）错误";
            case 1540 -> "使用不支持的坐标系";
            case 1541 -> "使用不支持的高度模式";
            case 1542 -> "使用不支持的过渡航线模式";
            case 1543 -> "使用不支持的 yaw 模式";
            case 1544 -> "使用不支持的 yaw 方向调转模式";
            case 1545 -> "使用不支持的航点类型";
            case 1546 -> "首尾点不能使用协调转弯类型";
            case 1547 -> "航线全局速度超过合理范围";
            case 1548 -> "航点数量异常";
            case 1549 -> "经纬度数据异常";
            case 1550 -> "转弯截距异常";
            case 1551 -> "航段最大速度超过合理范围";
            case 1552 -> "航段目标速度超过合理范围";
            case 1553 -> "航点 yaw 角度超过合理范围";
            case 1555 -> "断点续飞的 mission_id 输入错误";
            case 1556 -> "断点续飞的 progress 信息输入错误";
            case 1557 -> "断点续飞的任务状态异常";
            case 1558 -> "断点续飞的航点 index 信息输入错误";
            case 1559 -> "断点续飞的经纬度信息输入错误";
            case 1560 -> "断点续飞的航点 yaw 输入错误";
            case 1561 -> "断点续飞的标志位设置错误";
            case 1563 -> "航线生成失败";
            case 1564 -> "航线运行失败";
            case 1565 -> "航线避障紧急刹停";
            case 1588 -> "无法识别的动作类型";
            case 1595 -> "动作 ID 不能重复";
            case 1598 -> "动作 ID 值不能为 65535";
            case 1602 -> "动作组数量超过合理范围";
            case 1603 -> "动作组生效范围错误";
            case 1606 -> "断点续飞中动作 index 超过合理范围";
            case 1608 -> "断点信息中触发器运行结果异常";
            case 1609 -> "断点续飞中动作组 ID 信息不能重复";
            case 1610 -> "断点续飞中动作组位置不能重复";
            case 1611 -> "断点续飞中动作组位置超过合理范围";
            case 1612 -> "续飞中动作 ID 不在断点信息中";
            case 1613 -> "断点续飞中不能修改动作状态为中断";
            case 1614 -> "断点信息错误导致续飞失败";
            case 1634 -> "无法识别的动作类型";
            case 1649 -> "无法识别的触发器类型";
            case 65534 -> "未知内部错误";
            case 65535 -> "未知内部错误";
            default -> String.valueOf(breakReason);
        };
    }
}
