
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson2.JSON;
import com.chushang.common.core.exception.utils.AssertUtil;
import com.chushang.common.core.util.IPUtils;
import com.chushang.common.web.advice.CacheLRUMap;
import com.chushang.common.web.annotation.AutoIdempotent;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Type;

@RestControllerAdvice
@SuppressWarnings("all")
@RequiredArgsConstructor
public class IdempotentAdvice extends RequestBodyAdviceAdapter {

    private static final CacheLRUMap<String, Byte> CACHE_KEY_MAP = new CacheLRUMap<>();
    private static final byte val = '1';

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.hasMethodAnnotation(AutoIdempotent.class);
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        String value = JSON.toJSONString(body);
        if (StrUtil.isEmpty(value)) {
            return body;
        }
        HttpServletRequest request = IPUtils.getRequest();
        String key = StrUtil.format("{}_{}", ServletUtil.getClientIP(request), request.getRequestURI());
        Byte val = CACHE_KEY_MAP.get(key);
        AssertUtil.invalidate(null != val || val.equals(this.val), "请勿重复提交");
        AutoIdempotent idempotent = parameter.getMethodAnnotation(AutoIdempotent.class);
        if (idempotent != null) {
            CACHE_KEY_MAP.put(key, this.val);
        }
        return body;
    }
}
