package com.airlock.iam.servicecontainer.app.infrastructure.event;

import com.airlock.iam.base.api.domain.model.event.EventId;
import com.airlock.iam.base.domain.model.event.EventMetadataImpl;
import com.airlock.iam.base.domain.model.event.EventPublisher;
import com.airlock.iam.base.domain.service.event.SilentEventPublisher;
import com.airlock.iam.base.infrastructure.event.factory.EventCreationDataFactory;
import com.airlock.iam.base.infrastructure.event.factory.EventCreationDataFactoryImpl;
import com.airlock.iam.base.misc.time.RealTimeProvider;
import com.airlock.iam.common.api.domain.model.store.user.UserStore;
import com.airlock.iam.common.application.configuration.event.OutboxRepositoryConfig;
import com.airlock.iam.common.domain.model.event.UserEventFactory;
import com.airlock.iam.common.domain.service.event.EventOrderingTiebreaker;
import com.airlock.iam.common.domain.service.event.EventOutboxRepository;
import com.airlock.iam.common.infrastructure.event.CustomEventDataProvider;
import com.airlock.iam.common.infrastructure.event.DomainEventMapper;
import com.airlock.iam.common.infrastructure.event.EmptyEventDataProvider;
import com.airlock.iam.common.infrastructure.event.OutboxEventPublisher;
import com.airlock.iam.common.infrastructure.event.OutboxEventSerializer;
import com.airlock.iam.common.infrastructure.event.UserEventFactoryImpl;
import com.airlock.iam.common.infrastructure.event.UserStoreEventDataProvider;
import com.airlock.iam.common.infrastructure.repository.EventOutboxRepositoryImpl;
import com.airlock.iam.servicecontainer.app.domain.model.event.ServiceContainerTaskEventSource;
import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import org.mapstruct.factory.Mappers;

public final class EventFactories {
   private static UserEventFactory userEventFactory;

   public static UserEventFactory userFactory() {
      return userEventFactory;
   }

   @VisibleForTesting
   public static void setFactories(UserEventFactory userEventFactory) {
      EventFactories.userEventFactory = userEventFactory;
   }

   public static void buildEventFactories(Optional<OutboxRepositoryConfig> outboxRepositoryConfig, Optional<UserStore> userStore) {
      EventPublisher eventPublisher = (EventPublisher)(outboxRepositoryConfig.isPresent()
         ? createOutboxEventPublisher(outboxRepositoryConfig.get(), userStore)
         : SilentEventPublisher.get());
      EventCreationDataFactory eventCreationDataFactory = eventCreationDataFactory();
      UserEventFactory userEventFactory = new UserEventFactoryImpl(eventCreationDataFactory::newEventCreationData, () -> eventPublisher);
      setFactories(userEventFactory);
   }

   private static EventCreationDataFactory eventCreationDataFactory() {
      return EventCreationDataFactoryImpl.builder()
         .eventIdSupplier(EventId::newEventId)
         .source(new ServiceContainerTaskEventSource())
         .metadata(EventMetadataImpl.eventMetadata())
         .timeProvider(new RealTimeProvider())
         .build();
   }

   private static OutboxEventPublisher createOutboxEventPublisher(OutboxRepositoryConfig outboxRepositoryConfig, Optional<UserStore> userStore) {
      EventOutboxRepository outboxRepository = new EventOutboxRepositoryImpl(outboxRepositoryConfig.jooqConfig());
      DomainEventMapper mapper = (DomainEventMapper)Mappers.getMapper(ServiceContainerDomainEventMapper.class);
      CustomEventDataProvider eventDataProvider = userStore.<CustomEventDataProvider>map(store -> new UserStoreEventDataProvider(() -> store))
         .orElseGet(EmptyEventDataProvider::new);
      OutboxEventSerializer outboxEventSerializer = new OutboxEventSerializer(() -> mapper, () -> eventDataProvider);
      EventOrderingTiebreaker tiebreaker = new EventOrderingTiebreaker();
      return new OutboxEventPublisher(() -> outboxRepository, () -> outboxEventSerializer, () -> tiebreaker, var0x -> false);
   }

   private EventFactories() {
   }
}
