package org.framework.lazy.cloud.network.heartbeat.server.netty.tcp.handler;

import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.constant.TcpMessageType;
import org.framework.lazy.cloud.network.heartbeat.common.NettyCommunicationIdContext;
import org.framework.lazy.cloud.network.heartbeat.common.NettyProxyMsg;
import org.framework.lazy.cloud.network.heartbeat.common.NettyRealIdContext;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import org.wu.framework.core.utils.ObjectUtils;

/**
 * description 服务端数据处理器
 *
 * @author 吴佳伟
 * @date 2023/09/13 10:27
 */
@Slf4j
public class NettyTcpServerHandler extends SimpleChannelInboundHandler<NettyProxyMsg> {

    private final ChannelTypeAdapter channelTypeAdapter;
    /**
     * 空闲次数
     */
    private int idle_count = 1;
    /**
     * 传出数据延迟次数* 心跳时间作为关闭时间
     */
    private int transfer_count = 1;

    public NettyTcpServerHandler(ChannelTypeAdapter channelTypeAdapter) {
        this.channelTypeAdapter = channelTypeAdapter;
    }

    /**
     * Is called for each message of type {@link I}.
     *
     * @param ctx      the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *                 belongs to
     * @param nettyMsg the message to handle
     * @throws Exception is thrown if an error occurred
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, NettyProxyMsg nettyMsg) throws Exception {
        // 客户端读取到代理过来的数据了
        Channel channel = ctx.channel();
        byte type = nettyMsg.getType();
//        byte[] data = nettyMsg.getData();
//        log.info("客户端发送数据类型:{},发送数据:{}", type, new String(data));
        channelTypeAdapter.handler(channel, nettyMsg);

    }

    /**
     * 超时处理 * 如果5秒没有接受客户端的心跳，就触发; * 如果超过两次，则直接关闭;
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception {
        Channel channel = ctx.channel();
        if (obj instanceof IdleStateEvent event) {
            if (IdleState.READER_IDLE.equals(event.state())) {  //如果读通道处于空闲状态，说明没有接收到心跳命令
                String clientId = ChannelAttributeKeyUtils.getClientId(channel);
                String visitorId = ChannelAttributeKeyUtils.getVisitorId(channel);

                if (ObjectUtils.isEmpty(visitorId)) {
                    // 已经5秒没有接收到客户端：{}的信息了
                    log.warn("I haven't received any information from client: {} with channel:{} for 5 seconds", clientId, channel.id().toString());
                    // 关闭这个不活跃的channel client:{}
                    log.warn("close this inactive channel client:{} with no visitor", clientId);
                    // 给所有客户端发送 这个客户端离线了
                    NettyProxyMsg nettyMsg = new NettyProxyMsg();
                    nettyMsg.setClientId(clientId);
                    nettyMsg.setType(TcpMessageType.TCP_REPORT_CLIENT_DISCONNECTION);
                    channelTypeAdapter.handler(channel, nettyMsg);
                    channel.close();
                } else {
                    // 访客通道数据 5*100秒后关闭
                    if (transfer_count > 100) {
                        log.warn("close client:{} visitor: [{}]'s connection", clientId, visitorId);
                        NettyCommunicationIdContext.clear(visitorId);
                        NettyRealIdContext.clear(visitorId);
                        // 关闭通信通道
                        Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(channel);
                        Channel transferNextChannel = ChannelAttributeKeyUtils.getTransferNextChannel(channel);
                        channel.close();
                        nextChannel.close();
                        transferNextChannel.close();
                    }
                    transfer_count++;
                }

            }
        } else {
            super.userEventTriggered(ctx, obj);
        }
    }


    /**
     * Calls {@link ChannelHandlerContext#fireChannelInactive()} to forward
     * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
     * <p>
     * Sub-classes may override this method to change behavior.
     *
     * @param ctx
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        boolean open = channel.isOpen();
        // 下发当前客户端通道断开连接

        String clientId = ChannelAttributeKeyUtils.getClientId(channel);
        String visitorId = ChannelAttributeKeyUtils.getVisitorId(channel);

        if (!ObjectUtils.isEmpty(visitorId)) {
            // 客户端:{},断开访客的连接:{}
            log.warn("client: {} channel:{}, disconnect with visitorId:{}", clientId, channel.id().toString(), visitorId);
            // 访客通道 关闭访客通道
            NettyCommunicationIdContext.clear(visitorId);
            // 关闭通信通道
            Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(channel);
            Channel transferNextChannel = ChannelAttributeKeyUtils.getTransferNextChannel(channel);
            channel.close();
            nextChannel.close();
            transferNextChannel.close();
            super.channelInactive(ctx);
        } else if (!ObjectUtils.isEmpty(clientId)) {
            // 断开客户端的连接:{}
            log.warn("Disconnect client:{}", clientId);
            NettyProxyMsg nettyMsg = new NettyProxyMsg();
            nettyMsg.setType(TcpMessageType.TCP_REPORT_CLIENT_DISCONNECTION);
            nettyMsg.setClientId(clientId);
            channelTypeAdapter.handler(channel, nettyMsg);
            super.channelInactive(ctx);
        }

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//        super.exceptionCaught(ctx, cause);
        Channel channel = ctx.channel();
        //……
        if (channel.isActive()) ctx.close();
    }

    /**
     * Calls {@link ChannelHandlerContext#fireChannelWritabilityChanged()} to forward
     * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
     * <p>
     * Sub-classes may override this method to change behavior.
     *
     * @param ctx
     */
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {

        log.info("netty server handler channel writability changed: {}", ctx.channel());
        super.channelWritabilityChanged(ctx);
    }
}