import { followRef, PropRef } from "../ir.js";
function getLazyLoadTargets(rawQuery, lazyFrom, lazyAlias, lazySourceExpr, lazySource, aliasRemapping) {
  if (lazyFrom.type === `unionFrom`) {
    return getTargetsFromExpression(rawQuery, lazySourceExpr);
  }
  if (lazyFrom.type === `queryRef` && containsUnionFrom(lazyFrom.query.from)) {
    const targets = getTargetsFromQueryRef(
      lazyFrom.query,
      lazyAlias,
      lazySourceExpr
    );
    return dedupeLazyLoadTargets(targets);
  }
  if (!lazySource) {
    return [];
  }
  const lazySourceRef = toPropRef(lazySourceExpr);
  if (!lazySourceRef) {
    return [];
  }
  const followRefResult = followRef(rawQuery, lazySourceRef, lazySource);
  if (!followRefResult) {
    return [];
  }
  return [
    {
      alias: followRefResult.alias || aliasRemapping[lazyAlias] || lazyAlias,
      collection: followRefResult.collection,
      path: followRefResult.path
    }
  ];
}
function containsUnionFrom(from) {
  if (from.type === `unionFrom`) {
    return true;
  }
  if (from.type === `queryRef`) {
    return containsUnionFrom(from.query.from);
  }
  if (from.type === `unionAll`) {
    return from.queries.some((query) => containsUnionFrom(query.from));
  }
  return false;
}
function getTargetsFromQueryRef(query, outerAlias, expr) {
  if (!expr || typeof expr !== `object` || !(`type` in expr)) {
    return [];
  }
  const expression = expr;
  if (expression.type === `func` && expression.name === `coalesce`) {
    return dedupeLazyLoadTargets(
      expression.args.flatMap(
        (arg) => getTargetsFromQueryRef(query, outerAlias, arg)
      )
    );
  }
  const ref = toPropRef(expression);
  if (!ref || ref.path[0] !== outerAlias) {
    return [];
  }
  return getTargetsFromPropRef(query, new PropRef(ref.path.slice(1)));
}
function getTargetsFromExpression(query, expr) {
  if (!expr || typeof expr !== `object` || !(`type` in expr)) {
    return [];
  }
  const expression = expr;
  if (expression.type === `ref`) {
    return getTargetsFromPropRef(query, expression);
  }
  if (expression.type === `func` && expression.name === `coalesce`) {
    return dedupeLazyLoadTargets(
      expression.args.flatMap((arg) => getTargetsFromExpression(query, arg))
    );
  }
  return [];
}
function getTargetsFromPropRef(query, ref) {
  if (ref.path.length === 0) {
    return [];
  }
  if (ref.path.length === 1) {
    const field = ref.path[0];
    const selectedField = query.select?.[field];
    if (selectedField) {
      return getTargetsFromExpression(query, selectedField);
    }
    return [];
  }
  const [alias, ...path] = ref.path;
  const source = getSourceFromAlias(query, alias);
  if (!source) {
    return [];
  }
  if (source.type === `collectionRef`) {
    return [{ alias: source.alias, collection: source.collection, path }];
  }
  if (source.query.limit || source.query.offset) {
    return [];
  }
  return getTargetsFromQueryRef(source.query, source.alias, ref);
}
function getSourceFromAlias(query, alias) {
  if (query.join) {
    for (const join of query.join) {
      if (join.from.alias === alias) {
        return join.from;
      }
    }
  }
  const from = query.from;
  const sources = from.type === `unionFrom` ? from.sources : from.type === `unionAll` ? [] : [from];
  return sources.find((source) => source.alias === alias);
}
function dedupeLazyLoadTargets(targets) {
  const seen = /* @__PURE__ */ new Set();
  const deduped = [];
  for (const target of targets) {
    const key = `${target.alias}:${target.path.join(`.`)}`;
    if (!seen.has(key)) {
      seen.add(key);
      deduped.push(target);
    }
  }
  return deduped;
}
function toPropRef(expr) {
  if (expr instanceof PropRef) {
    return expr;
  }
  if (expr && typeof expr === `object` && `type` in expr && expr.type === `ref` && Array.isArray(expr.path)) {
    return new PropRef(expr.path);
  }
  return void 0;
}
export {
  containsUnionFrom,
  getLazyLoadTargets
};
//# sourceMappingURL=lazy-targets.js.map
