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

package io.opentelemetry.javaagent.tooling.muzzle;

import static java.lang.System.lineSeparator;

import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef;
import io.opentelemetry.javaagent.tooling.muzzle.references.FieldRef;
import io.opentelemetry.javaagent.tooling.muzzle.references.MethodRef;
import io.opentelemetry.javaagent.tooling.muzzle.references.Source;
import java.util.ServiceLoader;

@SuppressWarnings("SystemOut")
public final class ReferencesPrinter {

  private static final String INDENT = "  ";

  /**
   * For all {@link InstrumentationModule}s found in the current thread's context class loader this
   * method prints references returned by the {@link
   * InstrumentationModuleMuzzle#getMuzzleReferences()} method to the standard output.
   */
  public static void printMuzzleReferences() {
    for (InstrumentationModule instrumentationModule :
        ServiceLoader.load(InstrumentationModule.class, ReferencesPrinter.class.getClassLoader())) {
      try {
        System.out.println(instrumentationModule.getClass().getName());
        for (ClassRef ref :
            InstrumentationModuleMuzzle.getMuzzleReferences(instrumentationModule).values()) {
          System.out.print(prettyPrint(ref));
        }
      } catch (RuntimeException e) {
        String message =
            "Unexpected exception printing references for "
                + instrumentationModule.getClass().getName();
        System.out.println(message);
        throw new IllegalStateException(message, e);
      }
    }
  }

  private static String prettyPrint(ClassRef ref) {
    StringBuilder builder = new StringBuilder(INDENT).append(ref).append(lineSeparator());
    if (!ref.getSources().isEmpty()) {
      builder.append(INDENT).append(INDENT).append("Sources:").append(lineSeparator());
      for (Source source : ref.getSources()) {
        builder
            .append(INDENT)
            .append(INDENT)
            .append(INDENT)
            .append("at: ")
            .append(source)
            .append(lineSeparator());
      }
    }
    for (FieldRef field : ref.getFields()) {
      builder.append(INDENT).append(INDENT).append(field).append(lineSeparator());
    }
    for (MethodRef method : ref.getMethods()) {
      builder.append(INDENT).append(INDENT).append(method).append(lineSeparator());
    }
    return builder.toString();
  }

  private ReferencesPrinter() {}
}
