import { ensureArray } from '../../../internal/ensure-array.mjs';
import { isNil, isStringFull, 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 mapLayerFilterFromRuleSet(rule, config, options) {
  if (isRule(rule)) {
    if (!rule.enabled || !rule.field) {
      return void 0;
    }
    if (isNil(rule.field) || isNil(rule.operator)) {
      console.warn(`mapLayerFilterConverter: 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(`mapLayerFilterConverter: 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];
    }
    return predicateFromRule(rule, config, options);
  } else if (isRuleSet(rule)) {
    const condition = rule.condition === "and" ? " && " : " || ";
    const rules = rule.rules.map((r) => mapLayerFilterFromRuleSet(r, config, options)).filter((r) => isStringFull(r));
    return rules.length ? rules.map((r) => `(${r})`).join(condition) : "true";
  } else ;
}
function predicateFromRule(rule, config, options) {
  const fieldConfig = config.fields[rule.field];
  let fieldName = rule.field;
  if (options.renameFields && fieldName in options.renameFields) {
    fieldName = options.renameFields[fieldName];
  }
  let predicate;
  let field = `get("${fieldName}")`;
  let operator = "";
  let value = rule.value;
  let negate = false;
  if (isFunction(fieldConfig.convertValue)) {
    value = fieldConfig.convertValue(value);
  }
  const caseInsensitive = isStringType(fieldConfig) && fieldConfig.caseInsensitive;
  switch (rule.operator) {
    case "=":
      field = caseInsensitive ? `downcase(${field})` : field;
      operator = "==";
      break;
    case "!=":
      field = caseInsensitive ? `downcase(${field})` : field;
      operator = "!=";
      break;
    case "<":
      operator = "<";
      break;
    case "<=":
      operator = "<=";
      break;
    case ">":
      operator = ">";
      break;
    case ">=":
      operator = ">=";
      break;
    case "in":
      value = ensureArray(value);
      predicate = `in(${field}, literal([${value.map(JSON.stringify).join(", ")}]))`;
      break;
    case "!in":
      value = ensureArray(value);
      predicate = `!in(${field}, literal([${value.map(JSON.stringify).join(", ")}]))`;
      break;
    case "like":
      value = escape(String(value));
      value = value.replace(/\\\?/g, ".");
      value = value.replace(/\\\*/g, ".*");
      value = `^${value}$`;
      operator = caseInsensitive ? "~*" : "~";
      break;
    case "!like":
      value = escape(String(value));
      value = value.replace(/\\\?/g, ".");
      value = value.replace(/\\\*/g, ".*");
      value = `^${value}$`;
      operator = caseInsensitive ? "~*" : "~";
      negate = true;
      break;
    case "contains":
      field = caseInsensitive ? `downcase(${field})` : field;
      value = caseInsensitive ? String(value).toLowerCase() : value;
      predicate = `in(${JSON.stringify(value)}, ${field})`;
      break;
    case "!contains":
      field = caseInsensitive ? `downcase(${field})` : field;
      value = caseInsensitive ? String(value).toLowerCase() : value;
      predicate = `in(${JSON.stringify(value)}, ${field})`;
      negate = true;
      break;
    case "regex":
      operator = caseInsensitive ? "~*" : "~";
      break;
    case "!regex":
      operator = caseInsensitive ? "~*" : "~";
      negate = true;
      break;
    default:
      throw new Error(`Unrecognized query operator '${rule.operator}'`);
  }
  predicate ?? (predicate = `${field} ${operator} ${JSON.stringify(value)}`);
  if (negate) {
    predicate = `!(${predicate})`;
  }
  return predicate;
}
const converter = (ruleset, config, options) => {
  options = {
    throwOnError: false,
    ...options
  };
  if (options.excludeFields || options.includeFields || options.renameFields) {
    ruleset = filterRuleSet(ruleset, options);
  }
  return mapLayerFilterFromRuleSet(ruleset, config, options);
};
const converter$1 = converter;

export { converter$1 as default };
