package com.yeejoin.amos.boot.module.hygf.api.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * spring redis 工具类
 * <p>
 * ProjectName: qms
 * PackageName: com.yeejoin.amos.qms.api.spc.utils
 *
 * @author yangyang
 * @version v1.0
 * @date 2024/4/19 10:15
 */
@Component
@Slf4j
public class RedisLockUtil {

    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 加锁，阻塞
     *
     * @param key        锁主键
     * @param value      缓存值
     * @param expireTime 锁有效时间，-1永久存储，单位秒
     * @param waitTime   获取锁阻塞等待时间，单位秒
     * @return result
     */
    public Boolean tryLock(String key, String value, long expireTime, long waitTime) {
        long start = System.currentTimeMillis();
        // 默认10秒
        waitTime = (waitTime < 0 ? 10 : waitTime) * 1000;
        expireTime = expireTime < 0 ? -1 : expireTime;
        for (; ; ) {
            //SET命令返回OK ，则证明获取锁成功
            Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
            if (BooleanUtils.isTrue(ret)) {
                return true;
            }
            //否则循环等待，在等待超时时间内仍未获取到锁，则获取失败
            long end = System.currentTimeMillis() - start;
            if (end >= waitTime) {
                return false;
            }
        }
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean releaseLock(final String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 替代keys
     *
     * @param pattern key模糊
     */
    public Set<String> scan(final String pattern) {
        return (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keys = new HashSet<>();
            try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(pattern).count(1000).build())) {
                while (cursor.hasNext()) {
                    keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
                }
            } catch (Exception e) {
                log.error(e.getMessage());
            }
            return keys;
        });
    }
}
