package com.wecloud.im.ws.manager;

import com.wecloud.im.ws.cache.UserStateCacheManager;
import com.wecloud.im.ws.cache.UserStateListener;
import com.wecloud.im.ws.model.ClientInfo;
import com.wecloud.im.ws.model.redis.ClientChannelInfo;
import com.wecloud.rtc.service.RtcService;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * channel内容管理，在线、离线等信息在channel里
 * @author lixiaozhong
 */
@Component
@Slf4j
public class ChannelManager {

    /**
     * 本地维护 uid 对应多个 channel的shortID
     */
    public static final Map<String, Set<String>> CLIENTS_MAP = new ConcurrentHashMap<>();

    /**
     * channel的shortID对应client端数据
     */
    public static final Map<String, ClientInfo> SESSION_INFO_MAP = new ConcurrentHashMap<>();

    /**
     * CLIENT_ID,是客户端的字符串id
     */
    public static final AttributeKey<String> CLIENT_ID = AttributeKey.valueOf("ci");

    /**
     * 是app的字符串id
     */
    public static final AttributeKey<String> APP_KEY = AttributeKey.valueOf("ak");

    public static final AttributeKey<Integer> READ_IDLE_TIMES = AttributeKey.valueOf("readIdleTimes");

    /**
     * LANGUAGE
     */
    AttributeKey<String> LANGUAGE = AttributeKey.valueOf("la");

    /**
     * APP_VERSION
     */
    AttributeKey<String> APP_VERSION = AttributeKey.valueOf("av");
    AttributeKey<String> TOKEN = AttributeKey.valueOf("to");
    AttributeKey<String> DEVICEID = AttributeKey.valueOf("dc");
    AttributeKey<String> PLATFORM = AttributeKey.valueOf("pt");

    @Autowired
    private RtcService rtcService;

    @Autowired
    private UserStateCacheManager userStateCacheManager;

    /**
     * client上线
     * userID绑定channel
     *
     * @param channel
     */
    public void online(String appKey, String clientId, NioSocketChannel channel) {
        String longChannelId = channel.id().asLongText();
        this.putClientsMap(appKey, clientId, longChannelId);
        this.putSessionInfoMap(longChannelId, channel);

        UserStateListener.triggerOnlineEvent(appKey, clientId, longChannelId);
    }

    /**
     * 下线移除channel
     *
     * @param appKey
     * @param clientId
     * @param channelHandlerContext
     */
    public void offline(String appKey, String clientId, ChannelHandlerContext channelHandlerContext) {

        String longChannelId = channelHandlerContext.channel().id().asLongText();

        // 关掉连接
        channelHandlerContext.close();


        // 移除本地维护的channel
        delSessionInfoMap(longChannelId);
        delClientsMap(appKey, clientId, longChannelId);

        UserStateListener.triggerOfflineEvent(appKey, clientId, longChannelId);

    }

    /**
     * 获取用户在线状态
     *
     * @param toAppKey
     * @param toClientId
     * @return true:在线, false 不在线
     */
    public boolean getOnlineStatus(String toAppKey, String toClientId) {

        List<ClientChannelInfo> channelInfos = userStateCacheManager.getIpByClientIdAndOnline(toAppKey, toClientId);

        boolean flag = false;
        for (ClientChannelInfo channelInfo : channelInfos) {

            if (channelInfo.getOnlineStatus().equals(1)) {
                return true;
            }
        }

        return flag;
    }

    private void putSessionInfoMap(String longChannelId, NioSocketChannel channel) {
        ClientInfo clientInfo = new ClientInfo();
        clientInfo.setDeviceId("");
        clientInfo.setNioSocketChannel(channel);
        clientInfo.setToken("");
        ChannelManager.SESSION_INFO_MAP.put(longChannelId, clientInfo);
    }

    private void putClientsMap(String appKey, String clientId, String longChannelId) {
        Set<String> set = ChannelManager.CLIENTS_MAP.get(appKey + ":" + clientId);
        if (set == null || set.isEmpty()) {
            HashSet<String> hashSet = new HashSet<>();
            hashSet.add(longChannelId);
            ChannelManager.CLIENTS_MAP.put(appKey + ":" + clientId, hashSet);
        } else {
            set.add(longChannelId);
        }
    }

    private void delSessionInfoMap(String longChannelId) {
        ChannelManager.SESSION_INFO_MAP.remove(longChannelId);
    }

    private void delClientsMap(String appKey, String clientId, String longChannelId) {
        Set<String> set = ChannelManager.CLIENTS_MAP.get(appKey + ":" + clientId);
        if (set != null) {
            set.remove(longChannelId);
        }
    }
}
