package io.delphiplatform.api.util.logging;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.MDC;
import org.springframework.util.StopWatch;

import datadog.trace.api.CorrelationIdentifier;
import io.delphiplatform.api.aop.AppPerformanceMonitorInterceptor;
import lombok.Builder;
import lombok.Getter;

public class LoggingUtils {

    private static final Log PERFORMANCE_LOGGER = LogFactory.getLog(AppPerformanceMonitorInterceptor.class);

    private static final long PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_TRACE = 200;
    private static final long PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_DEBUG = 4000;
    private static final long PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_INFO = 30000;

    @Builder
    @Getter
    public static class PerformanceLogParams {

        private long timeMills;
        private long timeNanos;
        private String ddTraceId;
        private String traceType;
        private String traceName;
    }

    public static boolean needToLogMeasurement(long timeMills) {
        if (timeMills < PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_TRACE) {
            return PERFORMANCE_LOGGER.isTraceEnabled();
        } else if (timeMills < PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_DEBUG) {
            return PERFORMANCE_LOGGER.isDebugEnabled();
        } else if (timeMills < PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_INFO) {
            return PERFORMANCE_LOGGER.isInfoEnabled();
        } else {
            return PERFORMANCE_LOGGER.isWarnEnabled();
        }
    }

    public static void logPerformance(long timeMills, String message) {
        if (timeMills < PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_TRACE) {
            PERFORMANCE_LOGGER.trace(message);
        } else if (timeMills < PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_DEBUG) {
            PERFORMANCE_LOGGER.debug(message);
        } else if (timeMills < PERFORMANCE_LATENCY_BREAKPOINT_LEVEL_INFO) {
            PERFORMANCE_LOGGER.info(message);
        } else {
            PERFORMANCE_LOGGER.warn(message);
        }
    }

    public static void logPerformanceMeasurement(
        PerformanceLogParams params
    ) {
        long timeMills = params.getTimeMills();
        long timeNanos = params.getTimeNanos();

        if (needToLogMeasurement(timeMills)) {
            String logMessage = params.getTraceType() + " -"
                + "\t[" + params.getDdTraceId() + "]"
                + "\t" + params.getTraceName() + " - "
                + "\t" + timeMills + "ms";

            MDC.put("duration", String.valueOf(timeNanos));
            MDC.put("duration_mills", String.valueOf(timeMills));

            LoggingUtils.logPerformance(timeMills, logMessage);

            MDC.remove("duration");
            MDC.remove("duration_mills");
        }
    }

    public static void stopAndLogPerformanceMeasurement(StopWatch stopWatch, String traceName, String traceType) {
        stopWatch.stop();

        LoggingUtils.logPerformanceMeasurement(
            PerformanceLogParams.builder()
                .timeMills(stopWatch.getTotalTimeMillis())
                .timeNanos(stopWatch.getTotalTimeNanos())
                .ddTraceId(CorrelationIdentifier.getTraceId())
                .traceName(traceName)
                .traceType(traceType)
                .build()
        );
    }

    public static void stopAndLogAopPerformanceMeasurement(StopWatch stopWatch, String traceName) {
        stopAndLogPerformanceMeasurement(stopWatch, traceName, "AOP pointcut");
    }

    public static void stopAndLogManualPerformanceMeasurement(StopWatch stopWatch, String traceName) {
        stopAndLogPerformanceMeasurement(stopWatch, traceName, "Manual log");
    }
}
