import { unreachable, assert } from "../../../shared/src/asserts.js";
import { simplifyCondition } from "../query/expression.js";
import { getLikePredicate } from "./like.js";
function createPredicate(condition) {
  if (condition.type !== "simple") {
    const predicates = condition.conditions.map((c) => createPredicate(c));
    return condition.type === "and" ? (row) => {
      for (const predicate of predicates) {
        if (!predicate(row)) {
          return false;
        }
      }
      return true;
    } : (row) => {
      for (const predicate of predicates) {
        if (predicate(row)) {
          return true;
        }
      }
      return false;
    };
  }
  const { left } = condition;
  const { right } = condition;
  assert(
    right.type !== "static",
    "static values should be resolved before creating predicates"
  );
  assert(
    left.type !== "static",
    "static values should be resolved before creating predicates"
  );
  switch (condition.op) {
    case "IS":
    case "IS NOT": {
      const impl2 = createIsPredicate(right.value, condition.op);
      if (left.type === "literal") {
        const result = impl2(left.value);
        return () => result;
      }
      return (row) => impl2(row[left.name]);
    }
  }
  if (right.value === null || right.value === void 0) {
    return (_row) => false;
  }
  const impl = createPredicateImpl(right.value, condition.op);
  if (left.type === "literal") {
    if (left.value === null || left.value === void 0) {
      return (_row) => false;
    }
    const result = impl(left.value);
    return () => result;
  }
  return (row) => {
    const lhs = row[left.name];
    if (lhs === null || lhs === void 0) {
      return false;
    }
    return impl(lhs);
  };
}
function createIsPredicate(rhs, operator) {
  switch (operator) {
    case "IS":
      return (lhs) => lhs === rhs;
    case "IS NOT":
      return (lhs) => lhs !== rhs;
  }
}
function createPredicateImpl(rhs, operator) {
  switch (operator) {
    case "=":
      return (lhs) => lhs === rhs;
    case "!=":
      return (lhs) => lhs !== rhs;
    case "<":
      return (lhs) => lhs < rhs;
    case "<=":
      return (lhs) => lhs <= rhs;
    case ">":
      return (lhs) => lhs > rhs;
    case ">=":
      return (lhs) => lhs >= rhs;
    case "LIKE":
      return getLikePredicate(rhs, "");
    case "NOT LIKE":
      return not(getLikePredicate(rhs, ""));
    case "ILIKE":
      return getLikePredicate(rhs, "i");
    case "NOT ILIKE":
      return not(getLikePredicate(rhs, "i"));
    case "IN": {
      assert(Array.isArray(rhs));
      const set = new Set(rhs);
      return (lhs) => set.has(lhs);
    }
    case "NOT IN": {
      assert(Array.isArray(rhs));
      const set = new Set(rhs);
      return (lhs) => !set.has(lhs);
    }
    default:
      throw new Error(`Unexpected operator: ${operator}`);
  }
}
function not(f) {
  return (lhs) => !f(lhs);
}
function transformFilters(filters) {
  if (!filters) {
    return { filters: void 0, conditionsRemoved: false };
  }
  switch (filters.type) {
    case "simple":
      return { filters, conditionsRemoved: false };
    case "correlatedSubquery":
      return { filters: void 0, conditionsRemoved: true };
    case "and":
    case "or": {
      const transformedConditions = [];
      let conditionsRemoved = false;
      for (const cond of filters.conditions) {
        const transformed = transformFilters(cond);
        if (transformed.filters === void 0 && filters.type === "or") {
          return { filters: void 0, conditionsRemoved: true };
        }
        conditionsRemoved = conditionsRemoved || transformed.conditionsRemoved;
        if (transformed.filters) {
          transformedConditions.push(transformed.filters);
        }
      }
      return {
        filters: simplifyCondition({
          type: filters.type,
          conditions: transformedConditions
        }),
        conditionsRemoved
      };
    }
    default:
      unreachable();
  }
}
export {
  createPredicate,
  transformFilters
};
//# sourceMappingURL=filter.js.map
