/*
 * LanguageTool, a natural language style checker
 * Copyright (c) 2023.  Stefan Viol (https://stevio.de)
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 *  USA
 */

package org.languagetool.tools;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;

public enum TelemetryProvider {

  INSTANCE;

  private final OpenTelemetry openTelemetry;
  private final Tracer tracer;

  TelemetryProvider() {
    openTelemetry = GlobalOpenTelemetry.get();
    tracer = openTelemetry.getTracer("LanguageTool-Server");
  }

  /**
   * @param spanName     unique name of the span
   * @param attributes   custom attributes
   * @param tracedFunction function called within the span
   * @return the return value of wrappedValue function
   * @since 6.2   * @param spanName
   */
  public <T> T createSpan(String spanName, Attributes attributes, TracedFunction<T> tracedFunction) throws Exception {
    Span span = createSpan(spanName, attributes);
    span.setStatus(StatusCode.OK);
    try (Scope scope = span.makeCurrent()) {
      return tracedFunction.call(span);
    } catch (Exception ex) {
      span.recordException(ex);
      span.setStatus(StatusCode.ERROR);
      throw ex;
    } finally{
      span.end();
    }
  }


  /**
   * @param spanName     unique name of the span
   * @param attributes   custom attributes
   * @param wrappedValue function called within the span
   * @return the return value of wrappedValue function
   * @since 6.2   * @param spanName
   */
  public <T> T createSpan(String spanName, Attributes attributes, WrappedValue<T> wrappedValue) throws Exception {
    Span span = createSpan(spanName, attributes);
    span.setStatus(StatusCode.OK);
    try (Scope scope = span.makeCurrent()) {
      return wrappedValue.call();
    } catch (Exception ex) {
      span.recordException(ex);
      span.setStatus(StatusCode.ERROR);
      throw ex;
    } finally{
      span.end();
    }
  }

  /**
   * @param spanName    unique name of the span
   * @param attributes  custom attributes
   * @param wrappedVoid function called within the span
   * @since 6.2
   */
  public void createSpan(String spanName, Attributes attributes, WrappedVoid wrappedVoid) throws Exception {
    Span span = createSpan(spanName, attributes);
    span.setStatus(StatusCode.OK);
    try (Scope scope = span.makeCurrent()) {
      wrappedVoid.call();
    } catch (Exception ex) {
      span.recordException(ex);
      span.setStatus(StatusCode.ERROR);
      throw ex;
    } finally {
      span.end();
    }
  }

  /**
   * To use the openTelemetry instance directly without the helper functions
   * @since 6.2
   * @return a opentelemetry instance
   */
  public OpenTelemetry getOpenTelemetry() {
    return openTelemetry;
  }

  /**
   * @param spanName
   * @param attributes
   * @return a Span
   * @since 6.2
   */
  public Span createSpan(String spanName, Attributes attributes) {
    return tracer.spanBuilder(spanName)
            .setAllAttributes(attributes)
            .startSpan();
  }
}
