package cn.fateverse.common.code.engine;

import cn.fateverse.common.code.console.MultiThreadedCapture;
import cn.fateverse.common.code.lock.SegmentLock;
import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.common.core.exception.CustomException;
import com.alibaba.fastjson2.JSON;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.springframework.security.core.parameters.P;

import javax.script.*;
import java.util.HashMap;
import java.util.Map;

/**
 * js 工具类
 *
 * @author Clay
 * @date 2023-03-24
 */
public class JavaScriptEngine {


    // 创建 GraalVM 上下文
    private static final Context context = Context.newBuilder()
            .allowAllAccess(true)
//            .allowHostClassLoading(true)
//            .allowIO(true)
//            .allowNativeAccess(true)
            .build();


    private static final Map<String, Value> functionMap = new HashMap<>();


    /**
     * 执行js代码
     *
     * @param script       js脚本
     * @param functionName js函数名
     * @param args         参数
     * @return 返回结构
     */
    public static EngineResult execute(String script, String functionName, boolean development, Object args) {
        if (development) {
            return MultiThreadedCapture.capture(() -> {
                Context context = Context.newBuilder()
                        .allowAllAccess(true)
                        .allowHostClassLoading(true)
                        .allowIO(true)
                        .allowNativeAccess(true).build();
                context.eval("js", script);
                Value executeFunction = context.getBindings("js").getMember(functionName);
                Value javaObjectAsValue = Value.asValue(args);
                Value result = executeFunction.execute(javaObjectAsValue);
                return result.as(Object.class);
            });
        } else {
            Value executeFunction = getFunction(functionName, script);
            Value result = executeFunction.execute(JSON.toJSONString(args));
            return new EngineResult(result.as(Object.class));
        }
    }


    private static Value getFunction(String functionName, String script) {
        return SegmentLock.lock(functionName, () -> {
            if (functionMap.containsKey(functionName)) {
                return functionMap.get(functionName);
            }
            context.eval("js", script);
            Value executeFunction = context.getBindings("js").getMember(functionName);
            functionMap.put(functionName, executeFunction);
            return executeFunction;
        });
    }


}
