package me.zhengjie.modules.system.service.impl;//package me.zhengjie.modules.system.service.webstocket;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.modules.security.config.WebSocketConfig;
import me.zhengjie.modules.security.config.enums.*;
import me.zhengjie.modules.system.domain.BusDevice;
import me.zhengjie.modules.system.domain.dto.BusDeviceQueryCriteria;
import me.zhengjie.modules.system.domain.dto.ImageDataVo;
import me.zhengjie.modules.system.service.WebSocketService;
import me.zhengjie.modules.system.service.webstocket.SocketMsg;
import me.zhengjie.modules.system.service.webstocket.req.WebSocketReqDTO;
import me.zhengjie.modules.system.service.webstocket.req.WebSocketReqData;
import me.zhengjie.utils.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@ServerEndpoint("/webSocket/vue")
@Slf4j
@Component
public class WebSocketVueServiceImpl extends WebSocketService {

    // 初始化
    @PostConstruct
    public void init() {
        log.info(">>> 设备初始化");
        refreshDeviceList();
        log.info("<<< 设备初始化");
    }

    public void refreshDeviceList() {
        deviceList = new ArrayList<>();
        List<BusDevice> devices = myDeviceService.queryAll(new BusDeviceQueryCriteria());
        for (BusDevice device : devices) {
            deviceList.add(device.getDeviceSn());
        }
    }

    // 每3分钟检测用户是否断开，断开则需要将视频断开
    @Scheduled(fixedRate = 180000)
    public void checkCloseVideo() {
        List<String> uClients = clients.keySet().stream().filter(i -> !i.equals(WebSocketConfig.TOPIC_PREFIX)).collect(Collectors.toList());
        if (uClients.isEmpty() && !videoUser.isEmpty()) {
            List<String> deviceSns = new ArrayList<>(videoUser.keySet());
            for (String deviceSn : deviceSns) {
                long sysTimeLong = System.currentTimeMillis();
                Map<String, Object> dataParam = new HashMap<>();
                dataParam.put("Enable", 0);
                dataParam.put("UserId", "-1");
                dataParam.put("StreamID", "");
                WebSocketReqData data = new WebSocketReqData();
                data.setCmd(386);
                data.setData(dataParam);
                data.setRequestID(sysTimeLong + "");
                data.setMainboardID(deviceSn);
                data.setTimeStamp(sysTimeLong);
                WebSocketReqDTO req = new WebSocketReqDTO();
                req.setTopic(WebSocketConfig.TOPIC_PREFIX + "/" + MethodEnum.request + "/" + deviceSn);
                req.setData(data);
                sendThirdPartyMessage(JSON.toJSONString(req));
                videoUser.remove(deviceSn);
            }
        }
    }

    /**
     * 连接调用方法
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        this.channel = session.getId();
        log.info("VUE服务端 连接成功：channel={}", channel);
        clients.put(channel, this);
    }

    /**
     * 连接关闭调用的方法
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        WebSocketService collect = getSocket(session.getId());
        if (null == collect) { return; }
        log.info("VUE服务端 连接断开：channel={}", collect.channel);
        clients.remove(collect.channel);
    }

    /**
     * 连接异常时
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        WebSocketService collect = getSocket(session.getId());
        if (null == collect) { return; }
        long time = System.currentTimeMillis();
        log.info("【----------------------------- ↓ " + time + " ↓ -----------------------------】");
        log.info("VUE服务端 发现异常：channel={}，errorMsg={}", collect.channel, error.getMessage());
        error.printStackTrace();
        log.error("VUE服务端 发现异常", error);
        log.info("【----------------------------- ↑ " + time + " ↑ -----------------------------】");
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        WebSocketService collect = getSocket(session.getId());
        if (null == collect) { return; }
        long time = System.currentTimeMillis();
        log.info("【----------------------------- ↓ " + time + " ↓ -----------------------------】");
        log.info("VUE服务端 收到消息：channel={}，message={}", collect.channel, message);
        serviceMessageHandle(session, message);
        log.info("【----------------------------- ↑ " + time + " ↑ -----------------------------】");
    }

    private void serviceMessageHandle(Session session, String message) {
        if (StringUtils.isEmpty(message)) { return; }
        // 发送的指令
        message = message.replace("\\", "");
        WebSocketReqDTO param = null;
        MethodEnum method = null;
        try {
            param = JSON.parseObject(message, WebSocketReqDTO.class);
            method = param.getMethod();
        } catch (Exception e) { e.printStackTrace(); }
        if (null == method || method != MethodEnum.request) {
            log.info("<<< 请求类型有误，忽略");
            return;
        }
        String deviceSn = param.getMainboardID();
        // 只关心已有的设备
        if (!deviceList.contains(deviceSn)) {
            log.info("<<< 设备 {} 在系统未找到，忽略", deviceSn);
            return;
        }
        String command = param.getCommand();
        if (StringUtils.isEmpty(command)) {
            log.info("<<< 命令为空，忽略");
            return;
        }
        long sysTimeLong = System.currentTimeMillis();
        WebSocketReqData data = new WebSocketReqData();
        // 刷新
        // {"Topic":"sdcp/request/12345678","command":"REFRESH"}
        if (command.equals(CapabilitieEnum.REFRESH.name())) {
            data.setCmd(0);
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("UserId", session.getId());
            data.setData(dataParam);
            data.setType(0);
        }
        // 打印 status = (0 || 5)
        // {"Topic":"sdcp/request/12345678","command":"PRINT"}
        else if (command.equals(CapabilitieEnum.PRINT.name())) {
            BusDevice device = myDeviceService.getByDeviceCode(deviceSn);
            if (!(device.getStatus().equals(CurrentStatusEnum.SDCP_MACHINE_STATUS_IDLE.getCode()) || device.getStatus().equals(CurrentStatusEnum.SDCP_MACHINE_STATUS_COMPLETE.getCode()))) {
                Map<String, Object> commandResult = new HashMap<>();
                commandResult.put("deviceSn", deviceSn);
                sendVueMessage(session, JSON.toJSONString(new SocketMsg(commandResult, "当前状态无法操作打印", MsgEnum.ERROR)));
                return;
            }
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("Filename", param.getFilename());
            dataParam.put("UserId", session.getId());
            data.setCmd(128);
            data.setData(dataParam);
            data.setFrom(0);
        }
        // 暂停 status = (1 || 2)
        // {"Topic":"sdcp/request/12345678","command":"PAUSE"}
        else if (command.equals(CapabilitieEnum.PAUSE.name())) {
            BusDevice device = myDeviceService.getByDeviceCode(deviceSn);
            if (!(device.getStatus().equals(CurrentStatusEnum.SDCP_MACHINE_STATUS_PRINTING.getCode()) || device.getStatus().equals(CurrentStatusEnum.SDCP_MACHINE_STATUS_PREPARE.getCode()))) {
                Map<String, Object> commandResult = new HashMap<>();
                commandResult.put("deviceSn", deviceSn);
                sendVueMessage(session, JSON.toJSONString(new SocketMsg(commandResult, "当前状态无法操作暂停", MsgEnum.ERROR)));
                return;
            }
            data.setCmd(129);
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("UserId", session.getId());
            data.setData(dataParam);
            data.setFrom(0);
        }
        // 退出 status = !(-1 || 0 || 6)
        // {"Topic":"sdcp/request/12345678","command":"EXIT"}
        else if (command.equals(CapabilitieEnum.EXIT.name())) {
            BusDevice device = myDeviceService.getByDeviceCode(deviceSn);
            if (device.getStatus().equals(CurrentStatusEnum.SDCP_MACHINE_STATUS_DEFAULT.getCode())
                    || device.getStatus().equals(CurrentStatusEnum.SDCP_MACHINE_STATUS_IDLE.getCode())
                    || device.getStatus().equals(CurrentStatusEnum.SDCP_MACHINE_STATUS_ALARM.getCode())) {
                Map<String, Object> commandResult = new HashMap<>();
                commandResult.put("deviceSn", deviceSn);
                sendVueMessage(session, JSON.toJSONString(new SocketMsg(commandResult, "当前状态无法操作退出", MsgEnum.ERROR)));
                return;
            }
            data.setCmd(130);
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("UserId", session.getId());
            data.setData(dataParam);
            data.setFrom(0);
        }
        // 继续 status = 4 && PrintStatus = 6
        // {"Topic":"sdcp/request/12345678","command":"CONTINUE"}
        else if (command.equals(CapabilitieEnum.CONTINUE.name())) {
            BusDevice device = myDeviceService.getByDeviceCode(deviceSn);
            if (!(device.getStatus().equals(CurrentStatusEnum.SDCP_MACHINE_STATUS_PAUSED.getCode()) && device.getPrintStatus().equals(PrintInfoStatusEnum.SDCP_PRINT_STATUS_PAUSED.getCode()))) {
                Map<String, Object> commandResult = new HashMap<>();
                commandResult.put("deviceSn", deviceSn);
                sendVueMessage(session, JSON.toJSONString(new SocketMsg(commandResult, "当前状态无法操作继续", MsgEnum.ERROR)));
                return;
            }
            data.setCmd(131);
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("UserId", session.getId());
            data.setData(dataParam);
            data.setFrom(0);
        }
        // 图片异物报警继续
        // {"Topic":"sdcp/request/12345678","command":"ALARM_CONTINUE"}
        else if (command.equals(CapabilitieEnum.ALARM_CONTINUE.name())) {
            BusDevice device = myDeviceService.getByDeviceCode(deviceSn);
            if (!device.getErrorStatus().equals(ErrorStatusEnum.SDCP_PRINT_ERROR_PIC.getCode())) {
                Map<String, Object> commandResult = new HashMap<>();
                commandResult.put("deviceSn", deviceSn);
                sendVueMessage(session, JSON.toJSONString(new SocketMsg(commandResult, "当前无异物，无需操作", MsgEnum.ERROR)));
            }
            data.setCmd(132);
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("UserId", session.getId());
            data.setData(dataParam);
            data.setFrom(0);
        }
        // 图片异物报警暂停
        // {"Topic":"sdcp/request/12345678","command":"ALARM_EXIT"}
        else if (command.equals(CapabilitieEnum.ALARM_EXIT.name())) {
            BusDevice device = myDeviceService.getByDeviceCode(deviceSn);
            if (!device.getErrorStatus().equals(ErrorStatusEnum.SDCP_PRINT_ERROR_PIC.getCode())) {
                Map<String, Object> commandResult = new HashMap<>();
                commandResult.put("deviceSn", deviceSn);
                sendVueMessage(session, JSON.toJSONString(new SocketMsg(commandResult, "当前无异物，无需操作", MsgEnum.ERROR)));
            }
            data.setCmd(133);
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("UserId", session.getId());
            data.setData(dataParam);
            data.setFrom(0);
        }
        // 图片
        // {"Topic":"sdcp/request/12345678","command":"IMAGE","category":"0"}
        else if (command.equals(CapabilitieEnum.IMAGE.name())) {
            data.setCmd(385);
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("Category", param.getCategory());
            dataParam.put("UserId", session.getId());
            data.setData(dataParam);
            // 记录用户获取图片
            String mapKey = session.getId() + "_" + param.getCategory();
            imageUser.put(mapKey, new ImageDataVo());
        }
        // 视频
        // {"Topic":"sdcp/request/12345678","command":"OPEN_VIDEO"}
        // {"Topic":"sdcp/request/12345678","command":"CLOSE_VIDEO"}
        else if (command.equals(CapabilitieEnum.OPEN_VIDEO.name()) || command.equals(CapabilitieEnum.CLOSE_VIDEO.name())) {
            data.setCmd(386);
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("Enable", command.equals(CapabilitieEnum.OPEN_VIDEO.name()) ? 1 : 0);
            dataParam.put("UserId", session.getId());
            dataParam.put("StreamID", "");
            data.setData(dataParam);
            LinkedList<String> userIds = videoUser.get(deviceSn);
            // 打开视频
            if (command.equals(CapabilitieEnum.OPEN_VIDEO.name())) {
                userIds = (null == userIds ? new LinkedList<>() : userIds);
                userIds.add(session.getId());
                videoUser.put(deviceSn, userIds);
            }
            // 关闭视频
            else {
                userIds.remove(session.getId());
                if (!userIds.isEmpty()) {
                    videoUser.put(deviceSn, userIds);
                } else {
                    dataParam.put("UserId", "-1");
                    videoUser.remove(deviceSn);
                }
            }
        }
        else {
            log.info("<<< 命令类型未找到，忽略");
            return;
        }
        // 发送指令
        data.setRequestID(sysTimeLong + "");
        data.setMainboardID(deviceSn);
        data.setTimeStamp(sysTimeLong);
        WebSocketReqDTO req = new WebSocketReqDTO();
        req.setTopic(WebSocketConfig.TOPIC_PREFIX + "/" + MethodEnum.request + "/" + deviceSn);
        req.setData(data);
        sendThirdPartyMessage(JSON.toJSONString(req));
    }

    private void sendVueMessage(Session session, String message) {
        try {
            log.info("<<< 返回消息给VUE：message={}", message);
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
            log.info("<<< 返回消息给VUE-异常 原因：", e);
        }
    }

    // 发送消息给第三方
    private void sendThirdPartyMessage(String message) {
        for (Map.Entry<String, WebSocketService> entry : clients.entrySet()) {
            try {
                WebSocketService server = entry.getValue();
                String channel = server.channel;
                Session session = server.session;
                if (!channel.equals(WebSocketConfig.TOPIC_PREFIX) || null == session || !session.isOpen()) { continue; }
                log.info(">>> 发送指令消息给第三方：channel={}，message={}", channel, message);
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
                log.info("<<< 发送指令消息给第三方-异常 原因：", e);
            }
        }
    }
}