package com.dexorder.flink.ingestor;

import com.dexorder.flink.zmq.ZmqChannelManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Manages the ingestor work queue.
 * Sends DataRequest messages to ingestor workers via ZMQ PUB socket with exchange prefix filtering.
 */
public class IngestorWorkQueue {
    private static final Logger LOG = LoggerFactory.getLogger(IngestorWorkQueue.class);
    private static final byte PROTOCOL_VERSION = 0x01;
    private static final byte MSG_TYPE_DATA_REQUEST = 0x01;

    private final ZmqChannelManager zmqManager;
    private final BlockingQueue<DataRequestMessage> requestQueue;
    private volatile boolean running;
    private Thread workerThread;

    public IngestorWorkQueue(ZmqChannelManager zmqManager) {
        this.zmqManager = zmqManager;
        this.requestQueue = new LinkedBlockingQueue<>();
        this.running = false;
    }

    /**
     * Start the work queue processor thread.
     */
    public void start() {
        if (running) {
            LOG.warn("IngestorWorkQueue already running");
            return;
        }

        running = true;
        workerThread = new Thread(this::processQueue, "IngestorWorkQueue-Thread");
        workerThread.setDaemon(false);
        workerThread.start();
        LOG.info("IngestorWorkQueue started");
    }

    /**
     * Stop the work queue processor.
     */
    public void stop() {
        if (!running) {
            return;
        }

        running = false;
        if (workerThread != null) {
            workerThread.interrupt();
            try {
                workerThread.join(5000);
            } catch (InterruptedException e) {
                LOG.warn("Interrupted while waiting for worker thread to stop", e);
                Thread.currentThread().interrupt();
            }
        }
        LOG.info("IngestorWorkQueue stopped");
    }

    /**
     * Submit a data request to the queue.
     */
    public void submitRequest(DataRequestMessage request) {
        if (!running) {
            LOG.warn("Cannot submit request - work queue not running");
            return;
        }

        try {
            requestQueue.put(request);
            LOG.debug("Submitted data request: {}", request.getRequestId());
        } catch (InterruptedException e) {
            LOG.error("Interrupted while submitting request", e);
            Thread.currentThread().interrupt();
        }
    }

    /**
     * Process the request queue and send to ingestors.
     */
    private void processQueue() {
        LOG.info("IngestorWorkQueue processor started");

        while (running) {
            try {
                DataRequestMessage request = requestQueue.take();
                sendToIngestors(request);
            } catch (InterruptedException e) {
                if (running) {
                    LOG.error("Queue processing interrupted", e);
                }
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                LOG.error("Error processing request", e);
            }
        }

        LOG.info("IngestorWorkQueue processor stopped");
    }

    /**
     * Send a data request to ingestors via PUB socket with exchange prefix.
     * The topic prefix is extracted from the ticker (e.g., "BINANCE:BTC/USDT" -> "BINANCE:")
     */
    private void sendToIngestors(DataRequestMessage request) {
        try {
            byte[] protobufData = request.toProtobuf();

            // Extract exchange prefix from ticker (e.g., "BINANCE:BTC/USDT" -> "BINANCE:")
            String ticker = request.getTicker();
            String exchangePrefix = extractExchangePrefix(ticker);

            boolean sent = zmqManager.sendTopicMessage(
                ZmqChannelManager.Channel.INGESTOR_WORK_QUEUE,
                exchangePrefix,
                PROTOCOL_VERSION,
                MSG_TYPE_DATA_REQUEST,
                protobufData
            );

            if (sent) {
                LOG.info("Sent DataRequest to ingestors: requestId={}, type={}, ticker={}, prefix={}",
                    request.getRequestId(), request.getRequestType(), request.getTicker(), exchangePrefix);
            } else {
                LOG.error("Failed to send DataRequest: {}", request.getRequestId());
                // Re-queue the request
                requestQueue.offer(request);
            }
        } catch (Exception e) {
            LOG.error("Error sending request to ingestors: {}", request.getRequestId(), e);
            // Re-queue the request
            requestQueue.offer(request);
        }
    }

    /**
     * Extract exchange prefix from ticker string.
     * E.g., "BINANCE:BTC/USDT" -> "BINANCE:"
     */
    private String extractExchangePrefix(String ticker) {
        int colonIndex = ticker.indexOf(':');
        if (colonIndex > 0) {
            return ticker.substring(0, colonIndex + 1);
        }
        LOG.warn("Ticker '{}' does not contain exchange prefix, using empty prefix", ticker);
        return "";
    }

    public int getQueueSize() {
        return requestQueue.size();
    }

    public boolean isRunning() {
        return running;
    }
}
