package cz.moneta.test.dsl.imq;

import cz.moneta.test.dsl.Harness;
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionEndpoint;
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionQueue;
import cz.moneta.test.harness.messaging.ReceivedMessage;
import cz.moneta.test.harness.messaging.exception.MessagingTimeoutException;
import cz.moneta.test.harness.support.messaging.ImqRequest;
import cz.moneta.test.harness.messaging.MqMessageFormat;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

/**
 * IBM MQ First Vision DSL.
 * <p>
 * Usage example:
 * <pre>{@code
 * harness.withImqFirstVision()
 *     .toQueue(ImqFirstVisionQueue.PAYMENT_NOTIFICATIONS)
 *     .withPayload("{\"paymentId\": \"PAY-123\"}")
 *     .send();
 *
 * harness.withImqFirstVision()
 *     .fromQueue(ImqFirstVisionQueue.PAYMENT_NOTIFICATIONS)
 *     .receiveWhere(msg -> msg.extract("paymentId").equals("PAY-123"))
 *     .withTimeout(10, TimeUnit.SECONDS)
 *     .andAssertFieldValue("result", "OK");
 * }</pre>
 * </p>
 */
public class ImqFirstVision {

    private final Harness harness;

    public ImqFirstVision(Harness harness) {
        this.harness = harness;
    }

    /**
     * Start building a message to send to a queue.
     */
    public ImqRequest.QueuePhase toQueue(ImqFirstVisionQueue queue) {
        ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
        return ImqRequest.toQueue(endpoint, queue);
    }

    /**
     * Start building a message to receive from a queue.
     */
    public ImqRequest.ReceivePhase fromQueue(ImqFirstVisionQueue queue) {
        ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
        return ImqRequest.fromQueue(endpoint, queue);
    }

    /**
     * Send a JSON message to a queue.
     */
    public void sendToQueue(ImqFirstVisionQueue queue, String payload) {
        sendToQueue(queue, payload, MqMessageFormat.JSON, null);
    }

    /**
     * Send a message to a queue with specified format.
     */
    public void sendToQueue(ImqFirstVisionQueue queue, String payload, MqMessageFormat format) {
        sendToQueue(queue, payload, format, null);
    }

    /**
     * Send a message to a queue with specified format and properties.
     */
    public void sendToQueue(ImqFirstVisionQueue queue, String payload, MqMessageFormat format,
                            Map<String, String> properties) {
        ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
        endpoint.send(queue, payload, format, properties);
    }

    /**
     * Send a JSON message to a queue by physical name.
     */
    public void sendToQueue(String queueName, String payload) {
        sendToQueue(queueName, payload, MqMessageFormat.JSON, null);
    }

    /**
     * Send a message to a queue by physical name with specified format.
     */
    public void sendToQueue(String queueName, String payload, MqMessageFormat format) {
        sendToQueue(queueName, payload, format, null);
    }

    /**
     * Send a message to a queue by physical name with specified format and properties.
     */
    public void sendToQueue(String queueName, String payload, MqMessageFormat format,
                            Map<String, String> properties) {
        ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
        endpoint.send(queueName, payload, format, properties);
    }

    /**
     * Receive a message from a queue with timeout.
     */
    public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, Duration timeout) {
        return receiveFromQueue(queue, null, MqMessageFormat.JSON, timeout);
    }

    /**
     * Receive a message from a queue with selector and timeout.
     */
    public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, String selector, Duration timeout) {
        return receiveFromQueue(queue, selector, MqMessageFormat.JSON, timeout);
    }

    /**
     * Receive a message from a queue with format and timeout.
     */
    public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, MqMessageFormat format, Duration timeout) {
        return receiveFromQueue(queue, null, format, timeout);
    }

    /**
     * Receive a message from a queue with selector, format and timeout.
     */
    public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, String selector,
                                            MqMessageFormat format, Duration timeout) {
        ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
        return endpoint.receive(queue, selector, format, timeout);
    }

    /**
     * Receive a message matching predicate from a queue.
     */
    public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, Predicate<ReceivedMessage> filter,
                                            Duration timeout) {
        long startTime = System.currentTimeMillis();
        long timeoutMs = timeout.toMillis();

        while (System.currentTimeMillis() - startTime < timeoutMs) {
            try {
                ReceivedMessage message = receiveFromQueue(queue, MqMessageFormat.JSON, Duration.ofSeconds(1));
                if (filter.test(message)) {
                    return message;
                }
            } catch (MessagingTimeoutException e) {
                // Continue polling
            }
        }

        throw new MessagingTimeoutException(
                "No message matching filter found on queue '" + queue.getConfigKey() +
                        "' within " + timeout.toMillis() + "ms");
    }

    /**
     * Browse messages from a queue (non-destructive).
     */
    public List<ReceivedMessage> browseQueue(ImqFirstVisionQueue queue, int maxMessages) {
        return browseQueue(queue, null, MqMessageFormat.JSON, maxMessages);
    }

    /**
     * Browse messages from a queue with selector (non-destructive).
     */
    public List<ReceivedMessage> browseQueue(ImqFirstVisionQueue queue, String selector, int maxMessages) {
        return browseQueue(queue, selector, MqMessageFormat.JSON, maxMessages);
    }

    /**
     * Browse messages from a queue with format and max count (non-destructive).
     */
    public List<ReceivedMessage> browseQueue(ImqFirstVisionQueue queue, MqMessageFormat format, int maxMessages) {
        return browseQueue(queue, null, format, maxMessages);
    }

    /**
     * Browse messages from a queue with selector, format and max count (non-destructive).
     */
    public List<ReceivedMessage> browseQueue(ImqFirstVisionQueue queue, String selector,
                                              MqMessageFormat format, int maxMessages) {
        ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
        return endpoint.browse(queue, selector, format, maxMessages);
    }
}
