package com.app.framework.core.support.resubmit;

import cn.hutool.core.util.IdUtil;

import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 防重复提交锁
 */
public interface ResubmitLock {

    /**
     * 防重复提交令牌的缓存key
     */
    String RESUBMIT_TOKEN_KEY = "resubmit-token:%s:%s";

    /**
     * resubmit-token 过期时间，防止数据永不过期
     */
    Integer EXPIRE_TIME_MINUTE = 30;

    ConcurrentHashMap<String, Object> LOCK_CACHE = new ConcurrentHashMap<>(200);
    ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(5, new ThreadPoolExecutor.DiscardPolicy());


    /**
     * 参数形式防重提交 putIfAbsent 是原子操作保证线程安全
     *
     * @param key          对应的key
     * @param value
     * @param delaySeconds 延时时间
     * @return
     */
    default boolean lock(final String key, Object value, final int delaySeconds) {
        boolean lock = Objects.isNull(LOCK_CACHE.putIfAbsent(key, value));
        if (lock) {
            EXECUTOR.schedule(() -> {
                LOCK_CACHE.remove(key);
            }, delaySeconds, TimeUnit.SECONDS);
        }
        return lock;
    }

    /**
     * 参数形式防重提交 用以控制短时间内的重复提交
     *
     * @param lock 是否需要解锁
     * @param key  对应的key
     */
    default void unLock(final boolean lock, final String key) {
        if (lock) {
            LOCK_CACHE.remove(key);
        }
    }

    /**
     * 令牌形式防重复提交，生成令牌
     *
     * @param username 登录用户
     * @return
     */
    default String tokenCreate(final String username) {
        String token = IdUtil.randomUUID();
        String key = String.format(RESUBMIT_TOKEN_KEY, username, token);
        boolean lock = Objects.isNull(LOCK_CACHE.put(key, username));
        if (lock) {
            EXECUTOR.schedule(() -> {
                LOCK_CACHE.remove(key);
            }, EXPIRE_TIME_MINUTE, TimeUnit.MINUTES);
        }
        return token;
    }

    /**
     * 令牌形式防重复提交，删除令牌
     *
     * @param username 登录用户
     * @return
     */
    default boolean tokenDelete(final String username, final String token) {
        String key = String.format(RESUBMIT_TOKEN_KEY, username, token);
        Object o = LOCK_CACHE.remove(key);
        return !Objects.isNull(o);
    }

}
