package com.tsi.comm.server.tcp.initializer;

import com.tsi.comm.server.config.TsiCvimServerConfig;
import com.tsi.comm.server.process.packet.TsiChannelSession;
import com.tsi.comm.server.protocol.TsiCvibProtocolSpec;
import com.tsi.comm.server.repository.TsiAlarmManager;
import com.tsi.comm.server.repository.TsiNodeManager;
import com.tsi.comm.server.repository.TsiSessionManager;
import com.tsi.comm.server.tcp.codec.CvimServerByteBufMessageDecoder;
import com.tsi.comm.server.tcp.codec.CvimServerEncoder;
import com.tsi.comm.server.tcp.handler.CvimServerInboundMessageHandler;
import com.tsi.comm.server.tcp.handler.CvimServerPacketProcessHandler;
import com.tsi.comm.server.tcp.handler.MdcLoggingHandler;
import com.tsi.comm.server.vo.TsiAlarmConfigVo;
import com.tsi.comm.server.vo.TsiNodeVo;
import com.tsi.comm.server.xnet.NettyUtils;
import com.tsi.common.spring.SpringUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.timeout.IdleStateHandler;

public class CvimServerInitializer extends ChannelInitializer<Channel> {

    private final TsiCvimServerConfig config;
    private final CvimServerByteBufMessageDecoder cvimServerByteBufMessageDecoder;
    private final CvimServerPacketProcessHandler cvimServerPacketProcessHandler;
    private final CvimServerInboundMessageHandler cvimServerInboundMessageHandler;
    private final CvimServerEncoder cvimServerEncoder;
    private final TsiNodeManager nodeManager;
    private int readerIdleTimeSeconds;

    public CvimServerInitializer(TsiCvimServerConfig config) {
        this.config = config;
        this.cvimServerByteBufMessageDecoder = SpringUtils.getBean(CvimServerByteBufMessageDecoder.class);
        this.cvimServerPacketProcessHandler = SpringUtils.getBean(CvimServerPacketProcessHandler.class);
        this.cvimServerInboundMessageHandler = SpringUtils.getBean(CvimServerInboundMessageHandler.class);
        this.cvimServerEncoder = SpringUtils.getBean(CvimServerEncoder.class);
        this.nodeManager = SpringUtils.getBean(TsiNodeManager.class);
        TsiAlarmManager alarmManager = SpringUtils.getBean(TsiAlarmManager.class);
        this.readerIdleTimeSeconds = config.getReaderIdleTimeSeconds();
        TsiAlarmConfigVo vo = alarmManager.get(TsiAlarmConfigVo.COMM_02);
        if (vo != null) {
            this.readerIdleTimeSeconds = vo.getValue();
        }
        if (this.readerIdleTimeSeconds < 5) {
            this.readerIdleTimeSeconds = 5;
        }
    }

    @Override
    protected void initChannel(Channel channel) throws Exception {
        final String remoteIpAddress = NettyUtils.getRemoteIpAddress(channel);
        if (this.config.isInWhitelistIps(remoteIpAddress)) {
            // FOR L4, whitelist ips
//            channel.disconnect();
            channel.close();
            return;
        }

        TsiNodeVo nodeVo = this.nodeManager.getIpAddr(remoteIpAddress);

        // 교차로 제어기는 통신 연결시 IP로 체크하지 않고 처음 데이터를 수신했을 경우에 노드 ID를 확인 할 수 있다.
        // 공단에서 교차로 제어기의 IP 주소를 관리하지 않는다.
        IdleStateHandler idleStateHandler = new IdleStateHandler(this.readerIdleTimeSeconds, 0, 0);
        ChannelPipeline pipeline = channel.pipeline();

        if (nodeVo != null && nodeVo.isTcpDump()) {
            //pipeline.addLast(new LoggingHandler(LogLevel.INFO));
            pipeline.addLast(TsiSessionManager.MDC_LOGGING_HANDLER_NAME, new MdcLoggingHandler(LogLevel.INFO));
        }

        // 유휴 상태 감지(read timeout 처리)
        pipeline.addLast("idleStateHandler", idleStateHandler);

        // 패킷 프레이밍: ByteBuf -> ByteBuf (하나의 완전한 패킷, 패킷길이 체크)
        pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(
                TsiCvibProtocolSpec.MAX_FRAME_LENGTH,
                TsiCvibProtocolSpec.LENGTH_FIELD_OFFSET,
                TsiCvibProtocolSpec.LENGTH_FIELD_LENGTH,
                TsiCvibProtocolSpec.LENGTH_ADJUSTMENT,
                TsiCvibProtocolSpec.INITIAL_BYTES_TO_STRIP
        ));

        // 패킷 디코딩: ByteBuf -> TsiCpuPacket
        pipeline.addLast("packetDecoder", this.cvimServerByteBufMessageDecoder);

        // TsiCpuPacket -> 각종 큐잉 및 상태 변경
        pipeline.addLast("packetProcessor", this.cvimServerPacketProcessHandler);

        // 채널 상태 이벤트 처리 (Active, Inactive 등)
        pipeline.addLast("packetInboundHandler", this.cvimServerInboundMessageHandler);

        // Outbound 인코더(무선신호시스템에서는 전송하는 프로토콜 없음, 사용하지는 않음)
        pipeline.addLast("frameEncoder", this.cvimServerEncoder);

        TsiChannelSession.initChannel(nodeVo, remoteIpAddress);
    }
}
