package com.example.scaffold.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 学习切面 - 演示 AOP 各种通知类型
 * 
 * 学习要点：
 * 1. @Aspect - 标记为切面类
 * 2. @Pointcut - 切入点表达式
 * 3. @Before - 前置通知
 * 4. @After - 后置通知
 * 5. @AfterReturning - 返回通知
 * 6. @AfterThrowing - 异常通知
 * 7. @Around - 环绕通知
 */
@Slf4j
@Aspect
@Component
public class LearningAspect {
    
    /**
     * 切入点 - 所有 Service 层方法
     */
    @Pointcut("execution(* com.example.scaffold.service..*.*(..))")
    public void serviceLayer() {}
    
    /**
     * 切入点 - 所有 Controller 层方法
     */
    @Pointcut("execution(* com.example.scaffold.controller..*.*(..))")
    public void controllerLayer() {}
    
    /**
     * 切入点 - 所有 Mapper 方法
     */
    @Pointcut("execution(* com.example.scaffold.mapper..*.*(..))")
    public void mapperLayer() {}
    
    /**
     * 前置通知 - 方法执行前
     */
    @Before("serviceLayer()")
    public void beforeService(JoinPoint joinPoint) {
        log.info("🔹 [AOP @Before] 即将执行: {}.{}()",
                joinPoint.getTarget().getClass().getSimpleName(),
                joinPoint.getSignature().getName());
    }
    
    /**
     * 后置通知 - 方法执行后（无论是否异常）
     */
    @After("serviceLayer()")
    public void afterService(JoinPoint joinPoint) {
        log.info("🔸 [AOP @After] 执行完成: {}.{}()",
                joinPoint.getTarget().getClass().getSimpleName(),
                joinPoint.getSignature().getName());
    }
    
    /**
     * 返回通知 - 方法成功返回后
     */
    @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void afterReturningService(JoinPoint joinPoint, Object result) {
        log.info("✅ [AOP @AfterReturning] 方法返回: {}.{}() => {}",
                joinPoint.getTarget().getClass().getSimpleName(),
                joinPoint.getSignature().getName(),
                result != null ? result.getClass().getSimpleName() : "null");
    }
    
    /**
     * 异常通知 - 方法抛出异常后
     */
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
    public void afterThrowingService(JoinPoint joinPoint, Throwable ex) {
        log.error("❌ [AOP @AfterThrowing] 方法异常: {}.{}() => {}",
                joinPoint.getTarget().getClass().getSimpleName(),
                joinPoint.getSignature().getName(),
                ex.getMessage());
    }
    
    /**
     * 环绕通知 - 完全控制方法执行
     */
    @Around("controllerLayer()")
    public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        log.info("🔄 [AOP @Around] 开始: {}.{}() 参数: {}", className, methodName, Arrays.toString(args));
        
        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            long duration = System.currentTimeMillis() - startTime;
            log.info("🔄 [AOP @Around] 完成: {}.{}() 耗时: {}ms", className, methodName, duration);
            
            return result;
        } catch (Throwable ex) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("🔄 [AOP @Around] 异常: {}.{}() 耗时: {}ms 错误: {}", 
                    className, methodName, duration, ex.getMessage());
            throw ex;
        }
    }
}