package com.wecloud.im.ws.sender;

import com.wecloud.im.executor.SendMsgThreadPool;
import com.wecloud.im.router.RouterSendService;
import com.wecloud.im.ws.cache.UserStateCacheManager;
import com.wecloud.im.ws.model.ClientInfo;
import com.wecloud.im.ws.model.WsResponseModel;
import com.wecloud.im.ws.model.redis.ClientChannelInfo;
import com.wecloud.im.ws.model.request.ReceiveVO;
import com.wecloud.im.ws.manager.ChannelManager;
import com.wecloud.im.ws.utils.InitIp;
import com.wecloud.im.ws.utils.RedisUtils;
import com.wecloud.utils.JsonUtils;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.cluster.router.address.Address;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description 下发数据
 * @Author hewei hwei1233@163.com
 * @Date 2019-12-05
 */
@Component
@Slf4j
public class ChannelSender {

    @Autowired
    private UserStateCacheManager userStateCacheManager;

    @DubboReference(parameters = {"router", "address"})
    private RouterSendService routerSendService;

    /**
     * 固定"成功"状态码 带data
     *
     * @param receiveVO
     * @param data
     * @param toClientId
     */
    public void sendMsgData(ReceiveVO receiveVO, Object data, Long toClientId) {
        this.sendMsg(receiveVO, ApiCode.SUCCESS, data, toClientId);
    }

    /**
     * 固定"成功"状态码 无data
     *
     * @param receiveVO
     * @param apiCode
     * @param toClientId
     */
    public void sendMsgSucess(ReceiveVO receiveVO, ApiCode apiCode, Long toClientId) {
        this.sendMsg(receiveVO, apiCode, new HashMap<>(1), toClientId);
    }

    /**
     * 固定"参数错误"状态码 无data
     *
     * @param receiveVO
     * @param toClientId
     */
    public void sendMsgIllegeArgs(ReceiveVO receiveVO, Long toClientId) {
//        this.nullDataSuccess(requestModel, ResultStatus.PARAM_ERROR, userId);
    }


    /**
     * 可自定义状态码 带data
     *
     * @param receiveVO
     * @param data
     */
    public void sendMsg(ReceiveVO receiveVO, ApiCode apiCode, Object data, Long toClientId) {
        ApiResult<Boolean> apiResult = ApiResult.result(apiCode);
        WsResponseModel responseModel = new WsResponseModel();
        responseModel.setMsg(apiResult.getMessage());
        responseModel.setCmd(receiveVO.getCmd());
        responseModel.setReqId(receiveVO.getReqId());
        responseModel.setData(data);
        responseModel.setCode(apiResult.getCode());
        this.sendMsg(responseModel, toClientId);
    }

    /**
     * 批量发消息
     * @param responseModel
     * @param toIp
     * @param toClientIdAndPlatforms
     */
    public void batchSendMsg(WsResponseModel responseModel, String toIp, List<String> toClientIdAndPlatforms) {
        // 是否为当前机器的ip
        if (InitIp.lAN_IP.equals(toIp)) {
            String msgJson = JsonUtils.encodeJson(responseModel);
            batchSendMsgLocal(toClientIdAndPlatforms, msgJson);
        } else {
            String msgJson = JsonUtils.encodeJson(responseModel);
            Address address = new Address(toIp, 20881);
            RpcContext.getContext().setObjectAttachment("address", address);
            routerSendService.batchSendMsgRemote(toClientIdAndPlatforms, msgJson);
        }
    }

    /**
     * 调用ws处理响应逻辑
     *
     * @param responseModel
     * @param toClientId
     */
    public void sendMsg(WsResponseModel responseModel, Long toClientId) {

        String msgJson = JsonUtils.encodeJson(responseModel);

        List<ClientChannelInfo> channelInfos = userStateCacheManager.findOnlineInfosByClientId(toClientId);

        // 一个用户存在多端的情况，所以先进行分类，key是ip地址，value是channel的列表
        Map<String, List<ClientChannelInfo>> ipChannels = channelInfos.stream().collect(Collectors.groupingBy(ClientChannelInfo::getLanIp));

        for (Map.Entry<String, List<ClientChannelInfo>> channelInfoEntry : ipChannels.entrySet()) {

            // 是否为当前机器的ip
            if (InitIp.lAN_IP.equals(channelInfoEntry.getKey())) {
                // 调用本地下发
                for(ClientChannelInfo clientChannelInfo : channelInfoEntry.getValue()) {
                    this.sendMsgLocal(toClientId, clientChannelInfo.getPlatform(), msgJson);
                }
                continue;
            }
            // todo rpc调用下发，需要改成批量
//                RpcContext.getContext().set("ip", channelInfo.getLanIp());
            // 根据provider的ip,port创建Address实例
            for(ClientChannelInfo clientChannelInfo : channelInfoEntry.getValue()) {
                Address address = new Address(clientChannelInfo.getLanIp(), 20881);
                RpcContext.getContext().setObjectAttachment("address", address);
                routerSendService.sendMsgRemote(toClientId, clientChannelInfo.getPlatform(), msgJson);
            }
        }

    }

    /**
     * 本地直接下发，限定本机有的channel
     *
     * @param nioSocketChannel
     * @param responseModel
     */
    public void sendMsgLocal(NioSocketChannel nioSocketChannel, WsResponseModel responseModel) {

        String msgJson = JsonUtils.encodeJson(responseModel);

        // 本地直接下发
        nioSocketChannel.writeAndFlush(new TextWebSocketFrame(msgJson));
    }

    /**
     * 批量发送
     * @param toClientIdAndPlatforms list里的 结构为  client的主键id:platform
     * @param msgJson
     */
    public void batchSendMsgLocal(List<String> toClientIdAndPlatforms, String msgJson) {
        for(String arrStr : toClientIdAndPlatforms) {
            SendMsgThreadPool.SEND_MSG_THREAD_POOL_EXECUTOR.execute(()->{
                this.sendMsgLocal(arrStr, msgJson);
            });
        }
    }

    /**
     * 向指定channelId下发数据，限定本机有的channel
     *
     * @param clientId
     * @param platform
     * @param msg
     * @return
     */
    public boolean sendMsgLocal(Long clientId, Integer platform, String msg) {
        String key = ChannelManager.genKeyForSessionInfoMap(clientId, platform);
        return sendMsgLocal(key, msg);
    }

    /**
     * 本地的channel 推送
     * @param key 结构为  client的主键id:platform
     * @param msgJsonStr json结构
     * @return
     */
    private boolean sendMsgLocal(String key, String msgJsonStr) {
        ClientInfo clientInfo = ChannelManager.SESSION_INFO_MAP.get(key);
        if (clientInfo == null) {
            return false;
        }

        NioSocketChannel nioSocketChannel = clientInfo.getNioSocketChannel();
        if (null == nioSocketChannel) {
            log.info("writeData连接为空: {}", msgJsonStr);
            return false;
        }
        // 判断连接是否断开
        if (nioSocketChannel.isShutdown()) {
            log.warn("writeData 已连接断开: {}\n channelId: {}", msgJsonStr, nioSocketChannel.id().asLongText());
            return false;
        }

        log.info("writeData, channelId: {}", nioSocketChannel.id().asLongText());

        nioSocketChannel.writeAndFlush(new TextWebSocketFrame(msgJsonStr));

        return true;
    }

}
