package com.atlassian.bitbucket.mesh.spring;

import com.atlassian.util.profiling.Ticker;
import com.atlassian.util.profiling.Timers;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;

@Aspect
@Order(1)
public class ProfilingAspect {

    @Pointcut("@within(org.springframework.stereotype.Repository)")
    public void daoType() {
    }

    @Pointcut("target(io.grpc.BindableService)")
    public void grpcService() {
    }

    @Pointcut("@annotation(com.atlassian.bitbucket.mesh.annotation.NotProfiled)")
    public void notProfiledMethod() {
    }

    @Pointcut("@within(com.atlassian.bitbucket.mesh.annotation.NotProfiled)")
    public void notProfiledType() {
    }

    @Pointcut("@annotation(com.atlassian.bitbucket.mesh.annotation.Profiled)")
    public void profiledMethod() {
    }

    @Pointcut("@within(com.atlassian.bitbucket.mesh.annotation.Profiled)")
    public void profiledType() {
    }

    @Around("!notProfiledMethod() && " + // Never profile a method annotated with @NotProfiled
            "(profiledMethod() || " + // @Profiled at the method level should override @NotProfiled at the type level
            "((daoType() || grpcService() || profiledType()) && !notProfiledType()))")
    public Object profileMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        try (Ticker ignored = Timers.start(joinPoint.getSignature().toString())) {
            return joinPoint.proceed();
        }
    }
}
