package com.czg.service.order.utils;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

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

/**
 * @author Administrator
 */
@Slf4j
@Component
public class FunUtil {
    @Resource
    private  RedisTemplate<String, Object> redisTemplate;
    public static int retryCount = 5;

    /**
     * 执行任务并保证锁唯一
     * @param supplier 业务逻辑
     * @param lockKey Redis锁的Key
     * @return 业务逻辑返回值
     */
    public <T> T runFunAndCheckKey(Supplier<T> supplier, String lockKey) {
        String lockValue = String.valueOf(System.nanoTime() + Thread.currentThread().threadId());
        try {
            // 尝试获取锁，超时时间 5 秒，防止死锁
            boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
            int count = 0;
            // 初始等待 10ms
            int retryDelay = 10;

            while (!lock) {
                // 最多重试 10 次，大约 10 秒
                if (count++ > 50) {
                    throw new RuntimeException("系统繁忙, 稍后再试");
                }
                Thread.sleep(retryDelay);
                // 指数退避，最大等待 200ms
                retryDelay = Math.min(retryDelay * 2, 200);
                lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
            }

            // 执行任务
            return supplier.get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("线程被中断", e);
        } catch (Exception e) {
            log.error("执行出错：{}", e.getMessage(), e);
            throw e;
        } finally {
            // 释放锁（使用 Lua 脚本确保原子性）
            unlock(lockKey, lockValue);
        }
    }

    /**
     * 使用 Lua 脚本确保释放锁的原子性
     * @param lockKey 锁的 Key
     * @param lockValue 当前线程的锁值
     */
    private void unlock(String lockKey, String lockValue) {
        String luaScript =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "return redis.call('del', KEYS[1]) " +
                        "else return 0 end";
        redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
                Collections.singletonList(lockKey), lockValue);
    }

    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;
    }
}
