package com.webchat.controller.client;

import com.webchat.common.enums.RedisMessageChannelTopicEnum;
import com.webchat.common.enums.RoleCodeEnum;
import com.webchat.common.util.JsonUtil;
import com.webchat.common.util.SpringContextUtil;
import com.webchat.domain.vo.request.mess.VideoChatMessageRequestVO;
import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
import com.webchat.service.UserService;
import com.webchat.service.listener.RedisMessageSender;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;

@Data
@Slf4j
@Component
@ServerEndpoint(value = "/ws/group-video-chat/{groupId}/{userId}")
public class GroupVideoChatWebSocket {

    /**
     * 群聊多人音视频webscoket连接对象存储
     */
    public static ConcurrentHashMap<String, LinkedHashMap<String, GroupVideoChatWebSocket>> clients = new ConcurrentHashMap();

    // 任务队列
    private static final int QUEUE_SIZE = 5000;
    private static ArrayBlockingQueue<String> messageQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);

    //每个连接都会有自己的会话
    private Session session;
    private String groupId;
    private String userId;

    @OnOpen
    public void open(@PathParam("groupId") String groupId, @PathParam("userId") String userId, Session session, EndpointConfig config){
        log.info("音视频通话用户{}的客户端连接成功！", userId);
        LinkedHashMap<String, GroupVideoChatWebSocket> userSocketTable = clients.get(groupId);
        if (userSocketTable == null) {
            userSocketTable = new LinkedHashMap<>();
            clients.put(groupId, userSocketTable);
        }
        userSocketTable.put(userId, this);
        this.session = session;
        this.groupId = groupId;
        this.userId = userId;
        // 用户上线，同步当前在线群聊
        RedisMessageSender redisMessageSender = SpringContextUtil.getBean(RedisMessageSender.class);
        VideoCustomMessage videoCustomMessage = new VideoCustomMessage();
        videoCustomMessage.setType("online");
        videoCustomMessage.setGroupId(groupId);
        videoCustomMessage.setUserIds(getOnlineUserIdList(groupId));
        redisMessageSender.sendMessage(RedisMessageChannelTopicEnum.GROUP_VIDEO_ONLINE.getChannel(), JsonUtil.toJsonString(videoCustomMessage));
    }

    @OnClose
    public void close(){
        Map<String, GroupVideoChatWebSocket> userClients = clients.get(groupId);
        if (MapUtils.isNotEmpty(userClients)) {
            userClients.remove(userId);
        }
        // 用户下线 - 同步当前在线群聊
        RedisMessageSender redisMessageSender = SpringContextUtil.getBean(RedisMessageSender.class);
        VideoCustomMessage videoCustomMessage = new VideoCustomMessage();
        videoCustomMessage.setType("offline");
        videoCustomMessage.setGroupId(groupId);
        videoCustomMessage.setOfflineUserId(userId);
        redisMessageSender.sendMessage(RedisMessageChannelTopicEnum.GROUP_VIDEO_OFFLINE.getChannel(), JsonUtil.toJsonString(videoCustomMessage));
    }

    @OnError
    public void error(Throwable error){
        error.printStackTrace();
    }

    @OnMessage
    public void onMessage(String message) {
        log.info("收到来自用户{}的消息:{}", userId, message);
        VideoChatMessageRequestVO chatMessage = JsonUtil.fromJson(message, VideoChatMessageRequestVO.class);
        if (chatMessage == null) {
            return;
        }
        RedisMessageSender redisMessageSender = SpringContextUtil.getBean(RedisMessageSender.class);
        if (chatMessage.getType().equals("call")) {
            // 发送通过邀请提醒消息
            // 支持分布式场景，走广播模式
            redisMessageSender.sendMessage(RedisMessageChannelTopicEnum.GROUP_VIDEO_OFFER_SEND.getChannel(), message);
            return;
        }
        this.submit(message);
    }

    private void submit(String message) {
        try {
            messageQueue.put(message);
        } catch (Exception e) {
            log.error("多人音视频通话任务入队失败 =====> ", e);
        }
    }

    @PostConstruct
    public void consumeMessageQueue() {
        new Thread(() -> {
            while (true) {
                try {
                    String taskMessage = messageQueue.take();
                    if (StringUtils.isNotBlank(taskMessage)) {
                        this.handleVideoChattingMessage(taskMessage);
                    }
                } catch (Exception e) {
                    log.error("多人音视频队列消息消费异常 =====> ", e);
                }
            }
        }).start();
    }

    @Data
    public static class VideoCustomMessage {
        private String type;
        private String groupId;
        private Set<String> userIds;
        private String offlineUserId;
        private Map<String, UserBaseResponseInfoVO> onlineUserMap;
    }

    private Set<String> getOnlineUserIdList(String groupId) {
        LinkedHashMap<String, GroupVideoChatWebSocket> userClients = this.clients.get(groupId);
        if (MapUtils.isEmpty(userClients)) {
            return Collections.emptySet();
        }
        return userClients.keySet();
    }

    public void handleVideoChattingMessage(String message) {
        VideoChatMessageRequestVO chatMessage = JsonUtil.fromJson(message, VideoChatMessageRequestVO.class);
        if (chatMessage == null) {
            return;
        }
        UserService userService = SpringContextUtil.getBean(UserService.class);
        String groupId = chatMessage.getGroupId();
        String targetUserId = chatMessage.getTargetUserId();
        Map<String, GroupVideoChatWebSocket> wsMap = this.clients.get(groupId);
        UserBaseResponseInfoVO receiver = userService.getUserBaseInfoByUserId(targetUserId);
        List<String> receiverUserIds = new ArrayList<>();
        if (RoleCodeEnum.GROUP.getCode().equals(receiver.getRoleCode())) {
            // 群组
            receiverUserIds = userService.getGroupUserIdsFromCache(targetUserId);
        } else {
            // 个人
            receiverUserIds.add(targetUserId);
        }
        receiverUserIds.remove(chatMessage.getUserId());
        if (CollectionUtils.isEmpty(receiverUserIds)) {
            return;
        }
        for (String receiverUserId: receiverUserIds) {
            GroupVideoChatWebSocket ws = wsMap.get(receiverUserId);
            if (ws == null) {
                continue;
            }
            Session session = ws.getSession();
            if (session == null || !session.isOpen()) {
                continue;
            }
            try {
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                log.error("消息推送异常，userId:{}", receiverUserId, e);
            }
        }
    }
}

