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

package io.opentelemetry.instrumentation.runtimetelemetryjfr.internal.cpu;

import static io.opentelemetry.instrumentation.runtimetelemetryjfr.internal.Constants.UNIT_UTILIZATION;

import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.runtimetelemetryjfr.JfrFeature;
import io.opentelemetry.instrumentation.runtimetelemetryjfr.internal.RecordedEventHandler;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import jdk.jfr.consumer.RecordedEvent;

/**
 * This class is internal and is hence not for public use. Its APIs are unstable and can change at
 * any time.
 */
public final class OverallCpuLoadHandler implements RecordedEventHandler {
  private static final String METRIC_NAME_PROCESS = "process.runtime.jvm.cpu.utilization";
  private static final String METRIC_NAME_MACHINE = "process.runtime.jvm.system.cpu.utilization";
  private static final String METRIC_DESCRIPTION_PROCESS = "Recent CPU utilization for the process";
  private static final String METRIC_DESCRIPTION_MACHINE =
      "Recent CPU utilization for the whole system";

  private static final String EVENT_NAME = "jdk.CPULoad";
  private static final String JVM_USER = "jvmUser";
  private static final String JVM_SYSTEM = "jvmSystem";
  private static final String MACHINE_TOTAL = "machineTotal";

  private final List<AutoCloseable> observables = new ArrayList<>();

  private volatile double process = 0;
  private volatile double machine = 0;

  public OverallCpuLoadHandler(Meter meter) {
    observables.add(
        meter
            .gaugeBuilder(METRIC_NAME_PROCESS)
            .setDescription(METRIC_DESCRIPTION_PROCESS)
            .setUnit(UNIT_UTILIZATION)
            .buildWithCallback(measurement -> measurement.record(process)));
    observables.add(
        meter
            .gaugeBuilder(METRIC_NAME_MACHINE)
            .setDescription(METRIC_DESCRIPTION_MACHINE)
            .setUnit(UNIT_UTILIZATION)
            .buildWithCallback(measurement -> measurement.record(machine)));
  }

  @Override
  public void accept(RecordedEvent ev) {
    if (ev.hasField(JVM_USER) && ev.hasField(JVM_SYSTEM)) {
      process = ev.getDouble(JVM_USER) + ev.getDouble(JVM_SYSTEM);
    }
    if (ev.hasField(MACHINE_TOTAL)) {
      machine = ev.getDouble(MACHINE_TOTAL);
    }
  }

  @Override
  public String getEventName() {
    return EVENT_NAME;
  }

  @Override
  public JfrFeature getFeature() {
    return JfrFeature.CPU_UTILIZATION_METRICS;
  }

  @Override
  public Optional<Duration> getPollingDuration() {
    return Optional.of(Duration.ofSeconds(1));
  }

  @Override
  public void close() {
    RecordedEventHandler.closeObservables(observables);
  }
}
