package org.framework.lazy.cloud.network.heartbeat.client.netty.tcp.socket;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.client.config.NettyClientProperties;
import org.framework.lazy.cloud.network.heartbeat.client.netty.tcp.filter.NettyTcpClientPermeateClientRealFilter;
import org.framework.lazy.cloud.network.heartbeat.client.netty.tcp.filter.NettyTcpClientPermeateClientTransferRealFilter;
import org.framework.lazy.cloud.network.heartbeat.common.*;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.lazy.cloud.network.heartbeat.common.constant.TcpMessageType;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 客户端连接真实服务
 */
@Slf4j
public class NettyTcpClientPermeateClientRealSocket {
    static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();


    public static void buildRealServer(String clientId,
                                       String clientTargetIp,
                                       Integer clientTargetPort,
                                       Integer visitorPort,
                                       String visitorId,
                                       NettyClientProperties nettyClientProperties,
                                       List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {

        try {

            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
//                     设置读缓冲区为2M
                    .option(ChannelOption.SO_RCVBUF, 2048 * 1024)
//                     设置写缓冲区为1M
                    .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
//                    .option(ChannelOption.TCP_NODELAY, false)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
//                    .option(ChannelOption.SO_BACKLOG, 128)//务端接受连接的队列长度 默认128
//                    .option(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT
                    .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
                    .handler(new NettyTcpClientPermeateClientRealFilter())

            ;


            bootstrap.connect(clientTargetIp, clientTargetPort).addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    // 客户端链接真实服务成功 设置自动读写false 等待访客连接成功后设置成true
                    Channel realChannel = future.channel();
                    realChannel.config().setOption(ChannelOption.AUTO_READ, false);

                    log.info("访客通过 客户端:【{}】,visitorId:{},绑定本地服务,IP:{},端口:{} 新建通道成功", clientId,visitorId, clientTargetIp, clientTargetPort);
                    // 客户端真实通道
                    NettyRealIdContext.pushReal(realChannel, visitorId);
                    // 绑定访客ID到当前真实通道属性
                    ChannelAttributeKeyUtils.buildVisitorId(realChannel, visitorId);
                    ChannelAttributeKeyUtils.buildClientId(realChannel, clientId);
                    ChannelAttributeKeyUtils.buildVisitorPort(realChannel, visitorPort);

                    // 连接服务端 然后绑定通道
                    // 新建一个通道处理
                    newVisitorConnect2Server(clientId,
                            clientTargetIp,
                            clientTargetPort,
                            visitorPort,
                            visitorId,realChannel, nettyClientProperties, handleChannelTypeAdvancedList);


                } else {
                    log.error("客户：【{}】,无法连接当前网络内的目标IP：【{}】,目标端口:【{}】", clientId, clientTargetIp, clientTargetPort);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建访客连接服务端
     *
     * @param nettyClientProperties                服务端配置信息
     * @param handleChannelTypeAdvancedList        处理器适配器
     * @throws InterruptedException 异常
     */
    protected static void newVisitorConnect2Server(String clientId,
                                                   String clientTargetIp,
                                                   Integer clientTargetPort,
                                                   Integer visitorPort,
                                                   String visitorId,
                                                   Channel realChannel,
                                                   NettyClientProperties nettyClientProperties,
                                                   List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) throws InterruptedException {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                // 设置读缓冲区为2M
                .option(ChannelOption.SO_RCVBUF, 2048 * 1024)
                // 设置写缓冲区为1M
                .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
//                .option(ChannelOption.TCP_NODELAY, false)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
//                .option(ChannelOption.SO_BACKLOG, 256)//务端接受连接的队列长度 默认128
//                .option(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT

                .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))

                .handler(new NettyTcpClientPermeateClientTransferRealFilter(new ChannelTypeAdapter(handleChannelTypeAdvancedList)))
        ;

        String inetHost = nettyClientProperties.getInetHost();
        int inetPort = nettyClientProperties.getInetPort();
        // local client id

//        String clientId = nettyClientProperties.getClientId();




        // 客户端新建访客通道 连接服务端IP:{},连接服务端端口:{}
        log.info("client creates a new visitor channel to connect to server IP: {}, connecting to server port: {} with visitorId:{} & clientId:{}", inetHost, inetPort,visitorId,clientId);
        ChannelFuture future = bootstrap.connect(inetHost, inetPort);

        future.addListener((ChannelFutureListener) futureListener -> {
            Channel transferChannel = futureListener.channel();
            if (futureListener.isSuccess()) {
                realChannel.config().setOption(ChannelOption.AUTO_READ, true);
                // 通知服务端访客连接成功
                NettyProxyMsg nettyProxyMsg = new NettyProxyMsg();
                nettyProxyMsg.setVisitorId(visitorId);
                nettyProxyMsg.setClientId(clientId);
                nettyProxyMsg.setClientTargetIp(clientTargetIp);
                nettyProxyMsg.setClientTargetPort(clientTargetPort);
                nettyProxyMsg.setVisitorPort(visitorPort);
                nettyProxyMsg.setType(TcpMessageType.TCP_REPORT_CLIENT_PERMEATE_CLIENT_TRANSFER_CHANNEL_INIT_SUCCESSFUL);
                transferChannel.writeAndFlush(nettyProxyMsg);

                ChannelAttributeKeyUtils.buildNextChannel(transferChannel, realChannel);
                ChannelAttributeKeyUtils.buildNextChannel(realChannel, transferChannel);

                // 绑定客户端真实通信通道
                ChannelAttributeKeyUtils.buildVisitorId(transferChannel, visitorId);
                ChannelAttributeKeyUtils.buildClientId(transferChannel, clientId);

                ChannelAttributeKeyUtils.buildVisitorPort(transferChannel, visitorPort);



            } else {
                log.info("无法连接到服务端....");
                eventLoopGroup.schedule(() -> {
                    try {
                        newVisitorConnect2Server(clientId,
                                clientTargetIp,
                                clientTargetPort,
                                visitorPort,
                                visitorId,realChannel, nettyClientProperties, handleChannelTypeAdvancedList);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }, 2, TimeUnit.SECONDS);
            }
        });
    }

}