package com.njcn.gather.detection.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.detection.handler.SocketContrastResponseService;
import com.njcn.gather.detection.handler.SocketDevResponseService;
import com.njcn.gather.detection.handler.SocketSourceResponseService;
import com.njcn.gather.detection.pojo.constant.DetectionCommunicateConstant;
import com.njcn.gather.detection.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
import com.njcn.gather.detection.pojo.param.ContrastDetectionParam;
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
import com.njcn.gather.detection.pojo.param.SimulateDetectionParam;
import com.njcn.gather.detection.pojo.vo.SocketMsg;
import com.njcn.gather.detection.service.PreDetectionService;
import com.njcn.gather.detection.util.business.DetectionCommunicateUtil;
import com.njcn.gather.detection.util.socket.CnSocketUtil;
import com.njcn.gather.detection.util.socket.FormalTestManager;
import com.njcn.gather.detection.util.socket.SocketManager;
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
import com.njcn.gather.device.pojo.po.PqDev;
import com.njcn.gather.device.pojo.vo.PreDetection;
import com.njcn.gather.device.service.IPqDevService;
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
import com.njcn.gather.plan.pojo.po.AdPlan;
import com.njcn.gather.plan.pojo.po.AdPlanSource;
import com.njcn.gather.plan.service.IAdPlanService;
import com.njcn.gather.plan.service.IAdPlanSourceService;
import com.njcn.gather.result.pojo.enums.ResultUnitEnum;
import com.njcn.gather.script.pojo.param.PqScriptCheckDataParam;
import com.njcn.gather.script.pojo.param.PqScriptIssueParam;
import com.njcn.gather.script.pojo.po.SourceIssue;
import com.njcn.gather.script.service.IPqScriptCheckDataService;
import com.njcn.gather.script.service.IPqScriptDtlsService;
import com.njcn.gather.source.pojo.po.SourceInitialize;
import com.njcn.gather.source.service.IPqSourceService;
import com.njcn.gather.system.dictionary.pojo.enums.DictDataEnum;
import com.njcn.gather.system.dictionary.service.IDictDataService;
import com.njcn.web.utils.HttpServletUtil;
import com.njcn.web.utils.RequestUtil;
import io.netty.channel.Channel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
@Slf4j
public class PreDetectionServiceImpl implements PreDetectionService {

    private final IPqDevService iPqDevService;
    private final IDictDataService dictDataService;
    private final IAdPlanService iAdPlanService;
    private final IAdPlanSourceService adPlanSourceService;
    private final IPqSourceService pqSourceService;
    private final IPqScriptDtlsService pqScriptDtlsService;

    private final SocketDevResponseService socketDevResponseService;
    private final SocketSourceResponseService socketSourceResponseService;
    private final SocketContrastResponseService socketContrastResponseService;
    private final IPqScriptCheckDataService iPqScriptCheckDataService;
    private final SocketManager socketManager;

    @Value("${log.homeDir}")
    private String alignDataFilePath;


    @Override
    public void sourceCommunicationCheck(PreDetectionParam param) {
        // 参数校验，目前仅检查IP是否重复，后续可在里面扩展
        checkDevIp(param.getDevIds());
        //用于处理异常导致的socket通道未关闭，socket交互异常
        DetectionCommunicateUtil.checkCommunicateChannel(param);
        /*
         * 先组装源通讯协议
         * 查询计划什么模式的（除了对比式，其他都是一个计划对应一个源）
         */
        AdPlan plan = iAdPlanService.getById(param.getPlanId());
        param.setScriptId(plan.getScriptId());
        param.setErrorSysId(plan.getErrorSysId());
        param.setCode(String.valueOf(plan.getCode()));
        if (ObjectUtil.isNotNull(plan)) {
            String code = dictDataService.getDictDataById(plan.getPattern()).getCode();
            DictDataEnum dictDataEnumByCode = DictDataEnum.getDictDataEnumByCode(code);
            if (Objects.nonNull(dictDataEnumByCode)) {
                switch (dictDataEnumByCode) {
                    case DIGITAL:
                    case SIMULATE:
                        sendYtxSocket(param);
                        break;
                    case CONTRAST:
                        break;
                    default:
                        throw new BusinessException(DetectionResponseEnum.PLAN_PATTERN_NOT);
                }
            } else {
                throw new BusinessException(DetectionResponseEnum.PLAN_PATTERN_NOT);
            }
        } else {
            throw new BusinessException(DetectionResponseEnum.PLAN_NOT_EXIST);
        }

    }

    /**
     * 原本系数校准单独写的，现在合并了，该方法过期了，没有调用了
     */
    @Deprecated
    @Override
    public void coefficientCheck(PreDetectionParam param) {
        // 检测是否存在连接的通道，后期需要做成动态，如果组合中不是第一位，则不需要关闭，也不用初始化 todo....
        DetectionCommunicateUtil.checkCommunicateChannel(param);
        AdPlanSource planSource = adPlanSourceService.getOne(new LambdaQueryWrapper<AdPlanSource>()
                .eq(AdPlanSource::getPlanId, param.getPlanId())
        );
        if (ObjectUtil.isNotNull(planSource)) {
            SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(planSource.getSourceId());
            if (ObjectUtil.isNotNull(sourceParam)) {
                //开始组装socket报文请求头
                socketDevResponseService.initList(param);
                socketSourceResponseService.initList(param);
                SocketMsg<String> msg = new SocketMsg<>();
                msg.setRequestId(SourceOperateCodeEnum.YJC_YTXJY.getValue());
                msg.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
                msg.setData(JSON.toJSONString(sourceParam));
                param.setSourceId(sourceParam.getSourceId());
                // 使用智能发送工具类，自动管理连接
                socketManager.smartSendToSource(param, JSON.toJSONString(msg));
            } else {
                throw new BusinessException(DetectionResponseEnum.SOURCE_INFO_NOT);
            }
        } else {
            throw new BusinessException(DetectionResponseEnum.PLAN_AND_SOURCE_NOT);
        }
    }


    /**
     * 发送源通信校验Socket连接（数字式和模拟式检测模式）
     *
     * <p>该方法用于建立与程控源设备的Socket连接，进行源通信校验。主要流程：</p>
     * <ul>
     *   <li>1. 存储检测参数到全局管理器</li>
     *   <li>2. 根据计划ID获取计划源信息</li>
     *   <li>3. 获取源设备初始化参数</li>
     *   <li>4. 初始化设备和源响应服务列表</li>
     *   <li>5. 组装Socket请求报文</li>
     *   <li>6. 建立Netty客户端连接</li>
     * </ul>
     *
     * @param param 预检测参数，包含计划ID、用户ID等信息
     * @throws BusinessException 当计划源信息不存在或源初始化参数为空时抛出
     * @see SourceOperateCodeEnum#YJC_YTXJY 源通信校验操作码
     * @see SourceOperateCodeEnum#INIT_GATHER 初始化采集操作码
     */
    private void sendYtxSocket(PreDetectionParam param) {
        AdPlanSource planSource = adPlanSourceService.getOne(new LambdaQueryWrapper<AdPlanSource>().eq(AdPlanSource::getPlanId, param.getPlanId()));
        param.setSourceId(planSource.getSourceId());
        String loginName = RequestUtil.getLoginNameByToken();
        WebServiceManager.addPreDetectionParam(loginName, param);
        if (ObjectUtil.isNotNull(planSource)) {
            //获取源初始化参数
            SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(planSource.getSourceId());
            if (ObjectUtil.isNotNull(sourceParam)) {
                //开始组装socket报文请求头
                socketDevResponseService.initList(param);
                socketSourceResponseService.initList(param);
                SocketMsg<String> socketMsg = new SocketMsg<>();
                socketMsg.setRequestId(SourceOperateCodeEnum.YJC_YTXJY.getValue());
                socketMsg.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
                socketMsg.setData(JSON.toJSONString(sourceParam));
                //使用智能发送工具类，自动管理与源控程序的socket连接
                socketManager.smartSendToSource(param, JSON.toJSONString(socketMsg));
            } else {
                throw new BusinessException(DetectionResponseEnum.SOURCE_INFO_NOT);
            }
        } else {
            throw new BusinessException(DetectionResponseEnum.PLAN_AND_SOURCE_NOT);
        }
    }

    /**
     * 发送源通信校验Socket连接（仿真模式）
     *
     * <p>该方法专门用于仿真检测模式下的源通信校验。与普通模式的区别：</p>
     * <ul>
     *   <li>直接使用传入的sourceId获取源初始化参数</li>
     *   <li>不需要通过计划ID查询计划源信息</li>
     *   <li>适用于独立的源设备通信测试</li>
     * </ul>
     *
     * @param param 预检测参数，必须包含sourceId和userPageId
     * @throws BusinessException 当源初始化参数为空时抛出
     * @see #sendYtxSocket(PreDetectionParam) 普通检测模式的源通信校验
     * @see SourceOperateCodeEnum#YJC_YTXJY 源通信校验操作码
     * @see SourceOperateCodeEnum#INIT_GATHER 初始化采集操作码
     */
    private void sendYtxSocketSimulate(PreDetectionParam param) {
        SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(param.getSourceId());
        param.setSourceId(sourceParam.getSourceId());
        String loginName = RequestUtil.getLoginNameByToken();
        WebServiceManager.addPreDetectionParam(loginName, param);
        if (ObjectUtil.isNotNull(sourceParam)) {
            SocketMsg<String> socketMsg = new SocketMsg<>();
            socketMsg.setRequestId(SourceOperateCodeEnum.YJC_YTXJY.getValue());
            socketMsg.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
            socketMsg.setData(JSON.toJSONString(sourceParam));
            // 使用智能发送工具类，自动管理连接
            socketManager.smartSendToSource(param, JSON.toJSONString(socketMsg));
        } else {
            throw new BusinessException(DetectionResponseEnum.SOURCE_INFO_NOT);
        }
    }

    @Override
    public boolean temStopTest() {
        FormalTestManager.stopFlag = true;
        return true;
    }

    @Override
    public boolean restartTemTest(PreDetectionParam param) {
        FormalTestManager.stopFlag = false;
        socketDevResponseService.initRestart();
        List<SourceIssue> sourceIssueList = SocketManager.getSourceList();
        if (CollUtil.isNotEmpty(sourceIssueList)) {
            SourceIssue sourceIssues = SocketManager.getSourceList().get(0);
            SocketMsg<String> xuMsg = new SocketMsg<>();
            xuMsg.setOperateCode(SourceOperateCodeEnum.OPER_GATHER.getValue());
            xuMsg.setData(JSON.toJSONString(sourceIssues));
            xuMsg.setRequestId(SourceOperateCodeEnum.FORMAL_REAL.getValue() + "&&" + sourceIssues.getType());
            SocketManager.sendMsg(param.getUserPageId() + DetectionCommunicateConstant.SOURCE, JSON.toJSONString(xuMsg));
            // Resume_Success
        } else {
            //TODO 是否最终检测完成需要推送给用户  检测完成
            PqScriptCheckDataParam checkDataParam = new PqScriptCheckDataParam();
            checkDataParam.setScriptId(param.getScriptId());
            checkDataParam.setIsValueTypeName(false);
            List<String> adType = iPqScriptCheckDataService.getValueType(checkDataParam);

            iPqDevService.updateResult(param.getDevIds(), adType, param.getCode(), param.getUserId(), param.getTemperature(), param.getHumidity());
            CnSocketUtil.quitSend(param);
        }

        return true;
    }

    @Override
    public void ytxCheckSimulate(SimulateDetectionParam param) {
        PreDetectionParam preDetectionParam = new PreDetectionParam();
        preDetectionParam.setSourceId(param.getSourceId());
        preDetectionParam.setUserPageId(param.getUserPageId());
        preDetectionParam.setSendWebMsg(true);

        DetectionCommunicateUtil.checkCommunicateChannel(preDetectionParam);
        sendYtxSocketSimulate(preDetectionParam);
    }

    @Override
    public void sendScript(SimulateDetectionParam param) {
        Channel channel = SocketManager.getChannelByUserId(param.getUserPageId() + DetectionCommunicateConstant.SOURCE);
        if (Objects.isNull(channel) || !channel.isActive()) {
            // 进行源通信连接
            PreDetectionParam preDetectionParam = new PreDetectionParam();
            preDetectionParam.setSourceId(param.getSourceId());
            preDetectionParam.setUserPageId(param.getUserPageId());
            preDetectionParam.setSendWebMsg(false);
            this.sendYtxSocketSimulate(preDetectionParam);
        }
        //组装源控制脚本
        PqScriptIssueParam issueParam = new PqScriptIssueParam();
        issueParam.setSourceId(param.getSourceId());
        issueParam.setScriptId(param.getScriptId());
        issueParam.setType(1);
        issueParam.setIsPhaseSequence(SourceOperateCodeEnum.SIMULATE_TEST.getValue());

        List<SourceIssue> sourceIssues = pqScriptDtlsService.listSourceIssue(issueParam);
        sourceIssues = sourceIssues.stream()
                .filter(s -> s.getIndex().equals(param.getScriptIndex()))
                .collect(Collectors.toList());

        SourceIssue sourceIssue = sourceIssues.get(0);
        String type = sourceIssue.getType();
        if (sourceIssue.getIsP()) {
            sourceIssue.setType(ResultUnitEnum.V_ABSOLUTELY.getCode());
            type = ResultUnitEnum.P.getCode();
        }

        List<String> comm = sourceIssue.getDevValueTypeList();
        System.out.println("向装置下发的参数ddd>>>>>>>>" + comm);

        SocketMsg<String> socketMsg = new SocketMsg<>();
        socketMsg.setOperateCode(SourceOperateCodeEnum.OPER_GATHER.getValue());
        socketMsg.setRequestId(SourceOperateCodeEnum.FORMAL_REAL.getValue() + CnSocketUtil.STEP_TAG + type);
        socketMsg.setData(JSON.toJSONString(sourceIssues.get(0)));
        SocketManager.sendMsg(param.getUserPageId() + CnSocketUtil.SOURCE_TAG, JSON.toJSONString(socketMsg));
    }

    @Override
    public void closeTestSimulate(SimulateDetectionParam param) {
        Channel channel = SocketManager.getChannelByUserId(param.getUserPageId() + DetectionCommunicateConstant.SOURCE);
        if (Objects.isNull(channel) || !channel.isActive()) {
            throw new BusinessException(DetectionResponseEnum.SOURCE_NOT_CONNECT);
        }
        SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(param.getSourceId());
        PreDetectionParam preDetectionParam = new PreDetectionParam();
        preDetectionParam.setSourceId(sourceParam.getSourceId());
        preDetectionParam.setUserPageId(param.getUserPageId());
        CnSocketUtil.quitSendSource(preDetectionParam);
        WebServiceManager.removePreDetectionParam();
    }

    @Override
    public void startContrastTest(ContrastDetectionParam param) {
        // 参数校验，目前仅检查IP是否重复，后续可在里面扩展
        checkDevIp(param.getDevIds());
        socketContrastResponseService.init(param);

        // 和通信模块进行连接
        this.sendContrastSocket(param);
    }

    @Override
    public void exportAlignData() {
        String fileName = "实时数据.xlsx";
        HttpServletResponse response = HttpServletUtil.getResponse();
        response.reset();
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        response.setContentType("application/octet-stream;charset=UTF-8");
        try {
            InputStream inputStream = new FileInputStream(alignDataFilePath + "\\" + fileName);
            byte[] buffer = new byte[1024];
            int len = 0;
            ServletOutputStream outputStream = response.getOutputStream();
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
            outputStream.flush();
            outputStream.close();
            inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 比对式-与通信模块进行连接
     *
     * @param param
     */
    private void sendContrastSocket(ContrastDetectionParam param) {
        Map<String, List<PreDetection>> map = new HashMap<>(1);
        List<PreDetection> preDetections = new ArrayList<>();
        preDetections.addAll(FormalTestManager.devList);
        preDetections.addAll(FormalTestManager.standardDevList);

        preDetections.forEach(x -> {
            x.setDevType(x.getIcdType());
            x.getMonitorList().forEach(y -> {

                String ptStr = y.getPtStr();
                int i = ptStr.indexOf(":");
                y.setPt(BigDecimal.valueOf(Double.parseDouble(ptStr.substring(0, i))).divide(BigDecimal.valueOf(Double.parseDouble(ptStr.substring(i + 1))), 5, BigDecimal.ROUND_HALF_UP).doubleValue());

                String ctStr = y.getCtStr();
                i = ctStr.indexOf(":");
                y.setCt(BigDecimal.valueOf(Double.parseDouble(ctStr.substring(0, i))).divide(BigDecimal.valueOf(Double.parseDouble(ctStr.substring(i + 1))), 5, BigDecimal.ROUND_HALF_UP).doubleValue());
            });
        });
        map.put("deviceList", preDetections);
        String jsonString = JSON.toJSONString(map);
        SocketMsg<String> socketMsg = new SocketMsg<>();
        socketMsg.setRequestId(SourceOperateCodeEnum.YJC_SBTXJY.getValue());
        AdPlan currentTestPlan = FormalTestManager.currentTestPlan;
        String[] datasource = currentTestPlan.getDatasourceId().split(",");
        if (Arrays.stream(datasource).anyMatch(x -> x.equals(DataSourceEnum.REAL_DATA.getValue()))) {
            socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_02.getValue());
        } else if (Arrays.stream(datasource).anyMatch(x -> x.equals(DataSourceEnum.MINUTE_STATISTICS_AVG.getValue()) || x.equals(DataSourceEnum.MINUTE_STATISTICS_MAX.getValue()) || x.equals(DataSourceEnum.MINUTE_STATISTICS_MIN.getValue()) || x.equals(DataSourceEnum.MINUTE_STATISTICS_CP95.getValue()))) {
            socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_01.getValue());
        } else if (Arrays.stream(datasource).anyMatch(x -> x.equals(DataSourceEnum.WAVE_DATA.getValue()))) {
            socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_02.getValue());
        }
        socketMsg.setData(jsonString);
        PreDetectionParam preDetectionParam = new PreDetectionParam();
        preDetectionParam.setUserPageId(param.getLoginName());
        preDetectionParam.setTestItemList(param.getTestItemList());
        preDetectionParam.setDevIds(param.getDevIds());
        preDetectionParam.setUserId(param.getUserId());
        WebServiceManager.addPreDetectionParam(param.getLoginName(), preDetectionParam);
        socketManager.smartSendToContrast(param, JSON.toJSONString(socketMsg));
    }

    /**
     * 校验被检设备中是否存在IP重复的
     */
    private void checkDevIp(List<String> devIds) {
        List<PqDev> pqDevList = iPqDevService.listByIds(devIds);
        List<String> ipList = pqDevList.stream().map(PqDev::getIp).distinct().collect(Collectors.toList());
        if (ipList.size() != devIds.size()) {
            throw new BusinessException(DetectionResponseEnum.PLAN_DEV_IP_HAS);
        }
    }

}