package com.devjob.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * EP43: AOP — 공통 관심사(실행 시간 측정/로깅) 분리.
 *
 * com.devjob 하위 모든 service 패키지의 모든 메서드를 가로채 실행 시간을 측정한다.
 * - 500ms를 넘으면 [SLOW] 경고 로그, 그렇지 않으면 [OK] DEBUG 로그를 남긴다.
 * - 예외 발생 시 [FAIL] 로그를 남기고 반드시 다시 던진다(throw e).
 *
 * 주의(강의 "자주 나오는 에러"):
 * - spring-boot-starter-aop는 spring.aop.auto=true가 기본값이라 별도 설정 없이도
 *   @Aspect 빈이 자동 등록되지만, 같은 클래스 내부 메서드 호출(self-invocation)에는
 *   프록시가 적용되지 않아 Aspect가 동작하지 않는다 — 항상 다른 빈을 통해 호출해야 한다.
 * - @Around 안에서 jp.proceed()의 결과를 반드시 return 해야 원래 메서드의 반환값이
 *   호출자에게 정상 전달된다.
 */
@Slf4j
@Aspect
@Component
public class ServiceLoggingAspect {

    private static final long SLOW_THRESHOLD_MS = 500;

    @Around("execution(* com.devjob..service..*(..))")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long elapsed = System.currentTimeMillis() - start;
            if (elapsed > SLOW_THRESHOLD_MS) {
                log.warn("[SLOW] {} - {}ms", joinPoint.getSignature().toShortString(), elapsed);
            } else {
                log.debug("[OK] {} - {}ms", joinPoint.getSignature().toShortString(), elapsed);
            }
            return result;
        } catch (Exception e) {
            log.error("[FAIL] {}", joinPoint.getSignature().toShortString(), e);
            throw e;
        }
    }
}
