import { testLinearRegex } from "@open-mercato/shared/lib/regex/linear";
import { getNestedValue, resolveSpecialValue } from "./value-resolver.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("business_rules").child({ component: "expression-evaluator" });
function evaluateExpression(expression, data, context) {
  if (isGroupCondition(expression)) {
    return evaluateGroupCondition(expression, data, context);
  }
  return evaluateSimpleCondition(expression, data, context);
}
function isGroupCondition(expression) {
  return "rules" in expression && Array.isArray(expression.rules);
}
function evaluateSimpleCondition(condition, data, context) {
  const leftValue = resolveFieldPath(condition.field, data);
  let rightValue;
  if (condition.valueField) {
    rightValue = resolveFieldPath(condition.valueField, data);
  } else {
    rightValue = resolveStaticOrSpecialValue(condition.value, context);
  }
  const result = applyOperator(leftValue, condition.operator, rightValue);
  logger.debug("Simple condition evaluated", {
    field: condition.field,
    operator: condition.operator,
    expectedValue: rightValue,
    actualValue: leftValue,
    actualValueType: typeof leftValue,
    passed: result
  });
  return result;
}
function evaluateGroupCondition(condition, data, context) {
  const { operator, rules } = condition;
  if (!rules || rules.length === 0) {
    return true;
  }
  logger.debug("Group condition evaluating", { operator, ruleCount: rules.length });
  let result;
  switch (operator) {
    case "AND":
      result = rules.every((rule) => evaluateExpression(rule, data, context));
      break;
    case "OR":
      result = rules.some((rule) => evaluateExpression(rule, data, context));
      break;
    case "NOT":
      if (rules.length === 1) {
        result = !evaluateExpression(rules[0], data, context);
      } else {
        result = !rules.every((rule) => evaluateExpression(rule, data, context));
      }
      break;
    default:
      throw new Error(`Unknown logical operator: ${operator}`);
  }
  logger.debug("Group condition evaluated", { operator, passed: result });
  return result;
}
function resolveFieldPath(path, data) {
  return getNestedValue(data, path);
}
function resolveStaticOrSpecialValue(value, context) {
  if (typeof value !== "string") {
    return value;
  }
  if (value.startsWith("{{") && value.endsWith("}}")) {
    return resolveSpecialValue(value, context);
  }
  return value;
}
function applyOperator(left, operator, right) {
  switch (operator) {
    case "=":
    case "==":
      return equals(left, right);
    case "!=":
      return !equals(left, right);
    case ">":
      return compare(left, right) > 0;
    case ">=":
      return compare(left, right) >= 0;
    case "<":
      return compare(left, right) < 0;
    case "<=":
      return compare(left, right) <= 0;
    case "IN":
      return isIn(left, right);
    case "NOT_IN":
      return !isIn(left, right);
    case "CONTAINS":
      return contains(left, right);
    case "NOT_CONTAINS":
      return !contains(left, right);
    case "STARTS_WITH":
      return startsWith(left, right);
    case "ENDS_WITH":
      return endsWith(left, right);
    case "MATCHES":
      return matches(left, right);
    case "IS_EMPTY":
      return isEmpty(left);
    case "IS_NOT_EMPTY":
      return !isEmpty(left);
    default:
      throw new Error(`Unknown operator: ${operator}`);
  }
}
function equals(left, right) {
  if (left == null && right == null) return true;
  if (left == null || right == null) return false;
  if (typeof left === "number" || typeof right === "number") {
    return Number(left) === Number(right);
  }
  return left === right;
}
function compare(left, right) {
  if (left == null && right == null) return 0;
  if (left == null) return -1;
  if (right == null) return 1;
  if (left instanceof Date && right instanceof Date) {
    return left.getTime() - right.getTime();
  }
  if (isDateString(left) && isDateString(right)) {
    return new Date(left).getTime() - new Date(right).getTime();
  }
  const leftNum = Number(left);
  const rightNum = Number(right);
  if (!isNaN(leftNum) && !isNaN(rightNum)) {
    return leftNum - rightNum;
  }
  const leftStr = String(left);
  const rightStr = String(right);
  return leftStr.localeCompare(rightStr);
}
function isIn(value, array) {
  if (!Array.isArray(array)) {
    return false;
  }
  return array.some((item) => equals(value, item));
}
function contains(container, value) {
  if (container == null) return false;
  if (Array.isArray(container)) {
    return container.some((item) => equals(item, value));
  }
  if (typeof container === "string") {
    return container.includes(String(value));
  }
  return false;
}
function startsWith(str, prefix) {
  if (str == null || prefix == null) return false;
  return String(str).startsWith(String(prefix));
}
function endsWith(str, suffix) {
  if (str == null || suffix == null) return false;
  return String(str).endsWith(String(suffix));
}
const MAX_REGEX_LENGTH = 200;
const MAX_REGEX_INPUT_LENGTH = 1e4;
function matches(str, pattern) {
  if (str == null || pattern == null) return false;
  try {
    const patternStr = String(pattern);
    if (patternStr.length > MAX_REGEX_LENGTH) {
      return false;
    }
    if (/(\(.*[+*]\).*[+*])/.test(patternStr)) {
      return false;
    }
    const testStr = String(str);
    if (testStr.length > MAX_REGEX_INPUT_LENGTH) {
      return false;
    }
    const result = testLinearRegex(patternStr, testStr, {
      maxPatternLength: MAX_REGEX_LENGTH,
      maxInputLength: MAX_REGEX_INPUT_LENGTH
    });
    return result.ok ? result.matched : false;
  } catch (error) {
    const message = error instanceof Error ? error.message : "Unknown error";
    if (process.env.NODE_ENV !== "test") {
      logger.error("Regex matching failed", { message });
    }
    return false;
  }
}
function isEmpty(value) {
  if (value == null) return true;
  if (typeof value === "string") return value.trim() === "";
  if (Array.isArray(value)) return value.length === 0;
  if (typeof value === "object") return Object.keys(value).length === 0;
  return false;
}
function isDateString(value) {
  if (typeof value !== "string") return false;
  const isoDateRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?/;
  if (isoDateRegex.test(value)) {
    const date = new Date(value);
    return !isNaN(date.getTime());
  }
  return false;
}
export {
  evaluateExpression
};
//# sourceMappingURL=expression-evaluator.js.map
