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.live.constant.LiveConstant;
import com.tmerclub.cloud.live.constant.LiveRoomStatusType;
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.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 主播
 * @author TRACK
 */
@ServerEndpoint("/ua/im/websocket/live/anchor/{token}/{userId}/{roomId}")
@Component
public class LiveAnchorWebSocketServer {

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

    public static final ConcurrentHashMap<String, Session> LIVE_ANCHOR_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);
        liveRoomProdService = applicationContext.getBean(LiveRoomProdService.class);
        tokenFeignClient = applicationContext.getBean(TokenFeignClient.class);
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token, @PathParam("roomId") Long roomId) throws IOException {
        logger.info("创建主播直播连接");
        token = Base64.decodeStr(token);
        UidInfoBO 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 (checkLiveRoom(session, roomId, uidInfoBO.getUserId())) {
            return;
        }
        LiveRoomVO livingRoom = liveRoomService.getLivingRoomInfo(uidInfoBO.getUserId(), roomId);
        if (Objects.equals(livingRoom.getLiveStatus(), LiveRoomStatusType.FINISHED.value())) {
            logger.info("主播连接一个结束的直播间，返回" + livingRoom.getRoomId());
            return;
        }

        // 已经有连接
        Session oldSession = LIVE_ANCHOR_MAP.get(LiveConstant.LIVE_ANCHOR_PREFIX + livingRoom.getRoomId() + Constant.UNDERLINE + uidInfoBO.getUserId());
        if (Objects.nonNull(oldSession)) {
            if (oldSession.isOpen()) {
                oldSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.LOGIN_IN_OTHER_PLACE, "账户已在别处登录，请刷新")));
                oldSession.close();
            }
            return;
        }
        // 存储会话到map
        LIVE_ANCHOR_MAP.put(LiveConstant.LIVE_ANCHOR_PREFIX + livingRoom.getRoomId() + Constant.UNDERLINE + uidInfoBO.getUserId(), session);
        if (session.isOpen()) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success()));
        }
        long currentTimeMillis = System.currentTimeMillis();
        if (livingRoom.getStartTime().getTime() <= currentTimeMillis && livingRoom.getEndTime().getTime() >= currentTimeMillis
                && LiveRoomStatusType.isNeedOpen(livingRoom.getLiveStatus())) {
            // 更新直播为开播状态
            liveRoomService.updateLiveRoomStatus(LiveRoomStatusType.LIVING.value(), Collections.singletonList(livingRoom.getRoomId()));
            // 发送消息通知用户直播状态变更
            sendChangeLiveRoomStatusMsg(roomId, LiveRoomStatusType.LIVING);
        }
    }

    @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_ANCHOR_MAP.remove(LiveConstant.LIVE_ANCHOR_PREFIX + roomId + Constant.UNDERLINE + userId);
        setLiveRoomCloseStatus(userId, roomId);
    }

    private static void setLiveRoomCloseStatus(Long userId, Long roomId) throws IOException {
        LiveRoomVO livingRoom = liveRoomService.getLivingRoomInfo(userId, roomId);
        long currentTimeMillis = System.currentTimeMillis();
        if (livingRoom.getStartTime().getTime() <= currentTimeMillis && livingRoom.getEndTime().getTime() >= currentTimeMillis
                && Objects.equals(livingRoom.getLiveStatus(), LiveRoomStatusType.LIVING.value())) {
            // 更新直播为暂停状态
            liveRoomService.updateLiveRoomStatus(LiveRoomStatusType.STOP.value(), Collections.singletonList(livingRoom.getRoomId()));
            // 发送消息通知用户直播状态变更
            sendChangeLiveRoomStatusMsg(roomId, LiveRoomStatusType.STOP);
        }
        if (livingRoom.getEndTime().getTime() <= currentTimeMillis
                && LiveRoomStatusType.isNeedFinish(livingRoom.getLiveStatus())) {
            // 更新直播为结束状态
            liveRoomService.updateLiveRoomStatus(LiveRoomStatusType.FINISHED.value(), Collections.singletonList(livingRoom.getRoomId()));
            sendChangeLiveRoomStatusMsg(roomId, LiveRoomStatusType.FINISHED);
        }
    }

    private static void sendChangeLiveRoomStatusMsg(Long roomId, LiveRoomStatusType liveRoomStatusType) throws IOException {
        // 发送消息通知用户直播状态变更
        List<Session> onlineUserList = LiveUserWebSocketServer.getOnlineUserList(roomId);
        LiveMsgItemVO liveMsgItem = new LiveMsgItemVO();
        liveMsgItem.setMsgType(MsgType.LIVE_ROOM_STATUS);
        liveMsgItem.setLiveStatus(liveRoomStatusType.value());
        if (CollUtil.isNotEmpty(onlineUserList)) {
            for (Session onlineUser : onlineUserList) {
                if (Objects.nonNull(onlineUser) && onlineUser.isOpen()) {
                    onlineUser.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(liveMsgItem)));
                }
            }
        }
    }

    @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_ANCHOR_MAP.remove(LiveConstant.LIVE_ANCHOR_PREFIX + roomId + Constant.UNDERLINE + userId);
        setLiveRoomCloseStatus(userId, roomId);
    }

    @OnMessage
    public void onMessage(String sourceMessage, @PathParam("token") String token, Session session,
                          @PathParam("roomId") Long roomId) throws IOException {
        token = Base64.decodeStr(token);
        UidInfoBO 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, "无法获取主播信息")));
            }
        }
        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, uidInfoBO.getUserId())) {
            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(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;
            }
            // 发送弹幕给在线用户
            LiveUserWebSocketServer.sendMsgToUser(roomId, liveMessageInfo.getNickName(), liveMessageInfo.getContent(), 1);
            return;
        }
        // 主播更新商品讲解状态
        if (Objects.equals(liveMessageInfo.getMsgType(), MsgType.EXPLAIN_STATUS)) {
            if (Objects.isNull(liveMessageInfo.getSpuId()) || Objects.isNull(liveMessageInfo.getExplainStatus())) {
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.CONTENT_FORMAT_ERROR, "主播消息内容格式有误")));
                }
                return;
            }
            if (Objects.equals(liveMessageInfo.getExplainStatus(), 0)) {
                // 商品取消讲解，发送消息
                sendNotExplainMsg(session, roomId, liveMessageInfo);
                return;
            }
            // 检查商品状态
            LiveRoomProdVO liveRoomProdVO = getLiveRoomProdVO(roomId, liveMessageInfo);
            if (Objects.isNull(liveRoomProdVO)) {
                // 没有该商品，发送商品状态异常消息给所有人
                sendRefreshProdMsg(session, roomId);
                return;
            }
            int updateStatus = liveRoomProdService.updateExplainStatus(roomId, liveRoomProdVO.getSpuId(), liveMessageInfo.getExplainStatus());
            if (updateStatus < 1) {
                // 没有更新，发送商品状态异常消息给所有人
                sendRefreshProdMsg(session, roomId);
                return;
            }
            // 发送商品讲解状态更新消息给所有人
            sendChangeExplainStatusMsg(session, roomId, liveMessageInfo, liveRoomProdVO);
        }
    }

    private static LiveRoomProdVO getLiveRoomProdVO(Long roomId, LiveMessageDTO liveMessageInfo) {
        List<LiveRoomProdVO> liveProdList = liveRoomProdService.listLiveRoomProd(roomId);
        Map<Long, LiveRoomProdVO> liveProdMap = liveProdList.stream().collect(Collectors.toMap(LiveRoomProdVO::getSpuId, p -> p));
        LiveRoomProdVO liveRoomProdVO = liveProdMap.get(liveMessageInfo.getSpuId());
        return liveRoomProdVO;
    }
    private static void sendNotExplainMsg(Session session, Long roomId, LiveMessageDTO liveMessageInfo) throws IOException {
        liveRoomProdService.updateExplainStatus(roomId, liveMessageInfo.getSpuId(), liveMessageInfo.getExplainStatus());
        LiveMsgItemVO liveMsgItem = new LiveMsgItemVO();
        liveMsgItem.setMsgType(MsgType.EXPLAIN_STATUS);
        liveMsgItem.setSpuId(liveMessageInfo.getSpuId());
        liveMsgItem.setExplainStatus(liveMessageInfo.getExplainStatus());
        sendProdExplainStatusChangeMsg(session, roomId, liveMsgItem);
    }

    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;
    }

    private static void sendChangeExplainStatusMsg(Session session, Long roomId, LiveMessageDTO liveMessageInfo, LiveRoomProdVO liveRoomProdVO) throws IOException {
        // 发送商品讲解状态更新消息给所有人
        LiveMsgItemVO liveMsgItemParam = new LiveMsgItemVO();
        liveMsgItemParam.setMsgType(MsgType.EXPLAIN_STATUS);
        liveMsgItemParam.setSpuId(liveRoomProdVO.getSpuId());
        liveMsgItemParam.setExplainStatus(liveMessageInfo.getExplainStatus());
        LiveMsgItemVO liveMsgItem = liveRoomProdService.handleLiveProd(liveMsgItemParam);
        if (Objects.isNull(liveMsgItem)) {
            // 商品异常，发送商品状态异常消息给所有人
            sendRefreshProdMsg(session, roomId);
        }
        sendProdExplainStatusChangeMsg(session, roomId, liveMsgItem);
    }

    private static void sendProdExplainStatusChangeMsg(Session session, Long roomId, LiveMsgItemVO liveMsgItem) throws IOException {
        if (session.isOpen()) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(liveMsgItem)));
        }
        // 给当前直播间在线用户也发送刷新商品列表的状态码
        List<Session> onlineUserList = LiveUserWebSocketServer.getOnlineUserList(roomId);
        // 给在线用户发送刷新商品列表消息
        if (CollUtil.isNotEmpty(onlineUserList)) {
            for (Session onlineUser : onlineUserList) {
                if (Objects.nonNull(onlineUser) && onlineUser.isOpen()) {
                    onlineUser.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(liveMsgItem)));
                }
            }
        }
    }

    private static void sendRefreshProdMsg(Session session, Long roomId) throws IOException {
        // 发送刷新商品列表消息给所有人
        if (session.isOpen()) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.REFRUSH_PROD, "商品状态异常")));
        }
        // 给当前直播间在线用户也发送刷新商品列表的状态码
        List<Session> onlineUserList = LiveUserWebSocketServer.getOnlineUserList(roomId);
        // 给在线用户发送刷新商品列表消息
        if (CollUtil.isNotEmpty(onlineUserList)) {
            for (Session onlineUser : onlineUserList) {
                if (Objects.nonNull(onlineUser) && onlineUser.isOpen()) {
                    onlineUser.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.REFRUSH_PROD, "商品状态异常")));
                }
            }
        }
    }
}
