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

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException;
import com.multictrl.common.utils.*;
import com.multictrl.modules.business.dto.LatLonDTO;
import com.multictrl.modules.business.dto.error.DJICloudErrorCode;
import com.multictrl.modules.business.service.DJIBaseService;
import com.multictrl.modules.business.service.MqttPushService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 大疆基础服务
 *
 * @author Sdy
 * @since 1.0.0 2026/4/24
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DJIBaseServiceImpl implements DJIBaseService {
    private final MqttPushService mqttPushService;

    /** 设备SN → 当前正在等待回复的命令信息（供模拟回复使用） */
    private final ConcurrentHashMap<String, JSONObject> pendingCmds = new ConcurrentHashMap<>();

    @Override
    public JSONObject getPayload(String method, JSONObject data) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("bid", IdUtil.fastUUID());
        jsonObject.set("tid", IdUtil.fastUUID());
        jsonObject.set("timestamp", System.currentTimeMillis());
        jsonObject.set("method", method);
        if (data != null) {
            jsonObject.set("data", data);
        }
        return jsonObject;
    }

    @Override
    public JSONObject sendWaitReply(String topic, JSONObject payload) {
        mqttPushService.pushMessageByClient1(topic, payload.toString());
        String bid = payload.getStr("bid");
        String tid = payload.getStr("tid");
        String cacheKey = bid + "_" + tid;

        // 记录当前等待回复的命令，供前端模拟回复使用
        String deviceSn = extractDeviceSn(topic);
        if (deviceSn != null) {
            JSONObject pending = new JSONObject();
            pending.set("bid", bid);
            pending.set("tid", tid);
            pending.set("method", payload.getStr("method"));
            pending.set("cacheKey", cacheKey);
            pending.set("timestamp", System.currentTimeMillis());
            pendingCmds.put(deviceSn, pending);
        }

        try {
            int tryCount = 100;
            while (tryCount-- > 0) {
                Object object = CacheUtils.get(cacheKey);
                if (object != null) {
                    return (JSONObject) object;
                }
                Utils.sleep(100);
            }
            return null;
        } finally {
            if (deviceSn != null) pendingCmds.remove(deviceSn);
        }
    }

    private String extractDeviceSn(String topic) {
        // topic格式: thing/device/{sn}/services 或 thing/product/{sn}/services
        if (topic == null) return null;
        String[] parts = topic.split("/");
        return parts.length >= 3 ? parts[2] : null;
    }

    @Override
    public JSONObject getPendingCmd(String deviceSn) {
        return pendingCmds.get(deviceSn);
    }

    @Override
    public void injectMockReply(String deviceSn, JSONObject replyData) {
        JSONObject pending = pendingCmds.get(deviceSn);
        if (pending == null) {
            throw new RenException("当前设备没有待回复的命令，可能已超时或尚未发送");
        }
        String cacheKey = pending.getStr("cacheKey");
        JSONObject reply = replyData != null ? replyData : new JSONObject();
        CacheUtils.set(cacheKey, reply);
        log.debug("injectMockReply -> deviceSn={}, method={}, replyData={}", deviceSn, pending.getStr("method"), reply);
    }

    @Override
    public String sendWaitReplyJudgeResult(String topic, JSONObject payload) {
        JSONObject data = sendWaitReply(topic, payload);
        int result = -1;
        String message;
        if (data == null || data.isEmpty() || !data.containsKey("result") || (result = data.getInt("result")) != 0) {
            if (data == null) {
                message = MessageUtils.getMessage(ErrorCode.DOCK_NOT_REPLY);
            } else {
                message = Optional.of(data)
                        .map(j -> j.getJSONObject("output"))
                        .map(JSONObject::toString)
                        .orElse(null);
                if (data.containsKey("message")) {
                    message = data.getStr("message");
                }
            }
            DJICloudErrorCode errorMsg = DJIErrorCodeUtils.getErrorMsg(String.valueOf(result));
            if (errorMsg != null) {
//                throw new RenException(JsonUtils.toJsonString(errorMsg));
                message = JsonUtils.toJsonString(errorMsg);
            }
            log.debug("sendWaitReplyJudgeResult --> errorMsg: {}", message);
            throw new RenException(message);
        }
        if (data.containsKey("output") && !data.getJSONObject("output").isEmpty()) {
            message = data.getJSONObject("output").toString();
        } else {
            message = MessageUtils.getMessage(ErrorCode.DOCK_EXEC_SUCCESS);
        }

        log.debug("sendWaitReplyJudgeResult --> successMsg: {}", message);
        return message;
    }

    @Override
    public String executeAndReturnResult(String dockSn, String method) {

        return executeAndReturnResult(dockSn, method, null);
    }

    @Override
    public String executeAndReturnResult(String dockSn, String method, JSONObject data) {
        String topic = "thing/product/" + dockSn + "/services";

        return sendWaitReplyJudgeResult(topic, getPayload(method, data));
    }

    @Override
    public String executeOnly(String dockSn, String method) {
        return executeOnly(dockSn, method, null);
    }

    @Override
    public String executeOnly(String dockSn, String method, JSONObject data) {
        String topic = "thing/product/" + dockSn + "/services";
        mqttPushService.pushMessageByClient1(topic, getPayload(method, data).toString());

        return MessageUtils.getMessage(ErrorCode.DOCK_EXEC_SUCCESS);
    }

    @Override
    public String executeDrcAndReturnResult(String dockSn, String method) {

        return executeDrcAndReturnResult(dockSn, method, null);
    }

    @Override
    public String executeDrcAndReturnResult(String dockSn, String method, JSONObject data) {
        String topic = "thing/product/" + dockSn + "/drc/down";
        JSONObject message = new JSONObject();
        message.set("method", method);
        message.set("seq", System.currentTimeMillis());
        if (data != null) {
            message.set("data", data);
        }
        mqttPushService.pushMessageByClient2(topic, message.toString());
        String key = dockSn + "_" + method;
        int tryCount = 100;
        while (tryCount-- > 0) {
            Object object = CacheUtils.get(key);
            if (object != null) {
                CacheUtils.delete(key);
                JSONObject dataJson = (JSONObject) object;
                Integer result = dataJson.getInt("result");
                if (result == 0) {
                    return "执行成功";
                } else {
                    return "执行失败";
                }
            }
            Utils.sleep(100);
        }
        return "执行超时，请重试";
    }

    @Override
    public Double getDockEllipsoidHeight(String dockSn) {
        Object o = CacheUtils.get(BusinessConstant.DOCK_OSD + dockSn);
        if (o != null) {
            JSONObject data = (JSONObject) o;
            return data.getDouble("height");
        }
        return null;
    }

    @Override
    public Integer getDockModeCode(String dockSn) {
        Object o = CacheUtils.get(BusinessConstant.DOCK_OSD + dockSn);
        if (o != null) {
            JSONObject data = (JSONObject) o;
            return data.getInt("mode_code");
        }
        return null;
    }

    @Override
    public LatLonDTO getDockAlternateLandPoint(String dockSn) {
        Object o = CacheUtils.get(BusinessConstant.DOCK_OSD + dockSn);
        if (o != null) {
            JSONObject data = (JSONObject) o;
            if (!data.containsKey("alternate_land_point")) {
                return null;
            }
            JSONObject alternateLandPoint = data.getJSONObject("alternate_land_point");
            LatLonDTO gps = new LatLonDTO();
            gps.setLatitude(alternateLandPoint.getDouble("latitude"));
            gps.setLongitude(alternateLandPoint.getDouble("longitude"));
            return gps;
        }
        return null;
    }

    @Override
    public Boolean isDockOnline(String dockSn) {
        Object object = CacheUtils.get(BusinessConstant.DOCK_OSD + dockSn);
        return object != null;
    }
}
