package com.qqchen.deploy.backend.deploy.lock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;

/**
 * 智能同步任务本地锁管理器
 * <p>自动识别调用者（类名+方法名）和参数，生成唯一锁key，无需手动指定锁类型</p>
 * 
 * <p>使用示例：
 * <pre>
 * // 加锁（自动识别调用方法和参数）
 * if (!syncLockManager.tryLock(externalSystemId)) {
 *     return; // 已有同步任务在执行
 * }
 * try {
 *     // 执行同步逻辑
 * } finally {
 *     syncLockManager.unlock(externalSystemId);
 * }
 * </pre>
 * 
 * <p>锁key格式：ClassName.methodName:param1:param2:...</p>
 */
@Slf4j
@Component
public class SyncLockManager {

    // 统一的锁存储：key = ClassName.methodName:param1:param2:...
    private final ConcurrentHashMap<String, Boolean> locks = new ConcurrentHashMap<>();

    /**
     * 尝试获取锁（自动识别调用者）
     *
     * @param params 锁参数（如 externalSystemId, viewId等）
     * @return true-获取成功，false-已被锁定
     */
    public boolean tryLock(Object... params) {
        CallerInfo callerInfo = getCallerInfo();
        String lockKey = buildLockKey(callerInfo, params);
        boolean acquired = locks.putIfAbsent(lockKey, true) == null;
        
        if (!acquired) {
            log.warn("同步任务正在执行中，跳过本次同步: method={}, params={}", 
                callerInfo.getFullMethodName(), formatParams(params));
        } else {
            log.debug("获取同步锁成功: method={}, params={}", 
                callerInfo.getFullMethodName(), formatParams(params));
        }
        
        return acquired;
    }

    /**
     * 释放锁（自动识别调用者）
     *
     * @param params 锁参数（必须与tryLock时完全一致）
     */
    public void unlock(Object... params) {
        CallerInfo callerInfo = getCallerInfo();
        String lockKey = buildLockKey(callerInfo, params);
        locks.remove(lockKey);
        log.debug("同步任务完成，释放锁: method={}, params={}", 
            callerInfo.getFullMethodName(), formatParams(params));
    }

    /**
     * 检查锁是否被持有（自动识别调用者）
     *
     * @param params 锁参数
     * @return true-已被锁定，false-未锁定
     */
    public boolean isLocked(Object... params) {
        CallerInfo callerInfo = getCallerInfo();
        String lockKey = buildLockKey(callerInfo, params);
        return locks.containsKey(lockKey);
    }

    /**
     * 获取当前锁数量（用于监控）
     *
     * @return 当前持有的锁数量
     */
    public int getLockCount() {
        return locks.size();
    }

    /**
     * 获取调用者信息（通过栈追踪，智能跳过Spring代理层）
     *
     * @return 调用者信息
     */
    private CallerInfo getCallerInfo() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        
        // 从栈顶开始遍历，跳过本类和Spring的代理/拦截器类
        // 栈结构示例：
        // 0: Thread.getStackTrace()
        // 1: SyncLockManager.getCallerInfo()
        // 2: SyncLockManager.tryLock/unlock/isLocked()
        // 3+: 可能是Spring代理类或真实调用者
        
        for (int i = 3; i < stackTrace.length; i++) {
            StackTraceElement element = stackTrace[i];
            String className = element.getClassName();
            String methodName = element.getMethodName();
            
            // 跳过 Spring 和 JDK 的内部类/代理类
            if (shouldSkipFrame(className, methodName)) {
                continue;
            }
            
            // 找到真正的业务调用者
            String simpleClassName = className.substring(className.lastIndexOf('.') + 1);
            return new CallerInfo(simpleClassName, methodName, className + "." + methodName);
        }
        
        // 兜底：使用unknown
        return new CallerInfo("Unknown", "unknown", "Unknown.unknown");
    }

    /**
     * 判断是否应该跳过该栈帧
     *
     * @param className  类名
     * @param methodName 方法名
     * @return true-跳过，false-不跳过
     */
    private boolean shouldSkipFrame(String className, String methodName) {
        // 跳过 CGLIB 动态代理类（所有包含$$的类都是CGLIB代理）
        if (className.contains("$$")) {
            return true;
        }
        
        // 跳过 JDK 动态代理
        if (className.startsWith("jdk.proxy") || className.startsWith("com.sun.proxy")) {
            return true;
        }
        
        // 跳过 Spring AOP 拦截器
        if (className.contains("AsyncExecutionInterceptor") ||
            className.contains("TransactionInterceptor") ||
            className.contains("CglibAopProxy") ||
            className.contains("ReflectiveMethodInvocation") ||
            className.contains("DynamicAdvisedInterceptor")) {
            return true;
        }
        
        // 跳过线程池和任务执行器
        if (className.contains("ThreadPoolExecutor") ||
            className.contains("FutureTask") ||
            className.contains("CompletableFuture")) {
            return true;
        }
        
        // 跳过 Java 反射
        if (className.startsWith("java.lang.reflect") ||
            className.startsWith("jdk.internal.reflect")) {
            return true;
        }
        
        // 跳过 lambda 表达式的合成方法
        if (methodName.contains("lambda$")) {
            return true;
        }
        
        return false;
    }

    /**
     * 构建锁的唯一key
     *
     * @param callerInfo 调用者信息
     * @param params     参数列表
     * @return 锁key
     */
    private String buildLockKey(CallerInfo callerInfo, Object... params) {
        StringBuilder key = new StringBuilder(callerInfo.getFullMethodName());
        for (Object param : params) {
            key.append(":").append(param != null ? param : "null");
        }
        return key.toString();
    }

    /**
     * 格式化参数用于日志输出
     *
     * @param params 参数列表
     * @return 格式化字符串
     */
    private String formatParams(Object... params) {
        if (params.length == 0) {
            return "[]";
        }
        
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < params.length; i++) {
            if (i > 0) {
                sb.append(", ");
            }
            sb.append(params[i]);
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * 调用者信息
     */
    private static class CallerInfo {
        private final String simpleClassName;
        private final String methodName;
        private final String fullMethodName;

        public CallerInfo(String simpleClassName, String methodName, String fullMethodName) {
            this.simpleClassName = simpleClassName;
            this.methodName = methodName;
            this.fullMethodName = fullMethodName;
        }

        public String getSimpleClassName() {
            return simpleClassName;
        }

        public String getMethodName() {
            return methodName;
        }

        public String getFullMethodName() {
            return fullMethodName;
        }
    }
}
