package de.accso.flexinale.common.infrastructure.eventbus;

import de.accso.flexinale.common.api.eventbus.EventSubscriber;
import de.accso.flexinale.common.api.eventbus.EventSubscriptionAtStart;
import de.accso.flexinale.common.shared_kernel.FlexinaleIllegalStateException;

import java.util.HashMap;
import java.util.Map;

import static de.accso.flexinale.common.shared_kernel.Identifiable.Id.uuidString;
import static org.apache.kafka.clients.CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG;
import static org.apache.kafka.clients.consumer.ConsumerConfig.*;
import static org.apache.kafka.clients.producer.ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG;
import static org.apache.kafka.clients.producer.ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG;

//TODO externalize to kafka properties file (but than we have it fixed for all topics - in the Java code we could use different configs)
//Note that in the application.properties there is something configured already (like the (De)Serializer classes)
public final class KafkaConfiguration {

    public static final int kafkaTimeoutInMs = 30_000;

    public static final String KEY_BOOTSTRAP_SERVERS   = BOOTSTRAP_SERVERS_CONFIG;
    public static final String defaultBootstrapServers = "localhost:29092";

    public static final String KEY_SERIALIZER        = KEY_SERIALIZER_CLASS_CONFIG;
    public static final String keySerializer         = "org.apache.kafka.common.serialization.StringSerializer";
    public static final String VALUE_SERIALIZER      = VALUE_SERIALIZER_CLASS_CONFIG;
    public static final String valueSerializer       = "org.apache.kafka.common.serialization.StringSerializer";

    public static final String KEY_DESERIALIZER      = KEY_DESERIALIZER_CLASS_CONFIG;
    public static final String keyDeserializer       = "org.apache.kafka.common.serialization.StringDeserializer";
    public static final String VALUE_DESERIALIZER    = VALUE_DESERIALIZER_CLASS_CONFIG;
    public static final String valueDeserializer     = "org.apache.kafka.common.serialization.StringDeserializer";

    public static final String CONSUMER_GROUP_ID_CONFIG = GROUP_ID_CONFIG;

    public static Map<String, Object> producerConfig() {
        return Map.of(
            KEY_BOOTSTRAP_SERVERS, getBootstrapServers(),
            KEY_SERIALIZER,        getClassFor(keySerializer),
            VALUE_SERIALIZER,      getClassFor(valueSerializer)
        );
    }

    @SuppressWarnings("SameParameterValue")
    public static Map<String, Object> consumerConfig(final Class<?> eventType, final EventSubscriber<?> subscriber,
                                                     final EventSubscriptionAtStart eventSubscriptionAtStart) {
        Map<String, Object> config = new HashMap<>();

        config.put(KEY_BOOTSTRAP_SERVERS, getBootstrapServers());
        config.put(KEY_DESERIALIZER,      getClassFor(keyDeserializer));
        config.put(VALUE_DESERIALIZER,    getClassFor(valueDeserializer));

        switch (eventSubscriptionAtStart) {
            case START_READING_FROM_BEGINNING -> {
                // make new consumer group and read from earliest offset (works only for new consumer groups)
                config.put(CONSUMER_GROUP_ID_CONFIG, subscriber.getGroupName() + "<" + eventType + ">" + uuidString());
                config.put(AUTO_OFFSET_RESET_CONFIG, "earliest");
            }
            case START_READING_FROM_NOW -> {
                // make new consumer group and read from latest offset (works only for new consumer groups)
                config.put(CONSUMER_GROUP_ID_CONFIG, subscriber.getGroupName() + "<" + eventType + ">" + uuidString());
                config.put(AUTO_OFFSET_RESET_CONFIG, "latest");
            }
            case START_READING_FROM_LAST_TIME -> {
                config.put(CONSUMER_GROUP_ID_CONFIG, subscriber.getGroupName() + "<" + eventType + ">");
            }
        }

        return config;
    }

    public static String getBootstrapServers() {
        String systemPropertyBootstrapServers = System.getProperty(BOOTSTRAP_SERVERS_CONFIG);
        return (systemPropertyBootstrapServers == null) ? defaultBootstrapServers : systemPropertyBootstrapServers;
    }

    public static Map<String, Object> adminConfig() {
        return Map.of(
            KEY_BOOTSTRAP_SERVERS, getBootstrapServers()
        );
    }

    private static Class<?> getClassFor(final String classAsString) {
        try {
            return Class.forName(classAsString);
        }
        catch (ClassNotFoundException ex) {
            throw new FlexinaleIllegalStateException(ex);
        }
    }
}
