package cz.moneta.test.harness.endpoints.kafka;

import cz.moneta.test.harness.connectors.messaging.KafkaConnector;
import cz.moneta.test.harness.context.StoreAccessor;
import cz.moneta.test.harness.endpoints.Endpoint;
import cz.moneta.test.harness.support.auth.AuthSupport;
import cz.moneta.test.harness.support.messaging.kafka.ReceivedMessage;

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

import static cz.moneta.test.harness.constants.HarnessConfigConstants.VAULT_KAFKA_KEYS_CONFIG;

/**
 * Kafka endpoint that provides high-level API for Kafka messaging.
 * <p>
 * Reads configuration from StoreAccessor and credentials from HashiCorp Vault.
 * Implements singleton pattern per test class (managed by Harness).
 * </p>
 */
public class KafkaEndpoint implements Endpoint {

    private final KafkaConnector connector;
    private final StoreAccessor store;

    /**
     * Creates a new KafkaEndpoint.
     * <p>
     * Configuration is read from StoreAccessor:
     * - endpoints.kafka.bootstrap-servers
     * - endpoints.kafka.schema-registry-url
     * <p>
     * Credentials are retrieved from Vault:
     * - vault.kafka.secrets.path -> apiKey, apiSecret
     * </p>
     */
    public KafkaEndpoint(StoreAccessor store) {
        this.store = store;

        // Read configuration
        String bootstrapServers = store.getConfig("endpoints.kafka.bootstrap-servers");
        if (bootstrapServers == null) {
            throw new IllegalStateException("You need to configure " + bootstrapServers + " to work with Kafka");
        }

        String schemaRegistryUrl = store.getConfig("endpoints.kafka.schema-registry-url");
        if (schemaRegistryUrl == null) {
            throw new IllegalStateException("You need to configure " + schemaRegistryUrl + " url to work with Kafka");
        }

        // Retrieve credentials from Vault
        String apiKey = getVaultValue("apiKey");
        String apiSecret = getVaultValue("apiSecret");
        String schemaRegistryApiKey = getVaultValue("schemaRegistryApiKey");
        String schemaRegistryApiSecret = getVaultValue("schemaRegistryApiSecret");

        // Create connector
        this.connector = new KafkaConnector(
                bootstrapServers,
                apiKey,
                apiSecret,
                schemaRegistryUrl,
                schemaRegistryApiKey,
                schemaRegistryApiSecret
        );
    }

    /**
     * Sends a message to a Kafka topic.
     *
     * @param topic       Kafka topic name
     * @param key         Message key
     * @param jsonPayload Message body as JSON string
     * @param headers     Kafka headers (traceparent, requestID, activityID, etc.)
     */
    public void send(String topic, String key, String jsonPayload, Map<String, String> headers) {
        connector.send(topic, key, jsonPayload, headers);
    }

    /**
     * Receives a message from a Kafka topic matching the filter.
     *
     * @param topic   Kafka topic name
     * @param filter  Predicate to filter messages
     * @param timeout Maximum time to wait for a message
     * @return First matching ReceivedMessage
     */
    public List<ReceivedMessage> receive(String topic,
                                         java.util.function.Predicate<ReceivedMessage> filter,
                                         Duration timeout, boolean startFromBeginning) {
        return connector.receive(topic, filter, timeout, startFromBeginning);
    }

    /**
     * Receives a message with default timeout (30 seconds).
     */
    public List<ReceivedMessage> receive(String topic,
                                         java.util.function.Predicate<ReceivedMessage> filter, boolean startFromBeginning) {
        return receive(topic, filter, Duration.ofSeconds(30), startFromBeginning);
    }

    /**
     * Receives the first available message from a topic.
     *
     * @param topic   Kafka topic name
     * @param timeout Maximum time to wait
     * @return First message
     */
    public List<ReceivedMessage> receive(String topic, Duration timeout, boolean startFromBeginning) {
        return receive(topic, msg -> true, timeout, startFromBeginning);
    }

    /**
     * Closes the endpoint and releases resources.
     */
    @Override
    public void close() {
        if (connector != null) {
            connector.close();
        }
    }

    /**
     * Checks if Kafka is accessible.
     */
    @Override
    public boolean canAccess() {
        try {
            // Try to get topic metadata - if this succeeds, Kafka is accessible
            // Note: We don't actually make a network call here, just verify config
            String bootstrapServers = store.getConfig("endpoints.kafka.bootstrap-servers");
            return bootstrapServers != null && !bootstrapServers.isEmpty();
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Gets the underlying connector (for advanced use cases).
     */
    public KafkaConnector getConnector() {
        return connector;
    }

    /**
     * Gets the store accessor.
     */
    public StoreAccessor getStore() {
        return store;
    }

    /**
     * Retrieves a value from Vault.
     */
    private String getVaultValue(String key) {
        return AuthSupport.getClientSecret(key, store, VAULT_KAFKA_KEYS_CONFIG);
    }
}
