package com.eactive.testmaster.socket;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NettySocketHandler extends ChannelInboundHandlerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(NettySocketHandler.class);
    private final ScriptEngine engine;

    public NettySocketHandler(String scriptContent) {
        ScriptEngineManager manager = new ScriptEngineManager();
        this.engine = manager.getEngineByName("nashorn");
        try {
            engine.eval(scriptContent);
        } catch (ScriptException e) {
            logger.error("Failed to initialize script", e);
            throw new RuntimeException("Script initialization failed", e);
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        logger.info("Client connected: {}", ctx.channel().remoteAddress());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        logger.info("Client disconnected: {}", ctx.channel().remoteAddress());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        try {
            logger.debug("Received new message from client: {}", ctx.channel().remoteAddress());
            logger.debug("Total readable bytes: {}", in.readableBytes());

            // Read the entire message as it comes
            byte[] messageBytes = new byte[in.readableBytes()];
            in.readBytes(messageBytes);
            String message = new String(messageBytes);

            logger.debug("Message processing details:");
            logger.debug("- Raw message: {}", message);
            logger.debug("- Hex dump: {}", bytesToHex(messageBytes));

            // Process the message, removing the length prefix
            String messageContent = message.substring(4);
            logger.debug("- Message content (without length): {}", messageContent);

            String result;
            try {
                logger.debug("Executing script processor for message");
                Invocable invocable = (Invocable) engine;
                Object jsResult = invocable.invokeFunction("processMessage", messageContent);
                result = String.valueOf(jsResult);
                logger.debug("Script processing successful. Result: {}", result);
            } catch (ScriptException | NoSuchMethodException e) {
                logger.error("Script execution error", e);
                result = "Error: " + e.getMessage();
                logger.error("Script execution failed. Using error message as result: {}", result);
            }

            // Format response with length prefix
            byte[] responseBytes = result.getBytes();
            String lengthPrefix = String.format("%04d", responseBytes.length + 4);
            logger.debug("Preparing response:");
            logger.debug("- Response content: {}", result);
            logger.debug("- Response length: {}", responseBytes.length);
            logger.debug("- Length prefix: {}", lengthPrefix);

            ByteBuf response = ctx.alloc().buffer(4 + responseBytes.length);
            response.writeBytes(lengthPrefix.getBytes());
            response.writeBytes(responseBytes);

            logger.debug("Sending response to client: {}", ctx.channel().remoteAddress());
            logger.debug("- Total response size: {} bytes", 4 + responseBytes.length);
            ctx.writeAndFlush(response);

        } catch (Exception e) {
            logger.error("Error processing message from client: {}", ctx.channel().remoteAddress(), e);
            logger.error("Error occurred at reader index: {}", in.readerIndex());
        } finally {
            logger.debug("Releasing ByteBuf resources");
            ReferenceCountUtil.release(msg);
        }
    }

    /**
     * Utility method to convert byte array to hexadecimal string for logging
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.error("Channel error:", cause);
        ctx.close();
    }
}
