package org.example.Utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;

import javax.script.*;
import java.lang.reflect.Type;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class JsEngine {
    private static final Logger log = LoggerFactory.getLogger(JsEngine.class);

    // 缓存结果，有效期2小时
    private static final ConcurrentHashMap<String, CachedResult> cache = new ConcurrentHashMap<>();
    private static final long CACHE_DURATION_MS = 2 * 60 * 60 * 1000; // 2小时
    private static final int DEFAULT_MAX_CACHE_ENTRIES = 1000;
    private static volatile int maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
    private static final AtomicLong accessSequence = new AtomicLong();
    // 线程本地存储：每个线程拥有独立的引擎实例
    private static final ThreadLocal<ScriptEngine> ENGINE_THREAD_LOCAL = ThreadLocal.withInitial(() -> {
        ScriptEngineManager manager = new ScriptEngineManager();
        // 优先级：GraalJS > JavaScript（Nashorn）
        ScriptEngine engine = manager.getEngineByName("GraalJS");
        if (engine == null) {
            engine = manager.getEngineByName("javascript");
        }
        if (engine == null) {
            engine = manager.getEngineByName("JavaScript");
        }
        if (engine == null) {
            throw new IllegalStateException("无法初始化JavaScript引擎！请检查：\n" +
                    "1. JDK 8及以下需确保包含Nashorn引擎\n" +
                    "2. JDK 11及以上需添加GraalVM JavaScript依赖");
        }
        // GraalJS 额外配置：允许全访问（根据业务场景调整）
        if (engine instanceof GraalJSScriptEngine) {
            boolean allowAllAccess = Boolean.parseBoolean(WebConfigUtil.get("app.js.allow-all-access", "false"));
            engine.getContext().setAttribute("polyglot.js.allowAllAccess", allowAllAccess, ScriptContext.ENGINE_SCOPE);
            if (allowAllAccess) {
                log.warn(String.valueOf("WARNING: GraalJS all-access mode is enabled by app.js.allow-all-access=true"));
            }
        }
        return engine;
    });

    // 定时清理过期缓存（每小时执行一次）
    private static final ScheduledExecutorService CLEANER = Executors.newSingleThreadScheduledExecutor();

    static {
        // 初始化缓存清理任务
        CLEANER.scheduleAtFixedRate(() -> {
            long now = System.currentTimeMillis();
            cache.entrySet().removeIf(entry -> now - entry.getValue().timestamp > CACHE_DURATION_MS);
        }, 1, 1, TimeUnit.HOURS);
    }

    // 错误处理函数式接口
    @FunctionalInterface
    public interface ErrorHandler {
        Object handle(String input, Exception e);
    }

    /**
     * 动态计算 JavaScript 表达式的值（支持上下文参数）
     * @param statement JS表达式
     * @param contextParams 脚本执行上下文参数（可选）
     * @param onError 错误处理逻辑
     * @return 执行结果
     */
    public static Object Eval(String statement, Map<String, Object> contextParams, ErrorHandler onError) {
        if (statement == null || statement.isEmpty()) {
            return "";
        }

        // 生成更安全的缓存键：脚本内容 + 上下文参数哈希（避免碰撞）
        String cacheKey = generateCacheKey(statement, contextParams);

        // 检查缓存
        CachedResult cached = cache.get(cacheKey);
        if (cached != null && System.currentTimeMillis() - cached.timestamp < CACHE_DURATION_MS) {
            cached.updateAccessOrder();
            return cached.result;
        }

        ScriptEngine engine = null;
        try {
            // 获取当前线程的引擎实例
            engine = ENGINE_THREAD_LOCAL.get();
            // 创建独立的上下文（避免线程内多次执行的上下文污染）
            ScriptContext context = new SimpleScriptContext();
            Bindings bindings = engine.createBindings();

            // 注入上下文参数（如果有）
            if (contextParams != null && !contextParams.isEmpty()) {
                bindings.putAll(contextParams);
            }
            context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);

            // 执行JavaScript表达式
            Object result = engine.eval(statement, context);

            // 存入缓存
            putCachedResult(cacheKey, result);

            return result;
        } catch (ScriptException e) {
            if (onError != null) {
                return onError.handle(statement, e);
            }
            return null;
        } catch (Exception e) {
            // 捕获引擎初始化/上下文创建异常
            if (onError != null) {
                return onError.handle(statement, e);
            }
            return null;
        }
    }

    /**
     * 简化版：无上下文参数 + 默认错误处理
     */
    public static Object Eval(String statement) {
        return Eval(statement, null, null);
    }

    /**
     * 简化版：无上下文参数 + 自定义错误处理
     */
    public static Object Eval(String statement, ErrorHandler onError) {
        return Eval(statement, null, onError);
    }

    /**
     * 生成缓存键：脚本内容 + 上下文参数哈希
     */
    private static String generateCacheKey(String statement, Map<String, Object> contextParams) {
        StringBuilder keyBuilder = new StringBuilder();
        // 脚本内容（原始值，避免toLowerCase丢失语义）
        keyBuilder.append(statement.trim()).append("|");
        // 上下文参数哈希（无参数则为空）
        if (contextParams != null && !contextParams.isEmpty()) {
            keyBuilder.append(contextParams.hashCode());
        }
        return keyBuilder.toString();
    }

    // 缓存结果类
    private static class CachedResult {
        final Object result;
        final long timestamp;
        long lastAccessOrder;

        CachedResult(Object result) {
            this.result = result;
            this.timestamp = System.currentTimeMillis();
            updateAccessOrder();
        }

        void updateAccessOrder() {
            this.lastAccessOrder = accessSequence.incrementAndGet();
        }
    }

    // 关闭线程池（应用停止时调用）
    public static void setMaxCacheEntries(int configuredMaxEntries) {
        maxCacheEntries = Math.max(1, configuredMaxEntries);
        enforceMaxCacheEntries();
    }

    public static void setMaxCacheEntriesForTests(int configuredMaxEntries) {
        setMaxCacheEntries(configuredMaxEntries);
    }

    public static void putCachedResultForTests(String cacheKey, Object result) {
        putCachedResult(cacheKey, result);
    }

    public static int cacheSizeForTests() {
        evictExpiredResults();
        return cache.size();
    }

    public static boolean hasCachedResultForTests(String cacheKey) {
        return cache.containsKey(cacheKey);
    }

    public static void resetCacheForTests() {
        cache.clear();
        maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
        accessSequence.set(0);
        ENGINE_THREAD_LOCAL.remove();
    }

    private static void putCachedResult(String cacheKey, Object result) {
        cache.put(cacheKey, new CachedResult(result));
        enforceMaxCacheEntries();
    }

    private static void evictExpiredResults() {
        long now = System.currentTimeMillis();
        cache.entrySet().removeIf(entry -> now - entry.getValue().timestamp > CACHE_DURATION_MS);
    }

    private static void enforceMaxCacheEntries() {
        evictExpiredResults();
        while (cache.size() > maxCacheEntries) {
            cache.entrySet().stream()
                    .min(Comparator.comparingLong(entry -> entry.getValue().lastAccessOrder))
                    .ifPresent(entry -> cache.remove(entry.getKey()));
        }
    }

    public static void shutdown() {
        CLEANER.shutdown();
        try {
            if (!CLEANER.awaitTermination(5, TimeUnit.SECONDS)) {
                CLEANER.shutdownNow();
            }
        } catch (InterruptedException e) {
            CLEANER.shutdownNow();
        }
        // 清理ThreadLocal，避免内存泄漏
        ENGINE_THREAD_LOCAL.remove();
        cache.clear();
    }
}

//package org.example.Utils;
//
//import javax.script.ScriptEngine;
//import javax.script.ScriptEngineManager;
//import javax.script.ScriptException;
//import java.util.concurrent.ConcurrentHashMap;
//
//public class JsEngine {
//    // 缓存结果，有效期2小时（保留原有逻辑）
//    private static final ConcurrentHashMap<String, CachedResult> cache = new ConcurrentHashMap<>();
//    private static final long CACHE_DURATION_MS = 2 * 60 * 60 * 1000; // 2小时
//
//    // JavaScript 引擎管理器（保留，线程安全）
//    private static final ScriptEngineManager manager = new ScriptEngineManager();
//    // 【关键删除1】移除静态的ScriptEngine实例（多线程问题根源）
//    // private static final ScriptEngine engine;
//
//    // 【关键删除2】移除静态代码块中初始化engine的逻辑
//    // static { ... }
//
//    // 错误处理函数式接口（保留原有逻辑）
//    @FunctionalInterface
//    public interface ErrorHandler {
//        Object handle(String input, Exception e);
//    }
//
//    /**
//     * 动态计算 JavaScript 表达式的值（仅修复线程问题）
//     */
//    public static Object Eval(String statement, ErrorHandler onError) {
//        if (statement == null || statement.isEmpty()) {
//            return "";
//        }
//
//        // 生成缓存键（保留原有逻辑）
//        String cacheKey = statement.toLowerCase().trim().hashCode() + "";
//
//        // 检查缓存（保留原有逻辑）
//        CachedResult cached = cache.get(cacheKey);
//        if (cached != null && System.currentTimeMillis() - cached.timestamp < CACHE_DURATION_MS) {
//            return cached.result;
//        }
//
//        // 【关键新增1】每次执行时创建独立的ScriptEngine实例（解决多线程共享问题）
//        ScriptEngine engine = getScriptEngine();
//        // 校验引擎是否初始化成功（迁移原有静态代码块的校验逻辑）
//        if (engine == null) {
//            String errorMsg = "无法初始化JavaScript引擎！请检查：\n" +
//                    "1. JDK 8及以下需确保包含Nashorn引擎\n" +
//                    "2. JDK 11及以上需添加GraalVM JavaScript依赖";
//            if (onError != null) {
//                return onError.handle(statement, new IllegalStateException(errorMsg));
//            }
//            throw new IllegalStateException(errorMsg);
//        }
//
//        try {
//            // 执行JavaScript表达式（逻辑不变，只是engine变为方法内局部变量）
//            Object result = engine.eval(statement);
//
//            // 存入缓存（保留原有逻辑）
//            cache.put(cacheKey, new CachedResult(result));
//
//            return result;
//        } catch (ScriptException e) {
//            if (onError != null) {
//                return onError.handle(statement, e);
//            }
//            return null;
//        }
//    }
//
//    // 【关键新增2】抽取获取ScriptEngine的方法（复用原有初始化逻辑）
//    private static ScriptEngine getScriptEngine() {
//        ScriptEngine engine1;
//        // 优先尝试小写名称，兼容更多环境（迁移原有静态代码块的逻辑）
//        engine1 = manager.getEngineByName("javascript");
//        // 若失败，尝试大写名称
//        if (engine1 == null) {
//            engine1 = manager.getEngineByName("JavaScript");
//        }
//        return engine1;
//    }
//
//    // 简化版方法（保留原有逻辑）
//    public static Object Eval(String statement) {
//        return Eval(statement, null);
//    }
//
//    // 缓存结果类（保留原有逻辑）
//    private static class CachedResult {
//        final Object result;
//        final long timestamp;
//
//        CachedResult(Object result) {
//            this.result = result;
//            this.timestamp = System.currentTimeMillis();
//        }
//    }
//}
