package eu.solidcraft.infrastructure.metrics.logging;

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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StopWatch;

import static java.lang.String.format;

@Aspect
class LoggingAspect {

    @Pointcut("target(org.springframework.data.repository.Repository)")
    void allRepositories() {}

    @Pointcut("@within(eu.solidcraft.infrastructure.metrics.logging.Log)")
    void logAnnotation() {}

    @Around("allRepositories() || logAnnotation()")
    Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Object result = joinPoint.proceed();
        stopWatch.stop();
        log(joinPoint, stopWatch, result);
        return result;
    }

    private void log(ProceedingJoinPoint joinPoint, StopWatch stopWatch, Object result) {
        String operationName = getOperationName(joinPoint);
        String timerString = createTimerString(stopWatch);
        String resultString = createResultString(result);
        if (!timerString.isEmpty() || !resultString.isEmpty()) {
            Logger logger = getLogger(joinPoint);
            logger.info("{}{} {}", operationName, timerString, resultString);
        }
    }

    private Logger getLogger(ProceedingJoinPoint joinPoint) {
        return LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
    }

    private String createResultString(Object result) {
        return format("operation result: %s", result);
    }

    private String createTimerString(StopWatch stopWatch) {
        long millis = stopWatch.getTotalTimeMillis();
        return format(" [%d ms]", millis);
    }

    private String getOperationName(ProceedingJoinPoint joinPoint) {
        String classWithPackageName = joinPoint.getSignature().getDeclaringTypeName();
        String className = classWithPackageName.substring(classWithPackageName.lastIndexOf('.') + 1);
        return className + "." + joinPoint.getSignature().getName();
    }
}