package cn.reghao.autodop.common.docker.unixdomain;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollDomainSocketChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.UnixChannel;
import io.netty.handler.codec.http.*;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author reghao
 * @date 2020-01-14 17:14:08
 */
@Slf4j
public class UnixSocketClient {
    // TODO 在配置中指定
    private final String DEFAULT_UNIX_ENDPOINT = "/run/docker.sock";
    private EventLoopGroup group;
    private Bootstrap bootstrap;
    private DomainSocketAddress unixSock;
    private Channel channel;
    private HttpClientHandler clientHandler;

    public UnixSocketClient() {
        // TODO Unix Domain Socket 只能使用 EpollEventLoopGroup？
        group = new EpollEventLoopGroup(2);
        bootstrap = new Bootstrap();
        unixSock = new DomainSocketAddress(DEFAULT_UNIX_ENDPOINT);
        clientHandler = new HttpClientHandler();
        init();
    }

    private void init() {
        bootstrap.group(group)
                .channel(EpollDomainSocketChannel.class)
                .handler(new ChannelInitializer<UnixChannel>() {
                    @Override
                    public void initChannel(UnixChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new HttpClientCodec());
                        pipeline.addLast(new HttpObjectAggregator(65536*8192));
                        pipeline.addLast(clientHandler);
                    }
                });

        ChannelFuture channelFuture = bootstrap.connect(unixSock);
        channelFuture.addListener(future -> {
           if (future.isSuccess()) {
               log.info("已连接到 Unix Socket");
               channel = channelFuture.channel();
           } else {
               log.error("Unix Socket 连接失败, 检查 " + DEFAULT_UNIX_ENDPOINT + " 是否存在");
           }
        });

        // 注册 channel 关闭事件
        channelFuture.channel().closeFuture().addListener(listener -> close());
    }

    public void close() {
        if (channel != null) {
            channel.close();
        }

        if (group != null) {
            group.shutdownGracefully();
        }
    }

    public FullHttpResponse syncSend(FullHttpRequest request) throws InterruptedException, IOException {
        /*
        错误用法，忽略了异步的本质
        if (channel == null) {
            throw new IOException("Unix Socket 连接失败, 检查 " + DEFAULT_UNIX_ENDPOINT + " 是否存在");
        }*/

        ChannelPromise promise = clientHandler.sendRequest(request);
        if (promise == null) {
            return null;
        }

        promise.await(30, TimeUnit.SECONDS);
        return clientHandler.response();
    }
}
