import { Func, Aggregate } from "../ir.js";
import { toExpression, isRefProxy } from "./ref-proxy.js";
function eq(left, right) {
  return new Func(`eq`, [toExpression(left), toExpression(right)]);
}
function gt(left, right) {
  return new Func(`gt`, [toExpression(left), toExpression(right)]);
}
function gte(left, right) {
  return new Func(`gte`, [toExpression(left), toExpression(right)]);
}
function lt(left, right) {
  return new Func(`lt`, [toExpression(left), toExpression(right)]);
}
function lte(left, right) {
  return new Func(`lte`, [toExpression(left), toExpression(right)]);
}
function and(left, right, ...rest) {
  const allArgs = [left, right, ...rest];
  return new Func(
    `and`,
    allArgs.map((arg) => toExpression(arg))
  );
}
function or(left, right, ...rest) {
  const allArgs = [left, right, ...rest];
  return new Func(
    `or`,
    allArgs.map((arg) => toExpression(arg))
  );
}
function not(value) {
  return new Func(`not`, [toExpression(value)]);
}
function isUndefined(value) {
  return new Func(`isUndefined`, [toExpression(value)]);
}
function isNull(value) {
  return new Func(`isNull`, [toExpression(value)]);
}
function inArray(value, array) {
  return new Func(`in`, [toExpression(value), toExpression(array)]);
}
function like(left, right) {
  return new Func(`like`, [toExpression(left), toExpression(right)]);
}
function ilike(left, right) {
  return new Func(`ilike`, [toExpression(left), toExpression(right)]);
}
function upper(arg) {
  return new Func(`upper`, [toExpression(arg)]);
}
function lower(arg) {
  return new Func(`lower`, [toExpression(arg)]);
}
function length(arg) {
  return new Func(`length`, [toExpression(arg)]);
}
function concat(...args) {
  const toArrayArg = args.find(
    (arg) => arg instanceof ToArrayWrapper
  );
  if (toArrayArg) {
    if (args.length !== 1) {
      throw new Error(
        `concat(toArray(...)) currently supports only a single toArray(...) argument`
      );
    }
    return new ConcatToArrayWrapper(toArrayArg.query);
  }
  return new Func(
    `concat`,
    args.map((arg) => toExpression(arg))
  );
}
function coalesce(...args) {
  return new Func(
    `coalesce`,
    args.map((arg) => toExpression(arg))
  );
}
function caseWhen(...args) {
  if (args.length < 2) {
    throw new Error(`caseWhen() requires at least two arguments`);
  }
  const pairCount = Math.floor(args.length / 2);
  for (let i = 0; i < pairCount; i++) {
    const condition = args[i * 2];
    if (!isConditionValue(condition)) {
      throw new Error(`caseWhen() conditions must be expression-like values`);
    }
  }
  if (caseWhenHasOnlyExpressionValues(args)) {
    return new Func(
      `caseWhen`,
      args.map((arg) => toExpression(arg))
    );
  }
  return new CaseWhenWrapper(args);
}
function add(left, right) {
  return new Func(`add`, [
    toExpression(left),
    toExpression(right)
  ]);
}
function subtract(left, right) {
  return new Func(`subtract`, [
    toExpression(left),
    toExpression(right)
  ]);
}
function multiply(left, right) {
  return new Func(`multiply`, [
    toExpression(left),
    toExpression(right)
  ]);
}
function divide(left, right) {
  return new Func(`divide`, [
    toExpression(left),
    toExpression(right)
  ]);
}
function count(arg) {
  return new Aggregate(`count`, [toExpression(arg)]);
}
function avg(arg) {
  return new Aggregate(`avg`, [toExpression(arg)]);
}
function sum(arg) {
  return new Aggregate(`sum`, [toExpression(arg)]);
}
function min(arg) {
  return new Aggregate(`min`, [toExpression(arg)]);
}
function max(arg) {
  return new Aggregate(`max`, [toExpression(arg)]);
}
const operators = [
  // Comparison operators
  `eq`,
  `gt`,
  `gte`,
  `lt`,
  `lte`,
  `in`,
  `like`,
  `ilike`,
  // Logical operators
  `and`,
  `or`,
  `not`,
  // Null checking
  `isNull`,
  `isUndefined`,
  // String functions
  `upper`,
  `lower`,
  `length`,
  `concat`,
  // Numeric functions
  `add`,
  `subtract`,
  `multiply`,
  `divide`,
  // Utility functions
  `coalesce`,
  `caseWhen`,
  // Aggregate functions
  `count`,
  `avg`,
  `sum`,
  `min`,
  `max`
];
class ToArrayWrapper {
  constructor(query) {
    this.query = query;
    this.__brand = `ToArrayWrapper`;
  }
}
class ConcatToArrayWrapper {
  constructor(query) {
    this.query = query;
    this.__brand = `ConcatToArrayWrapper`;
  }
}
class CaseWhenWrapper {
  constructor(args) {
    this.args = args;
    this.__brand = `CaseWhenWrapper`;
  }
}
class MaterializeWrapper {
  constructor(query) {
    this.query = query;
    this.__brand = `MaterializeWrapper`;
  }
}
function toArray(query) {
  return new ToArrayWrapper(query);
}
function caseWhenHasOnlyExpressionValues(args) {
  const valueIndexes = getCaseWhenValueIndexes(args.length);
  return valueIndexes.every((index) => isExpressionValue(args[index]));
}
function getCaseWhenValueIndexes(argCount) {
  const valueIndexes = [];
  const hasDefaultValue = argCount % 2 === 1;
  const pairCount = Math.floor(argCount / 2);
  for (let i = 0; i < pairCount; i++) {
    valueIndexes.push(i * 2 + 1);
  }
  if (hasDefaultValue) {
    valueIndexes.push(argCount - 1);
  }
  return valueIndexes;
}
function isExpressionValue(value) {
  if (isRefProxy(value)) return true;
  if (value instanceof Aggregate || value instanceof Func) return true;
  if (value == null) return true;
  if (typeof value === `string` || typeof value === `number` || typeof value === `boolean` || typeof value === `bigint`) {
    return true;
  }
  if (value instanceof Date || Array.isArray(value)) return true;
  if (typeof value === `object`) {
    const candidate = value;
    if ((candidate.type === `agg` || candidate.type === `func`) && typeof candidate.name === `string` && Array.isArray(candidate.args)) {
      return true;
    }
    if (candidate.type === `ref` && Array.isArray(candidate.path)) return true;
    if (candidate.type === `val` && `value` in candidate) return true;
  }
  return false;
}
function isConditionValue(value) {
  return isExpressionValue(value) && !Array.isArray(value);
}
function materialize(query) {
  return new MaterializeWrapper(query);
}
export {
  CaseWhenWrapper,
  ConcatToArrayWrapper,
  MaterializeWrapper,
  ToArrayWrapper,
  add,
  and,
  avg,
  caseWhen,
  coalesce,
  concat,
  count,
  divide,
  eq,
  gt,
  gte,
  ilike,
  inArray,
  isNull,
  isUndefined,
  length,
  like,
  lower,
  lt,
  lte,
  materialize,
  max,
  min,
  multiply,
  not,
  operators,
  or,
  subtract,
  sum,
  toArray,
  upper
};
//# sourceMappingURL=functions.js.map
