package net.anotheria.moskito.integration.springboot;

import static net.anotheria.moskito.core.util.annotation.AnnotationUtils.findAnnotation;
import static net.anotheria.moskito.core.util.annotation.AnnotationUtils.findTypeAnnotation;

import java.lang.reflect.Method;
import net.anotheria.moskito.aop.annotation.Monitor;
import net.anotheria.moskito.aop.aspect.MonitoringBaseAspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
//@Component
public class MoskitoAspect extends MonitoringBaseAspect {

  /**
   * Common method profiling entry-point.
   *
   * @param pjp    {@link ProceedingJoinPoint}
   * @param method {@link Monitor}
   * @return call result
   * @throws Throwable in case of error during {@link ProceedingJoinPoint#proceed()}
   */
  @Around(value = "execution(* *(..)) && (@annotation(method))")
  public Object doProfilingMethod(ProceedingJoinPoint pjp, Monitor method) throws Throwable {
    return doProfiling(pjp, method.producerId(), method.subsystem(), method.category());
  }

//  @Around(value = "execution(* *(..)) && (@annotation(getMapping))")
//  public Object doProfilingMethodRest(ProceedingJoinPoint pjp, GetMapping getMapping) throws Throwable {
//    RequestMapping requestMapping = pjp.getTarget().getClass().getAnnotation(RequestMapping.class);
//    return doProfiling(pjp, String.join("/", requestMapping.value()) + "/" + String.join("/", getMapping.value()), "subsystem", "category");
//  }

  /**
   * Common class profiling entry-point.
   *
   * @param pjp     {@link ProceedingJoinPoint}
   * @param monitor {@link Monitor}
   * @return call result
   * @throws Throwable in case of error during {@link ProceedingJoinPoint#proceed()}
   */
  @Around(value = "execution(* *.*(..)) && @within(monitor) && !@annotation(net.anotheria.moskito.aop.annotation.DontMonitor)")
  public Object doProfilingClass(ProceedingJoinPoint pjp, Monitor monitor) throws Throwable {
    return doProfiling(pjp, monitor.producerId(), monitor.subsystem(), monitor.category());
  }

  /**
   * Special method profiling entry-point, which allow to fetch {@link Monitor} from one lvl up of
   * method annotation. Pattern will pre-select 2-nd lvl annotation on method scope.
   *
   * @param pjp {@link ProceedingJoinPoint}
   * @return call result
   * @throws Throwable in case of error during {@link ProceedingJoinPoint#proceed()}
   */
  @Around(value = "execution(@(@net.anotheria.moskito.aop.annotation.Monitor *) * *(..)) && !@annotation(net.anotheria.moskito.aop.annotation.DontMonitor)")
  public Object doProfilingMethod(final ProceedingJoinPoint pjp) throws Throwable {
    return doProfilingMethod(pjp, resolveAnnotation(pjp));
  }

  /**
   * Special class profiling entry-point, which allow to fetch {@link Monitor} from one lvl up of
   * class annotation. Pattern will pre-select 2-nd lvl annotation on class scope.
   *
   * @param pjp {@link ProceedingJoinPoint}
   * @return call result
   * @throws Throwable in case of error during {@link ProceedingJoinPoint#proceed()}
   */
  @Around(value = "execution(* (@(@net.anotheria.moskito.aop.annotation.Monitor *) *).*(..)) && !@annotation(net.anotheria.moskito.aop.annotation.DontMonitor)")
  public Object doProfilingClass(final ProceedingJoinPoint pjp) throws Throwable {
    return doProfilingClass(pjp, resolveAnnotation(pjp));
  }

  /**
   * Trying to resolve {@link Monitor} annotation first in method annotations scope, then in class
   * scope. Note - method will also check if {@link Monitor} is Placed to some other annotation as
   * meta! Search order : - 1 method; - 2 type.
   *
   * @param pjp {@link ProceedingJoinPoint}
   * @return {@link Monitor} or {@code null}
   */
  private Monitor resolveAnnotation(final ProceedingJoinPoint pjp) {
    final Signature signature = pjp.getSignature();
    final Class<?> type = signature.getDeclaringType();
    final Method method =
        (signature instanceof MethodSignature) ? MethodSignature.class.cast(signature).getMethod() :
            null;
    final Monitor methodMonitorAnn = method != null ? findAnnotation(method, Monitor.class) : null;
    if (methodMonitorAnn != null) {
      return methodMonitorAnn;
    }
    return findTypeAnnotation(type, Monitor.class);
  }
}
@@ -1183,438 +1183,8 @@
  %7D%0A%0A
-//  @Around(value = %22execution(* *(..)) && (@annotation(getMapping))%22)%0A//  public Object doProfilingMethodRest(ProceedingJoinPoint pjp, GetMapping getMapping) throws Throwable %7B%0A//    RequestMapping requestMapping = pjp.getTarget().getClass().getAnnotation(RequestMapping.class);%0A//    return doProfiling(pjp, String.join(%22/%22, requestMapping.value()) + %22/%22 + String.join(%22/%22, getMapping.value()), %22subsystem%22, %22category%22);%0A//  %7D%0A%0A
   /*

remove obsolete code