package org.framework.lazy.cloud.network.heartbeat.client.netty.udp.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.udp.filter.NettyUdpServerPermeateClientRealFilter;
import org.framework.lazy.cloud.network.heartbeat.client.netty.udp.filter.NettyUdpServerPermeateClientTransferFilter;
import org.framework.lazy.cloud.network.heartbeat.common.InternalNetworkPenetrationRealClient;
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.constant.UdpMessageType;
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.utils.ChannelAttributeKeyUtils;

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

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

    /**
     * 连接真实服务
     *
     * @param internalNetworkPenetrationRealClient 访客信息
     * @param nettyClientProperties                服务端地址信息
     */
    public static void buildRealServer(InternalNetworkPenetrationRealClient internalNetworkPenetrationRealClient,
                                       NettyClientProperties nettyClientProperties,
                                       List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {

        buildNewRealServer(internalNetworkPenetrationRealClient, nettyClientProperties, handleChannelTypeAdvancedList);

    }

    /**
     * @param internalNetworkPenetrationRealClient 访客信息
     * @param nettyClientProperties                服务端地址信息
     */
    private static void buildNewRealServer(InternalNetworkPenetrationRealClient internalNetworkPenetrationRealClient,
                                           NettyClientProperties nettyClientProperties,
                                           List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
        try {
            String clientId = internalNetworkPenetrationRealClient.getClientId();
            String clientTargetIp = internalNetworkPenetrationRealClient.getClientTargetIp();
            Integer clientTargetPort = internalNetworkPenetrationRealClient.getClientTargetPort();
            Integer visitorPort = internalNetworkPenetrationRealClient.getVisitorPort();
            String visitorId = internalNetworkPenetrationRealClient.getVisitorId();
            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.UDP_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 NettyUdpServerPermeateClientRealFilter())

            ;


            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("访客通过 客户端:【{}】,绑定本地服务,IP:{},端口:{} 新建通道成功", clientId, clientTargetIp, clientTargetPort);
                    // 客户端真实通道
//                    NettyRealIdContext.pushReal(realChannel, visitorId);
                    // 绑定访客ID到当前真实通道属性
                    ChannelAttributeKeyUtils.buildVisitorId(realChannel, visitorId);
                    ChannelAttributeKeyUtils.buildClientId(realChannel, clientId);
                    ChannelAttributeKeyUtils.buildVisitorPort(realChannel, visitorPort);
                    // 通知服务端访客连接成功


                    // 新建一个通道处理
                    newVisitorConnect2Server(internalNetworkPenetrationRealClient, nettyClientProperties, handleChannelTypeAdvancedList,realChannel);

                    // 是否等 服务端相应访客通道已经可以自动读写
//                    realChannel.config().setOption(ChannelOption.AUTO_READ, true);
                    // 模拟发送
                    String byteData = "GET /swagger-ui/index.html HTTP/1.1\n" +
                            "Host: 127.0.0.1:19080\n" +
                            "Connection: keep-alive\n" +
                            "Cache-Control: max-age=0\n" +
                            "sec-ch-ua: \"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"\n" +
                            "sec-ch-ua-mobile: ?0\n" +
                            "sec-ch-ua-platform: \"macOS\"\n" +
                            "Upgrade-Insecure-Requests: 1\n" +
                            "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\n" +
                            "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\n" +
                            "Sec-Fetch-Site: none\n" +
                            "Sec-Fetch-Mode: navigate\n" +
                            "Sec-Fetch-User: ?1\n" +
                            "Sec-Fetch-Dest: document\n" +
                            "Accept-Encoding: gzip, deflate, br\n" +
                            "Accept-Language: zh-CN,zh;q=0.9\n" +
                            "Cookie: XXL_JOB_LOGIN_IDENTITY=7b226964223a312c22757365726e616d65223a2261646d696e222c2270617373776f7264223a226531306164633339343962613539616262653536653035376632306638383365222c22726f6c65223a312c227065726d697373696f6e223a6e756c6c7d; Hm_lvt_173e771eef816c412396d2cb4fe2d632=1703040917\n";
//                    ChannelContext.ClientChannel clientChannel = ChannelContext.get(String.valueOf(visitorPort).getBytes(StandardCharsets.UTF_8));
//                    Channel channel = clientChannel.getChannel();
//                    channel.writeAndFlush(byteData.getBytes(StandardCharsets.UTF_8));
//                        future.channel().attr(Constant.VID).set(internalNetworkPenetrationRealClient);
//                        Constant.vrc.put(internalNetworkPenetrationRealClient, future.channel());
//                        ProxySocket.connectProxyServer(internalNetworkPenetrationRealClient);
                } else {
                    log.error("客户：【{}】,无法连接当前网络内的目标IP：【{}】,目标端口:【{}】", clientId, clientTargetIp, clientTargetPort);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 创建访客连接服务端
     *
     * @param internalNetworkPenetrationRealClient 内网穿透信息
     * @param nettyClientProperties                服务端配置信息
     * @param handleChannelTypeAdvancedList        处理器适配器
     * @throws InterruptedException 异常
     */
    protected static void newVisitorConnect2Server(InternalNetworkPenetrationRealClient internalNetworkPenetrationRealClient,
                                                   NettyClientProperties nettyClientProperties,
                                                   List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList,
                                                   Channel realChannel) 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.UDP_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 NettyUdpServerPermeateClientTransferFilter(new ChannelTypeAdapter(handleChannelTypeAdvancedList)))
        ;

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

        String clientId = nettyClientProperties.getClientId();


        String visitorId = internalNetworkPenetrationRealClient.getVisitorId();
        Integer visitorPort = internalNetworkPenetrationRealClient.getVisitorPort();
        String clientTargetIp = internalNetworkPenetrationRealClient.getClientTargetIp();
        Integer clientTargetPort = internalNetworkPenetrationRealClient.getClientTargetPort();
        String visitorClientId = internalNetworkPenetrationRealClient.getClientId();

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

        // 使用的客户端ID:{}
        log.info("Client ID used: {}" , visitorClientId);
        future.addListener((ChannelFutureListener) futureListener -> {
            Channel transferChannel = futureListener.channel();
            if (futureListener.isSuccess()) {

                NettyProxyMsg myMsg = new NettyProxyMsg();
                myMsg.setType(UdpMessageType.UDP_REPORT_SINGLE_CLIENT_REAL_CONNECT);
                myMsg.setClientId(visitorClientId);
                myMsg.setVisitorPort(visitorPort);
                myMsg.setClientTargetIp(clientTargetIp);
                myMsg.setClientTargetPort(clientTargetPort);

                myMsg.setVisitorId(visitorId);
                transferChannel.writeAndFlush(myMsg);
                // 绑定客户端真实通信通道
                NettyCommunicationIdContext.pushVisitor(transferChannel, visitorId);
                ChannelAttributeKeyUtils.buildVisitorId(transferChannel, visitorId);
                ChannelAttributeKeyUtils.buildClientId(transferChannel, visitorClientId);
                // 客户端真实通道自动读写打开

                realChannel.config().setOption(ChannelOption.AUTO_READ, true);

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




            } else {
                log.info("每隔2s重连....");
                // 离线
                eventLoopGroup.schedule(() -> {
                    try {
                        newVisitorConnect2Server(internalNetworkPenetrationRealClient, nettyClientProperties, handleChannelTypeAdvancedList,realChannel);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }, 2, TimeUnit.SECONDS);
            }
        });
    }
}