/**
 * Evaluates Semgrep-style boolean expressions for rule requirements.
 * Example: "SOURCE_A and not SANITIZER_B"
 */
export class BooleanEvaluator {
  /**
   * Evaluates the expression against a set of active labels.
   * 
   * @param expression The boolean expression string (e.g., "(A or B) and not C").
   * @param activeLabels The set of labels discovered during taint analysis.
   */
  public static evaluate(expression: string | undefined, activeLabels: Set<string>): boolean {
    if (!expression) return true; // No requirement means it matches

    // Minimalistic parser for Semgrep requirements
    try {
      const tokens = this.tokenize(expression);
      return this.evaluateExpression(tokens, activeLabels);
    } catch (e) {
      console.error(`Failed to evaluate boolean expression: ${expression}`, e);
      return false;
    }
  }

  private static tokenize(expr: string): string[] {
    // Standardize spacing and handle parentheses
    return expr
      .replace(/\(/g, ' ( ')
      .replace(/\)/g, ' ) ')
      .split(/\s+/)
      .filter(t => t.length > 0);
  }

  /**
   * Simple recursive descent-ish evaluator for boolean logic.
   * Semgrep typically uses: and, or, not, and parentheses.
   */
  private static evaluateExpression(tokens: string[], activeLabels: Set<string>): boolean {
    let index = 0;

    const parsePrimary = (): boolean => {
      const token = tokens[index++]!;

      if (token === '(') {
        const result = parseExpression();
        index++; // Consume ')'
        return result;
      }

      if (token === 'not') {
        return !parsePrimary();
      }

      // It's a label
      return activeLabels.has(token);
    };

    const parseAnd = (): boolean => {
      let left = parsePrimary();
      while (index < tokens.length && tokens[index] === 'and') {
        index++;
        left = left && parsePrimary();
      }
      return left;
    };

    const parseExpression = (): boolean => {
      let left = parseAnd();
      while (index < tokens.length && tokens[index] === 'or') {
        index++;
        left = left || parseAnd();
      }
      return left;
    };

    return parseExpression();
  }
}
