"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const INCLUDES_SCALAR_FIELD = `__includes_scalar__`;
class BaseExpression {
}
class CollectionRef extends BaseExpression {
  constructor(collection, alias) {
    super();
    this.collection = collection;
    this.alias = alias;
    this.type = `collectionRef`;
  }
}
class QueryRef extends BaseExpression {
  constructor(query, alias) {
    super();
    this.query = query;
    this.alias = alias;
    this.type = `queryRef`;
  }
}
class UnionFrom extends BaseExpression {
  constructor(sources) {
    super();
    this.sources = sources;
    this.type = `unionFrom`;
  }
  get alias() {
    return this.sources[0]?.alias ?? ``;
  }
}
class UnionAll extends BaseExpression {
  /**
   * Result-level UNION ALL. Downstream query clauses see the union result row
   * shape, not the branch source aliases. Optimizers may push safe operations
   * into branches, but compiler phases should treat this as a derived relation
   * unless they are explicitly handling branch lowering.
   */
  constructor(queries) {
    super();
    this.queries = queries;
    this.type = `unionAll`;
  }
  get alias() {
    return ``;
  }
}
class PropRef extends BaseExpression {
  constructor(path) {
    super();
    this.path = path;
    this.type = `ref`;
  }
}
class Value extends BaseExpression {
  constructor(value) {
    super();
    this.value = value;
    this.type = `val`;
  }
}
class Func extends BaseExpression {
  constructor(name, args) {
    super();
    this.name = name;
    this.args = args;
    this.type = `func`;
  }
}
class Aggregate extends BaseExpression {
  constructor(name, args) {
    super();
    this.name = name;
    this.args = args;
    this.type = `agg`;
  }
}
class IncludesSubquery extends BaseExpression {
  constructor(query, correlationField, childCorrelationField, fieldName, parentFilters, parentProjection, materialization = `collection`, scalarField) {
    super();
    this.query = query;
    this.correlationField = correlationField;
    this.childCorrelationField = childCorrelationField;
    this.fieldName = fieldName;
    this.parentFilters = parentFilters;
    this.parentProjection = parentProjection;
    this.materialization = materialization;
    this.scalarField = scalarField;
    this.type = `includesSubquery`;
  }
}
class ConditionalSelect extends BaseExpression {
  constructor(branches, defaultValue) {
    super();
    this.branches = branches;
    this.defaultValue = defaultValue;
    this.type = `conditionalSelect`;
  }
}
function isExpressionLike(value) {
  if (value instanceof Aggregate || value instanceof ConditionalSelect || value instanceof Func || value instanceof PropRef || value instanceof Value || value instanceof IncludesSubquery) {
    return true;
  }
  if (!value || typeof value !== `object`) {
    return false;
  }
  if (value.type === `conditionalSelect`) {
    return Array.isArray(value.branches);
  }
  if (value.type === `agg` || value.type === `func`) {
    return typeof value.name === `string` && Array.isArray(value.args);
  }
  if (value.type === `ref`) {
    return Array.isArray(value.path);
  }
  if (value.type === `val`) {
    return `value` in value;
  }
  if (value.type === `includesSubquery`) {
    return `query` in value && `fieldName` in value;
  }
  return false;
}
function getWhereExpression(where) {
  return typeof where === `object` && `expression` in where ? where.expression : where;
}
function getHavingExpression(having) {
  return typeof having === `object` && `expression` in having ? having.expression : having;
}
function isResidualWhere(where) {
  return typeof where === `object` && `expression` in where && where.residual === true;
}
function createResidualWhere(expression) {
  return { expression, residual: true };
}
function getRefFromAlias(query, alias) {
  if (query.from.type === `unionFrom`) {
    for (const source of query.from.sources) {
      if (source.alias === alias) {
        return source;
      }
    }
  } else if (query.from.type !== `unionAll` && query.from.alias === alias) {
    return query.from;
  }
  for (const join of query.join || []) {
    if (join.from.alias === alias) {
      return join.from;
    }
  }
}
function followRef(query, ref, collection) {
  if (ref.path.length === 0) {
    return;
  }
  if (ref.path.length === 1) {
    const field = ref.path[0];
    if (query.select) {
      const selectedField = query.select[field];
      if (selectedField && selectedField.type === `ref`) {
        return followRef(query, selectedField, collection);
      }
    }
    return { collection, path: [field] };
  }
  if (ref.path.length > 1) {
    const [alias, ...rest] = ref.path;
    const aliasRef = getRefFromAlias(query, alias);
    if (!aliasRef) {
      return;
    }
    if (aliasRef.type === `queryRef`) {
      return followRef(aliasRef.query, new PropRef(rest), collection);
    } else {
      return { collection: aliasRef.collection, path: rest, alias };
    }
  }
}
exports.Aggregate = Aggregate;
exports.CollectionRef = CollectionRef;
exports.ConditionalSelect = ConditionalSelect;
exports.Func = Func;
exports.INCLUDES_SCALAR_FIELD = INCLUDES_SCALAR_FIELD;
exports.IncludesSubquery = IncludesSubquery;
exports.PropRef = PropRef;
exports.QueryRef = QueryRef;
exports.UnionAll = UnionAll;
exports.UnionFrom = UnionFrom;
exports.Value = Value;
exports.createResidualWhere = createResidualWhere;
exports.followRef = followRef;
exports.getHavingExpression = getHavingExpression;
exports.getWhereExpression = getWhereExpression;
exports.isExpressionLike = isExpressionLike;
exports.isResidualWhere = isResidualWhere;
//# sourceMappingURL=ir.cjs.map
