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

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.InternalNetworkPenetrationRealClient;
import org.framework.lazy.cloud.network.heartbeat.common.NettyClientVisitorContext;
import org.framework.lazy.cloud.network.heartbeat.common.NettyVisitorPortContext;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelFlowAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.socket.PermeateVisitorSocket;
import org.framework.lazy.cloud.network.heartbeat.server.netty.tcp.filter.NettyTcpServerPermeateClientVisitorFilter;

/**
 * 内网穿透服务端访客通道
 *
 * @see NettyVisitorPortContext
 * @see NettyClientVisitorContext
 */
@Slf4j
public class NettyTcpServerPermeateClientVisitorSocket implements PermeateVisitorSocket {
    private final EventLoopGroup bossGroup = new NioEventLoopGroup();
    private final EventLoopGroup workerGroup = new NioEventLoopGroup();
    private final NettyTcpServerPermeateClientVisitorFilter nettyTcpServerPermeateClientVisitorFilter;
    @Getter
    private final String clientId;
    @Getter
    private final int visitorPort;

    public NettyTcpServerPermeateClientVisitorSocket(NettyTcpServerPermeateClientVisitorFilter nettyTcpServerPermeateClientVisitorFilter, String clientId, int visitorPort) {
        this.nettyTcpServerPermeateClientVisitorFilter = nettyTcpServerPermeateClientVisitorFilter;
        this.clientId = clientId;
        this.visitorPort = visitorPort;
    }

    /**
     * 启动服务代理
     *
     * @throws Exception
     */
    @Override
    public void start() {

        Channel visitor = NettyVisitorPortContext.getVisitorChannel(visitorPort);
        if (visitor == null) {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)


                    // 设置读缓冲区为2M
                    .childOption(ChannelOption.SO_RCVBUF, 2048 * 1024)
                    // 设置写缓冲区为1M
                    .childOption(ChannelOption.SO_SNDBUF, 1024 * 1024)


                    .childOption(ChannelOption.SO_KEEPALIVE, true)
//                    .childOption(ChannelOption.TCP_NODELAY, false)
                    .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
//                    .childOption(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认 AdaptiveRecvByteBufAllocator.DEFAULT
                    .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))


                    .childHandler(nettyTcpServerPermeateClientVisitorFilter);
            try {
                bootstrap.bind(visitorPort).sync().addListener((ChannelFutureListener) future -> {
                    if (future.isSuccess()) {
                        // 这里时异步处理
                        log.info("客户端:[{}]访客端口:[{}] 开启", clientId, visitorPort);
                        NettyVisitorPortContext.pushVisitorChannel(visitorPort, future.channel());

                    } else {
                        log.error("客户端:[{}]访客端口:[{}]绑定失败", clientId, visitorPort);
                    }
                });
                NettyClientVisitorContext.pushVisitorSocket(clientId, this);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        } else {
            log.warn("客户端:[{}]访客端口:[{}] 重复启动", clientId, visitorPort);
        }

    }

    @Override
    public void close() {
        if (!bossGroup.isShutdown()) {
            bossGroup.shutdownGracefully();
        }
        if (!workerGroup.isShutdown()) {
            workerGroup.shutdownGracefully();
        }
        Channel visitor = NettyVisitorPortContext.getVisitorChannel(visitorPort);
        if (visitor != null) {

            // close channel
            visitor.close();
            // remove visitor
            NettyVisitorPortContext.removeVisitorChannel(visitorPort);
            log.warn("关闭客户端 :【{}】 访客户端口:【{}】", clientId, visitorPort);
        } else {
            log.warn("关闭访客端口失败 未找到客户端通道 客户端 :【{}】 访客户端口:【{}】", clientId, visitorPort);
        }
    }


    public static final class NettyVisitorSocketBuilder {

        /**
         * 客户端ID
         */
        private String clientId;

        /**
         * 客户端目标地址
         */
        private String clientTargetIp;

        /**
         * 客户端目标端口
         */
        private Integer clientTargetPort;


        /**
         * 访问端口
         */
        private Integer visitorPort;
        /**
         * 访客ID
         */
        private String visitorId;

        /**
         * 流量适配器
         */
        private ChannelFlowAdapter channelFlowAdapter;

        public static NettyVisitorSocketBuilder builder() {
            return new NettyVisitorSocketBuilder();
        }

        /**
         * 填充客户端
         *
         * @param clientId 客户端
         * @return 返回当前对象
         */
        public NettyVisitorSocketBuilder builderClientId(String clientId) {
            this.clientId = clientId;
            return this;
        }

        /**
         * 绑定客户端目标IP
         *
         * @param clientTargetIp 客户端目标IP
         * @return 当前对象
         */
        public NettyVisitorSocketBuilder builderClientTargetIp(String clientTargetIp) {
            this.clientTargetIp = clientTargetIp;
            return this;
        }

        /**
         * 绑定客户端目标端口
         *
         * @param clientTargetPort 客户端目标端口
         * @return 当前对象
         */
        public NettyVisitorSocketBuilder builderClientTargetPort(Integer clientTargetPort) {
            this.clientTargetPort = clientTargetPort;
            return this;
        }

        /**
         * 绑定访客端口
         *
         * @param visitorPort 访客端口
         * @return 当前对象
         */
        public NettyVisitorSocketBuilder builderVisitorPort(Integer visitorPort) {
            this.visitorPort = visitorPort;
            return this;
        }

        /**
         * 绑定流量适配器
         *
         * @param channelFlowAdapter 流量适配器
         * @return 当前对象
         */
        public NettyVisitorSocketBuilder builderChannelFlowAdapter(ChannelFlowAdapter channelFlowAdapter) {
            this.channelFlowAdapter = channelFlowAdapter;
            return this;
        }

        /**
         * 绑定访客ID
         *
         * @param visitorId 访客ID
         * @return 当前对象
         */
        public NettyVisitorSocketBuilder builderVisitorId(String visitorId) {
            this.visitorId = visitorId;
            return this;
        }

        public NettyTcpServerPermeateClientVisitorSocket build() {
            if (clientId == null) {
                throw new IllegalArgumentException("clientId must not null");
            }
            if (clientTargetIp == null) {
                throw new IllegalArgumentException("clientTargetIp must not null");
            }
            if (clientTargetPort == null) {
                throw new IllegalArgumentException("clientTargetPort must not null");
            }
            if (visitorPort == null) {
                throw new IllegalArgumentException("visitorPort must not null");
            }
            InternalNetworkPenetrationRealClient internalNetworkPenetrationRealClient = InternalNetworkPenetrationRealClient
                    .builder()
                    .clientId(clientId)
                    .clientTargetIp(clientTargetIp)
                    .clientTargetPort(clientTargetPort)
                    .visitorPort(visitorPort)
                    .visitorId(visitorId).build();

            NettyTcpServerPermeateClientVisitorFilter nettyTcpServerPermeateClientVisitorFilter = new NettyTcpServerPermeateClientVisitorFilter(internalNetworkPenetrationRealClient, channelFlowAdapter);
            return new NettyTcpServerPermeateClientVisitorSocket(nettyTcpServerPermeateClientVisitorFilter, clientId, visitorPort);
        }


    }

}