package com.cmvr.test.util;

import com.alibaba.fastjson2.JSONObject;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.util.Map;
import java.util.concurrent.*;
import java.util.regex.Pattern;

@Service
public class NashornJsExecutor {

    // 执行超时时间（秒）
    private static final long TIMEOUT_SECONDS = 5;
    // 最大返回结果大小（1MB）
    private static final int MAX_RESULT_SIZE = 1024 * 1024;

    private final ExecutorService executor;
    private final ScriptEngine engine;

    // 危险代码模式
    private static final Pattern DANGEROUS_PATTERN = Pattern.compile(
            "(Java|Packages|java|javax|org|com|edu|net)\\s*\\.\\s*([a-zA-Z_$])|" +
                    "eval\\s*\\(|new\\s+Function\\s*\\(|java\\.lang\\.Runtime"
    );

    public NashornJsExecutor() {
        // 创建线程池
        this.executor = new ThreadPoolExecutor(
                2, 10, 60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100),
                new ThreadFactory() {
                    private int count = 0;
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread t = new Thread(r, "js-executor-" + (++count));
                        t.setDaemon(true);
                        return t;
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        // 创建 Nashorn 引擎，禁止访问所有 Java 类
        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
        String[] strings = {"--language=es6"};
        this.engine = factory.getScriptEngine(
                strings,
                null,
                new ClassFilter() {
                    @Override
                    public boolean exposeToScripts(String className) {
                        return false;
                    }
                }
        );
    }

    /**
     * 执行 JS 代码
     *
     * @param jsCode JS 代码，必须包含 handler 函数
     * @param params 传入的参数（Map 或 POJO）
     * @return 执行结果（自动反序列化后的原生类型/JSONObject）
     */
    public Object execute(String jsCode, Object params) {
        // 1. 代码安全检查
        validateCode(jsCode);

        // 2. 提交到线程池执行（支持超时中断）
        Future<Object> future = executor.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                return doExecute(jsCode, params);
            }
        });

        try {
            return future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            throw new RuntimeException("脚本执行超时（限制 " + TIMEOUT_SECONDS + " 秒）");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("执行被中断");
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            throw new RuntimeException(cause.getMessage(), cause);
        }
    }

    /**
     * 实际执行逻辑
     */
    private Object doExecute(String jsCode, Object params) throws ScriptException {
        // 将参数转为 JSON 字符串
        String jsonParams = toJson(params);

        // 构建安全的执行环境
        String wrappedScript = buildSafeScript(jsCode, jsonParams);

        // 执行脚本
        Object result = engine.eval(wrappedScript);

        if (result == null) {
            throw new RuntimeException("脚本返回 null");
        }

        // ========== 核心修复点 1 ==========
        // 将 JS 执行返回的 JSON 字符串反序列化为 Java 对象
        String resultJson = result.toString();

        // 检查结果大小
        if (resultJson.length() > MAX_RESULT_SIZE) {
            throw new RuntimeException("返回结果超过 " + (MAX_RESULT_SIZE/1024/1024) + "MB 限制");
        }

        // 反序列化 JSON 字符串为 Java 对象（JSONObject/原生类型）
        return fromJson(resultJson);
    }

    // 更简单的版本：不覆盖 eval，依赖其他安全措施
    private String buildSafeScript(String userCode, String jsonParams) {
        StringBuilder sb = new StringBuilder();

        sb.append("(function() {\n");
        sb.append("    \"use strict\";\n");

        // 只禁用 Java 访问
        sb.append("    var Java = undefined;\n");
        sb.append("    var Packages = undefined;\n");
        sb.append("    var java = undefined;\n");
        sb.append("    var javax = undefined;\n");
        sb.append("    var com = undefined;\n");
        sb.append("    var org = undefined;\n");
        sb.append("    var edu = undefined;\n");
        sb.append("    var net = undefined;\n");

        sb.append("    var console = {log: function(){}, error: function(){}};\n");
        sb.append("    \n");
        sb.append(userCode).append("\n");
        sb.append("    \n");
        sb.append("    var params = JSON.parse('").append(escapeJson(jsonParams)).append("');\n");
        sb.append("    var result = handler(params);\n");
        // ========== 核心修复点 2 ==========
        // JSON.stringify 是必要的：统一结果格式，确保能反序列化
        sb.append("    return JSON.stringify(result);\n");
        sb.append("})()");

        return sb.toString();
    }

    /**
     * 代码安全校验
     */
    private void validateCode(String code) {
        if (code == null || code.trim().isEmpty()) {
            throw new IllegalArgumentException("代码不能为空");
        }

        // 检查是否包含 handler 函数定义
        if (!code.contains("function handler")) {
            throw new IllegalArgumentException("代码必须包含 function handler(params) 定义");
        }

        // 黑名单检查
        if (DANGEROUS_PATTERN.matcher(code).find()) {
            throw new SecurityException("代码包含危险关键字，已被拦截");
        }
    }

    /**
     * JSON 字符串转义（防止注入）
     */
    private String escapeJson(String json) {
        StringBuilder sb = new StringBuilder();
        for (char c : json.toCharArray()) {
            switch (c) {
                case '\\':
                    sb.append("\\\\");
                    break;
                case '\'':
                    sb.append("\\'");
                    break;
                case '"':
                    sb.append("\\\"");
                    break;
                case '\n':
                    sb.append("\\n");
                    break;
                case '\r':
                    sb.append("\\r");
                    break;
                case '\t':
                    sb.append("\\t");
                    break;
                case '\b':
                    sb.append("\\b");
                    break;
                case '\f':
                    sb.append("\\f");
                    break;
                default:
                    if (c < 0x20) {
                        sb.append(String.format("\\u%04x", (int) c));
                    } else {
                        sb.append(c);
                    }
            }
        }
        return sb.toString();
    }

    /**
     * 对象转 JSON（使用 Fastjson）
     */
    private String toJson(Object obj) {
        try {
            // ========== 补充优化：使用 fastjson2 的 API 保持版本一致 ==========
            return com.alibaba.fastjson2.JSON.toJSONString(obj);
        } catch (Exception e) {
            throw new RuntimeException("参数序列化失败", e);
        }
    }

    /**
     * JSON 转 Java 对象（自动适配类型）
     */
    @SuppressWarnings("unchecked")
    private Object fromJson(String json) {
        try {
            // ========== 核心修复点 3 ==========
            // 使用 parse 而非 parseObject，自动识别类型（字符串/数字/布尔/JSONObject）
            return com.alibaba.fastjson2.JSON.parse(json);
        } catch (Exception e) {
            throw new RuntimeException("结果反序列化失败", e);
        }
    }

    @PreDestroy
    public void destroy() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}