content: /*
   *  Copyright (c) 2023-2025, Agents-Flex (fuhai999@gmail.com).
   *  <p>
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *  <p>
   *  http://www.apache.org/licenses/LICENSE-2.0
   *  <p>
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  package com.agentsflex.core.util;
  
  import com.agentsflex.core.chain.Chain;
  import com.agentsflex.core.util.graalvm.JsInteropUtils;
  import org.graalvm.polyglot.Context;
  import org.graalvm.polyglot.HostAccess;
  import org.graalvm.polyglot.Value;
  
  import java.util.HashMap;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  
  public class JsConditionUtil {
  
      // 使用 Context.Builder 构建上下文，线程安全
      private static final Context.Builder CONTEXT_BUILDER = Context.newBuilder("js")
          .option("engine.WarnInterpreterOnly", "false")
          .allowHostAccess(HostAccess.ALL)       // 允许访问 Java 对象的方法和字段
          .allowHostClassLookup(className -> false) // 禁止动态加载任意 Java 类
          .option("js.ecmascript-version", "2021");  // 使用较新的 ECMAScript 版本
  
      /**
       * 执行 JavaScript 表达式并返回 boolean 结果
       *
       * @param code    JS 表达式（应返回布尔或可转换为布尔的值）
       * @param chain   Chain 上下文对象
       * @param initMap 初始变量映射
       * @return true 表示满足条件，继续执行；false 表示跳过
       */
      public static boolean eval(String code, Chain chain, Map<String, Object> initMap) {
          try (Context context = CONTEXT_BUILDER.build()) {
              Map<String, Object> _result = new HashMap<>();
              Value bindings = context.getBindings("js");
  
              // 合并上下文变量
              Map<String, Object> contextVariables = collectContextVariables(chain, initMap);
              contextVariables.forEach((key, value) -> {
                  bindings.putMember(key, JsInteropUtils.wrapJavaValueForJS(context, value));
              });
  
              bindings.putMember("_result", _result);
              code = "_result.value = " + code;
  
              context.eval("js", code);
              Object value = _result.get("value");
              return toBoolean(value);
          } catch (Exception e) {
              throw new RuntimeException("JavaScript 执行失败: " + e.getMessage(), e);
          }
      }
  
      /**
       * 收集上下文中的变量
       */
      private static Map<String, Object> collectContextVariables(Chain chain, Map<String, Object> initMap) {
          Map<String, Object> variables = new ConcurrentHashMap<>();
  
          // 添加 Chain Memory 中的变量（去掉前缀）
          chain.getMemory().getAll().forEach((key, value) -> {
              int dotIndex = key.indexOf(".");
              String varName = (dotIndex >= 0) ? key.substring(dotIndex + 1) : key;
              variables.put(varName, value);
          });
  
          // 添加 _chain 和 initMap 变量
          variables.putAll(initMap);
  
          return variables;
      }
  
      /**
       * 将任意对象转换为布尔值
       */
      private static boolean toBoolean(Object value) {
          if (value == null) {
              return false;
          }
          if (value instanceof Boolean) {
              return (Boolean) value;
          }
          if (value instanceof Number) {
              return ((Number) value).intValue() != 0;
          }
          if (value instanceof String) {
              String str = ((String) value).trim().toLowerCase();
              return !str.isEmpty() && !"0".equals(str) && !"false".equals(str);
          }
          if (value instanceof Value) {
              Value v = (Value) value;
              if (v.isBoolean()) {
                  return v.asBoolean();
              } else if (v.isNumber()) {
                  return v.asDouble() != 0;
              } else if (v.isString()) {
                  String str = v.asString().trim().toLowerCase();
                  return !str.isEmpty() && !"0".equals(str) && !"false".equals(str);
              } else {
                  return !v.isNull();
              }
          }
          return true;
      }
  
  }
  