package com.xxx.base.springframework.redis.hand;

import com.xxx.base.springframework.core.web.exception.ServiceException;
import com.xxx.base.springframework.redis.interfaces.PreventDuplicateSubmit;
import com.xxx.base.springframework.util.MessageUtil;
import com.xxx.base.springframework.util.UserUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
public class DuplicateSubmitAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Around("@annotation(preventDuplicateSubmit)")
    public Object around(ProceedingJoinPoint point, PreventDuplicateSubmit preventDuplicateSubmit) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        // 构建key: 用户ID+URL+参数签名
        Long userId = UserUtil.getCurrentUser().getUserVO().getId();
        String url = request.getRequestURI();
        String key = "DUPLICATE_SUBMIT:" + url + ":" + userId;

        long expireTime = preventDuplicateSubmit.expireTime();
        long timeout = preventDuplicateSubmit.timeout();

        RLock lock = redissonClient.getLock(key);

        try {
            // 尝试获取分布式锁
            boolean isLocked = lock.tryLock(timeout, expireTime, TimeUnit.SECONDS);
            if (!isLocked) {
                log.warn("检测到重复提交请求，key={}", key);
                throw new ServiceException(MessageUtil.get("REPEAT_ERROR"));
            }

            // 执行业务方法
            return point.proceed();
        } finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private String getRequestParamSign(HttpServletRequest request) {
        SortedMap<String, String[]> params = new TreeMap<>(request.getParameterMap());
        StringBuilder sb = new StringBuilder();
        for (String key : params.keySet()) {
            sb.append(key).append("=").append(String.join(",", params.get(key))).append("&");
        }
        return md5(sb.toString());
    }

    private String md5(String content) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(content.getBytes("UTF-8"));
            StringBuilder result = new StringBuilder();
            for (byte b : bytes) {
                result.append(Integer.toHexString((b & 0xFF) | 0x100), 1, 3);
            }
            return result.toString();
        } catch (Exception e) {
            throw new RuntimeException("MD5 calculation error", e);
        }
    }
}