import { DateTime } from 'luxon';
import { isNil, isObjectFull, isFunction } from '../../../internal/type-checks.mjs';
import { escape } from '../../../utilities/regex.mjs';
import { filterRuleSet, isRule, isRuleSet } from '../helpers.mjs';

function isStringType(field) {
  return field.type === "string";
}
function whereClausesFromRuleSet(rule, config, options) {
  if (isRule(rule)) {
    if (!rule.enabled || !rule.field) {
      return void 0;
    }
    if (isNil(rule.field) || isNil(rule.operator)) {
      console.warn(`flowQueryConverter: Skipping rule with missing field or operator`, rule);
      return void 0;
    }
    const field = config.fields[rule.field];
    if (isNil(rule.value) && field.nullable !== true) {
      console.warn(`flowQueryConverter: Skipping nil field '${rule.field}' in rule (non-nullable)`, rule);
      return void 0;
    }
    let fieldName = rule.field;
    if (options?.renameFields && fieldName in options.renameFields) {
      fieldName = options.renameFields[fieldName];
    }
    let clause = ["is null", "not null"].includes(rule.operator) || !isNil(rule.value) ? { [fieldName]: predicateFromRule(rule, config) } : {};
    if ((rule.operator === "!=" || rule.operator === "not like") && isObjectFull(clause)) {
      clause = { $or: [{ [fieldName]: { $is: null } }, clause] };
    }
    return clause;
  } else if (isRuleSet(rule)) {
    const condition = rule.condition === "and" ? "$and" : "$or";
    const rules = rule.rules.map((r) => whereClausesFromRuleSet(r, config, options)).filter((r) => isObjectFull(r));
    return rules.length ? { [condition]: rules } : {};
  } else ;
}
function predicateFromRule(rule, config) {
  const fieldConfig = config.fields[rule.field];
  const field = config.fields[rule.field];
  let operator;
  let value = rule.value;
  if (isFunction(field.convertValue)) {
    value = field.convertValue(value);
  }
  const caseInsensitive = isStringType(fieldConfig) && fieldConfig.caseInsensitive;
  switch (rule.operator) {
    case "=":
      operator = caseInsensitive ? "$iEq" : "$eq";
      break;
    case "!=":
      operator = caseInsensitive ? "$iNe" : "$ne";
      break;
    case "<":
      operator = "$lt";
      break;
    case "<=":
      operator = "$lte";
      break;
    case ">":
      operator = "$gt";
      break;
    case ">=":
      operator = "$gte";
      break;
    case "in":
      operator = "$in";
      break;
    case "!in":
      operator = "$notIn";
      break;
    case "like":
      value = escape(String(value));
      value = value.replace(/\\\?/g, ".");
      value = value.replace(/\\\*/g, ".*");
      value = `^${value}$`;
      operator = caseInsensitive ? "$iRegexp" : "$regexp";
      break;
    case "!like":
      value = escape(String(value));
      value = value.replace(/\\\?/g, ".");
      value = value.replace(/\\\*/g, ".*");
      value = `^${value}$`;
      operator = caseInsensitive ? "$notIRegexp" : "$notRegexp";
      break;
    case "contains":
      value = escape(String(value));
      operator = caseInsensitive ? "$iRegexp" : "$regexp";
      break;
    case "!contains":
      value = escape(String(value));
      operator = caseInsensitive ? "$notIRegexp" : "$notRegexp";
      break;
    case "regex":
      operator = caseInsensitive ? "$iRegexp" : "$regexp";
      break;
    case "!regex":
      operator = caseInsensitive ? "$notIRegexp" : "$notRegexp";
      break;
    case "null":
      operator = "$is";
      value = null;
      break;
    case "!null":
      operator = "$not";
      value = null;
      break;
    case "between":
      operator = "$between";
      break;
    default:
      throw new Error(`Unrecognized query operator '${rule.operator}'`);
  }
  if (config.fields?.[rule.field]?.type === "datetime") {
    if (rule.operator === "between") {
      value = [DateTime.fromISO(value[0] || 0).toISO(), DateTime.fromISO(value[1] || 0).toISO()];
    } else {
      value = DateTime.fromISO(value || 0).toISO();
    }
  }
  return { [operator]: value };
}
const converter = (ruleset, config, options) => {
  options = {
    throwOnError: false,
    ...options
  };
  if (options.excludeFields || options.includeFields || options.renameFields) {
    ruleset = filterRuleSet(ruleset, options);
  }
  return whereClausesFromRuleSet(ruleset, config, options);
};
const converter$1 = converter;

export { converter$1 as default };
