package com.xueyi.nlt.netty.client;

import com.xueyi.nlt.netty.client.codec.WsChannelInitializer;
import com.xueyi.nlt.netty.client.handler.MockClientHandler;
import com.xueyi.nlt.netty.client.handler.NettyClientHandler;
import com.xueyi.nlt.netty.client.handler.NettyWebsocketClientHandler;
import com.xueyi.nlt.netty.client.message.ReceiveMessage;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.IdleStateHandler;
import okhttp3.HttpUrl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

@Component
public class NettyClient {
    private static final Logger log = LoggerFactory.getLogger(NettyClient.class);
    private final int PORT = 8000;


    public static String hostUrl = "https://spark-api.xf-yun.com/v1.1/chat";
    public static String APPID = "3d9282da";//从开放平台控制台中获取
    public static String APIKEY = "7c217b3a313f4b66fcc14a8e97f85103";//从开放平台控制台中获取
    public static String APISecret = "ZTRiNDQwMTRlOTlmZDQwMDUwYTdjMDM0";//从开放平台控制台中获取
    public static NettyClient nettyClient;

    private String mHost;

    private int mPort;

    private NettyClientHandler mClientHandler;

    private ChannelFuture mChannelFuture;

    /**
     * websocket配置
     */
    private WebsocketConfig websocketConfig;

    private Channel channel;

    /**
     * 接口消息的接口
     */
    private ReceiveMessage receiveMessage;


    public void connect() {


        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            HttpUrl authUrl = getAuthorizationUrl(hostUrl, APIKEY, APISecret);
            String url = authUrl.toString().replace("https://","wss://").replace("http://","ws://");
            URI uri = new URI(url);
            final boolean ssl = "wss".equalsIgnoreCase(url);
            final SslContext sslCtx;
            if (ssl) {
                sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
            } else {
                sslCtx = null;
            }
            NettyClientHandler webSocketClientHandler = new NettyClientHandler(
                    WebSocketClientHandshakerFactory.newHandshaker(authUrl.uri()
                            , WebSocketVersion.V07
                            , null
                            , false
                            , new DefaultHttpHeaders()));

            final NettyWebsocketClientHandler handler =
                    new NettyWebsocketClientHandler(
                            WebSocketClientHandshakerFactory.newHandshaker(
                                    authUrl.uri(), WebSocketVersion.V13, null, true, new DefaultHttpHeaders()), receiveMessage);

            handler.setWebsocketConfig(websocketConfig);

            Bootstrap b = new Bootstrap();
//            mClientHandler = new NettyClientHandler(webSocketClientHandler);
//            b.group(workerGroup).channel(NioSocketChannel.class)
//                    // KeepAlive
//                    .option(ChannelOption.SO_KEEPALIVE, true)
//                    // Handler
//                    .handler(new WsChannelInitializer(webSocketClientHandler));


            b.group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            //wss 连接
                            if (sslCtx != null) {
                                pipeline.addLast(sslCtx.newHandler(ch.alloc(), authUrl.host(), authUrl.port()));
                            }
                            pipeline.addLast(
                                    new HttpServerCodec(),
                                    new HttpObjectAggregator(65536),
                                    WebSocketClientCompressionHandler.INSTANCE,
                                    //new LoggingHandler(LogLevel.INFO), // only for debug
//                                    new IdleStateHandler(websocketConfig.getReaderIdleTimeSeconds(),
//                                            websocketConfig.getWriterIdleTimeSeconds(),
//                                            websocketConfig.getAllIdleTimeSeconds()),
                                    handler);
                        }
                    }).option(ChannelOption.SO_KEEPALIVE, true)

            ;

//            mChannelFuture = b.connect(authUrl.host(),authUrl.port()).sync();
            mChannelFuture = b.connect("spark-api.xf-yun.com",443).sync();
            channel = mChannelFuture.channel();
            handler.handshakeFuture().sync();
            if (mChannelFuture.isSuccess()) {
                log.info("Client,连接服务端成功");
            }
            channel.writeAndFlush("介绍下自己");
//            mChannelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

    //鉴权url
    public static HttpUrl  getAuthorizationUrl(String hostUrl , String apikey ,String apisecret) throws Exception {
        //获取host
        URL url = new URL(hostUrl);
        //获取鉴权时间 date
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        System.out.println("format:\n" + format );
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        //获取signature_origin字段
        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").
//                append("date: ").append(date).append("\n").
                append("date: ").append("Wed, 02 Aug 2023 05:35:33 GMT").append("\n").
                append("GET ").append(url.getPath()).append(" HTTP/1.1");
        System.out.println("signature_origin:\n" + builder);
        //获得signatue
        Charset charset = Charset.forName("UTF-8");
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec sp = new SecretKeySpec(apisecret.getBytes(charset),"hmacsha256");
        mac.init(sp);
        byte[] basebefore = mac.doFinal(builder.toString().getBytes(charset));
        String signature = Base64.getEncoder().encodeToString(basebefore);
        //获得 authorization_origin
        String authorization_origin = String.format("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"",apikey,"hmac-sha256","host date request-line",signature);
        //获得authorization
        String authorization = Base64.getEncoder().encodeToString(authorization_origin.getBytes(charset));
        //获取httpurl
        HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder().//
                addQueryParameter("authorization", authorization).//
                addQueryParameter("date", date).//
                addQueryParameter("host", url.getHost()).//
                build();
        System.out.println("httpUrl:\n" + httpUrl);
        return httpUrl;
    }

    public static void main(String[] args) {
        nettyClient = new NettyClient();
        nettyClient.connect();

    }
}
