package com.njcn.gather.detection.handler;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.njcn.gather.detection.pojo.dto.FreqConverterRespDTO;
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
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.SocketManager;
import com.njcn.gather.detection.util.socket.cilent.NettyClient;
import com.njcn.gather.detection.util.socket.cilent.NettyFreqConverterClientHandler;
import com.njcn.gather.detection.util.socket.config.SocketConnectionConfig;
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
import com.njcn.gather.freqConverter.config.FreqConverterConfig;
import com.njcn.gather.freqConverter.pojo.po.FreqConverterStatus;
import com.njcn.gather.freqConverter.pojo.po.PqFreqConverterConfig;
import com.njcn.gather.freqConverter.pojo.po.PqFreqConverterTestRes;
import com.njcn.gather.freqConverter.pojo.vo.TolerantPointVO;
import com.njcn.gather.freqConverter.service.IFreqConverterService;
import com.njcn.gather.freqConverter.service.IPqFreqConverterConfigService;
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.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author czh
 * @version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SocketFreqConverterService {
    private final IFreqConverterService freqConverterService;

    private final SocketConnectionConfig socketConnectionConfig;

    private final IPqFreqConverterConfigService pqFreqConverterConfigService;
    private final IPqFreqConverterTestResService pqFreqConverterTestResService;
    private String userId;
    /**
     * 上一个暂降点耐受检测结果
     */
    private TolerantPointVO lastTolerancePoint;
    private final FreqConverterConfig freqConverterConfig;

    /**
     * 连接变频器Socket
     *
     * @param converterChannelTag 变频器Channel唯一标识符
     */
    public void connectSocket(String converterChannelTag) {
        if (SocketManager.isChannelActive(converterChannelTag)) {
            return;
        }
        String ip = socketConnectionConfig.getFreqConverter().getIp();
        Integer port = socketConnectionConfig.getFreqConverter().getPort();

        NettyFreqConverterClientHandler handler = new NettyFreqConverterClientHandler(converterChannelTag, this);

        CompletableFuture.runAsync(() -> {
            NettyClient.commonConnect(ip, port, converterChannelTag, handler);
        });
    }

    /**
     * 重连
     *
     * @param converterChannelTag
     */
    public void reconnect(String converterChannelTag) {
        SocketManager.removeUser(converterChannelTag);

        String ip = socketConnectionConfig.getFreqConverter().getIp();
        Integer port = socketConnectionConfig.getFreqConverter().getPort();

        NettyFreqConverterClientHandler handler = new NettyFreqConverterClientHandler(converterChannelTag, this);

        CompletableFuture.runAsync(() -> {
            NettyClient.commonConnect(ip, port, converterChannelTag, handler);
        });
    }

    /**
     * 重试连接成功，重新开启定时任务，获取变频器状态数据
     *
     * @param converterChannelTag
     */
    public void onReconnectSuccess(String converterChannelTag) {
        log.info("变频器重连成功，恢复数据采集，converterChannelTag={}", converterChannelTag);

//        FormalTestManager.stopFlag = false;

        if (FormalTestManager.scheduler == null) {
            FormalTestManager.scheduler = Executors.newScheduledThreadPool(1);
            FormalTestManager.scheduledFuture = FormalTestManager.scheduler.scheduleAtFixedRate(() -> {
                this.sendGetDeviceStatusMsg(converterChannelTag);
            }, 0l, 200l, TimeUnit.MILLISECONDS);
        }
    }

    public void init(String userId, String converterId, String monitorId, Boolean reset) {
        this.lastTolerancePoint = null;
        this.userId = userId;
        FormalTestManager.freqConverterStep = null;
        FormalTestManager.currentFreqConverterId = converterId;
        Integer suffix = pqFreqConverterConfigService.getSuffix(converterId);
        if (reset) {
            freqConverterService.clearAllData(suffix);
        }
        FormalTestManager.freqConverterTableSuffix = suffix;
        FormalTestManager.isRemoveSocket = false;
        FormalTestManager.pendingDipTaskMap.clear();
        pqFreqConverterConfigService.updateTestStatus(converterId, 0);
        clearScheduleTask();
    }

    /**
     * 连接变频器
     */
    public void connectionFreqConverter(String userId, String freqConverterTag, String converterId, String monitorId, Boolean reset) {
        this.init(userId, converterId, monitorId, reset);

        SocketMsg<Map<String, Object>> socketMsg = new SocketMsg<>();
        socketMsg.setOperateCode(SourceOperateCodeEnum.CMD_INIT_SERIAL.getValue());
        String requestId = IdUtil.fastSimpleUUID();
        socketMsg.setRequestId(requestId);

        PqFreqConverterConfig freqConverterConfig = pqFreqConverterConfigService.getById(converterId);

        Map<String, Object> map = new HashMap<>();
        map.put("portName", freqConverterConfig.getPortName());
        map.put("slaveAddress", freqConverterConfig.getSlaveAddress());
        map.put("baudRate", freqConverterConfig.getBaudRate());
        map.put("parity", freqConverterConfig.getParity());
        map.put("dataBits", freqConverterConfig.getDataBits());
        map.put("stopBits", freqConverterConfig.getStopBits());
        map.put("timeoutMs", freqConverterConfig.getTimeoutMs());
        socketMsg.setData(map);

        SocketManager.sendMsg(freqConverterTag, JSON.toJSONString(socketMsg));
        FormalTestManager.freqConverterStep = SourceOperateCodeEnum.CMD_INIT_SERIAL;
    }

    public void handleRead(String converterChannelTag, String msg) {
        FreqConverterRespDTO respDTO = JSON.parseObject(msg, FreqConverterRespDTO.class);

        if (respDTO.getCode() != 0) {
            SocketDataMsg socketDataMsg = new SocketDataMsg();
            socketDataMsg.setRequestId(FormalTestManager.freqConverterStep.getValue());
            socketDataMsg.setCode(respDTO.getCode());
            WebServiceManager.sendMsg(this.userId, JSON.toJSONString(socketDataMsg));
        } else {
            switch (FormalTestManager.freqConverterStep) {
                case CMD_INIT_SERIAL:
                    handleInitSerial(converterChannelTag, respDTO);
                    break;
                case CMD_GET_DEVICE_STATUS:
                    handleGetDeviceStatus(converterChannelTag, respDTO);
                    break;
                case CMD_CLOSE_SERIAL:
                    handleCloseSerial(converterChannelTag, respDTO);
                    break;
            }
        }
    }

    public void stopTest(String converterTag, String devTag) {
        FormalTestManager.freqConverterStep = SourceOperateCodeEnum.CMD_CLOSE_SERIAL;
        this.sendClose(converterTag);
    }

    public void cleanup(String converterChannelTag) {
        String currentUserId = this.userId;
        clearScheduleTask();
        FormalTestManager.freqConverterStep = null;
//        FormalTestManager.stopFlag = false;
        FormalTestManager.isRemoveSocket = true;
        SocketManager.removeUser(converterChannelTag);
        updateCurrentTestStatus();
        clearStateIfStopped(currentUserId);
        this.userId = null;
        this.lastTolerancePoint = null;
    }

    private void handleInitSerial(String converterChannelTag, FreqConverterRespDTO respDTO) {
        if (respDTO.getCode() == 0 && respDTO.getSuccess()) {
            FormalTestManager.freqConverterStep = SourceOperateCodeEnum.CMD_GET_DEVICE_STATUS;

            if (Objects.isNull(FormalTestManager.scheduler)) {
                FormalTestManager.scheduler = Executors.newScheduledThreadPool(1);
                FormalTestManager.scheduledFuture = FormalTestManager.scheduler.scheduleAtFixedRate(() -> {
                    this.sendGetDeviceStatusMsg(converterChannelTag);
                }, 0l, freqConverterConfig.getSchedulePeriod(), TimeUnit.MILLISECONDS);
            }
        } else {
            log.warn("变频器初始化串口失败，converterChannelTag={}, converterId={}, converterTag={}, msg={}", converterChannelTag, converterChannelTag, converterChannelTag, respDTO.getMessage());
        }
    }

    private void handleGetDeviceStatus(String converterChannelTag, FreqConverterRespDTO respDTO) {
        JSONObject obj = JSONUtil.parseObj(respDTO.getData().toString());
        String timestamp = (String) obj.get("Timestamp");
        timestamp = timestamp.replace("+08:00", StrUtil.EMPTY);
        obj.set("Timestamp", timestamp);

        FreqConverterStatus freqConverterStatus = JSON.parseObject(obj.toString(), FreqConverterStatus.class);
        // 变频器故障中，移除这段时期内的设备数据
//        if (freqConverterStatus.getStatusWord1() == freqConverterConfig.getNoTolerant()) {
//            FormalTestManager.stopFlag = true;
//        } else {
//            FormalTestManager.stopFlag = false;
//        }
        this.consumePendingDipTasks(freqConverterStatus);
        freqConverterService.saveFreqConverterStatus(FormalTestManager.freqConverterTableSuffix, freqConverterStatus);
    }

    private void handleCloseSerial(String converterChannelTag, FreqConverterRespDTO respDTO) {
        if (respDTO.getCode() == 0 && respDTO.getSuccess()) {
            if (FormalTestManager.currentFreqConverterId != null) {
                pqFreqConverterConfigService.updateTestStatus(FormalTestManager.currentFreqConverterId, 1);
            }
            cleanup(converterChannelTag);
        } else {
            this.sendClose(converterChannelTag);
        }
    }


    private void sendGetDeviceStatusMsg(String converterId) {
        SocketMsg<Map<String, Object>> socketMsg = new SocketMsg<>();
        socketMsg.setOperateCode(SourceOperateCodeEnum.CMD_GET_DEVICE_STATUS.getValue());
        String requestId = IdUtil.fastSimpleUUID();
        socketMsg.setRequestId(requestId);

        Map<String, Object> map = new HashMap<>();
        socketMsg.setData(map);
        SocketManager.sendMsg(converterId, JSON.toJSONString(socketMsg));
    }

    private void sendClose(String converterTag) {
        SocketMsg<Map<String, Object>> socketMsg = new SocketMsg<>();
        socketMsg.setOperateCode(SourceOperateCodeEnum.CMD_CLOSE_SERIAL.getValue());
        String requestId = IdUtil.fastSimpleUUID();
        socketMsg.setRequestId(requestId);

        Map<String, Object> map = new HashMap<>();
        socketMsg.setData(map);
        SocketManager.sendMsg(converterTag, JSON.toJSONString(socketMsg));
    }


    private void clearScheduleTask() {
        if (FormalTestManager.scheduledFuture != null) {
            FormalTestManager.scheduledFuture.cancel(true);
            FormalTestManager.scheduledFuture = null;
        }
        if (FormalTestManager.scheduler != null) {
            FormalTestManager.scheduler.shutdown();
            FormalTestManager.scheduler = null;
        }
    }

    private void updateCurrentTestStatus() {
        if (StrUtil.isNotBlank(FormalTestManager.currentFreqConverterId)) {
            pqFreqConverterConfigService.updateTestStatus(FormalTestManager.currentFreqConverterId, 1);
        }
    }

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

    private void consumePendingDipTasks(FreqConverterStatus freqConverterStatus) {
        if (FormalTestManager.pendingDipTaskMap.isEmpty()) {
            return;
        }

        Integer suffix = FormalTestManager.freqConverterTableSuffix;
        List<String> finishedTestResIdList = new ArrayList<>();
        List<PqFreqConverterTestRes> saveTestResList = new ArrayList<>();
        List<PqFreqConverterTestRes> updateTestResList = new ArrayList<>();

        FormalTestManager.pendingDipTaskMap.forEach((key, task) -> {
            if (freqConverterStatus.getTimestamp().isAfter(task.getTargetEndTime())) {
                PqFreqConverterTestRes testRes = new PqFreqConverterTestRes();
                testRes.setId(key);
                testRes.setDurationMs(task.getPqDipData().getDurationMs());
                testRes.setResidualVoltage(task.getPqDipData().getResidualVoltage());
                testRes.setTolerant(task.getOriginalTolerant() & (freqConverterStatus.getStatusWord1() == freqConverterConfig.getTolerant() ? 1 : 0));

                finishedTestResIdList.add(key);

                SocketDataMsg socketDataMsg = new SocketDataMsg();
                socketDataMsg.setRequestId(SourceOperateCodeEnum.FORMAL_REAL + SocketFreqConverterDevService.DIP_DATA_SUFFIX);

                TolerantPointVO newTolerantPointVO = new TolerantPointVO();
                newTolerantPointVO.setResidualVoltage(task.getPqDipData().getResidualVoltage());
                newTolerantPointVO.setDurationMs(task.getPqDipData().getDurationMs());
                newTolerantPointVO.setTolerant(testRes.getTolerant());
                socketDataMsg.setData(JSON.toJSONString(newTolerantPointVO));
                WebServiceManager.sendMsg(this.userId, JSON.toJSONString(socketDataMsg));

                if (testRes.getTolerant() == 0) {
                    if (ObjectUtil.isNotNull(this.lastTolerancePoint) && this.lastTolerancePoint.getTolerant() == 1) {
                        TolerantPointVO featurePointVO = new TolerantPointVO();

                        featurePointVO.setResidualVoltage(Math.round((task.getPqDipData().getResidualVoltage() + this.lastTolerancePoint.getResidualVoltage()) / 2D * 100) / 100D);
                        featurePointVO.setDurationMs(Integer.valueOf((task.getPqDipData().getDurationMs().intValue() + this.lastTolerancePoint.getDurationMs().intValue()) / 2));
                        featurePointVO.setTolerant(2);
                        socketDataMsg.setData(JSON.toJSONString(featurePointVO));
                        WebServiceManager.sendMsg(this.userId, JSON.toJSONString(socketDataMsg));

                        PqFreqConverterTestRes featureTestRes = new PqFreqConverterTestRes();
                        featureTestRes.setId(IdUtil.fastSimpleUUID());
                        featureTestRes.setDurationMs(featurePointVO.getDurationMs());
                        featureTestRes.setResidualVoltage(featurePointVO.getResidualVoltage());
                        featureTestRes.setTolerant(2);
                        featureTestRes.setTime(LocalDateTime.now());
                        saveTestResList.add(featureTestRes);
                    }

                    // 从数据库按照列查询距离该暂降点最近的一个暂降点
                    if (freqConverterConfig.getDirection() == 0) {
                        PqFreqConverterTestRes lastByDuration = pqFreqConverterTestResService.getLastByDuration(suffix, key, task.getPqDipData().getDurationMs());
                        if (ObjectUtil.isNotNull(lastByDuration) && lastByDuration.getTolerant() == 1) {
                            TolerantPointVO featurePointVO = new TolerantPointVO();

                            featurePointVO.setResidualVoltage(Math.round((task.getPqDipData().getResidualVoltage() + lastByDuration.getResidualVoltage()) / 2D * 100) / 100D);
                            featurePointVO.setDurationMs(Integer.valueOf((task.getPqDipData().getDurationMs().intValue() + lastByDuration.getDurationMs().intValue()) / 2));
                            featurePointVO.setTolerant(2);
                            socketDataMsg.setData(JSON.toJSONString(featurePointVO));
                            WebServiceManager.sendMsg(this.userId, JSON.toJSONString(socketDataMsg));

                            PqFreqConverterTestRes featureTestRes = new PqFreqConverterTestRes();
                            featureTestRes.setId(IdUtil.fastSimpleUUID());
                            featureTestRes.setDurationMs(featurePointVO.getDurationMs());
                            featureTestRes.setResidualVoltage(featurePointVO.getResidualVoltage());
                            featureTestRes.setTolerant(2);
                            featureTestRes.setTime(LocalDateTime.now());
                            saveTestResList.add(featureTestRes);
                        }
                    }
                    // 从数据库按照行查询距离该暂降点最近的一个暂降点
                    if (freqConverterConfig.getDirection() == 1) {
                        PqFreqConverterTestRes lastByResidualVoltage = pqFreqConverterTestResService.getLastByResidualVoltage(suffix, key, task.getPqDipData().getResidualVoltage());
                        if (ObjectUtil.isNotNull(lastByResidualVoltage) && lastByResidualVoltage.getTolerant() == 1) {
                            TolerantPointVO featurePointVO = new TolerantPointVO();

                            featurePointVO.setResidualVoltage(Math.round((task.getPqDipData().getResidualVoltage() + lastByResidualVoltage.getResidualVoltage()) / 2D * 100) / 100D);
                            featurePointVO.setDurationMs(Integer.valueOf((task.getPqDipData().getDurationMs().intValue() + lastByResidualVoltage.getDurationMs().intValue()) / 2));
                            featurePointVO.setTolerant(2);
                            socketDataMsg.setData(JSON.toJSONString(featurePointVO));
                            WebServiceManager.sendMsg(this.userId, JSON.toJSONString(socketDataMsg));

                            PqFreqConverterTestRes featureTestRes = new PqFreqConverterTestRes();
                            featureTestRes.setId(IdUtil.fastSimpleUUID());
                            featureTestRes.setDurationMs(featurePointVO.getDurationMs());
                            featureTestRes.setResidualVoltage(featurePointVO.getResidualVoltage());
                            featureTestRes.setTolerant(2);
                            featureTestRes.setTime(LocalDateTime.now());
                            saveTestResList.add(featureTestRes);
                        }
                    }
                }
                this.lastTolerancePoint = newTolerantPointVO;
                updateTestResList.add(testRes);
            }
        });

        if (!saveTestResList.isEmpty()) {
            pqFreqConverterTestResService.saveTestRes(suffix, saveTestResList);
        }
        if (!updateTestResList.isEmpty()) {
            pqFreqConverterTestResService.updateTestRes(suffix, updateTestResList);
        }

        for (String dipId : finishedTestResIdList) {
            FormalTestManager.pendingDipTaskMap.remove(dipId);
        }
    }

    /**
     * 判断是否为新的一组测试脚本
     *
     * @param lastTolerancePoint 上一个暂降点
     * @param newTolerantPointVO 最新的暂降点
     * @return
     */
    private boolean isNewGroup(TolerantPointVO lastTolerancePoint, TolerantPointVO newTolerantPointVO) {
        // 横向分组
        if (freqConverterConfig.getDirection() == 0) {
            return lastTolerancePoint.getDurationMs() - newTolerantPointVO.getDurationMs() >= 10;
        }
        // 纵向分租
        if (freqConverterConfig.getDirection() == 1) {
            return lastTolerancePoint.getResidualVoltage() - newTolerantPointVO.getResidualVoltage() <= -2;
        }

//        if (Math.abs(lastTolerancePoint.getDurationMs() - newTolerantPointVO.getDurationMs()) >= 10 && Math.abs(lastTolerancePoint.getResidualVoltage() - newTolerantPointVO.getResidualVoltage()) >= 2) {
//            return true;
//        }
        return false;
    }
}


