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

package io.opentelemetry.javaagent.instrumentation.play.v2_4;

import static io.opentelemetry.javaagent.instrumentation.play.v2_4.PlayTracer.tracer;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import net.bytebuddy.asm.Advice;
import play.api.mvc.Action;
import play.api.mvc.Headers;
import play.api.mvc.Request;
import play.api.mvc.Result;
import scala.concurrent.Future;

public class PlayAdvice {
  @Advice.OnMethodEnter(suppress = Throwable.class)
  public static void onEnter(
      @Advice.Argument(0) Request<?> req,
      @Advice.Local("otelSpan") Span span,
      @Advice.Local("otelScope") Scope scope) {
    span = tracer().startSpan("play.request", SpanKind.INTERNAL);
    scope = span.makeCurrent();
  }

  @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
  public static void stopTraceOnResponse(
      @Advice.This Object thisAction,
      @Advice.Thrown Throwable throwable,
      @Advice.Argument(0) Request<?> req,
      @Advice.Return(readOnly = false) Future<Result> responseFuture,
      @Advice.Local("otelSpan") Span span,
      @Advice.Local("otelScope") Scope scope) {
    // Call onRequest on return after tags are populated.
    tracer().updateSpanName(span, req);

    scope.close();
    // span finished in RequestCompleteCallback
    if (throwable == null) {
      responseFuture.onComplete(
          new RequestCompleteCallback(span), ((Action<?>) thisAction).executionContext());
    } else {
      tracer().endExceptionally(span, throwable);
    }

    // set the span name on the upstream akka/netty span
    tracer().updateSpanName(BaseTracer.getCurrentServerSpan(), req);
  }

  // Unused method for muzzle
  public static void muzzleCheck(Headers headers) {
    // This distinguishes between 2.3 and 2.4, excluding the former
    headers.get("aKey");
    // system() method was removed in 2.6, so this line prevents from applying in play 2.6
    play.libs.Akka.system();
  }
}
