package com.wecloud.im.ws.strategy;


import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.param.ImConversationQueryVo;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.ws.enums.WsRequestCmdEnum;
import com.wecloud.im.ws.model.request.ReceiveVO;
import com.wecloud.im.ws.utils.SpringBeanUtils;
import com.wecloud.utils.JsonUtils;
import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;

/**
 * @Description 处理Cmd请求
 * 抽象类 策略设计模式
 * @Author hewei hwei1233@163.com
 * @Date 2020-01-02
 */
@Slf4j
public abstract class AbstractImCmdStrategy {

    private static ImCmdStrategyFactory imCmdStrategyFactory;
    private static ImApplicationService imApplicationService;
    private static ImClientService imClientService;
    private static ImConversationService imConversationService;

    public static void process(Long senderClientId, ChannelHandlerContext ctx, String data) {
        if(log.isDebugEnabled()) {
            log.debug("appWS收到data: {}\n senderClientId:{}, channelId:{}", data, senderClientId, ctx.channel().id().asLongText());
        }

        if(imCmdStrategyFactory == null) {
            synchronized (AbstractImCmdStrategy.class) {
                // 双空判断，懒汉模式下的绝对线程安全
                if(imCmdStrategyFactory == null) {
                    imCmdStrategyFactory = SpringBeanUtils.getBean(ImCmdStrategyFactory.class);
                    imApplicationService = SpringBeanUtils.getBean(ImApplicationService.class);
                    imClientService = SpringBeanUtils.getBean(ImClientService.class);
                    imConversationService = SpringBeanUtils.getBean(ImConversationService.class);
                }
            }
        }

        // 解析jsonO
        ReceiveVO receiveVO = JsonUtils.decodeJson(data, ReceiveVO.class);


        if (null == receiveVO || null == receiveVO.getCmd()) {
            throw new BusinessException("null == receiveVO || null == receiveVO.getCmd()");
        }

        // 获取会话id
        if (receiveVO.getData() == null || receiveVO.getData().getToConversation() == null) {
            log.warn("会话消息reqId: {} 不合法，缺少会话id！", receiveVO.getReqId());
            return;
        }

        WsRequestCmdEnum wsRequestPathEnum = WsRequestCmdEnum.getByCode(receiveVO.getCmd());

        //查看接收的群属性，是否万人群
        ImConversationQueryVo conversation = imConversationService.getImConversationById(receiveVO.getData().getToConversation());

        if(conversation == null) {
            log.warn("会话消息reqId: {} 会话id不合法！", receiveVO.getReqId());
            return;
        }

        if(BooleanUtils.isTrue(conversation.getIsThousand()) && WsRequestCmdEnum.NORMAL_CHAT == wsRequestPathEnum) {
            // 普通群升级为万人群
            wsRequestPathEnum = WsRequestCmdEnum.THROUSAND_CHAT;
        }
        // 使用策略模式， 根据不同类型请求调用不同实现类
        AbstractImCmdStrategy cmdStrategy = imCmdStrategyFactory.getStrategy(wsRequestPathEnum);

        // 查询发送者client
        ImClient imClientSender = imClientService.getCacheImClient(senderClientId);
        if (imClientSender == null) {
            log.warn("根据senderClientId: {} 查找不到 imClientSender！", senderClientId);
            return;
        }

        // 查询imApplication
        ImApplication imApplication = imApplicationService.getById(imClientSender.getFkAppid());
        if (imApplication == null) {
            log.warn("根据appId: {} 查找不到 imApplication！", imClientSender.getFkAppid());
            return;
        }

        cmdStrategy.process(imApplication, imClientSender, ctx, receiveVO);

    }

    /**
     * 处理业务流程
     *
     * @param imApplication
     * @param imSender
     * @param ctx
     * @param receiveVO
     * @throws Exception
     */
    public abstract void process(ImApplication imApplication, ImClient imSender, ChannelHandlerContext ctx, ReceiveVO receiveVO);
}
