package cn.ysk.cashier.utils;

import cn.ysk.cashier.exception.BadRequestException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class Utils {
    public static int retryCount = 5;
    private static final Logger log = LoggerFactory.getLogger(Utils.class);

    public static <T> void catchErrNoReturn(Supplier<T> supplier) {
        try {
            supplier.get();
        }catch (Exception e) {
            log.error("执行方法出现异常", e);
        }
    }

    public static <T, R> void runFunAndRetryNoReturn(
            Supplier<R> function,
            Function<R, Boolean> check, Consumer<R> errFun) {
        log.info("工具类开始执行函数");
        R result = function.get();
        boolean flag = check.apply(result);

        log.info("执行结果: {}", result);

        while (flag && retryCount-- > 0) {
            log.info("执行函数失败, 剩余尝试次数{}", retryCount);
            result = function.get();
            log.info("执行结果: {}", result);
            flag = check.apply(result);
        }

        if (flag) {
            errFun.accept(result);
        }
    }

    public static<T> T runFunAndCheckKey(Supplier<T> supplier, StringRedisTemplate redisTemplate, String lockKey) {
        try{
            // 创建线程id, 用作判断
            String clientId = UUID.randomUUID().toString();
            // 设置分布式锁
            boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.MILLISECONDS));
            int count = 0;
            while (!lock) {
                if (count++ > 100) {
                    throw new BadRequestException("系统繁忙, 稍后再试");
                }
                Thread.sleep(20);
                lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.MILLISECONDS));
            }
            return supplier.get();
        } catch (RuntimeException e){
            log.info("执行出错：{}", e.getMessage());
            throw e;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally{
            redisTemplate.delete(lockKey);
        }
    }

    public static <T, R> R runFunAndRetry(
            Supplier<R> function,
            Function<R, Boolean> check, Consumer<R> errFun) {
        log.info("工具类开始执行函数");
        R result = function.get();
        boolean flag = check.apply(result);

        log.info("执行结果: {}", result);

        while (flag && retryCount-- > 0) {
            log.info("执行函数失败, 剩余尝试次数{}", retryCount);
            result = function.get();
            log.info("执行结果: {}", result);
            flag = check.apply(result);
        }

        if (flag) {
            errFun.accept(result);
        }
        return result;
    }
}
