/**
 * visible_when 表达式解析与求值
 *
 * 支持语法：
 *   field == 'value'                 等值判断
 *   field != 'value'                 不等判断
 *   expr1 AND expr2                  逻辑与
 *   expr1 OR expr2                   逻辑或
 *   NOT expr                         逻辑非
 *   (expr)                           括号分组
 */

interface ExprNode {
  type: 'eq' | 'neq' | 'and' | 'or' | 'not';
  field?: string;
  value?: string;
  left?: ExprNode;
  right?: ExprNode;
  operand?: ExprNode;
}

function tokenize(input: string): string[] {
  const tokens: string[] = [];
  let i = 0;
  while (i < input.length) {
    if (input[i] === ' ') {
      i++;
      continue;
    }
    if (input[i] === '(' || input[i] === ')') {
      tokens.push(input[i]);
      i++;
      continue;
    }
    if (input[i] === "'") {
      let j = i + 1;
      while (j < input.length && input[j] !== "'") j++;
      tokens.push(input.substring(i, j + 1));
      i = j + 1;
      continue;
    }
    if (input[i] === '=' && input[i + 1] === '=') {
      tokens.push('==');
      i += 2;
      continue;
    }
    if (input[i] === '!' && input[i + 1] === '=') {
      tokens.push('!=');
      i += 2;
      continue;
    }
    if (input[i] === '&' && input[i + 1] === '&') {
      tokens.push('&&');
      i += 2;
      continue;
    }
    if (input[i] === '|' && input[i + 1] === '|') {
      tokens.push('||');
      i += 2;
      continue;
    }
    let j = i;
    while (
      j < input.length &&
      !' ()\''.includes(input[j]) &&
      !(input[j] === '=' && input[j + 1] === '=') &&
      !(input[j] === '!' && input[j + 1] === '=')
    ) {
      j++;
    }
    tokens.push(input.substring(i, j));
    i = j;
  }
  return tokens;
}

function parseAtom(tokens: string[]): ExprNode | null {
  const token = tokens.shift();
  if (!token) return null;
  if (token === '(') {
    const expr = parseOr(tokens);
    if (tokens[0] === ')') tokens.shift();
    return expr;
  }
  if (token === 'NOT') {
    const operand = parseAtom(tokens);
    return { type: 'not', operand: operand || undefined };
  }
  const field = token;
  const op = tokens.shift();
  if (op !== '==' && op !== '!=') return null;
  const rawValue = tokens.shift() || '';
  const value = rawValue.replace(/^'(.*)'$/, '$1');
  return { type: op === '!=' ? 'neq' : 'eq', field, value };
}

function parseAnd(tokens: string[]): ExprNode | null {
  let left = parseAtom(tokens);
  while (tokens[0] === 'AND' || tokens[0] === '&&') {
    tokens.shift();
    const right = parseAtom(tokens);
    if (left && right) {
      left = { type: 'and', left, right };
    }
  }
  return left;
}

function parseOr(tokens: string[]): ExprNode | null {
  let left = parseAnd(tokens);
  while (tokens[0] === 'OR' || tokens[0] === '||') {
    tokens.shift();
    const right = parseAnd(tokens);
    if (left && right) {
      left = { type: 'or', left, right };
    }
  }
  return left;
}

export function parseExpr(input: string): ExprNode | null {
  const tokens = tokenize(input);
  return parseOr(tokens);
}

export function evaluateExpr(node: ExprNode, values: Record<string, unknown>): boolean {
  switch (node.type) {
    case 'eq':
      return String(values[node.field!] ?? '') === node.value;
    case 'neq':
      return String(values[node.field!] ?? '') !== node.value;
    case 'and':
      return evaluateExpr(node.left!, values) && evaluateExpr(node.right!, values);
    case 'or':
      return evaluateExpr(node.left!, values) || evaluateExpr(node.right!, values);
    case 'not':
      return !evaluateExpr(node.operand!, values);
    default:
      return false;
  }
}

export function evaluateVisibleWhen(
  expr: string | undefined,
  values: Record<string, unknown>,
): boolean {
  if (!expr) return true;
  const ast = parseExpr(expr);
  return ast ? evaluateExpr(ast, values) : true;
}
