package com.its.dsrc.xnettcp.dsrc.handler;

import com.beanit.asn1dsrc.enums.eTerminate;
import com.its.app.AppUtils;
import com.its.app.utils.NettyUtils;
import com.its.app.utils.SysUtils;
import com.its.dsrc.entity.TbRseCtlr;
import com.its.dsrc.entity.TbRseCtlrCnncHs;
import com.its.dsrc.global.AppRepository;
import com.its.dsrc.process.DbmsData;
import com.its.dsrc.process.DbmsDataProcess;
import com.its.dsrc.process.DbmsDataType;
import com.its.dsrc.xnettcp.dsrc.process.service.ControlDeviceService;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.util.concurrent.TimeUnit;

@Slf4j
public class DsrcAsn1ServerIdleStatePacketHandler extends IdleStateHandler {

    private DbmsDataProcess dbmsDataProcess;

    public DsrcAsn1ServerIdleStatePacketHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) {
        super(readerIdleTime, writerIdleTime, allIdleTime, unit);
        this.dbmsDataProcess = (DbmsDataProcess) AppUtils.getBean(DbmsDataProcess.class);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.error("DsrcAsn1ServerIdleStatePacketHandler.-----channelActive: {}", NettyUtils.getTcpAddress(ctx.channel()));
    }

    @Override
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
        String ipAddress = NettyUtils.getRemoteIpAddress(ctx.channel());
        TbRseCtlr obj = AppRepository.getInstance().getCtlrIpMap().get(ipAddress);
        if (obj == null) {
            log.error("DsrcAsn1ServerIdleStatePacketHandler.-------channelIdle: ALL_IDLE, {}, Unknown ip address, will be closed.", ipAddress);
            DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(null, ctx.channel());
            return;
        }

        MDC.put("id", obj.getLogKey());

        if (evt.state() == IdleState.ALL_IDLE) {
            log.error("DsrcAsn1ServerIdleStatePacketHandler.-------channelIdle: ALL_IDLE, {}, ID: {}, {}, {}", ipAddress, obj.getID(), obj.getRSE_ID(), ctx.channel().toString());
            // 클라이언트로 종료 메시지를 전송한다.(AI_Terminate)
            ControlDeviceService.getInstance().sendTerminate(obj, ctx.channel(), eTerminate.Terminate_ClientCommProblesm.getValue());
            DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(obj, ctx.channel());
        }
        else if (evt.state() == IdleState.READER_IDLE) {
            log.warn("DsrcAsn1ServerIdleStatePacketHandler.-------channelIdle: READER_IDLE, {}, ID: {}, {}", ipAddress, obj.getID(), obj.getRSE_ID());
        }
        else if (evt.state() == IdleState.WRITER_IDLE) {
            log.warn("DsrcAsn1ServerIdleStatePacketHandler.-------channelIdle: WRITER_IDLE, {}, ID: {}, {}", ipAddress, obj.getID(), obj.getRSE_ID());
        }
        MDC.remove(obj.getLogKey());
        MDC.clear();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String ipAddress = NettyUtils.getRemoteIpAddress(ctx.channel());
        TbRseCtlr obj = AppRepository.getInstance().getCtlrIpMap().get(ipAddress);
        if (obj != null) {
            MDC.put("id", obj.getLogKey());
        }
        log.error("DsrcAsn1ServerIdleStatePacketHandler.---channelInactive: {}, {}", ipAddress, ctx.channel().toString());

        Channel oldChannel = null;
        if (obj != null) {

            oldChannel = obj.getChannel();

            if (!obj.isDupCon()) {
                // Duplication connection 인 경우 다른 채널이 통신을 대신하므로
                // 네트워크 상태를 변경시키지 않기 위함.
                obj.channelLogout();

                String logId = obj.getLOG_ID();
                if (logId == null || logId.equals("")) {
                    logId = "Unknown";
                }
                TbRseCtlrCnncHs voLog = new TbRseCtlrCnncHs();
                voLog.setRSE_CTLR_NMBR(obj.getID());
                voLog.setLOG_ID(logId);
                voLog.setLOG_ADDRESS(NettyUtils.getRemoteIpAddress(ctx.channel()));
                voLog.setLOG_TYPE(Integer.toString(TbRseCtlrCnncHs.LOG_TYPE_LOGOUT));
                voLog.setCLCT_DT(SysUtils.getSysTime());

                log.debug("DsrcAsn1ServerIdleStatePacketHandler.---channelInactive: {}. {}", ipAddress, voLog.toString());
                this.dbmsDataProcess.add(new DbmsData(DbmsDataType.DBMS_DATA_LOG_HS, false, voLog));
            }
            obj.setDupCon(false);
        }
        super.channelInactive(ctx);
        try {
            ctx.channel().close();
            if (oldChannel != null) {
                log.error("DsrcAsn1ServerIdleStatePacketHandler.---channelInactive: {}, old: {}, curr: {}", ipAddress, obj.getChannel().toString(), ctx.channel().toString());
                if (ctx.channel() != oldChannel && oldChannel.isActive()) {
                    log.error("DsrcAsn1ServerIdleStatePacketHandler.---channelInactive: {}, close, old: {}, curr: {}", ipAddress, obj.getChannel().toString(), ctx.channel().toString());
                    oldChannel.close();
                }
            }
        }
        catch (Exception e) {
        }

        if (obj != null) {
            MDC.remove(obj.getLogKey());
            MDC.clear();
        }
    }

    public static void disconnectChannel(TbRseCtlr obj, Channel channel) {
        // 로그인 한 세션에 대한 종료처리를 수행
        String ipAddress = NettyUtils.getRemoteIpAddress(channel);
        if (obj != null) {
            MDC.put("id", obj.getLogKey());
        }

        try {
            log.error("DsrcAsn1ServerIdleStatePacketHandler.-disconnectChannel: {}, {}", ipAddress, channel.toString());
            if (!channel.isActive()) {
                log.error("DsrcAsn1ServerIdleStatePacketHandler.-disconnectChannel: {}, channel already closed.", ipAddress);
            }

            channel.flush();
            ChannelFuture f = channel.disconnect().awaitUninterruptibly();  // channelInactive event fire
            if (!f.isDone() || !f.isSuccess()) {
                log.error("DsrcAsn1ServerIdleStatePacketHandler.-disconnectChannel: {}, isDone: {}, isSuccess: {}", ipAddress, f.isDone(), f.isSuccess());
            }
        }
        catch(Exception e) {
        }

        if (obj != null) {
            MDC.remove(obj.getLogKey());
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        String ipAddress = NettyUtils.getRemoteIpAddress(ctx.channel());
        TbRseCtlr obj = AppRepository.getInstance().getCtlrIpMap().get(ipAddress);
        if (obj != null) {
            MDC.put("id", obj.getLogKey());
        }
        log.error("DsrcAsn1ServerIdleStatePacketHandler.---exceptionCaught: {}, {}, Exception: {}", ipAddress, ctx.channel().toString(), cause);
        DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(obj, ctx.channel());
        super.exceptionCaught(ctx, cause);
        if (obj != null) {
            MDC.remove(obj.getLogKey());
        }
    }

}
