package com.tsi.comm.server.repository;

import com.tsi.comm.server.xnet.NettyUtils;
import com.tsi.comm.server.vo.TsiNodeVo;
import com.tsi.common.utils.Counter;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@RequiredArgsConstructor
@Component
public class TsiSessionManager {

    public static final AttributeKey<TsiNodeVo> TSI_NODE_ATTRIBUTE_KEY = AttributeKey.valueOf("TsiNode");
    public static final AttributeKey<Integer> PKT_Q_IDX_KEY = AttributeKey.valueOf("pktQIdx");
    public static final AttributeKey<Integer> DBMS_Q_IDX_KEY = AttributeKey.valueOf("dbmsQIdx");
    public static final AttributeKey<Integer> LOG_Q_IDX_KEY = AttributeKey.valueOf("logQIdx");

    // nodeId가 0이거나 유효하지 않아 인증되지 않은 채널임을 표시하는 키. 이 키가 채널에 존재하면, 해당 채널에서 오는 패킷은 무시
    public static final AttributeKey<Boolean> UNAUTHENTICATED_KEY = AttributeKey.valueOf("unauthenticated");

    private final Counter sessions = new Counter();

    @Getter
    private final ConcurrentHashMap<Channel, TsiNodeVo> channelNodeMap = new ConcurrentHashMap<>();
//    private final ConcurrentHashMap<String, TsiNodeVo> channelNodeMap = new ConcurrentHashMap<>();

    public int add() {
        return (int) this.sessions.increment();
    }
    public int remove() {
        return (int) this.sessions.decrement();
    }
    public int getCount() {
        return (int) this.sessions.get();
    }

    public int getChannelCount() {
        return this.channelNodeMap.size();
    }
    public void addChannel(Channel channel, TsiNodeVo vo) {
        if (channel == null) {
            return;
        }
        this.channelNodeMap.put(channel, vo);
    }
    public void removeChannel(Channel channel) {
        if (channel == null) {
            return;
        }
        this.channelNodeMap.remove(channel);
    }
    public TsiNodeVo getNodeVo(Channel channel) {
        if (channel == null) {
            return null;
        }
        return this.channelNodeMap.get(channel);
    }

    public void reportChannelSessions() {
        log.info("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
        log.info("Report Channel Sessions: {} Sessions.", this.sessions.get());
        log.info("{}", String.format("%10s %7s  Remote-Address", "ID", "Active"));
        log.info("----------------------------------------------------------------------------------------------------------------");
        for (Map.Entry<Channel, TsiNodeVo> obj : this.channelNodeMap.entrySet()) {
            Channel channel = obj.getKey();
            TsiNodeVo node = obj.getValue();
            log.info("{}", String.format("%10s %7s  %s", node.getNodeId(), channel.isActive(), NettyUtils.getRemoteAddress(channel)));
        }
//        for (Map.Entry<String, TsiNodeVo> obj : this.channelNodeMap.entrySet()) {
//            String channel = obj.getKey();
//            TsiNodeVo node = obj.getValue();
//            log.info("{}", String.format("%10s %s", node.getNodeId(), node.getIpAddr()));
//        }
        log.info("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
    }

}
