
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.api.multishop.bo.EsShopDetailBO;
import com.tmerclub.cloud.api.multishop.feign.ShopDetailFeignClient;
import com.tmerclub.cloud.api.multishop.feign.ShopUserFeignClient;
import com.tmerclub.cloud.api.multishop.vo.ShopUserVO;
import com.tmerclub.cloud.api.user.feign.UserFeignClient;
import com.tmerclub.cloud.api.user.vo.UserApiVO;
import com.tmerclub.cloud.common.cache.util.RedisUtil;
import com.tmerclub.cloud.common.database.vo.PageVO;
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.im.constants.Constant;
import com.tmerclub.cloud.im.constants.MsgCode;
import com.tmerclub.cloud.im.constants.MsgRead;
import com.tmerclub.cloud.im.constants.SendType;
import com.tmerclub.cloud.im.model.ImMsgBizUserHistory;
import com.tmerclub.cloud.im.service.ImMsgBizUserHistoryService;
import com.tmerclub.cloud.im.socket.app.UserOnlineWebSocketServer;
import com.tmerclub.cloud.im.socket.app.UserWebSocketServer;
import com.tmerclub.cloud.im.vo.MessageInfoVO;
import com.tmerclub.cloud.im.vo.MsgItemVO;
import com.tmerclub.cloud.im.vo.ShopDetailEmployee;
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;

/**
 * 商家收到的消息
 * 10,"无法获取用户信息"
 * 11,"账户已在别处登录，请刷新"
 * 12,"对方不在线"
 * 13, "内容格式有误"
 *
 * @author Lzk
 */
@ServerEndpoint("/m/ua/im/websocket/{token}/{tenantId}/{userId}")
@Component
public class ShopWebSocketServer {

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

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

    private static TokenFeignClient tokenFeignClient;

    private static UserFeignClient userFeignClient;

    private static ShopDetailFeignClient shopDetailFeignClient;

    private static ShopUserFeignClient shopUserFeignClient;

    private static ImMsgBizUserHistoryService imMsgBizUserHistoryService;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        tokenFeignClient = applicationContext.getBean(TokenFeignClient.class);
        userFeignClient = applicationContext.getBean(UserFeignClient.class);
        shopDetailFeignClient = applicationContext.getBean(ShopDetailFeignClient.class);
        imMsgBizUserHistoryService = applicationContext.getBean(ImMsgBizUserHistoryService.class);
        shopUserFeignClient = applicationContext.getBean(ShopUserFeignClient.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("ShopWebSocketServer-无法获取用户信息，token:{}", token);
            }
            return;
        }

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

        SHOP_EMPLOYEE_MAP.put(Constant.SHOP_EMPLOYEE_PREFIX + uidInfoBO.getTenantId() + Constant.UNDERLINE + uidInfoBO.getUserId(), session);
        if (session.isOpen()) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success("ok")));
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("token") String token, @PathParam("tenantId") Long tenantId, @PathParam("userId") Long userId) {
        SHOP_EMPLOYEE_MAP.remove(Constant.SHOP_EMPLOYEE_PREFIX + tenantId + Constant.UNDERLINE + userId);
        logger.info("ShopWebSocketServer-连接关闭 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) {
        SHOP_EMPLOYEE_MAP.remove(Constant.SHOP_EMPLOYEE_PREFIX + tenantId + Constant.UNDERLINE + userId);
        logger.error("ShopWebSocketServer-连接错误 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 (Objects.isNull(uidInfoBO)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CAN_NOT_FIND_USER_INFO, "无法获取用户信息")));
                logger.info("ShopWebSocketServer-无法获取用户信息，token:{}", token);
            }
            return;
        }

        if (StrUtil.isBlank(sourceMessage)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.CONTENT_FORMAT_ERROR, "内容格式有误")));
                logger.info("ShopWebSocketServer-内容格式有误，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("ShopWebSocketServer-内容格式有误，token:{}", token);
            }
            return;
        }

        // 保持心跳
        if (StrUtil.equals(messageInfo.getContent(), Constant.HEART_BEAT)) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.HEART_BEAT, "商家ws保持心跳")));
            }
            return;
        }

        Long userId = messageInfo.getToId();
        ServerResponseEntity<UserApiVO> userApiResponse = userFeignClient.getInsiderUserData(userId);
        if (!userApiResponse.isSuccess()) {
            throw new LuckException(userApiResponse.getMsg());
        }
        UserApiVO user = userApiResponse.getData();
        if (Objects.isNull(user)) {
            user = new UserApiVO();
            user.setUserId(userId);
            user.setUserName("*用户已注销*");
            user.setNickName("*用户已注销*");
        }

        sendToUser(messageInfo, uidInfoBO, user, session);
    }

    public void sendToUser(MessageInfoVO messageInfo, UidInfoBO uidInfoBO, UserApiVO user, Session session) throws IOException {
        ServerResponseEntity<EsShopDetailBO> shopResponse = shopDetailFeignClient.getShopByShopId(uidInfoBO.getTenantId());
        if (!shopResponse.isSuccess()) {
            throw new LuckException(shopResponse.getMsg());
        }
        EsShopDetailBO shopDetail = shopResponse.getData();

        // 获取消息历史
        if (Objects.nonNull(messageInfo.getHistory()) && Objects.equals(messageInfo.getHistory(), true)) {
            PageVO<MsgItemVO> msgItemPage = imMsgBizUserHistoryService.getShopUserHistory(uidInfoBO, messageInfo, user, shopDetail);
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.HISTORY, msgItemPage)));
            }
            return;
        }

        // 标记消息已读
        if (Objects.nonNull(messageInfo.getRead()) && Objects.equals(messageInfo.getRead(), true)) {
            markMsgIsRead(messageInfo, uidInfoBO, user, session);
            return;
        }

        sendOnlineMsg(messageInfo, user, session);

        String tempString = RedisUtil.get(Constant.IN_CHARGE_SHOP + uidInfoBO.getTenantId() + Constant.UNDERLINE + messageInfo.getToId());
        Long employeeId = null;
        if (Objects.nonNull(tempString)) {
            employeeId = Long.valueOf(tempString.substring(4));
            if (!Objects.equals(employeeId, uidInfoBO.getUserId())) {
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.NOT_YOUR_CLIENT, "不是你负责的客户")));
                    logger.info("ShopWebSocketServer-不是你负责的客户");
                }
                return;
            }
        }

        // 用户注销后不可以进行后面的操作
        if (Objects.isNull(user.getUserId())) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.USER_ACCOUNT_CANCELLATION, "无接收消息的联系人")));
            }
            return;
        }
        if (Objects.isNull(user.getStatus())) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.fail(MsgCode.USER_ACCOUNT_CANCELLATION, "用户账号已注销")));
                logger.info("ShopWebSocketServer-无法获取用户信息");
            }
            return;
        }

        // 获取可转接客服列表
        if (Objects.nonNull(messageInfo.getEmployeeList()) && Objects.equals(messageInfo.getEmployeeList(), true)) {
            getCanTransferList(uidInfoBO, session, shopDetail);
            return;
        }

        // 转接客服
        if (Objects.nonNull(messageInfo.getChangeTo()) && Objects.equals(messageInfo.getChangeTo(), true)) {
            transfer(messageInfo, uidInfoBO, user, session, shopDetail);
            return;
        }

        // 发送消息
        if (Objects.nonNull(messageInfo.getContent()) && !messageInfo.getContent().isEmpty()) {
            sendMsgToUser(messageInfo, uidInfoBO, user, shopDetail, employeeId);
        }
    }

    private static void sendMsgToUser(MessageInfoVO messageInfo, UidInfoBO uidInfoBO, UserApiVO user, EsShopDetailBO shopDetail, Long employeeId) throws IOException {
        ImMsgBizUserHistory imMsgBizUserHistory = imMsgBizUserHistoryService.saveShopUserHistory(messageInfo, uidInfoBO);
        // 在线的话发送消息，不在线的话留言
        Session userSession = UserWebSocketServer.USER_MAP.get(Constant.USER_PREFIX + messageInfo.getToId());
        if (Objects.nonNull(userSession)) {
            if (Objects.isNull(employeeId)) {
                RedisUtil.set(Constant.IN_CHARGE_SHOP + uidInfoBO.getTenantId() + Constant.UNDERLINE + messageInfo.getToId(), "test" + uidInfoBO.getUserId(), 0);
            }
            MsgItemVO msgItem = imMsgBizUserHistoryService.getUserShopMsgItem(shopDetail, user, imMsgBizUserHistory);
            if (userSession.isOpen()) {
                ShopUserVO shopUser = shopUserFeignClient.getSimpleByUserId(employeeId);
                msgItem.setNickName(shopUser.getNickName());
                userSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem)));
            }
            List<Session> sessionList = MapUtil.getSessionByKeyPrefix(UserOnlineWebSocketServer.USER_ONLINE_MAP, Constant.ONLINE_USER_PREFIX + messageInfo.getToId() + Constant.UNDERLINE);
            if (CollUtil.isNotEmpty(sessionList)) {
                MsgItemVO msgItem2 = new MsgItemVO();
                msgItem2.setNewMessage(true);
                for (Session userSession2 : sessionList) {
                    if (userSession2 != null) {
                        if (userSession2.isOpen()) {
                            userSession2.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(msgItem2)));
                        }
                    }
                }
            }
        }
    }

    private static void transfer(MessageInfoVO messageInfo, UidInfoBO uidInfoBO, UserApiVO user, Session session, EsShopDetailBO shopDetail) throws IOException {
        if (Objects.nonNull(messageInfo.getEmployeeId())) {
            Session userSession = UserWebSocketServer.USER_MAP.get(Constant.USER_PREFIX + messageInfo.getToId());
            Session shopSession = SHOP_EMPLOYEE_MAP.get(Constant.SHOP_EMPLOYEE_PREFIX + uidInfoBO.getTenantId() + Constant.UNDERLINE + messageInfo.getEmployeeId());
            if (Objects.nonNull(shopSession)) {
                RedisUtil.set(Constant.IN_CHARGE_SHOP + uidInfoBO.getTenantId() + Constant.UNDERLINE + messageInfo.getToId(), "test" + messageInfo.getEmployeeId(), 0);
                ServerResponseEntity<ShopUserVO> shopUserResponse = shopDetailFeignClient.getShopUserByShopUserId(messageInfo.getEmployeeId());
                if (!shopUserResponse.isSuccess()) {
                    throw new LuckException(shopUserResponse.getMsg());
                }
                ShopUserVO shopUserVO = shopUserResponse.getData();
                ImMsgBizUserHistory imMsgBizUserHistory = imMsgBizUserHistoryService.saveShopChangeEmployeeReply(uidInfoBO, shopUserVO, user);
                imMsgBizUserHistory.setNickName(shopUserVO.getNickName());
                MsgItemVO msgItem = imMsgBizUserHistoryService.getUserShopMsgItem(shopDetail, user, imMsgBizUserHistory);
                if (session.isOpen()) {
                    msgItem.setEmployeeUnread(MsgRead.UNREAD);
                    session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.CHANGE_EMPLOYEE_SUCCESS, msgItem)));
                }
                // 在线的话发送消息，不在线的话留言
                if (Objects.nonNull(userSession) && userSession.isOpen()) {
                    userSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.CHANGE_EMPLOYEE_SUCCESS, msgItem)));
                }
                if (shopSession.isOpen()) {
                    shopSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.CHANGE_EMPLOYEE_SUCCESS, msgItem)));
                }
            }
        }
    }

    private static void getCanTransferList(UidInfoBO uidInfoBO, Session session, EsShopDetailBO shopDetail) throws IOException {
        List<String> keys = MapUtil.getKeysByPrefixAndExcludeKey(SHOP_EMPLOYEE_MAP, Constant.SHOP_EMPLOYEE_PREFIX + uidInfoBO.getTenantId() + Constant.UNDERLINE,
                Constant.SHOP_EMPLOYEE_PREFIX + uidInfoBO.getTenantId() + Constant.UNDERLINE + uidInfoBO.getUserId());
        List<ShopDetailEmployee> listEmployee = new ArrayList<>(com.tmerclub.cloud.common.constant.Constant.INITIAL_CAPACITY);
        if (CollUtil.isEmpty(keys)) {
            return;
        }
        long tempLength = (Constant.SHOP_EMPLOYEE_PREFIX + uidInfoBO.getTenantId() + Constant.UNDERLINE).length();
        for (String key : keys) {
            Long tempEmployeeId = Long.valueOf(key.substring((int) tempLength));
            ServerResponseEntity<ShopUserVO> shopUserResponse = shopDetailFeignClient.getShopUserByShopUserId(tempEmployeeId);
            if (!shopUserResponse.isSuccess()) {
                throw new LuckException(shopUserResponse.getMsg());
            }
            ShopUserVO shopUserVO = shopUserResponse.getData();
            ShopDetailEmployee shopDetailEmployee = new ShopDetailEmployee();
            shopDetailEmployee.setShopName(shopDetail.getShopName());
            shopDetailEmployee.setPic(shopDetail.getShopLogo());
            shopDetailEmployee.setShopId(shopDetail.getShopId());
            shopDetailEmployee.setEmployeeId(shopUserVO.getShopUserId());
            shopDetailEmployee.setEmployeeNickname(shopUserVO.getNickName());
            listEmployee.add(shopDetailEmployee);
        }
        if (session.isOpen()) {
            session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.EMPLOYEE_ONLINE, listEmployee)));
        }
    }

    private static void sendOnlineMsg(MessageInfoVO messageInfo, UserApiVO user, Session session) throws IOException {
        if (Objects.nonNull(messageInfo.getOnlineOrOffline()) && Objects.equals(messageInfo.getOnlineOrOffline(), true)) {
            Boolean isExist = MapUtil.isExistKeyPrefix(UserOnlineWebSocketServer.USER_ONLINE_MAP, Constant.ONLINE_USER_PREFIX + user.getUserId() + Constant.UNDERLINE);
            MsgItemVO msgItem = new MsgItemVO();
            msgItem.setChatType(SendType.SHOP);
            msgItem.setUserId(user.getUserId());
            msgItem.setOnlineOrOffline(isExist);
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.ONLINE_OR_OFFLINE, msgItem)));
            }
        }
    }

    private static void markMsgIsRead(MessageInfoVO messageInfo, UidInfoBO uidInfoBO, UserApiVO user, Session session) throws IOException {
        Integer updateNum = imMsgBizUserHistoryService.markUserReadBySendType(SendType.SHOP, messageInfo, uidInfoBO, user.getUserId());
        if (updateNum > 0) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.READ, "ok")));
            }
            Session userSession = UserWebSocketServer.USER_MAP.get(Constant.USER_PREFIX + messageInfo.getToId());
            if (userSession != null) {
                if (userSession.isOpen()) {
                    userSession.getBasicRemote().sendText(Json.toJsonString(ServerResponseEntity.success(MsgCode.READ, messageInfo.getMsgIds())));
                }
            }
        }
    }

}
