package de.accso.flexinale.besucherportal.api_in.event;

import de.accso.flexinale.backoffice.api_contract.event.KinoCreatedEvent;
import de.accso.flexinale.backoffice.api_contract.event.KinoDeletedEvent;
import de.accso.flexinale.backoffice.api_contract.event.KinoUpdatedEvent;
import de.accso.flexinale.backoffice.api_contract.event.model.KinoSaalTO;
import de.accso.flexinale.backoffice.api_contract.event.model.KinoTO;
import de.accso.flexinale.besucherportal.application.services.FKKsVTInMemoryCache;
import de.accso.flexinale.common.api.eventbus.EventBus;
import de.accso.flexinale.common.api.eventbus.EventBusFactory;
import de.accso.flexinale.common.api.eventbus.EventNotification;
import de.accso.flexinale.common.api.eventbus.EventSubscriber;
import de.accso.flexinale.common.api.eventbus.EventSubscriber.EventSubscriber3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;

import static de.accso.flexinale.common.api.eventbus.EventSubscriptionAtStart.START_READING_FROM_BEGINNING;

@SuppressWarnings("unused")
public class KinoSubscriber implements EventSubscriber3<KinoCreatedEvent, KinoUpdatedEvent, KinoDeletedEvent> {
    private static final Logger LOGGER = LoggerFactory.getLogger(KinoSubscriber.class);

    private final FKKsVTInMemoryCache cache;

    private final EventNotification notification;

    @SuppressWarnings({"RedundantCast", "unchecked", "rawtypes"})
    public KinoSubscriber(final EventBusFactory eventBusFactory,
                          final FKKsVTInMemoryCache cache,
                          final EventNotification notification) {
        EventBus<KinoCreatedEvent> createBus = eventBusFactory.createOrGetEventBusFor(KinoCreatedEvent.class);
        EventBus<KinoUpdatedEvent> updateBus = eventBusFactory.createOrGetEventBusFor(KinoUpdatedEvent.class);
        EventBus<KinoDeletedEvent> deleteBus = eventBusFactory.createOrGetEventBusFor(KinoDeletedEvent.class);

        createBus.subscribe(KinoCreatedEvent.class, (EventSubscriber) this, START_READING_FROM_BEGINNING);
        updateBus.subscribe(KinoUpdatedEvent.class, (EventSubscriber) this, START_READING_FROM_BEGINNING);
        deleteBus.subscribe(KinoDeletedEvent.class, (EventSubscriber) this, START_READING_FROM_BEGINNING);

        this.cache = cache;
        this.notification = notification;
    }

    @Override
    public String getName() {
        return KinoSubscriber.class.getName();
    }

    @Override
    public String getGroupName() {
        return "flexinale-distributed-besucherportal"; //TODO make configurable, use "spring.kafka.consumer.group-id" from application.properties
    }

    @Override
    public void receive(final KinoCreatedEvent event) {
        LOGGER.debug("received new event " + event);

        KinoTO fixedKino = fixKinoSaalTO2KinoTORelation(event.kino);
        cache.addOrUpdate(fixedKino);
        this.notification.notify(event);
    }

    @Override
    public void receive2(final KinoUpdatedEvent event) {
        LOGGER.debug("received new event " + event);

        KinoTO fixedKino = fixKinoSaalTO2KinoTORelation(event.kino);
        cache.addOrUpdate(fixedKino);
        this.notification.notify(event);
    }

    @Override
    public void receive3(final KinoDeletedEvent event) {
        LOGGER.debug("received new event " + event);

        cache.delete(event.kino);
        this.notification.notify(event);
    }

    // need to fix in all KinoSaalTO the relation to the correct KinoTO (as it is null after Json deserialization)
    private KinoTO fixKinoSaalTO2KinoTORelation(final KinoTO kinoTOFromEvent) {
        if (kinoTOFromEvent == null) return null;

        final KinoTO newKinoTO = new KinoTO(kinoTOFromEvent.id(), kinoTOFromEvent.version(), kinoTOFromEvent.name(),
                kinoTOFromEvent.adresse(), kinoTOFromEvent.emailAdresse(), new HashSet<>());
        kinoTOFromEvent.kinoSaele()
                .stream()
                .map(kinoSaalTOFromEvent ->
                        new KinoSaalTO(kinoSaalTOFromEvent.id(), kinoSaalTOFromEvent.version(),
                                kinoSaalTOFromEvent.name(), kinoSaalTOFromEvent.anzahlPlaetze(), newKinoTO))
                .forEach(kinoSaalTOFromEvent ->
                        newKinoTO.kinoSaele().add(kinoSaalTOFromEvent));

        return newKinoTO;
    }

    public FKKsVTInMemoryCache getCache() {
        return cache;
    }
}
