package ink.wgink.module.instantmessage.websocket.manager;

import com.alibaba.fastjson.JSONObject;
import ink.wgink.exceptions.websocket.SessionException;
import ink.wgink.interfaces.manager.IWebSocketChannelManager;
import ink.wgink.interfaces.manager.IWebSocketUserSessionManager;
import ink.wgink.module.instantmessage.websocket.enums.MessageTypeEnum;
import ink.wgink.module.instantmessage.websocket.pojo.WebSocketClientMessage;
import ink.wgink.pojo.session.WebSocketSession;
import ink.wgink.properties.websocket.WebSocketProperties;
import ink.wgink.util.AesUtil;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.util.List;

/**
 * @ClassName: WebSocketChannelManager
 * @Description: WebSocket通道管理器
 * @Author: wanggeng
 * @Date: 2021/9/11 11:56 上午
 * @Version: 1.0
 */
public class WebSocketChannelManager implements IWebSocketChannelManager {
    public static final String FORM_SYSTEM = "SYSTEM";
    public static final String MESSAGE_AES_KEY = "SocKEtsEcReT_KeY";
    private static final WebSocketChannelManager webSocketChannelManager = WebSocketChannelManagerBuilder.webSocketChannelManager;
    private ChannelGroup globalGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    private IWebSocketUserSessionManager webSocketUserSessionManager;
    private WebSocketProperties webSocketProperties;

    private WebSocketChannelManager() {
    }

    public static WebSocketChannelManager getInstance() {
        return webSocketChannelManager;
    }

    /**
     * 初始化会话队列
     *
     * @param userId     用户ID
     * @param sessionId  会话ID
     * @param clientName 客户端名称
     */
    public void init(String userId, String sessionId, String clientName) {
        webSocketUserSessionManager.init(userId, sessionId, clientName);
    }

    /**
     * 登出
     *
     * @param userId
     * @param clientName
     */
    public void logout(String userId, String clientName) {
        List<WebSocketSession> webSocketSessions = webSocketUserSessionManager.listOnlineUser(userId, clientName);
        if (webSocketSessions.isEmpty()) {
            return;
        }
        WebSocketClientMessage webSocketClientMessage = new WebSocketClientMessage();
        webSocketClientMessage.setSystem(true);
        webSocketClientMessage.setType(MessageTypeEnum.LOGOUT.getValue());
        webSocketClientMessage.setFrom(WebSocketChannelManager.FORM_SYSTEM);
        webSocketClientMessage.setTo(userId);
        webSocketSessions.forEach(webSocketSession -> {
            sendText(webSocketSession.getChannel(), webSocketClientMessage);
        });
    }

    /**
     * 添加通道，绑定通道与会话
     *
     * @param sessionId
     * @param userId
     * @param channel
     * @return 返回创建的会话
     * @throws SessionException
     */
    public WebSocketSession addChannel(String sessionId, String userId, Channel channel) throws SessionException {
        WebSocketSession onlineUser = webSocketUserSessionManager.addChannel(sessionId, userId, channel);
        globalGroup.add(channel);
        return onlineUser;
    }

    /**
     * 删除通道
     *
     * @param channel
     */
    public void removeChannel(Channel channel) {
        webSocketUserSessionManager.removeChannel(channel);
        globalGroup.remove(channel);
    }

    /**
     * 获取在线用户客户端列表
     *
     * @param userId 用户ID
     * @return
     */
    public List<WebSocketSession> listOnlineUser(String userId) {
        return webSocketUserSessionManager.listOnlineUser(userId);
    }

    /**
     * 获取在线用户
     *
     * @param userId     用户ID
     * @param clientName 客户端名称
     * @return
     */
    public List<WebSocketSession> listOnlineUser(String userId, String clientName) {
        return webSocketUserSessionManager.listOnlineUser(userId, clientName);
    }

    /**
     * 获取在线用户客户端列表
     *
     * @param userIds 用户ID列表
     * @return
     */
    public List<WebSocketSession> listOnlineUser(List<String> userIds) {
        return webSocketUserSessionManager.listOnlineUser(userIds);
    }

    /**
     * 获取在线用户客户端列表
     *
     * @param userIds    用户ID列表
     * @param clientName 客户端名称
     * @return
     */
    public List<WebSocketSession> listOnlineUser(List<String> userIds, String clientName) {
        return webSocketUserSessionManager.listOnlineUser(userIds, clientName);
    }

    /**
     * 通过会话获取用户
     *
     * @param sessionId 会话ID
     * @return
     */
    private WebSocketSession getOnlineUserBySessionId(String sessionId) {
        return webSocketUserSessionManager.getOnlineUserBySessionId(sessionId);
    }

    /**
     * 通过通道获取用户
     *
     * @param channelId 通道ID
     * @return
     */
    private WebSocketSession getOnlineUserByChannelId(String channelId) {
        return webSocketUserSessionManager.getOnlineUserByChannelId(channelId);
    }

    /**
     * 全部发
     *
     * @param tws
     */
    public void sendAll(TextWebSocketFrame tws) {
        globalGroup.writeAndFlush(tws);
    }

    /**
     * 单发
     *
     * @param toChannel              接收人通道
     * @param webSocketClientMessage 发送内容
     * @throws AesUtil.AesEncodeException
     */
    public void sendText(Channel toChannel, WebSocketClientMessage webSocketClientMessage) throws AesUtil.AesEncodeException {
        if (toChannel == null || !toChannel.isOpen() || !toChannel.isActive()) {
            return;
        }
        String message;
        if (webSocketProperties.getEncrypt()) {
            message = AesUtil.aesCommonEncoder(webSocketProperties.getAesKey(), JSONObject.toJSONString(webSocketClientMessage));
        } else {
            message = JSONObject.toJSONString(webSocketClientMessage);
        }
        TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame(message);
        toChannel.writeAndFlush(textWebSocketFrame);
    }

    /**
     * 群发
     *
     * @param channels
     * @param webSocketClientMessage
     */
    public void sendGroupText(List<Channel> channels, WebSocketClientMessage webSocketClientMessage) throws AesUtil.AesEncodeException {
        for (Channel channel : channels) {
            sendText(channel, webSocketClientMessage);
        }
    }

    public void setWebSocketProperties(WebSocketProperties webSocketProperties) {
        this.webSocketProperties = webSocketProperties;
    }

    @Override
    public void setWebSocketUserSessionManager(IWebSocketUserSessionManager webSocketUserSessionManager) {
        this.webSocketUserSessionManager = webSocketUserSessionManager;
    }

    private static class WebSocketChannelManagerBuilder {
        public static final WebSocketChannelManager webSocketChannelManager = new WebSocketChannelManager();
    }

}
