package com.eactive.testmaster.socket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;

public class NettyServer {
    private final int port;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private Channel serverChannel;
    private volatile boolean isInitialized = false;
    private final String script;

    public NettyServer(int port, String script) {
        this.port = port;
        this.script = script;
    }

    public synchronized void start() throws InterruptedException {
        if (isInitialized) {
            return;
        }

        try {
            // Use custom thread factories to avoid naming conflicts
            bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("netty-boss"));
            workerGroup = new NioEventLoopGroup(
                Runtime.getRuntime().availableProcessors() * 2,
                new DefaultThreadFactory("netty-worker")
            );

            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel ch) {
                        ch.pipeline().addLast(new NettySocketHandler(script));
                    }
                });

            // Bind and wait for the server socket
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            if (!channelFuture.isSuccess()) {
                throw new RuntimeException("Failed to bind to port " + port);
            }

            serverChannel = channelFuture.channel();
            isInitialized = true;

            System.out.println("Netty server started on port " + port);
        } catch (Exception e) {
            // Clean up resources if initialization fails
            stop();
            throw new RuntimeException("Failed to start Netty server", e);
        }
    }

    public synchronized void stop() throws InterruptedException {
        try {
            if (serverChannel != null) {
                serverChannel.close().sync();
                serverChannel = null;
            }
        } finally {
            // Always attempt to shut down the event loop groups
            try {
                if (workerGroup != null) {
                    workerGroup.shutdownGracefully().sync();
                    workerGroup = null;
                }
            } finally {
                if (bossGroup != null) {
                    bossGroup.shutdownGracefully().sync();
                    bossGroup = null;
                }
            }
            isInitialized = false;
            System.out.println("Netty server stopped on port " + port);
        }
    }
}
