package com.tmerclub.cloud.live.socket;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.auth.bo.UidInfoBO;

import com.tmerclub.cloud.api.auth.feign.TokenFeignClient;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.common.util.MapUtil;
import com.tmerclub.cloud.live.constant.LiveConstant;
import com.tmerclub.cloud.live.constant.MsgCode;
import com.tmerclub.cloud.live.constant.MsgType;
import com.tmerclub.cloud.live.dto.LiveMessageDTO;
import com.tmerclub.cloud.live.service.LiveRoomProdService;
import com.tmerclub.cloud.live.service.LiveRoomService;
import com.tmerclub.cloud.live.vo.LiveMsgItemVO;
import com.tmerclub.cloud.live.vo.LiveRoomProdVO;
import com.tmerclub.cloud.live.vo.LiveRoomVO;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用户ws
 * userType 0游客 1用户
 * @author TRACK
 */
@ServerEndpoint("/ua/im/websocket/live/user/{userType}/{token}/{userId}/{roomId}")
@Component
public class LiveUserWebSocketServer {

    public static final Logger logger = LoggerFactory.getLogger(LiveAnchorWebSocketServer.class);

    public static final ConcurrentHashMap<String, Session> LIVE_USER_MAP = new ConcurrentHashMap<>(LiveConstant.DEFAULT_MAP_SIZE);

    private static LiveRoomService liveRoomService;

    private static LiveRoomProdService liveRoomProdService;

    private static TokenFeignClient tokenFeignClient;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        liveRoomService = applicationContext.getBean(LiveRoomService.class);
        tokenFeignClient = applicationContext.getBean(TokenFeignClient.class);
        liveRoomProdService = applicationContext.getBean(LiveRoomProdService.class);
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token, @PathParam("roomId") Long roomId,
                       @PathParam("userType") Integer userType, @PathParam("userId") Long uid) throws IOException {
        logger.info("创建用户直播连接");
        UidInfoBO uidInfoBO = new UidInfoBO();
        if (Objects.equals(userType, 1)) {
            token = Base64.decodeStr(token);
            uidInfoBO = tokenFeignClient.checkToken(token);
            if (Objects.isNull(uidInfoBO)) {
                logger.error("无法获取直播用户信息");
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_USER_INFO, "无法获取直播用户信息")));
                }
                return;
            }
        }

        // 检查直播间
        LiveRoomVO livingRoom = liveRoomService.getLivingRoomInfo(null, roomId);
        if (Objects.isNull(livingRoom)) {
            logger.error("直播间不存在");
            if (session.isOpen()) {
                // 当前未有直播权限
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_ROOM_INFO, "直播间不存在")));
            }
            return;
        }
        Long userId = uidInfoBO.getUserId();
        if (Objects.equals(userType, 0)) {
            userId = uid;
        } else {
            // 已经有连接
            Session oldSession = LIVE_USER_MAP.get(LiveConstant.LIVE_USER_PREFIX + livingRoom.getRoomId() + Constant.UNDERLINE + userId);
            if (Objects.nonNull(oldSession)) {
                if (oldSession.isOpen()) {
                    oldSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.LOGIN_IN_OTHER_PLACE, "账户已在别处登录，请刷新")));
                    oldSession.close();
                }
                return;
            }
        }
        // 存储会话到map
        LIVE_USER_MAP.put(LiveConstant.LIVE_USER_PREFIX + livingRoom.getRoomId() + Constant.UNDERLINE + userId, session);
        if (session.isOpen()) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success()));
        }
        // 给所有用户更新直播观看人数
        sendChangeLiveUserCountMsg(roomId, LIVE_USER_MAP.size(), userId, session);
        // 如果有商品是讲解状态，发送一条讲解消息给用户
        sendProdExplainingMsg(session, livingRoom);
    }

    private static void sendProdExplainingMsg(Session session, LiveRoomVO livingRoom) throws IOException {
        List<LiveRoomProdVO> prodList = liveRoomProdService.listLiveRoomProd(livingRoom.getRoomId());
        List<LiveRoomProdVO> livingProds = prodList.stream().filter(p -> Objects.equals(p.getExplainStatus(), 1)).toList();
        if (CollUtil.isNotEmpty(livingProds)) {
            LiveRoomProdVO liveRoomProdVO = livingProds.get(0);
            LiveMsgItemVO liveMsgItemParam = new LiveMsgItemVO();
            liveMsgItemParam.setMsgType(MsgType.EXPLAIN_STATUS);
            liveMsgItemParam.setSpuId(liveRoomProdVO.getSpuId());
            liveMsgItemParam.setExplainStatus(1);
            LiveMsgItemVO liveMsgItem = liveRoomProdService.handleLiveProd(liveMsgItemParam);
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(liveMsgItem)));
            }
        }
    }

    private static void sendChangeLiveUserCountMsg(Long roomId, Integer liveUserCount, Long userId, Session session) throws IOException {
        // 发送消息通知用户直播状态变更
        List<Session> onlineUserList = LiveUserWebSocketServer.getOnlineUserList(roomId);
        // 添加当前用户
        onlineUserList.add(session);
        LiveMsgItemVO liveMsgItem = new LiveMsgItemVO();
        liveMsgItem.setMsgType(MsgType.LIVE_USER_COUNT);
        liveMsgItem.setLiveUserCount(liveUserCount);
        if (CollUtil.isNotEmpty(onlineUserList)) {
            for (Session onlineUser : onlineUserList) {
                if (Objects.nonNull(onlineUser) && onlineUser.isOpen()) {
                    onlineUser.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(liveMsgItem)));
                }
            }
        }
    }

    @OnClose
    public void onClose(Session session, @PathParam("token") String token,
                        @PathParam(value = "userId") Long userId, @PathParam(value = "roomId") Long roomId) throws IOException {
        logger.info("关闭用户直播连接");
        LIVE_USER_MAP.remove(LiveConstant.LIVE_USER_PREFIX + roomId + Constant.UNDERLINE + userId);
        // 给所有用户更新直播观看人数
        sendChangeLiveUserCountMsg(roomId, LIVE_USER_MAP.size(), userId, session);
    }

    @OnError
    public void onError(Session session, @PathParam("token") String token, Throwable error,
                        @PathParam(value = "userId") Long userId, @PathParam("roomId") Long roomId) throws IOException {
        logger.error("用户直播连接发生错误{}", error.toString());
        LIVE_USER_MAP.remove(LiveConstant.LIVE_USER_PREFIX + roomId + Constant.UNDERLINE + userId);
        // 给所有用户更新直播观看人数
        sendChangeLiveUserCountMsg(roomId, LIVE_USER_MAP.size(), userId, session);
    }

    @OnMessage
    public void onMessage(String sourceMessage, @PathParam("token") String token, Session session,
                          @PathParam("roomId") Long roomId, @PathParam("userType") Integer userType, @PathParam("userId") Long uid) throws IOException {
        UidInfoBO uidInfoBO = new UidInfoBO();
        if (Objects.equals(userType, 1)) {
            token = Base64.decodeStr(token);
            uidInfoBO = tokenFeignClient.checkToken(token);
            if (Objects.isNull(uidInfoBO)) {
                logger.error("无法获取直播用户信息");
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_USER_INFO, "无法获取直播用户信息")));
                }
                return;
            }
        }
        if (StrUtil.isBlank(sourceMessage)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CONTENT_FORMAT_ERROR, "直播用户消息内容格式有误")));
            }
            return;
        }
        logger.info("直播用户sourceMessage:" + sourceMessage);
        LiveMessageDTO liveMessageInfo = Json.parseObject(sourceMessage, LiveMessageDTO.class);
        if (Objects.isNull(liveMessageInfo)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CONTENT_FORMAT_ERROR, "直播用户消息内容格式有误")));
            }
            return;
        }
        if (checkLiveRoom(session, roomId, null)) {
            return;
        }
        // 保持心跳
        if (Objects.equals(liveMessageInfo.getMsgType(), MsgType.HEART_BEAT)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.HEART_BEAT, "保持心跳")));
            }
            return;
        }
        if (Objects.equals(userType, 0) && Objects.equals(liveMessageInfo.getMsgType(), MsgType.BULLET_SCREEN)) {
            // 游客 不能发送弹幕
            return;
        }
        // 用户发送弹幕
        if (Objects.equals(liveMessageInfo.getMsgType(), MsgType.BULLET_SCREEN)) {
            if (StrUtil.isBlank(liveMessageInfo.getContent())) {
                return;
            }
            // 弹幕内容不超过50个字符
            if (liveMessageInfo.getContent().length() > LiveConstant.BULLET_SCREEN_MAX_LENGTH) {
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CONTENT_FORMAT_ERROR, "用户弹幕内容过长")));
                }
                return;
            }
            // 发送弹幕给在线用户
            sendMsgToUser(roomId, liveMessageInfo.getNickName(), liveMessageInfo.getContent(), 0);
        }
    }

    private static boolean checkLiveRoom(Session session, Long roomId, Long userId) throws IOException {
        // 检查直播间
        LiveRoomVO livingRoom = liveRoomService.getLivingRoomInfo(userId, roomId);
        if (Objects.isNull(livingRoom)) {
            logger.error("直播间不存在");
            if (session.isOpen()) {
                // 直播间不存在
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_ROOM_INFO, "直播间不存在")));
            }
            return true;
        }
        return false;
    }

    public static void sendMsgToUser(Long roomId, String nickName, String content, Integer userType) throws IOException {
        // 给在线用户发送弹幕消息
        List<Session> onlineUserList = getOnlineUserList(roomId);
        if (CollUtil.isNotEmpty(onlineUserList)) {
            LiveMsgItemVO liveMsgItem = new LiveMsgItemVO();
            liveMsgItem.setMsgType(MsgType.BULLET_SCREEN);
            liveMsgItem.setNickName(nickName);
            liveMsgItem.setContent(content);
            liveMsgItem.setUserType(userType);
            for (Session onlineUser : onlineUserList) {
                if (Objects.nonNull(onlineUser) && onlineUser.isOpen()) {
                    onlineUser.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(liveMsgItem)));
                }
            }
        }
    }

    public static List<Session> getOnlineUserList(Long roomId) {
        // 查找当前直播间在线用户
        List<Session> onlineUserList = new ArrayList<>(LiveConstant.DEFAULT_MAP_SIZE);
        for (String liveUserKey : LiveUserWebSocketServer.LIVE_USER_MAP.keySet()) {
            // 给在线用户发送消息
            if (liveUserKey.startsWith(LiveConstant.LIVE_USER_PREFIX + roomId + Constant.UNDERLINE)) {
                onlineUserList.add(LiveUserWebSocketServer.LIVE_USER_MAP.get(liveUserKey));
            }
        }
        // 给主播发送消息
        LiveRoomVO livingRoomInfo = liveRoomService.getLivingRoomInfo(null, roomId);
        if (Objects.isNull(livingRoomInfo)) {
            List<Session> sessions = MapUtil.getSessionByKeyPrefix(LiveAnchorWebSocketServer.LIVE_ANCHOR_MAP, LiveConstant.LIVE_ANCHOR_PREFIX + roomId + Constant.UNDERLINE);
            if (CollUtil.isNotEmpty(sessions)) {
                onlineUserList.addAll(sessions);
            }
        } else {
            Session anchorSession = LiveAnchorWebSocketServer.LIVE_ANCHOR_MAP.get(LiveConstant.LIVE_ANCHOR_PREFIX + roomId + Constant.UNDERLINE + livingRoomInfo.getUserId());
            if (Objects.nonNull(anchorSession)) {
                onlineUserList.add(anchorSession);
            }
        }
        return onlineUserList;
    }
}
