/*
 * Copyright 2018 The Data Transfer Project Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dataportabilityproject.gateway;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import org.dataportabilityproject.spi.gateway.auth.AuthDataGenerator;
import org.dataportabilityproject.spi.gateway.auth.extension.AuthServiceExtension;
import org.dataportabilityproject.spi.gateway.auth.AuthServiceProviderRegistry;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class PortabilityAuthServiceProviderRegistry implements AuthServiceProviderRegistry {

  private final ImmutableMap<String, AuthServiceExtension> authServiceProviderMap;
  private final ImmutableSet<String> supportedImportTypes;
  private final ImmutableSet<String> supportedExportTypes;

  @Inject
  public PortabilityAuthServiceProviderRegistry(
      Map<String, AuthServiceExtension> serviceProviderMap) {
    ImmutableMap.Builder<String, AuthServiceExtension> serviceProviderBuilder =
        ImmutableMap.builder();
    ImmutableSet.Builder<String> supportedImportTypesBuilder = ImmutableSet.builder();
    ImmutableSet.Builder<String> supportedExportTypesBuilder = ImmutableSet.builder();

    serviceProviderMap.forEach(
        (service, provider) -> {
          List<String> importTypes = provider.getImportTypes();
          List<String> exportTypes = provider.getExportTypes();
          for (String type : importTypes) {
            Preconditions.checkArgument(
                exportTypes.contains(type),
                "TransferDataType [%s] is available for import but not export in [%s] AuthServiceExtension",
                type,
                service);
            supportedImportTypesBuilder.add(type);
          }
          supportedExportTypesBuilder.addAll(exportTypes);
          serviceProviderBuilder.put(service, provider);
        });

    authServiceProviderMap = serviceProviderBuilder.build();
    supportedImportTypes = supportedImportTypesBuilder.build();
    supportedExportTypes = supportedExportTypesBuilder.build();
  }

  @Override
  public AuthDataGenerator getAuthDataGenerator(
      String serviceId, String transferDataType, AuthMode mode) {
    AuthServiceExtension provider = authServiceProviderMap.get(serviceId);
    Preconditions.checkArgument(
        provider != null, "AuthServiceExtension not found for serviceId [%s]", serviceId);
    switch (mode) {
      case EXPORT:
        Preconditions.checkArgument(
            supportedExportTypes.contains(transferDataType),
            "AuthMode [%s] not valid for TransferDataType [%s]",
            mode,
            transferDataType);
        break;
      case IMPORT:
        Preconditions.checkArgument(
            supportedImportTypes.contains(transferDataType),
            "AuthMode [%s] not valid for TransferDataType [%s]",
            mode,
            transferDataType);
        break;
      default:
        throw new IllegalArgumentException("AuthMode [" + mode + "] not supported");
    }

    return provider.getAuthDataGenerator(transferDataType, mode);
  }

  /**
   * Returns the set of service ids that can transfered for the given {@code transferDataType}.
   *
   * @param transferDataType the transfer data type
   */
  @Override
  public Set<String> getServices(String transferDataType) {
    Preconditions.checkArgument(
        supportedExportTypes.contains(transferDataType),
        "TransferDataType [%s] is not valid for export",
        transferDataType);
    Preconditions.checkArgument(
        supportedImportTypes.contains(transferDataType),
        "TransferDataType [%s] is not valid for import",
        transferDataType);
    return authServiceProviderMap.keySet();
  }

  /** Returns the set of data types that support both import and export. */
  @Override
  public Set<String> getTransferDataTypes() {
    return Sets.intersection(supportedExportTypes, supportedImportTypes);
  }
}

@@ -990,18 +990,8 @@
 uth.
-extension.
 Auth
@@ -997,25 +997,32 @@
 hService
-Extension
+ProviderRegistry
 ;%0Aimport
@@ -1058,32 +1058,42 @@
 pi.gateway.auth.
+extension.
 AuthServiceProvi
@@ -1087,32 +1087,25 @@
 hService
-ProviderRegistry
+Extension
 ;%0A%0Aimpor
@@ -1165,16 +1165,52 @@
 til.Set;
+%0Aimport java.util.stream.Collectors;
 %0A%0Apublic
@@ -1296,17 +1296,16 @@
 istry %7B%0A
-%0A
   privat
@@ -4376,49 +4376,858 @@
 e);%0A
+%0A
     
-return authServiceProviderMap.keySet(
+Set%3CString%3E exportServices =%0A        authServiceProviderMap%0A            .values()%0A            .stream()%0A            .filter(%0A                sp -%3E%0A                    sp.getExportTypes()%0A                        .stream()%0A                        .anyMatch(e -%3E e.equalsIgnoreCase(transferDataType)))%0A            .map(AuthServiceExtension::getServiceId)%0A            .collect(Collectors.toSet());%0A%0A    Set%3CString%3E importServices =%0A        authServiceProviderMap%0A            .values()%0A            .stream()%0A            .filter(%0A                sp -%3E%0A                    sp.getImportTypes()%0A                        .stream()%0A                        .anyMatch(e -%3E e.equalsIgnoreCase(transferDataType)))%0A            .map(AuthServiceExtension::getServiceId)%0A            .collect(Collectors.toSet());%0A    return Sets.union(exportServices, importServices
 );%0A 

Return services corresponding to transferDataType in getServices(). (#288)