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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.multictrl.common.config.MqttConfig;
import com.multictrl.common.config.SysConfig;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.constant.SysParamsKey;
import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException;
import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.Result;
import com.multictrl.common.utils.Utils;
import com.multictrl.modules.business.dao.ZhimouCallbackDao;
import com.multictrl.modules.business.dto.AiWarningDTO;
import com.multictrl.modules.business.dto.zhimou.*;
import com.multictrl.modules.business.entity.ZhimouCallbackEntity;
import com.multictrl.modules.business.service.AiWarningService;
import com.multictrl.modules.business.service.SrsService;
import com.multictrl.modules.business.service.ZhiMouService;
import com.multictrl.modules.sys.dao.SysParamsDao;
import com.multictrl.modules.sys.entity.SysParamsEntity;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 智眸AI接口封装
 *
 * @author Sdy
 * @since 1.0.0 2026/6/9
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ZhiMouServiceImpl implements ZhiMouService {
    private final SysParamsDao paramsDao;
    private final SrsService srsService;
    private final MqttConfig mqttConfig;
    private final SysConfig sysConfig;
    private final AiWarningService aiWarningService;
    private final ZhimouCallbackDao zhimouCallbackDao;
    private static final String APP_KEY = "appkey";
    private static final String APP_SECRET = "appsecret";
    private static final String AUTHORIZATION = "Authorization";
    private static final String BEARER = "Bearer ";
    private static final String GET_TOKEN = "/api/token";
    private static final String GET_APP = "/api/app";
    private static final String TASK = "/api/task";
    private static final String MESSAGE_RECORD = "/api/message_record";
    private static final String MESSAGE_RECORD_COUNT = "/api/task_record/%s/message_trace";
    private static final String REPORT = "/api/task/%s/report";
    private static final String TASK_RECORD = "/api/task_record?device=%s&offset=%s&limit=%s&is_stop=1";
    private static final String UPLOAD_FLIGHT_STATUS = "/api/task/%s/flight";
    private static final String TASK_RECORD_2 = "/api/task_record/%s";
    List<String> zhiMouRequestPara = Arrays.asList(SysParamsKey.ZHIMOU_AI_URL, SysParamsKey.ZHIMOU_AI_APP_KEY, SysParamsKey.ZHIMOU_AI_APP_SECRET);
    private static final Map<String, String> ERROR_CODE_MAP = new HashMap<>();

    static {
        ERROR_CODE_MAP.put("10000", "无效的平台授权或授权已过期");
        ERROR_CODE_MAP.put("10001", "错误的appkey或appsecret");
        ERROR_CODE_MAP.put("10002", "无效的接口token");
        ERROR_CODE_MAP.put("20000", "无效的zapk安装包");
        ERROR_CODE_MAP.put("20001", "错误的应用包格式");
        ERROR_CODE_MAP.put("20002", "无效的应用包授权");
        ERROR_CODE_MAP.put("20003", "应用包模型安装错误");
        ERROR_CODE_MAP.put("20004", "应用已过期");
        ERROR_CODE_MAP.put("20005", "无效的应用ID");
        ERROR_CODE_MAP.put("20006", "应用配置存储失败");
        ERROR_CODE_MAP.put("20007", "应用无可用规则");
        ERROR_CODE_MAP.put("30000", "文件写入失败");
        ERROR_CODE_MAP.put("30001", "文件删除失败");
        ERROR_CODE_MAP.put("40000", "无效的请求体");
        ERROR_CODE_MAP.put("50000", "任务输入流或输出流打开失败");
        ERROR_CODE_MAP.put("50001", "无效的任务ID");
        ERROR_CODE_MAP.put("50002", "无效的任务输出流ID");
        ERROR_CODE_MAP.put("50003", "无效的任务状态");
        ERROR_CODE_MAP.put("50004", "任务输出流打开失败");
        ERROR_CODE_MAP.put("50005", "无效的任务图片数据");
        ERROR_CODE_MAP.put("50006", "无效的任务图片ID");
        ERROR_CODE_MAP.put("50007", "任务图片上报频次过高");
        ERROR_CODE_MAP.put("50008", "任务可用并发数不足");
        ERROR_CODE_MAP.put("50009", "开启任务模型加载失败");
    }

    @Override
    public Result<Object> resultHandle(JSONObject result) {
        if (result == null) {
            return new Result<>().error(ErrorCode.ZHIMOU_REQUEST_ERROR);
        }
        if (result.containsKey("code")) {
            int code = result.getIntValue("code");
            if (code == 0) {
                return new Result<>().ok(result.get("data"));
            } else {
                String msg = ERROR_CODE_MAP.get(code + "");
                if (StrUtil.isNotBlank(msg)) {
                    return new Result<>().error(msg);
                }
            }
        }
        log.error("invoke zhiMou resultHandle result: {}", result);
        return new Result<>().error(ErrorCode.ZHIMOU_REQUEST_ERROR);
    }

    @Override
    public ZhiMouInfo getToken() {
        Object tokenObj = CacheUtils.getWithoutReset(BusinessConstant.ZHIMOU_TOKEN);
        if (tokenObj instanceof ZhiMouInfo) {
            return (ZhiMouInfo) tokenObj;
        }
        List<SysParamsEntity> paramList = paramsDao.selectList(new QueryWrapper<SysParamsEntity>()
                .in("param_code", zhiMouRequestPara));
        if (CollUtil.isEmpty(paramList) || paramList.size() != zhiMouRequestPara.size()) {
            throw new RenException(ErrorCode.ZHIMOU_NO_CONFIG);
        }
        String url = null, appKey = null, appSecret = null;
        for (SysParamsEntity entity : paramList) {
            String paramCode = entity.getParamCode();
            if (SysParamsKey.ZHIMOU_AI_URL.equals(paramCode)) {
                url = entity.getParamValue();
            } else if (SysParamsKey.ZHIMOU_AI_APP_KEY.equals(paramCode)) {
                appKey = entity.getParamValue();
            } else if (SysParamsKey.ZHIMOU_AI_APP_SECRET.equals(paramCode)) {
                appSecret = entity.getParamValue();
            }
        }
        if (StrUtil.hasBlank(url, appKey, appSecret)) {
            throw new RenException(ErrorCode.ZHIMOU_NO_CONFIG);
        }
        JSONObject request = new JSONObject();
        request.put(APP_KEY, appKey);
        request.put(APP_SECRET, appSecret);
        String resultStr = HttpUtil.post(url + GET_TOKEN, request.toJSONString());
        log.debug("invoke zhiMou getToken url:{}, request: {},result: {}", url + GET_TOKEN, request, resultStr);
        if (StrUtil.isEmpty(resultStr)) {
            throw new RenException(ErrorCode.ZHIMOU_REQUEST_ERROR);
        }
        JSONObject result = JSONObject.parse(resultStr);
        if (result.getInteger("code") == 0 && result.getString("msg").equals("ok")) {
            ZhiMouInfo info = new ZhiMouInfo();
            info.setUrl(url);
            info.setToken(result.getJSONObject("data").getString("token"));
            info.setExpire(result.getJSONObject("data").getLong("expires_at"));
            //官方说过期2小时，这里设置半小时缓存
            CacheUtils.set(BusinessConstant.ZHIMOU_TOKEN, info, 30 * 60 * 1000);
            return info;
        }

        throw new RenException(ErrorCode.ZHIMOU_REQUEST_ERROR);
    }

    @Override
    public JSONObject getApp() {
        ZhiMouInfo zhiMouInfo = getToken();
        if (StrUtil.isBlank(zhiMouInfo.getToken())) {
            throw new RenException(ErrorCode.ZHIMOU_REQUEST_ERROR);
        }
        String result = HttpUtil.createGet(zhiMouInfo.getUrl() + GET_APP)
                .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken()).execute().body();
        log.debug("invoke zhiMou getApp result: {}", result);
        if (StrUtil.isNotBlank(result)) {
            List<AiWarningDTO> aiList = aiWarningService.list(new HashMap<>());
            JSONObject data = JSONObject.parse(result);
            JSONArray apps = data.getJSONObject("data").getJSONArray("apps");
            for (Object app : apps) {
                JSONObject appObj = (JSONObject) app;
                String appId = appObj.getString("app_id");
                JSONArray rules = appObj.getJSONArray("rules");
                for (Object rule : rules) {
                    JSONObject ruleObj = (JSONObject) rule;
                    String ruleId = ruleObj.getString("rule_id");
                    ruleObj.put("ai_id", null);
                    ruleObj.put("ai_status", null);
                    ruleObj.put("ai_warning_level", null);
                    for (AiWarningDTO dto : aiList) {
                        if (dto.getAppId().equals(appId) && dto.getRuleId().equals(ruleId)) {
                            ruleObj.put("ai_id", dto.getId());
                            ruleObj.put("ai_status", dto.getStatus());
                            ruleObj.put("ai_warning_level", dto.getWarningLevel());
                        }
                    }
                }
            }
            return data;
        }
        return null;
    }

    @Override
    public JSONObject startByWeb(ZhiMouAiStart zhiMouAiStart) {
        String dockSn = zhiMouAiStart.getDockSn();
        ZhiMouTask zhiMouTask = new ZhiMouTask();
        zhiMouTask.setMode("video");
        zhiMouTask.setAppId(zhiMouAiStart.getAppId());
        zhiMouTask.setDevice(dockSn);
        String dockTaskId = (String) CacheUtils.get(BusinessConstant.WORKING_TASK_ID + dockSn);
        zhiMouTask.setBusinessId(dockTaskId);
        zhiMouTask.setRules(zhiMouAiStart.getRules());
        zhiMouTask.setInput(srsService.getUavPushLiveUrl(dockSn));
        zhiMouTask.setOutput(srsService.getUavAiPushLiveUrl(dockSn));
        //内网部署的服务，想要使用公网的知眸服务，需要把srs-rtmp的1935映射到公网，这样知眸才能拉到视频流处理并推流
        SysParamsEntity rtmpMapUrl = paramsDao.selectOne(new QueryWrapper<SysParamsEntity>()
                .eq("param_code", SysParamsKey.SRS_RTMP_MAP_URL));
        if (rtmpMapUrl != null) {
            String paramValue = rtmpMapUrl.getParamValue();
            if (StrUtil.isNotBlank(paramValue) && paramValue.contains(":")) {
                //替换掉input中的ip和端口
                zhiMouTask.setInput(replaceRtmpIpPort(zhiMouTask.getInput(), paramValue.split(":")[0],
                        paramValue.split(":")[1]));
                zhiMouTask.setOutput(replaceRtmpIpPort(zhiMouTask.getOutput(), paramValue.split(":")[0],
                        paramValue.split(":")[1]));
            }
        }
        zhiMouTask.setOsd(new Osd());
        Report report = new Report();
        ReportMqtt mqtt = new ReportMqtt();
        MqttConfig.Client client1 = mqttConfig.getClient1();
        String hostUrl = client1.getUrl().replaceAll(BusinessConstant.TCP_PROTOCOL, "");
        mqtt.setBrokerHost(hostUrl.split(":")[0]);
        mqtt.setBrokerPort(Long.parseLong(hostUrl.split(":")[1]));
        //内网部署的服务，想要使用公网的知眸服务，需要把emqx的1883映射到公网，这样知眸才能通知系统检测结果
        SysParamsEntity emqxMapUrl = paramsDao.selectOne(new QueryWrapper<SysParamsEntity>()
                .eq("param_code", SysParamsKey.EMQX_MAP_URL));
        if (emqxMapUrl != null) {
            String paramValue = emqxMapUrl.getParamValue();
            if (StrUtil.isNotBlank(paramValue) && paramValue.contains(":")) {
                mqtt.setBrokerHost(paramValue.split(":")[0]);
                mqtt.setBrokerPort(Long.parseLong(paramValue.split(":")[1]));
            }
        }
        mqtt.setSsl(false);
        mqtt.setClientID("zhi_mou_ai_" + IdUtil.getSnowflakeNextIdStr());
        mqtt.setUsername(client1.getUsername());
        mqtt.setPassword(client1.getPassword());
        mqtt.setTopic(BusinessConstant.ZHIMOU_AI_CALLBACK_TOPIC.formatted(dockSn));
        mqtt.setQos(0L);
        report.setMqtt(mqtt);
        zhiMouTask.setReport(report);
        JSONObject jsonObject = Utils.beanToSnakeJsonFastJson(zhiMouTask);
        //执行命令
        ZhiMouInfo zhiMouInfo = getToken();
        String result = HttpUtil.createPost(zhiMouInfo.getUrl() + TASK)
                .body(jsonObject.toJSONString())
                .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken())
                .execute()
                .body();
        log.debug("invoke zhiMou start dockTaskId: {}", dockTaskId);
        log.debug("invoke zhiMou start request: {},result: {}", jsonObject, result);
        if (StrUtil.isNotBlank(result)) {
            return JSONObject.parse(result);
        }
        return null;
    }

    @Override
    public JSONObject stopByWeb(String dockSn) {
        JSONObject running = getRunningTaskByWeb(dockSn);
        if (running == null) {
            throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST);
        }
        String taskId = running.getString("task_id");
        ZhiMouInfo zhiMouInfo = getToken();
        String result = HttpUtil.createRequest(Method.DELETE, zhiMouInfo.getUrl() + TASK + "/" + taskId)
                .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken())
                .execute()
                .body();
        log.debug("invoke zhiMou stop taskId: {},result: {}", taskId, result);
        if (StrUtil.isNotBlank(result)) {
            return JSONObject.parse(result);
        }
        return null;
    }

    @Override
    public JSONObject getRunningTaskByWeb(String dockSn) {
        ZhiMouInfo zhiMouInfo = getToken();
        String result = HttpUtil.createGet(zhiMouInfo.getUrl() + TASK)
                .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken()).execute().body();
        log.debug("invoke zhiMou getRunningTask result: {}", result);
        if (StrUtil.isNotBlank(result)) {
            JSONObject runningTask = JSONObject.parse(result);
            JSONObject jsonObject = runningTask.getJSONObject("data");
            if (jsonObject != null) {
                JSONArray jsonArray = jsonObject.getJSONArray("tasks");
                if (jsonArray != null && !jsonArray.isEmpty()) {
                    for (int i = 0; i < jsonArray.size(); i++) {
                        JSONObject j2 = jsonArray.getJSONObject(i);
                        if (j2.getString("device").equals(dockSn)) {
                            String protocol = BusinessConstant.HTTP_PROTOCOL;
                            if (sysConfig.getIsSsl()) {
                                protocol = BusinessConstant.HTTPS_PROTOCOL;
                            }
                            j2.remove("report");
                            j2.remove("input");
                            j2.remove("output");
                            j2.remove("dji");
                            j2.put("appId", j2.getString("app_id"));
                            j2.remove("app_id");
                            j2.put("businessId", j2.getString("business_id"));
                            j2.remove("business_id");
                            String webrtcUrl = protocol + sysConfig.getIp() + ":" + sysConfig.getPort() + "/rtc/v1/whep/?app=" + dockSn + "&stream=uav_ai.flv";
                            j2.put("webrtcUrl", webrtcUrl);
                            j2.put("osd", CacheUtils.get(BusinessConstant.DOCK_ZHIMOU_CONFIG + dockSn));
                            return j2;
                        }
                    }
                }
            }
        }
        return null;
    }

    @Override
    public JSONObject configurationByWeb(Osd osd) {
        String dockSn = osd.getDockSn();
        JSONObject running = getRunningTaskByWeb(dockSn);
        if (running == null) {
            throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST);
        }
        osd.setTaskId(running.getString("task_id"));
        JSONObject osdObject = Utils.beanToSnakeJsonFastJson(osd);
        ZhiMouInfo zhiMouInfo = getToken();
        String result = HttpUtil.createRequest(Method.PUT,
                        zhiMouInfo.getUrl() + TASK + "/" + osd.getTaskId())
                .body(osdObject.toJSONString())
                .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken())
                .execute()
                .body();
        log.debug("invoke zhiMou configuration request: {},result: {}", osdObject, result);
        if (StrUtil.isNotBlank(result)) {
            JSONObject parse = JSONObject.parse(result);
            if (parse != null && parse.containsKey("ok")) {
                CacheUtils.set(BusinessConstant.DOCK_ZHIMOU_CONFIG + dockSn, osd);
            }
            return parse;
        }
        return null;
    }

    @Override
    public JSONObject currentTaskMessageRecord(String dockSn) {
        JSONObject running = getRunningTaskByWeb(dockSn);
        if (running == null) {
            throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST);
        }
        String taskId = running.getString("task_id");

        return getMessageRecord(taskId);
    }

    @Override
    public JSONObject getMessageRecord(String taskId) {
        ZhiMouInfo zhiMouInfo = getToken();
        String result = HttpUtil.createGet(zhiMouInfo.getUrl() + MESSAGE_RECORD
                        + String.format("?task_id=%s&limit=%s", taskId, Integer.MAX_VALUE))
                .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken())
                .header("Referer", zhiMouInfo.getUrl())
                .execute().body();
        log.debug("invoke zhiMou getMessageRecord taskId:{} result: {}", taskId, result);
        if (!result.contains("\"code\":0")) {
            JSONObject temp = new JSONObject();
            temp.put("code", 0);
            temp.put("data", new JSONArray());
            return temp;
        }
        JSONObject jsonObject = JSONObject.parseObject(result);
        JSONArray data = jsonObject.getJSONArray("data");
        if (data == null || data.isEmpty()) {
            JSONObject temp = new JSONObject();
            temp.put("code", 0);
            temp.put("data", new JSONArray());
            return temp;
        }
        for (int i = 0; i < data.size(); i++) {
            JSONObject t = data.getJSONObject(i);
            JSONArray t2 = t.getJSONArray("objects");
            int count = 0;
            if (t2 != null) {
                count = t2.size();
            }
            //筛选出带车牌的事件
            JSONArray carIds = new JSONArray();
            if (t2 != null) {
                for (int j = 0; j < t2.size(); j++) {
                    JSONObject t3 = t2.getJSONObject(j);
                    if (t3.containsKey("attr_name") && "车牌".equals(t3.getString("attr_name"))) {
                        carIds.add(t3);
                    }
                }
            }
            t.remove("objects");
            if (!carIds.isEmpty()) {
                t.put("objects", carIds);
            }
            t.put("count", count);
            t.put("thumbnail", zhiMouInfo.getUrl() + "/thumbnail" + t.getString("image"));
            t.put("image", zhiMouInfo.getUrl() + t.getString("image"));
        }
        return jsonObject;
    }

    @Override
    public JSONArray getTaskMessageRecordList(String taskId) {
        List<ZhimouCallbackEntity> entityList = zhimouCallbackDao.selectList(new QueryWrapper<ZhimouCallbackEntity>()
                .eq("business_id", taskId));
        if (CollUtil.isEmpty(entityList)) {
            throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST);
        }
        Set<String> taskIds = entityList.stream().map(ZhimouCallbackEntity::getTaskId).collect(Collectors.toSet());
        JSONArray jsonArray = new JSONArray();
        for (String zhimouTaskId : taskIds) {
            JSONObject result = getMessageRecord(zhimouTaskId);
            jsonArray.addAll(result.getJSONArray("data"));
        }
        jsonArray = jsonArray.stream().map(obj -> (JSONObject) obj)
                .sorted(Comparator.comparingLong(
                        o -> o.getLong("id")))
                .collect(JSONArray::new, JSONArray::add, JSONArray::addAll);
        return jsonArray;
    }

    @Override
    public byte[] downloadReport(DownloadReport downloadReport) {
        List<ZhimouCallbackEntity> entityList = zhimouCallbackDao.selectList(new QueryWrapper<ZhimouCallbackEntity>()
                .eq("business_id", downloadReport.getTaskId()));
        if (CollUtil.isEmpty(entityList)) {
            throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST);
        }
        Set<String> taskIds = entityList.stream().map(ZhimouCallbackEntity::getTaskId).collect(Collectors.toSet());
        ZhiMouInfo zhiMouInfo = getToken();
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ZipOutputStream zipOut = new ZipOutputStream(baos)) {

            for (String taskId : taskIds) {
                JSONObject para = JSON.parseObject(JSON.toJSONString(downloadReport));
                para.remove("taskId");
                para.put("rule_ids", new JSONArray());
                byte[] fileBytes = HttpUtil.createPost(zhiMouInfo.getUrl() + String.format(REPORT, taskId))
                        .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken())
                        .body(para.toJSONString())
                        .execute()
                        .bodyBytes();

                String fileName = para.getString("title") + "_" + taskId + ".docx";
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipOut.putNextEntry(zipEntry);
                zipOut.write(fileBytes);
                zipOut.closeEntry();
            }

            return baos.toByteArray();
        } catch (Exception e) {
            log.error("下载 AI报告ZIP 失败", e);
            throw new RuntimeException("下载AI报告失败", e);
        }
    }

    @Override
    public Integer getMediaCount(String taskId) {
        try {
            JSONObject record = getMessageRecord(taskId);
            return record.getJSONArray("data").size();
        } catch (Exception e) {
            return 0;
        }
    }

    @Override
    public boolean hasAiService() {
        List<SysParamsEntity> paramList = paramsDao.selectList(new QueryWrapper<SysParamsEntity>()
                .in("param_code", zhiMouRequestPara));

        return !CollUtil.isEmpty(paramList) && paramList.size() == zhiMouRequestPara.size();
    }

    private String replaceRtmpIpPort(String originalUrl, String newIp, String newPort) {
        // 定义RTMP URL的正则表达式模式
        Pattern pattern = Pattern.compile("^rtmp://([^/:]+):([^/]+)(/.*)$");
        Matcher matcher = pattern.matcher(originalUrl);

        if (matcher.matches()) {
            // 构建新的URL
            return "rtmp://" + newIp + ":" + newPort + matcher.group(3);
        } else {
            // 如果URL格式不符合预期，返回原始URL或抛出异常
            return originalUrl;
        }
    }
}
