package com.eactive.testmaster.queue.service;

import com.eactive.testmaster.config.HazelcastJSBridge;
import com.eactive.testmaster.loadtest.service.Console;
import com.eactive.testmaster.queue.entity.QueueHandler;
import com.hazelcast.collection.IQueue;
import com.hazelcast.core.HazelcastInstance;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class QueueListenerService {

    private static final Logger logger = LoggerFactory.getLogger(QueueListenerService.class);

    private IQueue<Object> taskQueue;
    private final ExecutorService executorService;
    private final AtomicBoolean running;
    private final HazelcastInstance hazelcastInstance;
    private final HazelcastJSBridge hazelcastJSBridge;
    private QueueHandler queueHandler;
    private final ScriptEngine scriptEngine;  // Add ScriptEngine as a field
    private final Console console;

    public void setQueueHandler(QueueHandler queueHandler) {
        this.queueHandler = queueHandler;
    }

    @Autowired
    public QueueListenerService(HazelcastInstance hazelcastInstance, HazelcastJSBridge hazelcastJSBridge) {
        this.hazelcastInstance = hazelcastInstance;
        this.hazelcastJSBridge = hazelcastJSBridge;

        ScriptEngineManager manager = new ScriptEngineManager();
        this.scriptEngine = manager.getEngineByName("nashorn");
        this.hazelcastJSBridge.enhanceEngine(this.scriptEngine);
        this.executorService = Executors.newSingleThreadExecutor();
        this.running = new AtomicBoolean(false);
        this.console = new Console();
    }

    @PostConstruct
    public void startListening() {
        if (queueHandler != null) {
            // Set running flag BEFORE starting the processing
            this.running.set(true);
            taskQueue = hazelcastInstance.getQueue(queueHandler.getQueueName());
            executorService.submit(this::processQueueItems);
            logger.info("Queue listener started for queue: {}", queueHandler.getQueueName());
        }
    }

    private void processQueueItems() {
        while (running.get()) {
            try {
                // Use take() instead of poll() to block until an item is available
                Object item = taskQueue.take();
                processItem(item);
            } catch (InterruptedException e) {
                // Handle interruption (likely from shutdown)
                logger.info("Queue listener interrupted, ending processing");
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                logger.error("Error processing queue item", e);
            }
        }
        logger.info("Queue listener stopped processing items");
    }


    private void processItem(Object message) {

        String script = queueHandler.getHandlerScript();
        if (script != null && !script.isEmpty()) {
            Bindings scriptBindings = new SimpleBindings();
            scriptBindings.put("console", this.console);
            scriptBindings.put("message", message);

            try {
                scriptEngine.eval(script, scriptBindings);
            } catch (ScriptException e) {
                logger.error("Error executing request script", e);
                e.printStackTrace();
            }
        }
    }

    public boolean isRunning() {
        return running.get();
    }


    @PreDestroy
    public void stopListening() {
        logger.info("Stopping queue listener...");
        running.set(false);
        // Interrupt the blocking take() call
        executorService.shutdownNow();
        try {
            if (!executorService.awaitTermination(15, TimeUnit.SECONDS)) {
                logger.warn("Queue listener did not terminate in time");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        logger.info("Queue listener stopped");
    }

}
