package com.airlock.iam.common.infrastructure.factory;

import com.airlock.iam.base.api.domain.model.event.DomainEventSubscriber;
import com.airlock.iam.base.domain.model.event.EventPublisher;
import com.airlock.iam.base.infrastructure.event.EventBus;
import com.airlock.iam.common.infrastructure.event.ConfiguredEventSubscribers;
import com.airlock.iam.common.infrastructure.event.EventSubscribers;
import com.airlock.iam.common.infrastructure.event.EventSubscribersFactory;
import com.airlock.iam.common.infrastructure.event.OutboxEventPublisher;
import com.airlock.iam.common.infrastructure.event.ScannedEventSubscribers;
import com.google.inject.Injector;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.servlet.RequestScoped;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

public final class EventBusModule extends PrivateModule {
   @Provides
   @RequestScoped
   EventBus eventBus(Collection<DomainEventSubscriber<?>> domainEventSubscribers, Optional<OutboxEventPublisher> outboxPublisher) {
      return new EventBus(domainEventSubscribers, outboxPublisher.map(EventPublisher.class::cast));
   }

   @Provides
   Collection<DomainEventSubscriber<?>> provideAllDomainEventSubscribers(
      ScannedEventSubscribers scannedEventSubscribers, ConfiguredEventSubscribers configuredEventSubscribers
   ) {
      return EventSubscribersFactory.eventSubscribersFactory(scannedEventSubscribers, configuredEventSubscribers).merge();
   }

   @Provides
   ScannedEventSubscribers provideScannedDomainEventSubscribers(EventSubscribers eventSubscribers, Injector injector) {
      List<DomainEventSubscriber<?>> subscribers = new ArrayList<>(eventSubscribers.getSubscriberTypes().size());

      for (Class<? extends DomainEventSubscriber> subscriberType : eventSubscribers.getSubscriberTypes()) {
         DomainEventSubscriber<?> subscriber = (DomainEventSubscriber<?>)injector.getInstance(subscriberType);
         subscribers.add(subscriber);
      }

      return ScannedEventSubscribers.scannedEventSubscribers(subscribers);
   }

   protected void configure() {
      this.bind(EventPublisher.class).to(EventBus.class).in(RequestScoped.class);
      this.expose(EventPublisher.class);
   }
}
