/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.exporter.otlp.internal;

import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF;
import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.configureOtlpHeaders;
import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.readFileBytes;
import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.validateEndpoint;

import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import java.net.URL;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * This class is internal and is hence not for public use. Its APIs are unstable and can change at
 * any time.
 */
public final class OtlpDeclarativeConfigUtil {

  /** Determine the configured OTLP protocol for the {@code dataType}. */
  public static String getStructuredConfigOtlpProtocol(DeclarativeConfigProperties config) {
    // NOTE: The default OTLP protocol is different for declarative config than for env var / system
    // property based config. This is intentional. OpenTelemetry changed the default protocol
    // recommendation from grpc to http/protobuf, but the autoconfigure's env var / system property
    // based config did not update to reflect this before stabilizing, and changing is a breaking
    // change requiring a major version bump. Declarative config is not yet stable and therefore can
    // switch to the current default recommendation, which aligns also aligns with the behavior of
    // the OpenTelemetry Java Agent 2.x+.
    return config.getString("protocol", PROTOCOL_HTTP_PROTOBUF);
  }

  /** Invoke the setters with the OTLP configuration for the {@code dataType}. */
  @SuppressWarnings("TooManyParameters")
  public static void configureOtlpExporterBuilder(
      String dataType,
      DeclarativeConfigProperties config,
      Consumer<String> setEndpoint,
      BiConsumer<String, String> addHeader,
      Consumer<String> setCompression,
      Consumer<Duration> setTimeout,
      Consumer<byte[]> setTrustedCertificates,
      BiConsumer<byte[], byte[]> setClientTls,
      Consumer<RetryPolicy> setRetryPolicy,
      Consumer<MemoryMode> setMemoryMode) {
    String protocol = getStructuredConfigOtlpProtocol(config);
    boolean isHttpProtobuf = protocol.equals(PROTOCOL_HTTP_PROTOBUF);
    URL endpoint = validateEndpoint(config.getString("endpoint"), isHttpProtobuf);
    if (endpoint != null) {
      setEndpoint.accept(endpoint.toString());
    }

    String headerList = config.getString("headers_list");
    if (headerList != null) {
      ConfigProperties headersListConfig =
          DefaultConfigProperties.createFromMap(
              Collections.singletonMap("otel.exporter.otlp.headers", headerList));
      configureOtlpHeaders(headersListConfig, dataType, addHeader);
    }

    List<DeclarativeConfigProperties> headers = config.getStructuredList("headers");
    if (headers != null) {
      headers.forEach(
          header -> {
            String name = header.getString("name");
            String value = header.getString("value");
            if (name != null && value != null) {
              addHeader.accept(name, value);
            }
          });
    }

    String compression = config.getString("compression");
    if (compression != null) {
      setCompression.accept(compression);
    }

    Integer timeoutMs = config.getInt("timeout");
    if (timeoutMs != null) {
      setTimeout.accept(Duration.ofMillis(timeoutMs));
    }

    String certificatePath = config.getString("certificate");
    String clientKeyPath = config.getString("client_key");
    String clientKeyChainPath = config.getString("client_certificate");

    if (clientKeyPath != null && clientKeyChainPath == null) {
      throw new ConfigurationException(
          "client_key provided without client_certificate - both client_key and client_certificate must be set");
    } else if (clientKeyPath == null && clientKeyChainPath != null) {
      throw new ConfigurationException(
          "client_certificate provided without client_key - both client_key and client_certificate must be set");
    }
    byte[] certificateBytes = readFileBytes(certificatePath);
    if (certificateBytes != null) {
      setTrustedCertificates.accept(certificateBytes);
    }
    byte[] clientKeyBytes = readFileBytes(clientKeyPath);
    byte[] clientKeyChainBytes = readFileBytes(clientKeyChainPath);
    if (clientKeyBytes != null && clientKeyChainBytes != null) {
      setClientTls.accept(clientKeyBytes, clientKeyChainBytes);
    }

    IncubatingExporterBuilderUtil.configureExporterMemoryMode(config, setMemoryMode);
  }

  private OtlpDeclarativeConfigUtil() {}
}
