package com.ruoyi.common.aspectj;

import com.ruoyi.common.annotation.SerialExecution;
import com.ruoyi.common.utils.StringUtils;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 若依框架适配 - 串行执行注解切面
 * 参考若依 SysLogAspect 实现风格
 *
 * @author RuoYi
 */
@Aspect
@Component
public class SerialExecutionAspect {

    /**
     * 分组锁缓存：key=分组名，value=锁对象
     */
    private final Map<String, Lock> groupLockCache = new ConcurrentHashMap<>();

    /**
     * 切入点：拦截所有标记@SerialExecution的方法
     */
    @Pointcut("@annotation(com.ruoyi.common.annotation.SerialExecution)")
    public void serialExecutionPointCut() {
    }

    /**
     * 环绕通知：加锁执行，确保串行
     */
    @Around("serialExecutionPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 获取方法注解信息（参考若依日志切面的参数解析方式）
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SerialExecution serialAnnotation = method.getAnnotation(SerialExecution.class);
        if (serialAnnotation == null) {
            return joinPoint.proceed();
        }

        // 2. 解析分组（默认空=全局分组）
        String group = StringUtils.trimToEmpty(serialAnnotation.group());
        boolean fair = serialAnnotation.fair();

        // 3. 获取/创建分组锁（复用若依 StringUtils 工具类）
        Lock lock = groupLockCache.computeIfAbsent(group, k -> new ReentrantLock(fair));

        // 4. 加锁执行（try-finally 确保锁释放，避免死锁）
        try {
            lock.lock();
            // 执行原方法（若依业务方法的执行逻辑）
            return joinPoint.proceed();
        } finally {
            lock.unlock();
            // 可选：若依日志记录（可接入若依的日志框架）
            // LogUtils.info("串行方法执行完成，分组：{}，方法：{}", group, method.getName());
        }
    }
}