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

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
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.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.modules.system.service.webstocket.res.*;
import me.zhengjie.utils.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@ServerEndpoint("/webSocket/sdcp")
@Slf4j
@Component
public class WebSocketSdcpServiceImpl extends WebSocketService {

    /**
     * 连接调用方法
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        this.channel = WebSocketConfig.TOPIC_PREFIX;
        log.info("【SDCP服务端】连接成功：channel={}", channel);
        clients.put(channel, this);
        openDeviceStatus();
    }
    /**
     * 连接关闭调用的方法
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        WebSocketService collect = getSocket(WebSocketConfig.TOPIC_PREFIX);
        if (null == collect) { return; }
        log.info("【SDCP服务端】连接断开：channel={}", collect.channel);
        closeDeviceStatus();
    }

    /**
     * 连接异常时
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        WebSocketService collect = getSocket(WebSocketConfig.TOPIC_PREFIX);
        if (null == collect) { return; }
        log.info("【SDCP服务端】发现异常：channel={}", collect.channel);
        error.printStackTrace();
    }

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

    // 连接关闭状态刷新
    private void closeDeviceStatus() {
        for (String deviceSn : deviceList) {
            WebSocketReqDTO reqDTO = new WebSocketReqDTO();
            reqDTO.setTopic(WebSocketConfig.TOPIC_PREFIX + "/" + MethodEnum.close.name() + "/" + deviceSn);
            serviceMessageHandle(null, JSON.toJSONString(reqDTO));
        }
    }

    // 初始状态刷新消息(Cmd:0) 指令
    private void openDeviceStatus() {
        for (String deviceSn : deviceList) {
            long currentTimeLong = System.currentTimeMillis();
            WebSocketReqDTO reqDTO = new WebSocketReqDTO();
            reqDTO.setTopic(WebSocketConfig.TOPIC_PREFIX + "/" + MethodEnum.request.name() + "/" + deviceSn);
            WebSocketReqData data = new WebSocketReqData();
            data.setCmd(0);
            data.setData(new HashMap());
            data.setRequestID(currentTimeLong + "");
            data.setMainboardID(deviceSn);
            data.setTimeStamp(currentTimeLong);
            data.setFrom(0);
            reqDTO.setData(data);
            serviceMessageHandle(null, JSON.toJSONString(reqDTO));
        }
    }

    // 处理服务端接收到的消息
    private void serviceMessageHandle(Session session, String message) {
        if (StringUtils.isEmpty(message)) { return; }
        // 如果是第三方发来的[心跳]检测，返回 pong
        if (WebSocketConfig.PING.equals(message)) {
            log.info("<<< 心跳反馈");
            try { session.getBasicRemote().sendText(WebSocketConfig.PONG);
            } catch (IOException e) { throw new RuntimeException(e); }
            return;
        }
        message = message.replace("\\", "");
        WebSocketResDTO param = null;
        MethodEnum method = null;
        try {
            param = JSON.parseObject(message, WebSocketResDTO.class);
            method = param.getMethod();
        } catch (Exception e) { e.printStackTrace(); }
        if (null == method) {
            log.info("<<< 请求类型有误，忽略");
            return;
        }
        String deviceSn = param.getMainboardID();
        // 只关心已有的设备
        if (!deviceList.contains(deviceSn)) {
            log.info("<<< 设备【{}】在系统未找到，忽略", deviceSn);
            return;
        }
        if (method == MethodEnum.request) {
            try {
                WebSocketService socket = getSocket(WebSocketConfig.TOPIC_PREFIX);
                socket.session.getBasicRemote().sendText(message);
            } catch (IOException e) { throw new RuntimeException(e); }
            return;
        }
        WebSocketResDTO resDTO = JSON.parseObject(message, WebSocketResDTO.class);
        Map<String, Object> commandResult = new HashMap<>();
        commandResult.put("deviceSn", deviceSn);
        if (method == MethodEnum.response) {
            // 指令响应消息
            WebSocketResData data = param.getData();
            Integer cmd = data.getCmd();
            // 指令-获取图片
            if (cmd.equals(385)) {
                JSONObject cData = JSON.parseObject(data.getData().toString());
                int Ack = Integer.parseInt(cData.get("Ack").toString());
                if (Ack == 0) {
                    commandResult.put("imageType", cData.get("ImageType"));
                    commandResult.put("imageData", cData.get("ImageData"));
                    sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "指令响应成功", MsgEnum.INFO)));
                } else if (Ack == 1) {
                    sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "图片获取失败", MsgEnum.ERROR)));
                } else if (Ack == 2) {
                    sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "不支持该类型图片", MsgEnum.ERROR)));
                } else if (Ack == 3) {
                    sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "未知错误", MsgEnum.ERROR)));
                }
            }
            // 指令-打开/关闭视频
            else if (cmd.equals(386)) {
                JSONObject cData = JSON.parseObject(data.getData().toString());
                int Ack = Integer.parseInt(cData.get("Ack").toString());
                if (Ack == 0) {
                    commandResult.put("videoUrl", cData.get("VideoUrl"));
                    sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "指令响应成功", MsgEnum.INFO)));
                } else if (Ack == 1) {
                    sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "超过最大同时拉流限制", MsgEnum.ERROR)));
                } else if (Ack == 2) {
                    sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "摄像头不存在", MsgEnum.ERROR)));
                } else if (Ack == 3) {
                    sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "未知错误", MsgEnum.ERROR)));
                }
            }
        } else if (method == MethodEnum.status) {
            WebSocketResStatus status = resDTO.getStatus();
            WebSocketResPrintInfo printInfo = status.getPrintInfo();
            myDeviceService.updateDeviceStatus(
                    deviceSn,
                    CurrentStatusEnum.getEnumByNum(status.getCurrentStatus()),
                    PrintInfoStatusEnum.getEnumByNum(printInfo.getStatus()),
                    ErrorStatusEnum.SDCP_PRINT_ERROR_NONE,
                    null
            );
            sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "状态更新", MsgEnum.INFO)));
        } else if (method == MethodEnum.attributes) {
            WebSocketResAttributes attributes = resDTO.getAttributes();
            // String[] capabilities = attributes.getCapabilities();
            BusDevice entity = new BusDevice();
            entity.setDeviceSn(deviceSn);
            entity.setName(attributes.getName());
            entity.setModel(attributes.getMachineName());
            entity.setFirmwareVersion(attributes.getFirmwareVersion());
            entity.setProtocolVersion(attributes.getProtocolVersion());
            // List<BusDeviceCommand> commandList = new ArrayList<>();
            // for (String capability : capabilities) {
            //     BusDeviceCommand command = new BusDeviceCommand();
            //     command.setCommandType(capability);
            //     commandList.add(command);
            // }
            // entity.setCommandList(commandList);
            myDeviceService.updateOrAdd(entity);
        } else if (method == MethodEnum.error) {
            WebSocketResStatus status = resDTO.getStatus();
            WebSocketResPrintInfo printInfo = status.getPrintInfo();
            myDeviceService.updateDeviceStatus(
                    deviceSn,
                    CurrentStatusEnum.getEnumByNum(status.getCurrentStatus()),
                    PrintInfoStatusEnum.getEnumByNum(printInfo.getStatus()),
                    ErrorStatusEnum.SDCP_PRINT_ERROR_NONE,
                    null
            );
            sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, "设备发生异常", MsgEnum.ERROR)));
        } else if (method == MethodEnum.notice) {

        } else if (method == MethodEnum.close || method == MethodEnum.disconnect) {
            String msg = (method == MethodEnum.close ? "已关闭连接" : "已断开连接");
            MsgEnum en = (method == MethodEnum.close ? MsgEnum.CLOSE : MsgEnum.DISCONNECT);
            myDeviceService.updateDeviceStatus(
                    null,
                    CurrentStatusEnum.SDCP_MACHINE_STATUS_DEFAULT,
                    PrintInfoStatusEnum.SDCP_PRINT_STATUS_DEFAULT,
                    ErrorStatusEnum.SDCP_PRINT_ERROR_DEFAULT,
                    msg
            );
            sendVueMessage(JSON.toJSONString(new SocketMsg(commandResult, msg, en)));
        }
    }

    // 发送消息给第三方
    private void sendVueMessage(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(">>> 推送消息给VUE：channel={}，message={}", channel, message);
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
                log.info("<<< 推送消息给VUE-异常 原因：", e);
            }
        }
    }
}