package shop.alien.storeplatform.aspect;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import shop.alien.entity.result.R;
import shop.alien.storeplatform.annotation.PreventDuplicateSubmit;
import shop.alien.storeplatform.util.LoginUserUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 防重复提交切面
 * 
 * @author ssk
 * @since 2025-11-25
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class PreventDuplicateSubmitAspect {

    private final StringRedisTemplate stringRedisTemplate;

    @Around("@annotation(shop.alien.storeplatform.annotation.PreventDuplicateSubmit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 获取注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        PreventDuplicateSubmit annotation = method.getAnnotation(PreventDuplicateSubmit.class);

        // 2. 构建Redis锁的key
        String lockKey = buildLockKey(joinPoint, annotation);
        
        log.info("PreventDuplicateSubmitAspect - 尝试获取锁: key={}", lockKey);

        // 3. 尝试获取锁
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
                lockKey,
                String.valueOf(System.currentTimeMillis()),
                annotation.expireSeconds(),
                TimeUnit.SECONDS
        );

        // 4. 获取锁失败，说明重复提交
        if (success == null || !success) {
            log.warn("PreventDuplicateSubmitAspect - 重复提交被拦截: key={}", lockKey);
            return R.fail(annotation.message());
        }

        try {
            // 5. 执行业务方法
            log.info("PreventDuplicateSubmitAspect - 获取锁成功，执行业务: key={}", lockKey);
            Object result = joinPoint.proceed();
            
            // 6. 如果业务执行失败，立即释放锁，允许重试
            if (result instanceof R) {
                R<?> r = (R<?>) result;
                if (!r.isSuccess()) {
                    stringRedisTemplate.delete(lockKey);
                    log.info("PreventDuplicateSubmitAspect - 业务执行失败，释放锁: key={}", lockKey);
                }
            }
            
            return result;
        } catch (Exception e) {
            // 7. 发生异常，释放锁
            stringRedisTemplate.delete(lockKey);
            log.error("PreventDuplicateSubmitAspect - 执行异常，释放锁: key={}", lockKey, e);
            throw e;
        }
    }

    /**
     * 构建锁的key
     * 格式: prefix:userId:methodName:参数摘要
     */
    private String buildLockKey(ProceedingJoinPoint joinPoint, PreventDuplicateSubmit annotation) {
        // 获取当前用户ID
        Integer userId = null;
        try {
            userId = LoginUserUtil.getCurrentUserId();
        } catch (Exception e) {
            log.warn("PreventDuplicateSubmitAspect - 无法获取用户ID，使用IP作为标识");
        }

        // 获取方法名
        String methodName = joinPoint.getSignature().getName();

        // 获取请求IP（作为备用标识）
        String ip = getRequestIP();

        // 获取参数摘要（只取金额等关键参数）
        String paramKey = buildParamKey(joinPoint.getArgs());

        // 构建key: prevent_duplicate:cashOut:userId_123:10000
        return String.format("%s:%s:%s:%s",
                annotation.keyPrefix(),
                methodName,
                userId != null ? "user_" + userId : "ip_" + ip,
                paramKey
        );
    }

    /**
     * 构建参数key（针对提现金额等关键参数）
     */
    private String buildParamKey(Object[] args) {
        if (args == null || args.length == 0) {
            return "noargs";
        }

        // 针对提现接口，提取金额参数
        StringBuilder sb = new StringBuilder();
        for (Object arg : args) {
            if (arg == null) {
                continue;
            }
            
            // 如果是CashOutDTO，提取金额
            if (arg.getClass().getSimpleName().contains("CashOutDTO")) {
                try {
                    Method getWithdrawalMoney = arg.getClass().getMethod("getWithdrawalMoney");
                    Object money = getWithdrawalMoney.invoke(arg);
                    sb.append(money);
                } catch (Exception e) {
                    log.debug("PreventDuplicateSubmitAspect - 无法提取提现金额");
                }
            }
        }

        return sb.length() > 0 ? sb.toString() : "default";
    }

    /**
     * 获取请求IP
     */
    private String getRequestIP() {
        try {
            ServletRequestAttributes attributes = 
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                String ip = request.getHeader("X-Forwarded-For");
                if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("X-Real-IP");
                }
                if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getRemoteAddr();
                }
                return ip != null ? ip.replaceAll("[^0-9.]", "") : "unknown";
            }
        } catch (Exception e) {
            log.warn("PreventDuplicateSubmitAspect - 获取IP失败", e);
        }
        return "unknown";
    }
}

