package com.multictrl.modules.business.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.multictrl.common.config.MinioConfig;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.constant.DockMode;
import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException;
import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.JsonUtils;
import com.multictrl.common.utils.Utils;
import com.multictrl.modules.business.dto.RouteDTO;
import com.multictrl.modules.business.dto.flight.FlightExecute;
import com.multictrl.modules.business.dto.flight.FlightTaskPrepare;
import com.multictrl.modules.business.dto.flight.InFlightWaylineDeliver;
import com.multictrl.modules.business.service.DJIBaseService;
import com.multictrl.modules.business.service.FlightTaskService;
import com.multictrl.modules.business.service.RouteFlightService;
import com.multictrl.modules.business.service.RouteService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 航线飞行
 *
 * @author Sdy
 * @since 1.0.0 2026/4/28
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RouteFlightServiceImpl implements RouteFlightService {
    private final DJIBaseService djiBaseService;
    private final RouteService routeService;
    private final MinioConfig minioConfig;
    private final FlightTaskService flightTaskService;
    private final Map<String, ReentrantLock> cacheLock = new ConcurrentHashMap<>();

    @Override
    public String flightTaskUndo(String dockSn, List<String> taskIds) {
        JSONObject data = new JSONObject();
        data.set("flight_ids", taskIds);
        return djiBaseService.executeAndReturnResult(dockSn, "flighttask_undo", data);
    }

    @Override
    public String flightExecute(String dockSn, FlightExecute flightExecute, Boolean isRecord) {
        ReentrantLock lock = cacheLock.computeIfAbsent(dockSn, k -> new ReentrantLock());
        lock.lock();
        try {
            Boolean dockOnline = djiBaseService.isDockOnline(dockSn);
            if (!dockOnline) {
                throw new RenException(ErrorCode.DOCK_OSD_NOT_EXIST, "无法操作");
            }
            Integer dockModeCode = djiBaseService.getDockModeCode(dockSn);
            if (!Objects.equals(DockMode.IDLE.getCode(), dockModeCode)) {
                throw new RenException(ErrorCode.DOCK_MODE_NOT_IDLE, DockMode.getDescByCode(dockModeCode));
            }
            //获取航线信息
            RouteDTO route = routeService.getRoute(flightExecute.getRouteId());
            if (route == null) {
                throw new RenException(ErrorCode.ROUTE_NOT_EXIST);
            }
            //任务标识
            String taskId = IdUtil.fastSimpleUUID();
            CacheUtils.set(BusinessConstant.WORKING_TASK_ID + dockSn, taskId);
            //设置默认相机模式，需要定频推送前端
            CacheUtils.set(BusinessConstant.UAV_VIDEO_TYPE + dockSn, "wide");
            //判断kmz是否存在
            String kmzPath = minioConfig.getOther().getDataPath() + "/" + route.getKmzUrl();
            if (!new File(kmzPath).exists()) {
                throw new RenException(ErrorCode.ROUTE_KMZ_NOT_EXIST);
            }
            //生成md5文件签名
            String md5File = Utils.md5File(kmzPath);
            if (StrUtil.isBlank(md5File)) {
                throw new RenException(ErrorCode.CONVERSION_FAILED, "md5文件签名");
            }
            //返航高度限制>50
            Double globalRthHeight = route.getGlobalRthHeight();
            if (globalRthHeight <= 50) {
                throw new RenException(ErrorCode.GLOBAL_RTH_HEIGHT_NOT_BELOW, "50");
            }
            //下发任务
            int exitOnRcLost = "executeLostAction".equals(route.getExitOnRcLost()) ? 1 : 0;
            String kmzUrl = minioConfig.getEndpoint() + "/" + route.getKmzUrl();
            FlightTaskPrepare flightTaskPrepare = new FlightTaskPrepare();
            flightTaskPrepare.setFlight_id(taskId);
            //立即执行
            flightTaskPrepare.setTask_type(0);
            flightTaskPrepare.setExecute_time(System.currentTimeMillis() + 5000);
            FlightTaskPrepare.FileInfo fileInfo = new FlightTaskPrepare.FileInfo(kmzUrl, md5File);
            CacheUtils.set(BusinessConstant.FLIGHT_TASK_KMZ_FILE_INFO + dockSn, fileInfo, 5 * 60 * 1000);
            flightTaskPrepare.setFile(fileInfo);
            //获取断点，如果断点续飞请设置
//        flightTaskPrepare.setBreak_point(null);
            flightTaskPrepare.setRth_mode(1);
            flightTaskPrepare.setRth_altitude((int) Math.round(globalRthHeight));
            //{"0":"返航","1":"悬停","2":"降落"}
            //失控动作，当前固定传的值是 0，即返航。注意该枚举值定义跟飞控跟机场定义的不一致，机场端会进行转换。
            flightTaskPrepare.setOut_of_control_action(0);
            flightTaskPrepare.setExit_wayline_when_rc_lost(exitOnRcLost);
            //{"0":"GPS 任务","1":"高精度 RTK 任务"}
            flightTaskPrepare.setWayline_precision_type(1);
            //模拟器执行
            if (flightExecute.getSimulateMission() != null) {
                flightTaskPrepare.setSimulate_mission(flightExecute.getSimulateMission());
            }
            JSONObject data = JsonUtils.parseObject(JsonUtils.toJsonString(flightTaskPrepare), JSONObject.class);
            String result = djiBaseService.executeAndReturnResult(dockSn, "flighttask_prepare", data);
            log.debug("flight_task_prepare --> result:{}", result);
            //执行任务
            Utils.sleep(1000);
            String resultMsg = djiBaseService.executeAndReturnResult(dockSn, "flighttask_execute", new JSONObject().set("flight_id", taskId));
            if (isRecord) {
                flightTaskService.addRouteTask(taskId, dockSn, route.getId());
            }
            return resultMsg;
        } finally {
            Utils.sleep(2000);
            lock.unlock();
        }
    }

    @Override
    public String inFlightWaylineDeliver(String dockSn, Long routeId) {
        //作业中才能空中下发航线
        Integer dockModeCode = djiBaseService.getDockModeCode(dockSn);
        if (!Objects.equals(DockMode.WORKING.getCode(), dockModeCode)) {
            throw new RenException(ErrorCode.DOCK_MODE_NOT_WORKING, DockMode.getDescByCode(dockModeCode));
        }
        //获取航线信息
        RouteDTO route = routeService.getRoute(routeId);
        if (route == null) {
            throw new RenException(ErrorCode.ROUTE_NOT_EXIST);
        }
        //任务标识
        String taskId = IdUtil.fastSimpleUUID();
        CacheUtils.set(BusinessConstant.IN_FLIGHT_WORKING_TASK_ID + dockSn, taskId);
        //判断kmz是否存在
        String kmzPath = minioConfig.getOther().getDataPath() + "/" + route.getKmzUrl();
        if (!new File(kmzPath).exists()) {
            throw new RenException(ErrorCode.ROUTE_KMZ_NOT_EXIST);
        }
        //生成md5文件签名
        String md5File = Utils.md5File(kmzPath);
        if (StrUtil.isBlank(md5File)) {
            throw new RenException(ErrorCode.CONVERSION_FAILED, "md5文件签名");
        }
        //返航高度限制>50
        Double globalRthHeight = route.getGlobalRthHeight();
        if (globalRthHeight <= 50) {
            throw new RenException(ErrorCode.GLOBAL_RTH_HEIGHT_NOT_BELOW, "50");
        }
        //下发任务
        int exitOnRcLost = "executeLostAction".equals(route.getExitOnRcLost()) ? 1 : 0;
        String kmzUrl = minioConfig.getEndpoint() + "/" + route.getKmzUrl();
        InFlightWaylineDeliver inFlightWaylineDeliver = new InFlightWaylineDeliver();
        InFlightWaylineDeliver.FileInfo fileInfo = new InFlightWaylineDeliver.FileInfo(kmzUrl, md5File);
        inFlightWaylineDeliver.setFile(fileInfo);
        inFlightWaylineDeliver.setIn_flight_wayline_id(taskId);
        inFlightWaylineDeliver.setExit_wayline_when_rc_lost(exitOnRcLost);
        inFlightWaylineDeliver.setRth_altitude((int) Math.round(globalRthHeight));
        inFlightWaylineDeliver.setOut_of_control_action(0);
        inFlightWaylineDeliver.setRth_mode(1);
        inFlightWaylineDeliver.setWayline_precision_type(1);
        JSONObject data = JsonUtils.parseObject(JsonUtils.toJsonString(inFlightWaylineDeliver), JSONObject.class);
        return djiBaseService.executeAndReturnResult(dockSn, "in_flight_wayline_deliver", data);
    }

    @Override
    public String inFlightWaylineStop(String dockSn) {
        Object flightId = CacheUtils.get(BusinessConstant.IN_FLIGHT_WORKING_TASK_ID + dockSn);
        if (flightId == null) {
            throw new RenException(ErrorCode.ROUTE_TASK_NOT_EXIST);
        }
        return djiBaseService.executeAndReturnResult(dockSn, "in_flight_wayline_stop", new JSONObject().set("in_flight_wayline_id", flightId));
    }

    @Override
    public String inFlightWaylineRecover(String dockSn) {
        Object flightId = CacheUtils.get(BusinessConstant.IN_FLIGHT_WORKING_TASK_ID + dockSn);
        if (flightId == null) {
            throw new RenException(ErrorCode.ROUTE_TASK_NOT_EXIST);
        }
        return djiBaseService.executeAndReturnResult(dockSn, "in_flight_wayline_recover", new JSONObject().set("in_flight_wayline_id", flightId));
    }
}
