package com.skr.mdm.websocket;

import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.skr.mdm.dao.AuAdminDao;
import com.skr.mdm.entity.AuAdministratorDO;
import com.skr.mdm.util.RedisGetKeyUtil;
import com.skr.mdm.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author nfq
 * @date 2021/1/15 14:51
 */
@ServerEndpoint(value = "/websocket/{adminId}/{sessionId}")
@Component
@Slf4j
public class WebsocketServer {

    private static RedisUtils redisUtils;
    private static AuAdminDao adminDao;

    @Autowired
    public void setWebsocketServer(RedisUtils redisUtils, AuAdminDao adminDao) {
        WebsocketServer.redisUtils = redisUtils;
        WebsocketServer.adminDao = adminDao;
    }

    private static final Map<String, Session> websocketMap = new ConcurrentHashMap<>(16);

    private String adminId = "";
    private String sessionId = "";


    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("adminId") String adminId, @PathParam("sessionId") String sessionId) {
        if (redisUtils.get(RedisGetKeyUtil.getAdminLoginIdKey(adminId)) == null) {
            AuAdministratorDO administratorDO = adminDao.selectById(adminId);
            if (administratorDO == null) {
                log.warn("adminId不存在：{}", adminId);
                return;
            }
        }
        this.adminId = adminId;
        this.sessionId = sessionId;
        websocketMap.put(adminId + "-" + sessionId, session);
        log.info("websocket连接成功 adminId:{} sessionId:{}", adminId, sessionId);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        log.info("websocket连接关闭 adminId:{} sessionId:{}", adminId, sessionId);
        websocketMap.remove(adminId + "-" + sessionId);
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
//        log.info("服务端收到客户端 {} 的消息:{}", adminId, message);
        if ("getSessionId".equals(message)) {
            log.info("sessionId:{}", sessionId);
            sendMessage(session, "sessionId:" + sessionId);
            return;
        }
        sendMessage(session, "pong:" + message);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("websocket连接异常:", error);
    }

    /**
     * 发送消息
     * @param session
     * @param message
     */
    public static void sendMessage(Session session, String message) {
        if (session == null) {
            return;
        }
        synchronized (session) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("websocket发送消息异常", e);
            }
        }
    }

    /**
     * 群发消息
     * @param message
     * @param companyId
     */
    public static void groupSendMessage(String message, Integer companyId) {
        if (MapUtil.isNotEmpty(websocketMap)) {
            Set<String> keySet = websocketMap.keySet();
            if (CollectionUtils.isEmpty(keySet)) {
                return;
            }
            Set<String> adminIdSet = new HashSet<>();
            Set<String> sessionIdSet = new HashSet<>();
            keySet.forEach(s -> {
                String adminId = StringUtils.substringBefore(s, "-");
                String sessionId = StringUtils.substringAfter(s, "-");
                adminIdSet.add(adminId);
                sessionIdSet.add(sessionId);
            });
            List<AuAdministratorDO> list = new LambdaQueryChainWrapper<>(adminDao)
                    .eq(AuAdministratorDO::getCompanyId, companyId)
                    .in(AuAdministratorDO::getId, adminIdSet)
                    .list();
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            for (AuAdministratorDO auAdministratorDO : list) {
                sessionIdSet.forEach(sessionId -> {
                    String key = auAdministratorDO.getId().toString() + "-" + sessionId;
                    if (keySet.contains(key)) {
                        Session session = websocketMap.get(key);
                        sendMessage(session, message);
                    }
                });
            }
        }
    }

    /**
     * 给管理员发送
     * @param adminSession
     * @param message
     */
    public static void sendAdminMessage(String adminSession, String message) {
        sendMessage(websocketMap.get(adminSession), message);
    }

}
