package com.wecloud.im.ws.sender;

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.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 toAppKey
     * @param toClientId
     */
    public void sendMsgData(ReceiveVO receiveVO, Object data, String toAppKey, String toClientId) {
        this.sendMsg(receiveVO, ApiCode.SUCCESS, data, toAppKey, toClientId);
    }

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

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


    /**
     * 可自定义状态码 带data
     *
     * @param receiveVO
     * @param data
     */
    public void sendMsg(ReceiveVO receiveVO, ApiCode apiCode, Object data, String toAppKey, String 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, toAppKey, toClientId);
    }


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

        String msgJson = JsonUtils.encodeJson(responseModel);

        List<ClientChannelInfo> channelInfos = userStateCacheManager.getIpByClientIdAndOnline(toAppKey, 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(clientChannelInfo.getChannelId(), 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(clientChannelInfo.getChannelId(), msgJson);
            }
        }

    }

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

        String msgJson = JsonUtils.encodeJson(responseModel);

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

    /**
     * 向指定channelId下发数据，限定本机有的channel
     *
     * @param toChannelId
     * @param msg
     * @return
     */
    public boolean sendMsgLocal(String toChannelId, String msg) {

        ClientInfo clientInfo = ChannelManager.SESSION_INFO_MAP.get(toChannelId);
        if (clientInfo == null) {
            return false;
        }

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

        log.info("writeData:" + ",\nchannelId:" + nioSocketChannel.id().asLongText());

        nioSocketChannel.writeAndFlush(new TextWebSocketFrame(msg));

        return true;
    }

}
