// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT

import { Validator } from "./validator";

export class Evaluator<Context extends object> {
  context?: Context;
  validator: Validator;

  constructor({
    context,
    predicate = Validator.defaultPredicate,
  }: { context?: Context; predicate?: (identifier: string) => boolean } = {}) {
    let actualPredicate = predicate;
    if (context) {
      this.context = new Proxy(context, {
        has: () => true,
        get: (t, k) => (k === Symbol.unscopables ? undefined : t[k as keyof typeof t]),
      });
      actualPredicate = (identifier: string) =>
        predicate(identifier) || Object.getOwnPropertyNames(context).includes(identifier);
    }
    this.validator = new Validator(actualPredicate);
  }

  evaluate(expression: string): unknown {
    this.validator.validate(expression);

    const body = `with (sandbox) { return ${expression}; }`;
    const evaluate = new Function("sandbox", body);
    return evaluate(this.context ?? {});
  }
}

export const defaultEvaluator = new Evaluator();

export const mathEvaluator = new Evaluator({ context: Math });
