package com.airlock.iam.common.infrastructure;

import com.airlock.iam.common.api.application.configuration.RestApplicationConfiguration;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Stream;

public class RestApplicationPackages {
   private final String[] resources;
   private final String[] filters;
   private final String[] exceptionMappers;
   private final String[] dependencyProviders;
   private final String[] pluginLogics;
   private final String[] domainEventSubscribers;
   private final String[] beanMappers;

   public RestApplicationPackages(Collection<RestApplicationConfiguration> annotations) {
      this.resources = collectPackages(annotations, RestApplicationConfiguration::resources);
      this.filters = collectPackages(annotations, RestApplicationConfiguration::filters);
      this.exceptionMappers = collectPackages(annotations, RestApplicationConfiguration::exceptionMappers);
      this.dependencyProviders = collectPackages(annotations, RestApplicationConfiguration::dependencyProviders);
      this.pluginLogics = collectPackages(annotations, RestApplicationConfiguration::pluginLogics);
      this.domainEventSubscribers = collectPackages(annotations, RestApplicationConfiguration::domainEventSubscribers);
      this.beanMappers = collectPackages(annotations, RestApplicationConfiguration::beanMappers);
   }

   public String[] jaxRs() {
      return merge(this.resources, this.filters, this.exceptionMappers);
   }

   public String[] all() {
      return this.isEmpty()
         ? new String[0]
         : merge(
            this.resources, this.filters, this.exceptionMappers, this.dependencyProviders, this.pluginLogics, this.domainEventSubscribers, this.beanMappers
         );
   }

   public boolean isEmpty() {
      return Stream.of(
            this.resources, this.filters, this.exceptionMappers, this.dependencyProviders, this.pluginLogics, this.domainEventSubscribers, this.beanMappers
         )
         .mapToInt(Array::getLength)
         .allMatch(l -> 0 == l);
   }

   private static String[] collectPackages(Collection<RestApplicationConfiguration> annotations, Function<RestApplicationConfiguration, String[]> mapper) {
      return annotations.stream().map(mapper).flatMap(Stream::of).distinct().toArray(String[]::new);
   }

   private static String[] merge(String[]... arrays) {
      return Stream.of(arrays).flatMap(Stream::of).distinct().toArray(String[]::new);
   }

   @SuppressFBWarnings(justification = "generated code")
   public String[] resources() {
      return this.resources;
   }

   @SuppressFBWarnings(justification = "generated code")
   public String[] filters() {
      return this.filters;
   }

   @SuppressFBWarnings(justification = "generated code")
   public String[] exceptionMappers() {
      return this.exceptionMappers;
   }

   @SuppressFBWarnings(justification = "generated code")
   public String[] dependencyProviders() {
      return this.dependencyProviders;
   }

   @SuppressFBWarnings(justification = "generated code")
   public String[] pluginLogics() {
      return this.pluginLogics;
   }

   @SuppressFBWarnings(justification = "generated code")
   public String[] domainEventSubscribers() {
      return this.domainEventSubscribers;
   }

   @SuppressFBWarnings(justification = "generated code")
   public String[] beanMappers() {
      return this.beanMappers;
   }
}
