package tech.glinfo.enbao.modules.socketio;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tech.glinfo.enbao.common.utils.JwtUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service(value = "socketIOService")
public class SocketIOServiceImpl implements SocketIOService {

    static Log log = LogFactory.get(SocketIOServiceImpl.class);


    // 用来存已连接的客户端
    private static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>();

    @Autowired
    private SocketIOServer socketIOServer;

    @Autowired
    private JwtUtils jwtUtils;

    /**
     * 自定义事件`push_data_event`,用于服务端与客户端通信
     */
    private static final String PUSH_DATA_EVENT = "push_data_event";

    /**
     * Spring IoC容器创建之后，在加载SocketIOServiceImpl Bean之后启动
     *
     * @throws Exception
     */
    @PostConstruct
    private void autoStartup() throws Exception {
        start();
    }

    /**
     * Spring IoC容器在销毁SocketIOServiceImpl Bean之前关闭,避免重启项目服务端口占用问题
     *
     * @throws Exception
     */
    @PreDestroy
    private void autoStop() throws Exception {
        stop();
    }

    @Override
    public void start() throws Exception {
        // 监听客户端连接
        socketIOServer.addConnectListener(client -> {
            String token = getParamsByClient(client, "token");
            Claims claims = jwtUtils.getClaimByToken(token);
            if(claims == null || jwtUtils.isTokenExpired(claims.getExpiration())){
                log.error("token 不存在或已经过期:{}", token);
                client.disconnect();
                return;
            }

//            String userId = claims.getSubject().split("#")[0];

            String deviceId = getParamsByClient(client, "deviceId");

            String key = token+"#"+deviceId;
            if (clientMap.containsKey(key)) {
                clientMap.remove(key);
                clientMap.put(key, client);
            } else {
                clientMap.put(key, client);
            }
            log.info("建立连接:  " + client.getSessionId());
            log.info("RemoteAddress:  " + client.getRemoteAddress());
            log.info("Transport:  " + client.getTransport());
            log.info("设备连接:" + key + ",当前在线设备为:" + clientMap.size());

        });

        // 监听客户端断开连接
        socketIOServer.addDisconnectListener(client -> {
            String token = getParamsByClient(client, "token");
            String deviceId = getParamsByClient(client, "deviceId");
            String key = token+"#"+deviceId;
            if (clientMap.containsKey(key)) {
                clientMap.remove(key);
            }
            client.disconnect();
            log.info("断开连接： " + client.getSessionId());
        });

        // 处理自定义的事件，与连接监听类似
        socketIOServer.addEventListener("send_data", Object.class, (client, data, ackSender) -> {
//            HandshakeData handshakeData = client.getHandshakeData();
            log.info("接收到客户端信息：{}", data);
        });

        socketIOServer.start();
    }

    @Override
    public void stop() {
        if (socketIOServer != null) {
            socketIOServer.stop();
            socketIOServer = null;
        }
    }

    @Override
    public void pushMessageToAll(String event, String jsonString) {
        for (SocketIOClient client : clientMap.values()) {
            client.sendEvent(event, jsonString);
        }
    }

    /**
     * 此方法为获取client连接中的参数，可根据需求更改
     * @param client
     * @param key
     * @return
     */
    private String getParamsByClient(SocketIOClient client, String key) {
        // 从请求的连接中拿出参数（这里的loginUserNum必须是唯一标识）
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        List<String> list = params.get(key);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public void pushMessageToDevice(String deviceId, String msgContent) {
        /*SocketIOClient client = clientMap.get(deviceId);
        if (client != null) {
            client.sendEvent(PUSH_DATA_EVENT, msgContent);
        }*/
        //遍历这台机器上的所有socket，给指定设备发送消息
        Iterator<Map.Entry<String, SocketIOClient>> it = clientMap.entrySet().iterator();
        boolean isExist = false;
        while (it.hasNext()) {
            Map.Entry<String, SocketIOClient> next = it.next();
            String key = next.getKey();
            String[] keys = key.split("#");
            if(deviceId.equals(keys[1])) {
                SocketIOClient client = next.getValue();
                client.sendEvent(PUSH_DATA_EVENT, msgContent);
                isExist = true;
            }
        }
        if(isExist) {
            log.error("设备:{} 已发送：{}", deviceId, msgContent);
        }
    }

    /**
     * 获取连接的客户端ip地址
     *
     * @param client: 客户端
     * @return: java.lang.String
     */
    private String getIpByClient(SocketIOClient client) {
        String sa = client.getRemoteAddress().toString();
        String clientIp = sa.substring(1, sa.indexOf(":"));
        return clientIp;
    }
}