package com.multictrl.modules.business.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.constant.DockMode;
import com.multictrl.common.constant.FlightTaskType;
import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.JsonUtils;
import com.multictrl.modules.business.dao.RouteAiDao;
import com.multictrl.modules.business.dto.NoflyZoneDTO;
import com.multictrl.modules.business.dto.zhimou.ZhiMouAiStart;
import com.multictrl.modules.business.entity.FlightTaskEntity;
import com.multictrl.modules.business.entity.RouteAiEntity;
import com.multictrl.modules.business.influxdb.FlightLog;
import com.multictrl.modules.business.influxdb.UavReport;
import com.multictrl.modules.business.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * osd设备属性定频上报0.5HZ
 *
 * @author Sdy
 * @since 1.0.0 2026/4/24
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class OsdHandler implements MessageHandler {
    private static final String GPS_ZERO = "0.000000";
    private final InfluxService influxService;
    private final FlightTaskService flightTaskService;
    private final DockService dockService;
    private final DJIBaseService djiBaseService;
    private final MqttPushService mqttPushService;
    private final NoflyZoneService noflyZoneService;
    private final ZhiMouService zhiMouService;
    private final RouteAiDao routeAiDao;

    @Override
    public void handleMessage(String topic, String payload, String gateway) {
        JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
        if (message != null) {
            String dockSn = message.getStr(BusinessConstant.GATEWAY);
            JSONObject data = message.getJSONObject(BusinessConstant.DATA);
            if (gateway.equals(dockSn)) {
//                log.debug("dock osd --> topic: {}, payload: {}", topic, payload);
                if (data.containsKey("network_state")) {//网络状态，机库osd分3次上报信息，只有网络状态对程序有用，其他前端展示即可
                    CacheUtils.set(BusinessConstant.DOCK_OSD + dockSn, data, 1000 * 5);
                    //判断飞行结束，从“任务中”切到“空闲“
                    if (data.containsKey("mode_code")) {
                        int modeCode = data.getInt("mode_code", -1);
                        //任务中
                        if (modeCode == DockMode.WORKING.getCode()) {
                            if (CacheUtils.get(BusinessConstant.DOCK_IN_WORK + dockSn) == null) {
                                flightTaskService.updateTaskStart(dockSn);
                                log.debug("{}机库架次开始，更新架次表", dockSn);
                                //更新机库经纬度
                                dockService.updateDockLocation(dockSn, data);
                            }
                            CacheUtils.set(BusinessConstant.DOCK_IN_WORK + dockSn, true);
                        }
                        //刚从“任务中”切到“空闲“，说明架次结束入库了
                        if (modeCode == DockMode.IDLE.getCode()
                                && CacheUtils.get(BusinessConstant.DOCK_IN_WORK + dockSn) != null) {
                            CacheUtils.delete(BusinessConstant.DOCK_IN_WORK + dockSn);
                            log.debug("{}机库架次飞行完成，取消DRC心跳", dockSn);
                            List<String> list = Optional.ofNullable(CacheUtils.get(BusinessConstant.DRC_HEART_BEAT_SN))
                                    .filter(obj -> obj instanceof List)
                                    .map(obj -> (List<String>) obj)
                                    .orElse(Collections.emptyList());
                            if (CollUtil.isNotEmpty(list)) {
                                list.remove(dockSn);
                            }
                            //更新架次表
                            flightTaskService.updateTaskComplete(dockSn);
                            log.debug("{}机库架次结束，更新架次表", dockSn);
                        }
                    }
                }
            } else {
//                log.debug("uav osd --> topic: {}, payload: {}", topic, payload);
                CacheUtils.set(BusinessConstant.UAV_OSD + dockSn, data);
                //保存无人机状态 {"0":"待机","1":"起飞准备","2":"起飞准备完毕","3":"手动飞行","4":"自动起飞","5":"航线飞行","6":"全景拍照","7":"智能跟随","8":"ADS-B 躲避","9":"自动返航","10":"自动降落","11":"强制降落","12":"三桨叶降落","13":"升级中","14":"未连接","15":"APAS","16":"虚拟摇杆状态","17":"指令飞行","18":"空中 RTK 收敛模式","19":"机场选址中","20":"POI环绕","21":"进离场航线飞行过程中"}
                Integer mode = data.getInt("mode_code");
                if (mode != null) {
                    CacheUtils.set(BusinessConstant.UAV_MODE_CODE + dockSn, mode, 1000 * 5);
                }
                //保存osd到influxdb
                String latitude = data.getStr("latitude");
                String longitude = data.getStr("longitude");
                if (latitude != null && longitude != null && !GPS_ZERO.equals(latitude) && !GPS_ZERO.equals(
                        longitude)) {
                    String payload_index = data.getJSONArray("cameras") != null ?
                            data.getJSONArray("cameras").getJSONObject(0).getStr("payload_index") : null;
                    //获取电池电量，如果单电池则把 a赋值给b
                    int capacity_percent_a;
                    int capacity_percent_b;
                    JSONArray batteries = data.getJSONObject("battery").getJSONArray("batteries");
                    capacity_percent_a = batteries.getJSONObject(0).getInt("capacity_percent");
                    if (batteries.size() > 1) {
                        capacity_percent_b = batteries.getJSONObject(1).getInt("capacity_percent");
                    } else {
                        capacity_percent_b = capacity_percent_a;
                    }

                    UavReport uavReport = UavReport.builder()
                            .dockSn(dockSn)
                            .obstacle_avoidance_horizon(data.getJSONObject("obstacle_avoidance").getInt("horizon"))
                            .obstacle_avoidance_upside(data.getJSONObject("obstacle_avoidance").getInt("upside"))
                            .obstacle_avoidance_downside(data.getJSONObject("obstacle_avoidance").getInt("downside"))
                            .is_near_area_limit(data.getInt("is_near_area_limit"))
                            .is_near_height_limit(data.getInt("is_near_height_limit"))
                            .payload_index(payload_index)
                            .gimbal_pitch(
                                    payload_index != null ? data.getJSONObject(payload_index).getDouble("gimbal_pitch") : null)
                            .gimbal_roll(
                                    payload_index != null ? data.getJSONObject(payload_index).getDouble("gimbal_roll") : null)
                            .gimbal_yaw(
                                    payload_index != null ? data.getJSONObject(payload_index).getDouble("gimbal_yaw") : null)
                            .zoom_factor(
                                    payload_index != null ? data.getJSONObject(payload_index).getDouble("zoom_factor") : null)
                            .track_id(data.getStr("track_id"))
                            .position_state_is_fixed(data.getJSONObject("position_state").getInt("is_fixed"))
                            .position_state_is_quality(data.getJSONObject("position_state").getInt("quality"))
                            .position_state_is_gps_number(data.getJSONObject("position_state").getInt("gps_number"))
                            .position_state_is_rtk_number(data.getJSONObject("position_state").getInt("rtk_number"))
                            .battery_capacity_percent(data.getJSONObject("battery").getInt("capacity_percent"))
                            .battery_remain_flight_time(data.getJSONObject("battery").getInt("remain_flight_time"))
                            .battery_return_home_power(data.getJSONObject("battery").getInt("return_home_power"))
                            .battery_landing_power(data.getJSONObject("battery").getInt("landing_power"))
                            .battery_capacity_percent_a(capacity_percent_a)
                            .battery_capacity_percent_b(capacity_percent_b)
                            .wind_direction(data.getInt("wind_direction"))
                            .wind_speed(data.getDouble("wind_speed"))
                            .home_distance(data.getDouble("home_distance"))
                            //state上报
//                                .home_latitude(data.getDouble("home_latitude"))
//                                .home_longitude(data.getDouble("home_longitude"))
                            .attitude_head(data.getInt("attitude_head"))
                            .attitude_roll(data.getDouble("attitude_roll"))
                            .attitude_pitch(data.getDouble("attitude_pitch"))
                            .elevation(data.getDouble("elevation"))
                            .height(data.getDouble("height"))
                            .latitude(data.getDouble("latitude"))
                            .longitude(data.getDouble("longitude"))
                            .vertical_speed(data.getDouble("vertical_speed"))
                            .horizontal_speed(data.getDouble("horizontal_speed"))
                            .build();
                    influxService.addRecord(uavReport);

                    //禁飞区校验
                    Double height = data.getDouble("height");
                    NoflyZoneDTO.point point = new NoflyZoneDTO.point(uavReport.getLongitude(), uavReport.getLatitude(), height);
                    judgingNoFlyZone(dockSn, point, mode);

                    //智眸AI
                    routeAiTask(dockSn, mode);
                }
            }
        } else {
            log.debug("osd --> payload解析失败，解析后为null");
        }
    }

    //禁飞区限制
    // 允许触发返航的飞行模式集合
    //"3":"手动飞行","4":"自动起飞","5":"航线飞行","16":"虚拟摇杆状态","17":"指令飞行"
    private static final Set<Integer> ALLOWED_MODE_CODES = Set.of(3, 4, 5, 16, 17);
    // 按设备隔离的计数器（线程安全）
    private final ConcurrentHashMap<String, AtomicInteger> counterMap = new ConcurrentHashMap<>();
    // 单线程执行器（若需提高并发，可调整线程数或使用同步）
    private final ExecutorService executor = new ThreadPoolExecutor(1, 1, 0L,
            TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));

    private void judgingNoFlyZone(String dockSn, NoflyZoneDTO.point point, Integer modeCode) {
        // 1. 模式过滤（快速失败）
        if (!ALLOWED_MODE_CODES.contains(modeCode)) {
            return;
        }
        // 2. 按设备自增计数器，判断是否需要校验（每3次上报校验一次）
        AtomicInteger deviceCounter = counterMap.computeIfAbsent(dockSn, k -> new AtomicInteger(0));
        int count = deviceCounter.incrementAndGet();
        if (count % 3 != 1) {
            return;
        }
        // 3. 获取禁飞区列表
        Object obj = CacheUtils.get(BusinessConstant.DOCK_NOFLY_ZONE + dockSn);
        if (!(obj instanceof List)) {
            return;
        }
        List<NoflyZoneDTO> noFlyZoneList = (List<NoflyZoneDTO>) obj;
        // 4. 遍历禁飞区，检查是否已触发返航
        String triggerKey = BusinessConstant.DOCK_NOFLY_ZONE_TRIGGER_SIGN + dockSn;
        for (NoflyZoneDTO dto : noFlyZoneList) {
            // 先判断是否已在触发中（快速路径）
            if (CacheUtils.get(triggerKey) != null) {
                return;
            }
            // 执行点是否在禁飞区内
            if (noflyZoneService.isInNoFlyZone(dto.getExtent(), point)) {
                // 原子设置触发标记，防止并发重复触发
                if (trySetTrigger(triggerKey)) {
                    // 异步执行返航任务
                    CompletableFuture.runAsync(() -> executeReturnHome(dockSn, dto), executor);
                }
                // 只要命中一个禁飞区就停止遍历
                return;
            }
        }
    }

    /**
     * 原子设置触发标记（使用缓存框架的 putIfAbsent 或本地锁）
     * 返回 true 表示当前线程成功设置，false 表示已被其他线程设置
     */
    private boolean trySetTrigger(String key) {
        synchronized (this) {
            if (CacheUtils.get(key) == null) {
                CacheUtils.set(key, true, 1000 * 60);
                return true;
            }
            return false;
        }
    }

    /**
     * 执行返航并推送状态（抽离重复逻辑）
     */
    private void executeReturnHome(String dockSn, NoflyZoneDTO dto) {
        String zoneName = dto.getName();
        // 1. 发送进入禁飞区通知（type=0）
        logAndPush(dockSn, zoneName, 0, "飞机进入禁飞区，强制返航");

        try {
            // 2. 执行返航指令
            djiBaseService.executeAndReturnResult(dockSn, "return_home");
            // 3. 发送成功通知（type=1）
            logAndPush(dockSn, zoneName, 1, "飞机进入禁飞区，强制返航成功");
        } catch (Exception e) {
            // 记录异常堆栈，便于排查
            log.error("返航失败，dockSn={}, zone={}", dockSn, zoneName, e);
            // 4. 发送失败通知（type=2）
            logAndPush(dockSn, zoneName, 2, "飞机进入禁飞区，强制返航失败，请手动返航");
        }
    }

    /**
     * 统一记录飞行日志并推送 MQTT 消息
     */
    private void logAndPush(String dockSn, String zoneName, int type, String msg) {
        // 记录 InfluxDB 日志
        FlightLog flightLog = new FlightLog();
        flightLog.setDockSn(dockSn);
        flightLog.setLevel(2);
        flightLog.setMessage(msg);
        influxService.addRecord(flightLog);

        // 推送 MQTT 事件
        JSONObject data = new JSONObject();
        data.set("name", zoneName);
        data.set("type", type);
        data.set("msg", msg);
        JSONObject payload = djiBaseService.getPayload(BusinessConstant.NOFLY_ZONE_METHOD, data);
        mqttPushService.pushMessageByClient1(BusinessConstant.WEB_EVENT_TOPIC.formatted(dockSn), payload.toString());
    }

    /**
     * 航线AI任务（智眸）判断开启
     * {"0":"待机","1":"起飞准备","2":"起飞准备完毕","3":"手动飞行","4":"自动起飞","5":"航线飞行","6":"全景拍照","7":"智能跟随","8":"ADS-B 躲避","9":"自动返航",
     * "10":"自动降落","11":"强制降落","12":"三桨叶降落","13":"升级中","14":"未连接","15":"APAS","16":"虚拟摇杆状态","17":"指令飞行","18":"空中 RTK 收敛模式",
     * "19":"机场选址中","20":"POI环绕","21":"进离场航线飞行过程中"}
     */
    private void routeAiTask(String dockSn, Integer modeCode) {
        //起飞
        if (modeCode == 4) {
            CacheUtils.delete(BusinessConstant.ZHIMOU_AI_STOP + dockSn);
            if (CacheUtils.get(BusinessConstant.ZHIMOU_AI_START + dockSn) == null) {
                CacheUtils.set(BusinessConstant.ZHIMOU_AI_START + dockSn, true);
                log.info("航线自动检测AI任务:{}", dockSn);
                String taskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn);
                FlightTaskEntity entity = flightTaskService.selectById(taskId);
                if (!Objects.equals(FlightTaskType.MANUAL.getCode(), entity.getTaskType())) {
                    RouteAiEntity routeAiEntity = routeAiDao.selectOne(new QueryWrapper<RouteAiEntity>().eq("route_id", entity.getRouteId()));
                    if (routeAiEntity != null) {
                        log.info("航线检测到AI任务，dockSn:{}, routeId:{}", dockSn, entity.getRouteId());
                        ZhiMouAiStart zhiMouAiStart = new ZhiMouAiStart();
                        zhiMouAiStart.setDockSn(dockSn);
                        zhiMouAiStart.setAppId(routeAiEntity.getZhimouAppId());
                        zhiMouAiStart.setRules(Arrays.asList(routeAiEntity.getZhimouChildId().split(",")));
                        com.alibaba.fastjson2.JSONObject result = zhiMouService.startByWeb(zhiMouAiStart);
                        log.info("航线开启AI任务，dockSn:{}, result:{}", dockSn, result);
                    }
                }
            }
        }

        //返航
        if (modeCode == 9) {
            if (CacheUtils.get(BusinessConstant.ZHIMOU_AI_STOP + dockSn) == null) {
                CacheUtils.set(BusinessConstant.ZHIMOU_AI_STOP + dockSn, true);
                if (CacheUtils.get(BusinessConstant.ZHIMOU_AI_START + dockSn) != null) {
                    CacheUtils.delete(BusinessConstant.ZHIMOU_AI_START + dockSn);
                    com.alibaba.fastjson2.JSONObject result = zhiMouService.stopByWeb(dockSn);
                    log.info("航线自动停止AI任务，dockSn:{}, result:{}", dockSn, result);
                    if (result != null && result.getIntValue("code") == 0) {
                        JSONObject data = new JSONObject();
                        data.set("device", dockSn);
                        data.set("event", "stop");
                        data.set("timestamp", System.currentTimeMillis());
                        JSONObject payload = djiBaseService.getPayload(BusinessConstant.ZHIMOU_AI_METHOD, data);
                        mqttPushService.pushMessageByClient1(BusinessConstant.WEB_EVENT_TOPIC.formatted(dockSn), payload.toString());
                        //修改AI媒体
                        String taskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn);
                        flightTaskService.updateTaskAiMediaNum(taskId);
                    }
                }
            }
        }
    }
}
