
package com.tmerclub.cloud.im.socket.multishop;

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.cache.util.RedisUtil;
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.im.constants.Constant;
import com.tmerclub.cloud.im.constants.MsgCode;
import com.tmerclub.cloud.im.constants.SendType;
import com.tmerclub.cloud.im.socket.app.UserWebSocketServer;
import com.tmerclub.cloud.im.vo.MessageInfoVO;
import com.tmerclub.cloud.im.vo.MsgItemVO;
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.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Lzk
 */
@ServerEndpoint("/m/ua/im/websocket/online/{token}/{tenantId}/{userId}")
@Component
public class ShopOnlineWebSocketServer {

    private static final Logger logger = LoggerFactory.getLogger(ShopOnlineWebSocketServer.class);

    /**
     * shopId为key
     */
    public static final ConcurrentHashMap<String, Session> SHOP_ONLINE_EMPLOYEE_MAP = new ConcurrentHashMap<>(256);

    private static TokenFeignClient tokenFeignClient;

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

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) throws IOException {
        token = Base64.decodeStr(token);
        UidInfoBO uidInfoBO = tokenFeignClient.checkToken(token);
        if (uidInfoBO == null) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_USER_INFO, "无法获取用户信息")));
                logger.info("ShopOnlineWebSocketServer-无法获取用户信息，token:{}", token);
            }
            return;
        }

        // 如果原来就有token
        Session oldSession = SHOP_ONLINE_EMPLOYEE_MAP.get(Constant.ONLINE_SHOP_EMPLOYEE_PREFIX + uidInfoBO.getTenantId() + Constant.UNDERLINE + uidInfoBO.getUserId() + Constant.UNDERLINE + token);
        if (oldSession != null) {
            // 客户端收到11，关闭ws链接，弹出提示
            if (oldSession.isOpen()) {
                oldSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.LOGIN_IN_OTHER_PLACE, "账户已在别处登录，请刷新")));
                oldSession.close();
                logger.info("ShopOnlineWebSocketServer-账户已在别处登录，请刷新，token:{}", token);
            }
        }

        Boolean isExist = MapUtil.isExistKeyPrefix(SHOP_ONLINE_EMPLOYEE_MAP, Constant.ONLINE_SHOP_EMPLOYEE_PREFIX + uidInfoBO.getTenantId() + Constant.UNDERLINE);
        if (!isExist) {
            Set<String> set2 = UserWebSocketServer.USER_MAP.keySet();
            if (!set2.isEmpty()) {
                MsgItemVO msgItem = new MsgItemVO();
                msgItem.setChatType(SendType.SHOP);
                msgItem.setShopId(uidInfoBO.getTenantId());
                msgItem.setOnlineOrOffline(true);
                sendText(set2, msgItem);
            }
        }

        SHOP_ONLINE_EMPLOYEE_MAP.put(Constant.ONLINE_SHOP_EMPLOYEE_PREFIX + uidInfoBO.getTenantId() + Constant.UNDERLINE + uidInfoBO.getUserId() + Constant.UNDERLINE + token, session);
        if (session.isOpen()) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success()));
        }
        Integer unRead = RedisUtil.get(Constant.UNREAD_SHOP_PREFIX + uidInfoBO.getUserId() + Constant.UNDERLINE);
        if (Objects.nonNull(unRead) && 1 == unRead) {
            MsgItemVO msgItem = new MsgItemVO();
            msgItem.setNewMessage(true);
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem)));
            RedisUtil.del(Constant.UNREAD_SHOP_PREFIX + uidInfoBO.getUserId() + Constant.UNDERLINE);
        }
    }

    private static void sendText(Set<String> set2, MsgItemVO msgItem) throws IOException {
        for (String key : set2) {
            Session userSession = UserWebSocketServer.USER_MAP.get(key);
            if (Objects.nonNull(userSession)) {
                if (userSession.isOpen()) {
                    userSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.ONLINE_OR_OFFLINE, msgItem)));
                }
            }
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("token") String token, @PathParam("tenantId") Long tenantId, @PathParam("userId") Long userId) throws IOException {
        token = Base64.decodeStr(token);
        SHOP_ONLINE_EMPLOYEE_MAP.remove(Constant.ONLINE_SHOP_EMPLOYEE_PREFIX + tenantId + Constant.UNDERLINE + userId + Constant.UNDERLINE + token);

        Boolean isExist = MapUtil.isExistKeyPrefix(SHOP_ONLINE_EMPLOYEE_MAP, Constant.ONLINE_SHOP_EMPLOYEE_PREFIX + tenantId + Constant.UNDERLINE);
        if (!isExist) {
            Set<String> set2 = UserWebSocketServer.USER_MAP.keySet();
            if (CollUtil.isNotEmpty(set2)) {
                MsgItemVO msgItem = new MsgItemVO();
                msgItem.setShopId(tenantId);
                msgItem.setChatType(SendType.SHOP);
                msgItem.setOnlineOrOffline(false);
                sendText(set2, msgItem);
            }
        }
        logger.info("ShopOnlineWebSocketServer-连接关闭 tenantId:{}, userId:{}, token:{}", tenantId, userId, token);
    }

    /**
     * 连接错误调用的方法
     */
    @OnError
    public void onError(Session session, @PathParam("token") String token, Throwable error, @PathParam("tenantId") Long tenantId, @PathParam("userId") Long userId) throws IOException {
        token = Base64.decodeStr(token);
        SHOP_ONLINE_EMPLOYEE_MAP.remove(Constant.ONLINE_SHOP_EMPLOYEE_PREFIX + tenantId + Constant.UNDERLINE + userId + Constant.UNDERLINE + token);

        Boolean isExist = MapUtil.isExistKeyPrefix(SHOP_ONLINE_EMPLOYEE_MAP, Constant.ONLINE_SHOP_EMPLOYEE_PREFIX + tenantId + Constant.UNDERLINE);
        if (!isExist) {
            Set<String> set2 = UserWebSocketServer.USER_MAP.keySet();
            if (!set2.isEmpty()) {
                MsgItemVO msgItem = new MsgItemVO();
                msgItem.setChatType(SendType.SHOP);
                msgItem.setShopId(tenantId);
                msgItem.setOnlineOrOffline(false);
                sendText(set2, msgItem);
            }
        }
        logger.info("ShopOnlineWebSocketServer-连接错误 tenantId:{}, userId:{}, token:{}, error:{}", tenantId, userId, token, error);
    }

    /**
     * 收到客户端消息后调用的方法，该消息由商家发过来
     *
     * @param sourceMessage 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String sourceMessage, @PathParam("token") String token, Session session) throws IOException {
        token = Base64.decodeStr(token);
        UidInfoBO uidInfoBO = tokenFeignClient.checkToken(token);
        if (uidInfoBO == null) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_USER_INFO, "无法获取用户信息")));
                logger.info("ShopOnlineWebSocketServer-无法获取用户信息，token:{}", token);
            }
            return;
        }

        if (StrUtil.isBlank(sourceMessage)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CONTENT_FORMAT_ERROR, "内容格式有误")));
                logger.info("ShopOnlineWebSocketServer-内容格式有误，token:{}", token);
            }
            return;
        }

        MessageInfoVO messageInfo = Json.parseObject(sourceMessage, MessageInfoVO.class);

        if (Objects.isNull(messageInfo)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CONTENT_FORMAT_ERROR, "内容格式有误")));
                logger.info("ShopOnlineWebSocketServer-内容格式有误，token:{}", token);
            }
            return;
        }

        // 保持心跳
        if (StrUtil.equals(messageInfo.getContent(), Constant.HEART_BEAT)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.HEART_BEAT, "保持心跳")));
                logger.info("ShopOnlineWebSocketServer-保持心跳，token:{}", token);
            }
        }
    }

}
