package com.tsi.comm.server.repository;

import com.tsi.app.common.app.AppUtils;
import com.tsi.app.common.xnet.NettyUtils;
import com.tsi.comm.server.mybatis.vo.AbstractDbmsVo;
import com.tsi.comm.server.mybatis.vo.AlarmOccrVo;
import com.tsi.comm.server.process.dbms.TsiCvimDbmsProcess;
import com.tsi.comm.server.vo.TsiAlarmConfigVo;
import com.tsi.comm.server.vo.TsiNodeVo;
import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class TsiNodeManager {

    private static TsiNodeManager _instance = null;

    public static TsiNodeManager getInstance() {
        if (_instance == null) {
            synchronized (TsiNodeManager.class) {
                if (_instance == null)
                    _instance = new TsiNodeManager();
            }
        }
        return _instance;
    }

    private TsiNodeManager() {
        this.tsiNodeVoMap = new ConcurrentHashMap<>();
        this.tsiCvimDbmsService = (TsiCvimDbmsProcess) AppUtils.getBean(TsiCvimDbmsProcess.class);
    }

    private ConcurrentHashMap<Long, TsiNodeVo> tsiNodeVoMap;
    // DB Process Service
    private TsiCvimDbmsProcess tsiCvimDbmsService;

    public TsiNodeVo get(long nodeId) {
        return this.tsiNodeVoMap.get(nodeId);
    }
    public void put(long nodeId, TsiNodeVo vo) {
        this.tsiNodeVoMap.put(nodeId, vo);
    }

    public int size() {
        return this.tsiNodeVoMap.size();
    }

    public boolean containsKey(long key) {
        return this.tsiNodeVoMap.containsKey(key);
    }

    public void beforeLoading() {
        for (Map.Entry<Long, TsiNodeVo> obj : this.tsiNodeVoMap.entrySet()) {
            obj.getValue().setCheckInstalled(false);
        }
    }

    public void afterLoading() {
        for (Map.Entry<Long, TsiNodeVo> obj : this.tsiNodeVoMap.entrySet()) {
            if (obj.getValue().isRegistered()) {
                obj.getValue().setInstalled(obj.getValue().isCheckInstalled());
            }
        }
    }

    public void reportNodeSessions() {
        log.info("================================================================================================================");
        log.info("Report Node Sessions: {} Nodes.", this.tsiNodeVoMap.size());
        int registered = 0;
        int unknown = 0;
        int connected = 0;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9"));
        String connect;
        String connectTm;
        String disconnectTm;
        String lastCommTm;
        String info;
        log.info("{}", String.format("%10s %4s %7s %19s %19s %9s %6s %19s  Remote-Address", "ID", "UNTC", "Connect", "Connect Time", "Disconnect Time", "Connected", "Closed", "Last-Recv-Time"));
        log.info("----------------------------------------------------------------------------------------------------------------");
        for (Map.Entry<Long, TsiNodeVo> obj : this.tsiNodeVoMap.entrySet()) {
            TsiNodeVo node = obj.getValue();
            String check = (node.isInstalled() ? "Y" : "N");
            check = check + (node.isSendNode() ? "Y" : "N");
            check = check + (node.isSendTest() ? "Y" : "N");
            check = check + (node.isSendCvim() ? "Y" : "N");
            if (node.isRegistered()) {
                registered++;
            }
            else {
                unknown++;
            }
            if (node.isConnect()) {
                connected++;
                connect = "Y";
                info = NettyUtils.getRemoteAddress(node.getChannel());
            }
            else {
                connect = "N";
                info = "---";
            }

            connectTm = sdf.format(new Date(node.getConnectTm()));
            disconnectTm = sdf.format(new Date(node.getDisconnectTm()));
            lastCommTm = sdf.format(new Date(node.getLastCommTm()));

            log.info("{}", String.format("%10s %4s %7s %19s %19s %9d %6d %19s  %s", node.getKey(), check, connect, connectTm, disconnectTm, node.getConnectCount().get(), node.getDisconnectCount().get(), lastCommTm, info));
        }
        log.info("----------------------------------------------------------------------------------------------------------------");
        log.info("Total Nodes: {} EA, Registered: {} EA, Unknown: {} EA, Connected: {} EA", this.tsiNodeVoMap.size(), registered, unknown, connected);
        log.info("==============================================");
    }

    public void checkSessionTimeout(long timeout) {

        String connectTm;
        String lastCommTm;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9"));
        long currTm = System.currentTimeMillis();
        for (Map.Entry<Long, TsiNodeVo> obj : this.tsiNodeVoMap.entrySet()) {
            TsiNodeVo node = obj.getValue();
            if (node.isConnect()) {
                if ((currTm - node.getLastCommTm()) > timeout) {
                    try {
                        AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS);
                        alarm.setAlarmCode(TsiAlarmConfigVo.COMM_02);
                        alarm.setAlarmTarget(node.getKey());
                        alarm.setAlarmValue(NettyUtils.getRemoteIpAddress(node.getChannel()));
                        this.tsiCvimDbmsService.add(alarm, (int) Thread.currentThread().getId());

                        connectTm = sdf.format(new Date(node.getConnectTm()));
                        lastCommTm = sdf.format(new Date(node.getLastCommTm()));
                        log.warn("Node: {}, Recv Timeout: Connect: {}, LastCommTime: {}", node.getKey(), connectTm, lastCommTm);
                        node.getChannel().close();
                    }
                    catch (Exception e) {
                        log.error("{}", e.getMessage());
                    }
                }
            }
        }
    }
}
