package quant.rich.emoney.util;


import java.lang.StackWalker;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

public class CallerLockUtil {

    private static final StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
    private static final ConcurrentMap<LockKey, WeakReference<ReentrantLock>> lockMap = new ConcurrentHashMap<>();

    /**
     * ✅ 方式一：一键加锁执行
     */
    public static void runWithCallerLock(Runnable task, Object... extraKeys) {
        ReentrantLock lock = acquireLock(extraKeys);
        lock.lock();
        try {
            task.run();
        } finally {
            lock.unlock();
        }
    }

    /**
     * ✅ 方式二：手动获取锁
     * 用于：lock() + try/finally + unlock()
     */
    public static ReentrantLock acquireLock(Object... extraKeys) {
        LockKey key = buildLockKey(extraKeys);
        return lockMap.compute(key, (k, ref) -> {
            ReentrantLock l = (ref == null || ref.get() == null) ? new ReentrantLock() : ref.get();
            return new WeakReference<>(l);
        }).get();
    }
    
    /**
     * ✅ 方式三：尝试获取锁并运行，支持超时，失败后不阻塞
     * @return true 表示成功执行，false 表示未获得锁
     */
    public static boolean tryRunWithCallerLock(Runnable task, long timeoutMs, Object... extraKeys) {
        ReentrantLock lock = acquireLock(extraKeys);
        boolean locked = false;
        try {
            locked = lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS);
            if (locked) {
                task.run();
            }
            return locked;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        } finally {
            if (locked) lock.unlock();
        }
    }
    
    /**
     * ✅ 非阻塞获取锁，超时失败返回 null 或抛异常
     */
    public static <T> Optional<T> tryCallWithCallerLock(Callable<T> task, long timeoutMs, Object... extraKeys) {
        ReentrantLock lock = acquireLock(extraKeys);
        boolean locked = false;
        try {
            locked = lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS);
            if (!locked) return Optional.empty();
            return Optional.of(task.call());
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (locked) lock.unlock();
        }
    }

    /**
     * 构造调用者方法 + 附加参数为 key
     */
    private static LockKey buildLockKey(Object... extraKeys) {
        String caller = walker.walk(frames ->
            frames.skip(3).findFirst() // skip getStackTrace → buildLockKey → acquireLock → 调用者
                  .map(f -> f.getClassName() + "#" + f.getMethodName())
                  .orElse("unknown")
        );
        return new LockKey(caller, extraKeys);
    }

    /**
     * 复合 Key 类（调用方法 + 参数）
     */
    private static class LockKey {
        private final String method;
        private final Object[] extra;
        private final int hash;

        LockKey(String method, Object[] extra) {
            this.method = method;
            this.extra = (extra == null) ? new Object[0] : extra;
            this.hash = Objects.hash(method, Arrays.deepHashCode(this.extra));
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof LockKey)) return false;
            LockKey other = (LockKey) obj;
            return method.equals(other.method) && Arrays.deepEquals(this.extra, other.extra);
        }

        @Override
        public int hashCode() {
            return hash;
        }
    }
}