package org.dotwebstack.framework.ext.orchestrate.config;

import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.TypeDefinitionRegistry;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Consumer;
import lombok.SneakyThrows;
import org.dotwebstack.framework.core.scalars.CoreScalars;
import org.dotwebstack.framework.ext.orchestrate.SubschemaModifier;
import org.dotwebstack.framework.ext.orchestrate.config.OrchestrateConfigurationProperties.SubschemaProperties;
import org.dotwebstack.graphql.orchestrate.schema.RemoteExecutor;
import org.dotwebstack.graphql.orchestrate.schema.SchemaIntrospector;
import org.dotwebstack.graphql.orchestrate.schema.Subschema;
import org.dotwebstack.graphql.orchestrate.transform.TransformUtils;
import org.dotwebstack.graphql.orchestrate.wrap.SchemaWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
class OrchestrateConfiguration {

  private final OrchestrateConfigurationProperties configurationProperties;

  private final WebClient.Builder webClientBuilder;

  private final Collection<SubschemaModifier> subschemaModifiers;

  public OrchestrateConfiguration(OrchestrateConfigurationProperties configurationProperties,
      WebClient.Builder webClientBuilder, Collection<SubschemaModifier> subschemaModifiers) {
    this.configurationProperties = configurationProperties;
    this.webClientBuilder = webClientBuilder;
    this.subschemaModifiers = subschemaModifiers;
  }

  @Bean
  @Primary
  public GraphQLSchema graphQlSchema() {
    var rootSubschemaProperties = configurationProperties.getSubschemas()
        .get(configurationProperties.getRoot());

    return SchemaWrapper.wrap(createSubschema(configurationProperties.getRoot(), rootSubschemaProperties));
  }

  private Subschema createSubschema(String key, SubschemaProperties subschemaProperties) {
    var remoteExecutor = createRemoteExecutor(subschemaProperties);
    var schema = loadSchema(remoteExecutor);

    var subschema = Subschema.newSubschema()
        .schema(schema)
        .executor(remoteExecutor)
        .build();

    return subschemaModifiers.stream()
        .reduce(subschema, (acc, modifier) -> modifier.modify(key, subschema), TransformUtils::noopCombiner);
  }

  private RemoteExecutor createRemoteExecutor(SubschemaProperties subschemaProperties) {
    var endpoint = subschemaProperties.getEndpoint();

    Consumer<HttpHeaders> headerBuilder = headers -> Optional.ofNullable(subschemaProperties.getBearerAuth())
        .ifPresent(bearerAuth -> headers.add("Authorization", "Bearer ".concat(bearerAuth)));

    var webClient = webClientBuilder.defaultHeaders(headerBuilder)
        .build();

    return RemoteExecutor.newExecutor()
        .endpoint(endpoint)
        .webClient(webClient)
        .build();
  }

  @SneakyThrows
  private GraphQLSchema loadSchema(RemoteExecutor remoteExecutor) {
    TypeDefinitionRegistry typeDefinitionRegistry = SchemaIntrospector.introspectSchema(remoteExecutor)
        .get();

    return new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, RuntimeWiring.newRuntimeWiring()
        .scalar(CoreScalars.DATE)
        .scalar(CoreScalars.DATETIME)
        .scalar(CoreScalars.OBJECT)
        .build());
  }
}

@@ -1120,24 +1120,100 @@
 ttpHeaders;%0A
+import org.springframework.web.reactive.function.client.ExchangeStrategies;%0A
 import org.s
@@ -3057,16 +3057,207 @@
 uilder)%0A
+        .exchangeStrategies(ExchangeStrategies.builder()%0A            .codecs(configurer -%3E configurer.defaultCodecs()%0A                .maxInMemorySize(5 * 1024 * 1024))%0A            .build())%0A
         

enable large schema download (#501)