package com.njcn.gather.detection.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.njcn.db.mybatisplus.handler.DynamicTableNameHandler;
import com.njcn.gather.detection.pojo.enums.DetectionCodeEnum;
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
import com.njcn.gather.detection.pojo.enums.SourceResponseCodeEnum;
import com.njcn.gather.detection.pojo.param.DevPhaseSequenceParam;
import com.njcn.gather.detection.pojo.po.DevData;
import com.njcn.gather.detection.pojo.vo.SocketDataMsg;
import com.njcn.gather.detection.pojo.vo.SocketMsg;
import com.njcn.gather.detection.util.socket.CnSocketUtil;
import com.njcn.gather.detection.util.socket.FormalTestManager;
import com.njcn.gather.detection.util.socket.MsgUtil;
import com.njcn.gather.detection.util.socket.SocketManager;
import com.njcn.gather.detection.util.socket.cilent.NettyClient;
import com.njcn.gather.detection.util.socket.cilent.NettyFreqConverterDevClientHandler;
import com.njcn.gather.detection.util.socket.config.SocketConnectionConfig;
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.dip.pojo.po.PqDipData;
import com.njcn.gather.dip.service.IPqDipDataService;
import com.njcn.gather.freqConverter.config.FreqConverterConfig;
import com.njcn.gather.freqConverter.pojo.po.FreqConverterStatus;
import com.njcn.gather.freqConverter.pojo.po.PqFreqConverterTestRes;
import com.njcn.gather.freqConverter.service.IFreqConverterService;
import com.njcn.gather.freqConverter.service.IPqFreqConverterTestResService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class SocketFreqConverterDevService {

    private final SocketConnectionConfig socketConnectionConfig;
    private final IPqDevService pqDevService;
    private final IPqDipDataService pqDipDataService;
    private final IFreqConverterService freqConverterService;
    private final IPqFreqConverterTestResService pqFreqConverterTestResService;
    private final FreqConverterConfig freqConverterConfig;
    private String monitorId;
    private String userId;

    public static final String DIP_DATA_SUFFIX = "&&VOLTAGE";

    /**
     * 连接设备Socket
     *
     * @param devTag 设备Channel唯一标识符
     */
    public void connectSocket(String devTag) {
        if (SocketManager.isChannelActive(devTag)) {
            return;
        }
        String ip = socketConnectionConfig.getDevice().getIp();
        Integer port = socketConnectionConfig.getDevice().getPort();

        NettyFreqConverterDevClientHandler handler = new NettyFreqConverterDevClientHandler(devTag, this);
        CompletableFuture.runAsync(() -> {
            NettyClient.commonConnect(ip, port, devTag, handler);
        });
    }

    private void init(String userId, String converterId, String monitorId, Boolean reset) {
        FormalTestManager.freqConverterDevStep = null;
//        FormalTestManager.stopFlag = false;
        FormalTestManager.isRemoveSocket = false;
        FormalTestManager.pendingDipTaskMap.clear();
        if (reset) {
            pqDipDataService.clearAllData(FormalTestManager.freqConverterTableSuffix);
            pqFreqConverterTestResService.clearAllData(FormalTestManager.freqConverterTableSuffix);
        }
        this.userId = userId;
        this.monitorId = monitorId;
    }

    /**
     * 连接设备
     */
    public void connectionDev(String userId, String devTag, String converterId, String monitorId, Boolean reset) {
        this.init(userId, converterId, monitorId, reset);

        String payload = buildSingleMonitorPayload(monitorId);
        if (StrUtil.isBlank(payload)) {
            return;
        }

        SocketMsg<String> socketMsg = new SocketMsg<>();
        socketMsg.setRequestId(SourceOperateCodeEnum.YJC_SBTXJY.getValue());
        socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_INIT_GATHER_03.getValue());
        socketMsg.setData(payload);
        SocketManager.sendMsg(devTag, JSON.toJSONString(socketMsg));
        FormalTestManager.freqConverterDevStep = SourceOperateCodeEnum.YJC_SBTXJY;
    }

    public void handleRead(String devTag, String msg) {
        SocketDataMsg socketDataMsg = MsgUtil.socketDataMsg(msg);

        switch (FormalTestManager.freqConverterDevStep) {
            case YJC_SBTXJY:
                handleYjcSbtxjy(devTag, socketDataMsg);
                break;
            case FORMAL_REAL:
                handleFormalReal(devTag, socketDataMsg);
                break;
            case QUITE:
                handleQuit(devTag, socketDataMsg);
                break;
        }
    }

    private void handleYjcSbtxjy(String devTag, SocketDataMsg socketDataMsg) {
        SourceResponseCodeEnum responseCodeEnum = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());
        switch (Objects.requireNonNull(responseCodeEnum)) {
            case UNPROCESSED_BUSINESS:
                break;
            case RE_OPERATE:
                WebServiceManager.sendMsg(this.userId, JSON.toJSONString(socketDataMsg));
                break;
            case SUCCESS:
                // 暂态协议触发后等待5秒，将装置历史缓存的暂态数据给抛掉
                CompletableFuture.runAsync(() -> {
                    try {
                        Thread.sleep(5000);
                        this.sendGetDipDataMsg(devTag);
                        FormalTestManager.freqConverterDevStep = SourceOperateCodeEnum.FORMAL_REAL;
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        log.error("异步调用sendGetDipDataMsg被中断", e);
                    }
                });
                break;
            default:
                log.warn("设备响应异常，devTag={}, operateCode={}, code={}, data={}", devTag, socketDataMsg.getOperateCode(), socketDataMsg.getCode(), socketDataMsg.getData());
                break;
        }
    }

    private void handleFormalReal(String devTag, SocketDataMsg socketDataMsg) {
        SourceResponseCodeEnum responseCodeEnum = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());

        switch (responseCodeEnum) {
            case UNPROCESSED_BUSINESS:
                break;
            case SUCCESS:
            case NORMAL_RESPONSE:
                DevData devData = JSON.parseObject(socketDataMsg.getData(), DevData.class);
                // 如果变频器不是处于 “故障中” 状态，就保存数据，反之，这段时期内的数据不保存
//                if (!FormalTestManager.stopFlag) {
//                    saveDipData(devData);
//                }
                saveDipData(devData);
                break;
            case DEV_ERROR:
            case DEV_TARGET:
            case COMMUNICATION_ERR:
            case DATA_RESOLVE:
            case NO_INIT_DEV:
            default:
                log.warn("设备响应异常，devTag={}, operateCode={}, code={}, data={}", devTag, socketDataMsg.getOperateCode(), socketDataMsg.getCode(), socketDataMsg.getData());
                break;
        }
    }

    private void handleQuit(String devTag, SocketDataMsg socketDataMsg) {
        SourceResponseCodeEnum responseCodeEnum = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode());

        switch (responseCodeEnum) {
            case UNPROCESSED_BUSINESS:
                break;
            case SUCCESS:
                cleanup(devTag);
                break;
            default:
                log.warn("设备关闭响应失败，devTag={}, operateCode={}, code={}, data={}", devTag, socketDataMsg.getOperateCode(), socketDataMsg.getCode(), socketDataMsg.getData());
                break;
        }
    }

    public void stopTest(String converterTag, String devTag) {
        FormalTestManager.freqConverterDevStep = SourceOperateCodeEnum.QUITE;
        sendQuitMsg(devTag, SourceOperateCodeEnum.QUIT_INIT_03);
    }


    private String buildSingleMonitorPayload(String monitorId) {
        String[] split = monitorId.split(CnSocketUtil.SPLIT_TAG);
        if (split.length < 2 || StrUtil.isBlank(split[0]) || StrUtil.isBlank(split[1])) {
            return null;
        }

        List<PreDetection> preDetections = pqDevService.getDevInfo(Collections.singletonList(split[0]));
        if (CollUtil.isEmpty(preDetections)) {
            return null;
        }

        PreDetection preDetection = preDetections.get(0);
        List<PreDetection.MonitorListDTO> monitorList = preDetection.getMonitorList();
        if (CollUtil.isEmpty(monitorList)) {
            return null;
        }

        List<PreDetection.MonitorListDTO> matchedMonitorList = monitorList.stream()
                .filter(item -> split[1].equals(StrUtil.EMPTY + item.getLine()))
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(matchedMonitorList)) {
            return null;
        }

        preDetection.setMonitorList(matchedMonitorList);
        Map<String, List<PreDetection>> payload = new HashMap<>(1);
        payload.put("deviceList", Collections.singletonList(preDetection));
        return JSON.toJSONString(payload);
    }

    private void sendGetDipDataMsg(String devTag) {
        SocketMsg<String> socketMsg = new SocketMsg<>();
        socketMsg.setRequestId(SourceOperateCodeEnum.FORMAL_REAL.getValue() + DIP_DATA_SUFFIX);
        socketMsg.setOperateCode(SourceOperateCodeEnum.OPER_GATHER.getValue());

        DevPhaseSequenceParam phaseSequenceParam = new DevPhaseSequenceParam();
        String[] split = this.monitorId.split(String.valueOf(StrUtil.C_UNDERLINE));
        PqDev dev = pqDevService.getById(split[0]);

        // 设置监测点ID列表
        phaseSequenceParam.setMoniterIdList(ListUtil.of(dev.getIp() + StrUtil.C_UNDERLINE + split[1]));

        // 设置数据类型列表
        phaseSequenceParam.setDataType(ListUtil.of("avg$MAG", "avg$DUR"));
        // 设置读取次数
        phaseSequenceParam.setReadCount(0);
        // 设置忽略次数
        phaseSequenceParam.setIgnoreCount(0);
        socketMsg.setData(JSON.toJSONString(phaseSequenceParam));
        socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_03.getValue());
        SocketManager.sendMsg(devTag, JSON.toJSONString(socketMsg));
    }

    private void sendQuitMsg(String devTag, SourceOperateCodeEnum operateCodeEnum) {
        SocketMsg<String> socketMsg = new SocketMsg<>();
        socketMsg.setRequestId(SourceOperateCodeEnum.QUITE.getValue());
        socketMsg.setOperateCode(operateCodeEnum.getValue());
        SocketManager.sendMsg(devTag, JSON.toJSONString(socketMsg));
    }

    private void saveDipData(DevData devData) {
        if (Objects.isNull(devData) || CollUtil.isEmpty(devData.getSqlData())) {
            return;
        }

        Double residualVoltage = null;
        Integer durationMs = null;
        for (DevData.SqlDataDTO sqlDataDTO : devData.getSqlData()) {
            if (Objects.isNull(sqlDataDTO) || Objects.isNull(sqlDataDTO.getList())) {
                continue;
            }

            Double value = getSqlDataValue(sqlDataDTO.getList());
            if (Objects.isNull(value)) {
                continue;
            }

            if (DetectionCodeEnum.MAG.getCode().equalsIgnoreCase(sqlDataDTO.getDesc())) {
                residualVoltage = value;
                if (residualVoltage <= 6) {
                    return;
                }
            } else if (DetectionCodeEnum.DUR.getCode().equalsIgnoreCase(sqlDataDTO.getDesc())) {
                durationMs = (int) Math.round(value * 1000);
            }
        }

        PqDipData pqDipData = new PqDipData();
        pqDipData.setId(IdUtil.fastSimpleUUID());
        pqDipData.setStartTime(LocalDateTime.parse(devData.getTime()));
        pqDipData.setResidualVoltage(residualVoltage);
        pqDipData.setDurationMs(durationMs);
        DynamicTableNameHandler.setTableName("pq_dip_data_" + FormalTestManager.freqConverterTableSuffix);
        pqDipDataService.save(pqDipData);
        DynamicTableNameHandler.remove();

        this.initDipTestRes(pqDipData);
    }

    private void initDipTestRes(PqDipData pqDipData) {
        Integer suffix = FormalTestManager.freqConverterTableSuffix;
        FreqConverterStatus lastStatusData = freqConverterService.getLastStatusData(suffix, pqDipData.getStartTime());
        if (Objects.isNull(lastStatusData)) {
            return;
        }

        List<FreqConverterStatus> statusList = freqConverterService.getDipDurationStatusData(suffix, lastStatusData.getTimestamp(), pqDipData.getStartTime().plusNanos(pqDipData.getDurationMs() * 1000_000L));
        Integer originalTolerant = (lastStatusData.getStatusWord1() == freqConverterConfig.getTolerant()) ? 1 : 0;
        LocalDateTime targetEndTime = pqDipData.getStartTime()
                .plusNanos(pqDipData.getDurationMs() * 1000_000L)
                .plusNanos(freqConverterConfig.getDt() * 1000_000L);

        if (CollUtil.isNotEmpty(statusList)) {
            FreqConverterStatus status = statusList.get(statusList.size() - 1);
            PqFreqConverterTestRes testRes = new PqFreqConverterTestRes();
            testRes.setId(IdUtil.fastSimpleUUID());
            testRes.setTolerant(originalTolerant == 1 ?
                    (status.getStatusWord1() == freqConverterConfig.getTolerant() ? 1 : 0)
                    : 0);
            testRes.setDurationMs(pqDipData.getDurationMs());
            testRes.setResidualVoltage(pqDipData.getResidualVoltage());
            testRes.setTime(LocalDateTime.now());

            FormalTestManager.pendingDipTaskMap.put(testRes.getId(), new FormalTestManager.PendingDipTask(
                    pqDipData,
                    targetEndTime,
                    originalTolerant
            ));

            pqFreqConverterTestResService.saveTestRes(suffix, Collections.singletonList(testRes));
        }
    }

    private Double getSqlDataValue(DevData.SqlDataDTO.ListDTO listDTO) {
        if (Objects.nonNull(listDTO.getA())) {
            return listDTO.getA();
        }
        if (Objects.nonNull(listDTO.getB())) {
            return listDTO.getB();
        }
        if (Objects.nonNull(listDTO.getC())) {
            return listDTO.getC();
        }
        return listDTO.getT();
    }

    public void cleanup(String devTag) {
        String currentUserId = this.userId;
        FormalTestManager.freqConverterDevStep = null;
        FormalTestManager.isRemoveSocket = true;
        SocketManager.removeUser(devTag);
        clearStateIfStopped(currentUserId);
        this.userId = null;
        this.monitorId = null;
    }

    /**
     * 如果设备已停止，则清除共享的运行时状态
     *
     * @param currentUserId 当前用户ID
     */
    private void clearStateIfStopped(String currentUserId) {
        if (StrUtil.isBlank(currentUserId)) {
            FormalTestManager.clearFreqConverterRuntimeState();
            return;
        }
        String freqConverterTag = currentUserId + CnSocketUtil.FREQ_CONVERTER_TAG;
        // 避免过早把 freqConverterTableSuffix 等全局值清掉，造成变频器无法使用全局变量
        if (!SocketManager.isChannelActive(freqConverterTag)) {
            FormalTestManager.freqConverterStep = null;
            FormalTestManager.clearFreqConverterRuntimeState();
        }
    }
}
