/**
 * This file is part of the NocoBase (R) project.
 * Copyright (c) 2020-2024 NocoBase Co., Ltd.
 * Authors: NocoBase Team.
 *
 * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
 * For more information, please refer to: https://www.nocobase.com/agreement.
 */

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var filter_exports = {};
__export(filter_exports, {
  collectAstInvalidResourceFilterCalls: () => collectAstInvalidResourceFilterCalls
});
module.exports = __toCommonJS(filter_exports);
var _ = __toESM(require("lodash"));
var import_errors = require("../../errors");
var import_service_helpers = require("../../service-helpers");
var import_filter_group = require("../../filter-group");
var import_constants = require("../runtime/constants");
var import_surface = require("../runtime/surface");
var import_source = require("../ast/source");
var import_walk = require("../ast/walk");
var import_bindings = require("../ast/bindings");
var import_static_values = require("../ast/static-values");
var import_resource = require("./resource");
function collectAstInvalidResourceFilterCalls(ast, source, aliases, identifierBindings, ctxMethodAliases, stringBindings, staticFilterValueBindings, context) {
  const entries = [];
  const ctxResourceStates = /* @__PURE__ */ new Map();
  const aliasStates = /* @__PURE__ */ new Map();
  const events = [];
  const scopeKey = (scope) => `${scope.start}:${scope.end}`;
  const aliasKey = (alias) => `${alias.name}:${alias.declarationStart ?? alias.start}`;
  const mergeResourceState = (state, nextState) => {
    state.capability = nextState.capability;
    state.dataSourceKey = nextState.dataSourceKey;
    state.resourceType = nextState.resourceType;
    state.collectionName = nextState.collectionName;
    return state;
  };
  const defaultCtxState = () => ({
    capability: "ctx.resource",
    collectionName: (0, import_surface.normalizeText)(context.currentCollectionName || context.hostCollectionName) || void 0,
    dataSourceKey: (0, import_surface.normalizeText)(context.currentDataSourceKey || context.hostDataSourceKey) || "main",
    resourceType: "unknown"
  });
  const getAliasState = (alias) => {
    const key = aliasKey(alias);
    const current = aliasStates.get(key);
    if (current) {
      return current;
    }
    const initial = alias.capability === "ctx.resource" ? {
      ...defaultCtxState(),
      resourceType: alias.resourceType === "unknown" ? defaultCtxState().resourceType : alias.resourceType
    } : {
      capability: alias.capability,
      dataSourceKey: "main",
      resourceType: alias.resourceType
    };
    aliasStates.set(key, initial);
    return initial;
  };
  const setAliasState = (alias, state) => {
    if (alias.resourceType !== "unknown") {
      state.resourceType = alias.resourceType;
    }
    aliasStates.set(aliasKey(alias), state);
    return state;
  };
  const setCtxState = (executionScope, state) => {
    const nextState = {
      ...state,
      capability: "ctx.resource"
    };
    ctxResourceStates.set(scopeKey(executionScope), {
      scope: executionScope,
      state: nextState
    });
    return nextState;
  };
  const getCtxState = (executionScope) => {
    var _a, _b;
    const exact = (_a = ctxResourceStates.get(scopeKey(executionScope))) == null ? void 0 : _a.state;
    if (exact) {
      return exact;
    }
    const inherited = (_b = Array.from(ctxResourceStates.values()).filter(({ scope }) => scope.start <= executionScope.start && scope.end >= executionScope.end).sort(
      (left, right) => left.scope.end - left.scope.start - (right.scope.end - right.scope.start) || right.scope.start - left.scope.start
    )[0]) == null ? void 0 : _b.state;
    if (inherited) {
      return inherited;
    }
    return setCtxState(executionScope, defaultCtxState());
  };
  const findDeclaredAlias = (aliasName, index) => aliases.filter((alias) => alias.name === aliasName && (alias.declarationStart ?? alias.start) <= index).sort((left, right) => (right.declarationStart ?? right.start) - (left.declarationStart ?? left.start))[0];
  const resolveStaticStringArg = (node) => {
    const resolved = (0, import_static_values.resolveAstResourceTypeExpression)(node, source, stringBindings, identifierBindings);
    return resolved.status === "resolved" ? resolved.value : void 0;
  };
  const applyStaticResourceStateMethod = (state, method, node) => {
    var _a, _b;
    if (method === "setDataSourceKey") {
      const dataSourceKey = resolveStaticStringArg((_a = node.arguments) == null ? void 0 : _a[0]);
      return {
        ...state,
        dataSourceKey: dataSourceKey || ""
      };
    }
    if (method === "setResourceName") {
      const collectionName = resolveStaticStringArg((_b = node.arguments) == null ? void 0 : _b[0]);
      return {
        ...state,
        collectionName
      };
    }
    return state;
  };
  const getAliasResourceTarget = (alias, executionScope) => ({
    capability: alias.capability,
    state: getAliasState(alias),
    updateState: (nextState) => {
      mergeResourceState(getAliasState(alias), nextState);
    }
  });
  const resolveResourceExpressionTarget = (node, executionScope) => {
    const unwrapped = (0, import_bindings.unwrapAstChainExpression)(node);
    if (!unwrapped) {
      return void 0;
    }
    if ((0, import_resource.isAstCtxResourceMember)(unwrapped, identifierBindings)) {
      return {
        capability: "ctx.resource",
        state: getCtxState(executionScope),
        updateState: (nextState) => mergeResourceState(getCtxState(executionScope), nextState)
      };
    }
    if (unwrapped.type === "Identifier") {
      const alias = (0, import_static_values.resolveAstAliasBinding)(unwrapped.name, unwrapped.start || 0, aliases, identifierBindings);
      return alias ? getAliasResourceTarget(alias, executionScope) : void 0;
    }
    const factory = (0, import_resource.getAstFlowResourceFactoryCallFromAst)(
      unwrapped,
      ctxMethodAliases,
      source,
      stringBindings,
      identifierBindings
    );
    if (factory) {
      return {
        capability: factory.capability,
        state: {
          capability: factory.capability,
          dataSourceKey: "main",
          resourceType: factory.resourceType
        },
        updateState: _.noop
      };
    }
    if (unwrapped.type === "CallExpression") {
      const callTarget = getResourceCallTarget(unwrapped, executionScope);
      if (!callTarget || !import_constants.RUNJS_RESOURCE_CHAINABLE_STATE_METHODS.has(callTarget.method)) {
        return void 0;
      }
      return {
        ...callTarget,
        state: applyStaticResourceStateMethod(callTarget.state, callTarget.method, unwrapped)
      };
    }
    return void 0;
  };
  const resolveExpressionState = (node, executionScope) => {
    var _a;
    return (_a = resolveResourceExpressionTarget(node, executionScope)) == null ? void 0 : _a.state;
  };
  const getResourceCallTarget = (node, executionScope) => {
    const callee = (0, import_bindings.unwrapAstChainExpression)(node.callee);
    if (!callee || callee.type !== "MemberExpression") {
      return void 0;
    }
    const method = (0, import_static_values.getAstStaticPropertyName)(callee);
    if (!method) {
      return void 0;
    }
    const objectTarget = resolveResourceExpressionTarget(callee.object, executionScope);
    if (objectTarget) {
      return {
        ...objectTarget,
        capability: (0, import_source.getAstSource)(callee, source),
        method
      };
    }
    return void 0;
  };
  (0, import_walk.walkAstAncestor)(ast, {
    AssignmentExpression(node, ancestors) {
      var _a, _b;
      if (!(0, import_static_values.isAstCtxApiAliasAssignmentOperator)(node.operator)) {
        return;
      }
      const alwaysRuns = (0, import_bindings.isAstAlwaysExecutedInCurrentExecutionScope)(ancestors);
      const executionScope = (0, import_bindings.getAstExecutionScopeRange)(ancestors, source.length);
      if (((_a = node.left) == null ? void 0 : _a.type) === "Identifier") {
        events.push({
          type: "aliasBind",
          aliasName: node.left.name,
          alwaysRuns,
          end: typeof node.end === "number" ? node.end : node.start || 0,
          executionScope,
          index: node.start || 0,
          sourceNode: node.right
        });
        return;
      }
      if (((_b = node.left) == null ? void 0 : _b.type) === "ObjectPattern" && (0, import_static_values.isUnshadowedCtxIdentifier)(node.right, identifierBindings)) {
        (0, import_static_values.collectAstObjectPatternAliases)(node.left, (aliasName, member, aliasNode) => {
          if (member !== "resource") {
            return;
          }
          events.push({
            type: "ctxResourceAliasBind",
            aliasName,
            alwaysRuns,
            end: typeof (aliasNode == null ? void 0 : aliasNode.end) === "number" ? aliasNode.end : node.end || node.start || 0,
            executionScope,
            index: typeof (aliasNode == null ? void 0 : aliasNode.start) === "number" ? aliasNode.start : node.start || 0
          });
        });
      }
    },
    CallExpression(node, ancestors) {
      var _a;
      const executionScope = (0, import_bindings.getAstExecutionScopeRange)(ancestors, source.length);
      const alwaysRuns = (0, import_bindings.isAstAlwaysExecutedInCurrentExecutionScope)(ancestors);
      const ctxMethod = (0, import_static_values.resolveCtxMethodCall)(node, ctxMethodAliases, identifierBindings);
      if ((ctxMethod == null ? void 0 : ctxMethod.method) === "initResource") {
        const resolved = ((_a = node.arguments) == null ? void 0 : _a[0]) ? (0, import_static_values.resolveAstResourceTypeExpression)(node.arguments[0], source, stringBindings, identifierBindings) : void 0;
        events.push({
          type: "initResource",
          alwaysRuns,
          end: typeof node.end === "number" ? node.end : node.start || 0,
          executionScope,
          index: node.start || 0,
          resourceType: (resolved == null ? void 0 : resolved.status) === "resolved" && import_constants.INIT_RESOURCE_CLASS_NAMES.has(resolved.value) ? resolved.value : "unknown"
        });
      }
      if (!getResourceCallTarget(node, executionScope)) {
        return;
      }
      events.push({
        type: "resourceCall",
        alwaysRuns,
        end: typeof node.end === "number" ? node.end : node.start || 0,
        executionScope,
        index: node.start || 0,
        node
      });
    },
    VariableDeclarator(node, ancestors) {
      var _a, _b;
      const alwaysRuns = (0, import_bindings.isAstAlwaysExecutedInCurrentExecutionScope)(ancestors);
      const executionScope = (0, import_bindings.getAstExecutionScopeRange)(ancestors, source.length);
      if (((_a = node.id) == null ? void 0 : _a.type) === "Identifier") {
        events.push({
          type: "aliasBind",
          aliasName: node.id.name,
          alwaysRuns,
          end: typeof node.end === "number" ? node.end : node.start || 0,
          executionScope,
          index: node.start || 0,
          sourceNode: node.init
        });
        return;
      }
      if (((_b = node.id) == null ? void 0 : _b.type) === "ObjectPattern" && (0, import_static_values.isUnshadowedCtxIdentifier)(node.init, identifierBindings)) {
        (0, import_static_values.collectAstObjectPatternAliases)(node.id, (aliasName, member, aliasNode) => {
          if (member !== "resource") {
            return;
          }
          events.push({
            type: "ctxResourceAliasBind",
            aliasName,
            alwaysRuns,
            end: typeof (aliasNode == null ? void 0 : aliasNode.end) === "number" ? aliasNode.end : node.end || node.start || 0,
            executionScope,
            index: typeof (aliasNode == null ? void 0 : aliasNode.start) === "number" ? aliasNode.start : node.start || 0
          });
        });
      }
    }
  });
  ctxResourceStates.clear();
  aliasStates.clear();
  const resourceFilterScopes = Array.from(
    new Map(events.map((event) => [scopeKey(event.executionScope), event.executionScope])).values()
  );
  const resourceFilterScopeDepth = (scope) => resourceFilterScopes.filter(
    (candidate) => candidate.start < scope.start && candidate.end >= scope.end && !(0, import_bindings.isSameAstRange)(candidate, scope)
  ).length;
  events.sort(
    (left, right) => resourceFilterScopeDepth(left.executionScope) - resourceFilterScopeDepth(right.executionScope) || left.executionScope.start - right.executionScope.start || left.index - right.index || left.end - right.end
  ).forEach((event) => {
    var _a;
    if (event.type === "initResource") {
      if (!event.alwaysRuns) {
        return;
      }
      setCtxState(event.executionScope, {
        capability: "ctx.resource",
        dataSourceKey: "main",
        resourceType: event.resourceType
      });
      return;
    }
    if (event.type === "aliasBind") {
      if (!event.alwaysRuns) {
        return;
      }
      const alias = findDeclaredAlias(event.aliasName, event.index);
      const state = resolveExpressionState(event.sourceNode, event.executionScope);
      if (alias && state) {
        setAliasState(alias, state);
      }
      return;
    }
    if (event.type === "ctxResourceAliasBind") {
      if (!event.alwaysRuns) {
        return;
      }
      const alias = findDeclaredAlias(event.aliasName, event.index);
      if (alias) {
        setAliasState(alias, getCtxState(event.executionScope));
      }
      return;
    }
    const target = getResourceCallTarget(event.node, event.executionScope);
    if (!target) {
      return;
    }
    if (target.method === "setDataSourceKey") {
      if (!event.alwaysRuns) {
        return;
      }
      target.updateState(applyStaticResourceStateMethod(target.state, target.method, event.node));
      return;
    }
    if (target.method === "setResourceName") {
      if (!event.alwaysRuns) {
        return;
      }
      target.updateState(applyStaticResourceStateMethod(target.state, target.method, event.node));
      return;
    }
    const filterArgumentIndex = target.method === "setFilter" ? 0 : target.method === "addFilterGroup" ? 1 : void 0;
    if (typeof filterArgumentIndex !== "number") {
      return;
    }
    const filterArg = resolveRunJsStaticFilterRootNode(
      (_a = event.node.arguments) == null ? void 0 : _a[filterArgumentIndex],
      identifierBindings,
      staticFilterValueBindings
    );
    if (!filterArg) {
      return;
    }
    entries.push(
      ...collectAstResourceFilterRootErrors({
        source,
        path: "",
        capability: target.capability,
        dataSourceKey: target.state.dataSourceKey,
        collectionName: target.state.collectionName,
        resourceType: target.state.resourceType,
        filterNode: filterArg,
        context,
        identifierBindings,
        staticFilterValueBindings,
        index: typeof filterArg.node.start === "number" ? filterArg.node.start : event.node.start || 0
      })
    );
  });
  entries.push(
    ...collectAstForwardedResourceFilterArgumentErrors({
      ast,
      source,
      identifierBindings,
      stringBindings,
      staticFilterValueBindings,
      context
    })
  );
  return entries;
}
function collectAstForwardedResourceFilterArgumentErrors(input) {
  const helpers = collectAstResourceFilterHelpers(input.ast, input.source, input.identifierBindings);
  if (!helpers.length) {
    return [];
  }
  const errors = [];
  (0, import_walk.walkAstAncestor)(input.ast, {
    CallExpression(node) {
      var _a;
      const callee = (0, import_bindings.unwrapAstChainExpression)(node.callee);
      if ((callee == null ? void 0 : callee.type) !== "Identifier") {
        return;
      }
      const helper = (0, import_static_values.resolveAstAliasBinding)(callee.name, node.start || 0, helpers, input.identifierBindings);
      if (!helper) {
        return;
      }
      const filterArg = resolveRunJsStaticFilterRootNode(
        (_a = node.arguments) == null ? void 0 : _a[helper.filterParamIndex],
        input.identifierBindings,
        input.staticFilterValueBindings
      );
      if (!filterArg) {
        return;
      }
      const collectionName = helper.collectionName || resolveAstResourceFilterHelperCollectionName(
        helper,
        node.arguments,
        input.source,
        input.stringBindings,
        input.identifierBindings
      );
      if (!collectionName) {
        return;
      }
      errors.push(
        ...collectAstResourceFilterRootErrors({
          source: input.source,
          path: "",
          capability: `${helper.name}.setFilter`,
          dataSourceKey: helper.dataSourceKey || "main",
          collectionName,
          resourceType: helper.resourceType || "unknown",
          filterNode: filterArg,
          context: input.context,
          identifierBindings: input.identifierBindings,
          staticFilterValueBindings: input.staticFilterValueBindings,
          index: typeof filterArg.node.start === "number" ? filterArg.node.start : node.start || 0
        })
      );
    }
  });
  return errors;
}
function collectAstResourceFilterHelpers(ast, source, identifierBindings) {
  const helpers = [];
  const addHelper = (name, functionNode, bindingNode, scope) => {
    var _a;
    if (!name || !((_a = functionNode == null ? void 0 : functionNode.params) == null ? void 0 : _a.length)) {
      return;
    }
    const paramNames = functionNode.params.map(import_static_values.getAstBindingIdentifierName);
    const resourceStates = /* @__PURE__ */ new Map();
    const isInsideNestedFunction = (ancestors) => ancestors.some((ancestor) => ancestor !== functionNode.body && (0, import_bindings.isAstFunctionLike)(ancestor));
    const ensureResourceState = (name2, factory) => {
      if (!name2) {
        return void 0;
      }
      const existing = resourceStates.get(name2);
      if (existing) {
        if (factory == null ? void 0 : factory.resourceType) {
          existing.resourceType = factory.resourceType;
        }
        return existing;
      }
      const state = {
        collectionName: "",
        collectionParamNames: /* @__PURE__ */ new Set(),
        dataSourceKey: "main",
        filterParamNames: /* @__PURE__ */ new Set(),
        resourceType: factory == null ? void 0 : factory.resourceType
      };
      resourceStates.set(name2, state);
      return state;
    };
    const getResourceStateForMember = (memberExpression) => {
      const object = (0, import_bindings.unwrapAstChainExpression)(memberExpression == null ? void 0 : memberExpression.object);
      if ((object == null ? void 0 : object.type) !== "Identifier") {
        return void 0;
      }
      return resourceStates.get(object.name);
    };
    const collectResourceAlias = (aliasName, sourceNode) => {
      const factory = (0, import_resource.getAstFlowResourceFactoryCallFromAst)(sourceNode, [], source, [], identifierBindings);
      if (factory) {
        ensureResourceState(aliasName, factory);
      }
    };
    (0, import_walk.walkAstAncestor)(functionNode.body || functionNode, {
      AssignmentExpression(node, ancestors) {
        var _a2;
        if (isInsideNestedFunction(ancestors) || ((_a2 = node.left) == null ? void 0 : _a2.type) !== "Identifier") {
          return;
        }
        collectResourceAlias(node.left.name, node.right);
      },
      CallExpression(node, ancestors) {
        var _a2, _b, _c;
        if (isInsideNestedFunction(ancestors)) {
          return;
        }
        const callee = (0, import_bindings.unwrapAstChainExpression)(node.callee);
        if ((callee == null ? void 0 : callee.type) !== "MemberExpression") {
          return;
        }
        const resourceState = getResourceStateForMember(callee);
        if (!resourceState) {
          return;
        }
        const method = (0, import_static_values.getAstStaticPropertyName)(callee);
        if (method === "setFilter" || method === "addFilterGroup") {
          const arg = (0, import_bindings.unwrapAstChainExpression)((_a2 = node.arguments) == null ? void 0 : _a2[method === "addFilterGroup" ? 1 : 0]);
          if ((arg == null ? void 0 : arg.type) === "Identifier" && paramNames.includes(arg.name)) {
            resourceState.filterParamNames.add(arg.name);
          }
          return;
        }
        if (method === "setResourceName") {
          const arg = (0, import_bindings.unwrapAstChainExpression)((_b = node.arguments) == null ? void 0 : _b[0]);
          if ((arg == null ? void 0 : arg.type) === "Identifier" && paramNames.includes(arg.name)) {
            resourceState.collectionParamNames.add(arg.name);
            return;
          }
          const resolved = (0, import_static_values.resolveAstStaticStringValue)(arg, source);
          if (typeof resolved === "string") {
            resourceState.collectionName = resolved;
          }
          return;
        }
        if (method === "setDataSourceKey") {
          const resolved = (0, import_static_values.resolveAstStaticStringValue)((_c = node.arguments) == null ? void 0 : _c[0], source);
          if (typeof resolved === "string") {
            resourceState.dataSourceKey = resolved;
          }
        }
      },
      VariableDeclarator(node, ancestors) {
        var _a2;
        if (isInsideNestedFunction(ancestors) || ((_a2 = node.id) == null ? void 0 : _a2.type) !== "Identifier") {
          return;
        }
        collectResourceAlias(node.id.name, node.init);
      }
    });
    for (const state of resourceStates.values()) {
      state.filterParamNames.forEach((filterParamName) => {
        const filterParamIndex = paramNames.indexOf(filterParamName);
        if (filterParamIndex < 0) {
          return;
        }
        const collectionParamIndex = Array.from(state.collectionParamNames).map((paramName) => paramNames.indexOf(paramName)).find((index) => index >= 0);
        helpers.push({
          collectionName: state.collectionName,
          collectionParamIndex,
          dataSourceKey: state.dataSourceKey,
          declarationStart: typeof (bindingNode == null ? void 0 : bindingNode.start) === "number" ? bindingNode.start : scope.start,
          filterParamIndex,
          name,
          resourceType: state.resourceType,
          start: scope.start,
          end: scope.end
        });
      });
    }
  };
  (0, import_walk.walkAstAncestor)(ast, {
    FunctionDeclaration(node, ancestors) {
      var _a;
      if (((_a = node.id) == null ? void 0 : _a.type) !== "Identifier") {
        return;
      }
      addHelper(node.id.name, node, node.id, (0, import_bindings.getAstBindingScopeRange)(ancestors.slice(0, -1), source.length));
    },
    VariableDeclarator(node, ancestors) {
      var _a;
      if (((_a = node.id) == null ? void 0 : _a.type) !== "Identifier" || !(0, import_bindings.isAstFunctionLike)((0, import_bindings.unwrapAstChainExpression)(node.init))) {
        return;
      }
      const declaration = (0, import_bindings.findAstAncestor)(ancestors, "VariableDeclaration");
      addHelper(
        node.id.name,
        (0, import_bindings.unwrapAstChainExpression)(node.init),
        node.id,
        (0, import_bindings.getAstBindingScopeRange)(ancestors, source.length, (declaration == null ? void 0 : declaration.kind) === "var")
      );
    }
  });
  return helpers;
}
function resolveAstResourceFilterHelperCollectionName(helper, args, source, stringBindings, identifierBindings) {
  if (typeof helper.collectionParamIndex !== "number") {
    return "";
  }
  const arg = args == null ? void 0 : args[helper.collectionParamIndex];
  if (!arg) {
    return "";
  }
  const resolved = (0, import_static_values.resolveAstResourceTypeExpression)(arg, source, stringBindings, identifierBindings);
  return resolved.status === "resolved" ? resolved.value : "";
}
function collectAstResourceFilterRootErrors(input) {
  if (!isRunJsRecordFilterResourceType(input.resourceType)) {
    return [];
  }
  if (input.filterNode.kind === "array") {
    return [buildRunJsResourceFilterShapeError({ ...input, invalidShape: "array" })];
  }
  if (isRunJsFilterGroupObjectNode(input.filterNode.node, input.identifierBindings, input.staticFilterValueBindings)) {
    return [buildRunJsResourceFilterShapeError({ ...input, invalidShape: "filter-group" })];
  }
  return collectAstResourceFilterObjectErrors({
    ...input,
    filterObject: input.filterNode.node
  });
}
function collectAstResourceFilterObjectErrors(input) {
  if (!isRunJsRecordFilterResourceType(input.resourceType)) {
    return [];
  }
  if (!input.dataSourceKey || !input.collectionName || !input.context.getCollection) {
    return [];
  }
  const dataSourceKey = input.dataSourceKey;
  const collectionName = input.collectionName;
  const collection = input.context.getCollection(dataSourceKey, collectionName);
  if (!collection) {
    return [
      {
        capability: input.capability,
        collectionName,
        dataSourceKey,
        index: input.index,
        message: `flowSurfaces authoring ${input.capability}(...) references unknown collection '${dataSourceKey}.${collectionName}' while validating setFilter(...)`,
        resourceType: input.resourceType,
        ruleId: "runjs-resource-collection-unknown"
      }
    ];
  }
  return collectAstResourceFilterProperties({
    ...input,
    collectionName,
    dataSourceKey,
    collection,
    rootCollection: collection
  });
}
function isRunJsRecordFilterResourceType(resourceType) {
  return resourceType === "unknown" || resourceType === "MultiRecordResource" || resourceType === "SingleRecordResource";
}
function collectAstResourceFilterProperties(input) {
  var _a;
  const errors = [];
  for (const property of input.filterObject.properties || []) {
    if (!property || property.type === "SpreadElement") {
      continue;
    }
    const key = (0, import_static_values.getAstStaticPropertyName)(property);
    if (!key) {
      continue;
    }
    const keyIndex = typeof ((_a = property.key) == null ? void 0 : _a.start) === "number" ? property.key.start : property.start || 0;
    if (key === "$not") {
      const value2 = resolveRunJsStaticFilterObjectNode(
        property.value,
        input.identifierBindings,
        input.staticFilterValueBindings
      );
      if (value2) {
        errors.push(
          ...collectAstResourceFilterProperties({
            ...input,
            filterObject: value2
          })
        );
      }
      continue;
    }
    if (key === "$and" || key === "$or") {
      const value2 = resolveRunJsStaticFilterArrayNode(
        property.value,
        input.identifierBindings,
        input.staticFilterValueBindings
      );
      if (value2) {
        for (const element of value2.elements || []) {
          const child = resolveRunJsStaticFilterObjectNode(
            element,
            input.identifierBindings,
            input.staticFilterValueBindings
          );
          if (child) {
            errors.push(
              ...collectAstResourceFilterProperties({
                ...input,
                filterObject: child
              })
            );
          }
        }
      }
      continue;
    }
    if (key.startsWith("$")) {
      collectAstResourceFilterOperatorErrors(key, keyIndex, input).forEach((entry) => errors.push(entry));
      continue;
    }
    const fieldPath = input.path ? `${input.path}.${key}` : key;
    const resolved = resolveRunJsResourceFilterFieldPath(
      input.rootCollection,
      input.dataSourceKey,
      fieldPath,
      input.context
    );
    if (!resolved.field) {
      errors.push(
        buildRunJsResourceFilterFieldError({
          ...input,
          collection: resolved.collection || input.collection,
          fieldPath,
          index: keyIndex
        })
      );
      continue;
    }
    const value = resolveRunJsStaticFilterObjectNode(
      property.value,
      input.identifierBindings,
      input.staticFilterValueBindings
    );
    if (value) {
      errors.push(
        ...collectAstResourceFilterValueObjectErrors({
          ...input,
          field: resolved.field,
          fieldPath,
          filterObject: value,
          collection: resolved.collection || input.collection
        })
      );
    }
  }
  return errors;
}
function collectAstResourceFilterValueObjectErrors(input) {
  var _a;
  const errors = [];
  if (isRunJsJsonLikeField(input.field)) {
    return errors;
  }
  for (const property of input.filterObject.properties || []) {
    if (!property || property.type === "SpreadElement") {
      continue;
    }
    const key = (0, import_static_values.getAstStaticPropertyName)(property);
    if (!key) {
      continue;
    }
    const keyIndex = typeof ((_a = property.key) == null ? void 0 : _a.start) === "number" ? property.key.start : property.start || 0;
    if (key === "$not") {
      collectAstResourceFilterOperatorErrors(key, keyIndex, input).forEach((entry) => errors.push(entry));
      const value2 = resolveRunJsStaticFilterObjectNode(
        property.value,
        input.identifierBindings,
        input.staticFilterValueBindings
      );
      if (value2) {
        errors.push(
          ...collectAstResourceFilterValueObjectErrors({
            ...input,
            filterObject: value2
          })
        );
      }
      continue;
    }
    if (key.startsWith("$")) {
      collectAstResourceFilterOperatorErrors(key, keyIndex, input).forEach((entry) => errors.push(entry));
      const invalidDateValue = buildRunJsResourceDateFilterValueError(key, keyIndex, property.value, input);
      if (invalidDateValue) {
        errors.push(invalidDateValue);
      }
      continue;
    }
    if (!(0, import_service_helpers.isAssociationField)(input.field)) {
      errors.push(buildRunJsResourceFilterOperatorError(key, keyIndex, input));
      continue;
    }
    const nestedFieldPath = `${input.fieldPath}.${key}`;
    const resolved = resolveRunJsResourceFilterFieldPath(
      input.rootCollection,
      input.dataSourceKey,
      nestedFieldPath,
      input.context
    );
    if (!resolved.field) {
      errors.push(
        buildRunJsResourceFilterFieldError({
          ...input,
          collection: resolved.collection || input.collection,
          fieldPath: nestedFieldPath,
          index: keyIndex
        })
      );
      continue;
    }
    const value = resolveRunJsStaticFilterObjectNode(
      property.value,
      input.identifierBindings,
      input.staticFilterValueBindings
    );
    if (value) {
      errors.push(
        ...collectAstResourceFilterValueObjectErrors({
          ...input,
          collection: resolved.collection || input.collection,
          field: resolved.field,
          fieldPath: nestedFieldPath,
          filterObject: value
        })
      );
    }
  }
  return errors;
}
function collectAstResourceFilterOperatorErrors(operator, index, input) {
  if (isRunJsSupportedFilterOperator(operator)) {
    return [];
  }
  const suggestedOperator = getSuggestedRunJsFilterOperator(operator);
  return [
    {
      capability: input.capability,
      collectionName: input.collectionName,
      dataSourceKey: input.dataSourceKey,
      fieldPath: input.fieldPath,
      index,
      message: suggestedOperator ? `flowSurfaces authoring ${input.capability}(...) uses unsupported filter operator '${operator}'${input.fieldPath ? ` for field '${input.fieldPath}'` : ""}; use '${suggestedOperator}'` : `flowSurfaces authoring ${input.capability}(...) uses unsupported filter operator '${operator}'`,
      operator,
      resourceType: input.resourceType,
      ruleId: "runjs-resource-filter-operator-invalid",
      suggestedOperator
    }
  ];
}
function buildRunJsResourceFilterOperatorError(operator, index, input) {
  const suggestedOperator = getSuggestedRunJsFilterOperator(operator);
  if (suggestedOperator) {
    return {
      capability: input.capability,
      collectionName: input.collectionName,
      dataSourceKey: input.dataSourceKey,
      fieldPath: input.fieldPath,
      index,
      message: `flowSurfaces authoring ${input.capability}(...) uses filter operator '${operator}' without '$' for field '${input.fieldPath}'; use '${suggestedOperator}'`,
      operator,
      resourceType: input.resourceType,
      ruleId: "runjs-resource-filter-operator-missing-dollar",
      suggestedOperator
    };
  }
  return {
    capability: input.capability,
    collectionName: input.collectionName,
    dataSourceKey: input.dataSourceKey,
    fieldPath: input.fieldPath,
    index,
    message: `flowSurfaces authoring ${input.capability}(...) uses unsupported filter operator '${operator}' for field '${input.fieldPath}'`,
    operator,
    resourceType: input.resourceType,
    ruleId: "runjs-resource-filter-operator-invalid"
  };
}
function buildRunJsResourceDateFilterValueError(operator, index, valueNode, input) {
  const fieldType = String((0, import_service_helpers.getFieldType)(input.field) || "").trim();
  const fieldInterface = String((0, import_service_helpers.getFieldInterface)(input.field) || "").trim();
  const shouldValidateDateValue = isRunJsDateFilterOperator(operator) || (0, import_filter_group.isFlowSurfaceDateLikeFieldMeta)({
    type: fieldType,
    interface: fieldInterface
  });
  if (!shouldValidateDateValue) {
    return null;
  }
  const valueObject = resolveRunJsStaticFilterObjectNode(
    valueNode,
    input.identifierBindings,
    input.staticFilterValueBindings
  );
  if (valueObject) {
    const unsupportedKeys = collectRunJsUnsupportedDateRangeValueKeys(valueObject);
    if (unsupportedKeys.length) {
      return buildRunJsUnsupportedDateRangeValueError(operator, index, valueObject, unsupportedKeys, input);
    }
  }
  const resolvedValue = resolveRunJsStaticFilterValue(
    valueNode,
    input.source,
    input.identifierBindings,
    input.staticFilterValueBindings
  );
  if (resolvedValue.status !== "resolved") {
    return null;
  }
  try {
    (0, import_filter_group.normalizeFlowSurfaceDateConditionValue)(operator, resolvedValue.value, `${input.fieldPath}.${operator}`, {
      fieldPath: input.fieldPath,
      fieldType,
      fieldInterface
    });
    return null;
  } catch (error) {
    return buildRunJsStrictDateFilterValueError(operator, index, resolvedValue.value, input, error);
  }
}
function buildRunJsUnsupportedDateRangeValueError(operator, index, valueObject, unsupportedKeys, input) {
  const suggestedValue = buildSuggestedRunJsDateRangeValue(valueObject, input.source);
  const relativeExample = suggestedValue || { type: "past", number: 7, unit: "day" };
  const examples = {
    relativePeriod: {
      [input.fieldPath]: {
        [operator]: relativeExample
      }
    },
    explicitRange: {
      [input.fieldPath]: {
        $dateBetween: ["YYYY-MM-DD", "YYYY-MM-DD"]
      }
    }
  };
  const suggestedText = suggestedValue ? `use ${operator}: ${JSON.stringify(suggestedValue)} for this relative period` : `use a frontend relative period object such as ${JSON.stringify(relativeExample)}`;
  const fieldType = String((0, import_service_helpers.getFieldType)(input.field) || "").trim();
  const fieldInterface = String((0, import_service_helpers.getFieldInterface)(input.field) || "").trim();
  return {
    capability: input.capability,
    collectionName: input.collectionName,
    dataSourceKey: input.dataSourceKey,
    examples,
    fieldPath: input.fieldPath,
    index,
    message: `flowSurfaces authoring ${input.capability}(...) uses invalid date filter value for field '${input.fieldPath}' with operator '${operator}': ${unsupportedKeys.join(
      ", "
    )} are not supported FlowResource date filter keys. ${suggestedText}, or use $dateBetween with ["YYYY-MM-DD", "YYYY-MM-DD"].`,
    operator,
    resourceType: input.resourceType,
    ruleId: "runjs-resource-filter-date-range-object-invalid",
    suggestedOperator: operator === "$dateBetween" ? "$dateBetween" : operator,
    suggestedValue: relativeExample,
    unsupportedKeys,
    fieldType,
    fieldInterface
  };
}
function buildRunJsStrictDateFilterValueError(operator, index, invalidValue, input, error) {
  var _a;
  const details = error instanceof import_errors.FlowSurfaceBadRequestError && _.isPlainObject((_a = error.options) == null ? void 0 : _a.details) ? error.options.details : {};
  const invalidReason = typeof details.invalidReason === "string" && details.invalidReason ? details.invalidReason : "date filter value is invalid";
  const fieldType = String((0, import_service_helpers.getFieldType)(input.field) || "").trim();
  const fieldInterface = String((0, import_service_helpers.getFieldInterface)(input.field) || "").trim();
  return {
    capability: input.capability,
    collectionName: input.collectionName,
    dataSourceKey: input.dataSourceKey,
    fieldPath: input.fieldPath,
    index,
    invalidReason,
    invalidValue: Object.prototype.hasOwnProperty.call(details, "invalidValue") ? details.invalidValue : invalidValue,
    message: `flowSurfaces authoring ${input.capability}(...) uses invalid date filter value for field '${input.fieldPath}' with operator '${operator}': ${invalidReason}`,
    operator,
    repairExample: _.isPlainObject(details.repairExample) ? details.repairExample : void 0,
    repairHint: typeof details.repairHint === "string" ? details.repairHint : void 0,
    resourceType: input.resourceType,
    ruleId: "runjs-resource-filter-date-value-invalid",
    suggestedOperator: operator,
    fieldType,
    fieldInterface
  };
}
function resolveRunJsStaticFilterRootNode(node, identifierBindings, staticFilterValueBindings) {
  const objectNode = resolveRunJsStaticFilterObjectNode(node, identifierBindings, staticFilterValueBindings);
  if (objectNode) {
    return {
      kind: "object",
      node: objectNode
    };
  }
  const arrayNode = resolveRunJsStaticFilterArrayNode(node, identifierBindings, staticFilterValueBindings);
  if (arrayNode) {
    return {
      kind: "array",
      node: arrayNode
    };
  }
  return void 0;
}
function resolveRunJsStaticFilterObjectNode(node, identifierBindings, staticFilterValueBindings, seen = /* @__PURE__ */ new Set()) {
  const unwrapped = (0, import_bindings.unwrapAstChainExpression)(node);
  if (!unwrapped) {
    return void 0;
  }
  if (unwrapped.type === "ObjectExpression") {
    return unwrapped;
  }
  if (unwrapped.type !== "Identifier") {
    return void 0;
  }
  const binding = (0, import_static_values.resolveAstAliasBinding)(
    unwrapped.name,
    unwrapped.start || 0,
    staticFilterValueBindings,
    identifierBindings
  );
  if (!binding || seen.has(binding)) {
    return void 0;
  }
  const nextSeen = new Set(seen);
  nextSeen.add(binding);
  return resolveRunJsStaticFilterObjectNode(binding.valueNode, identifierBindings, staticFilterValueBindings, nextSeen);
}
function resolveRunJsStaticFilterArrayNode(node, identifierBindings, staticFilterValueBindings, seen = /* @__PURE__ */ new Set()) {
  const unwrapped = (0, import_bindings.unwrapAstChainExpression)(node);
  if (!unwrapped) {
    return void 0;
  }
  if (unwrapped.type === "ArrayExpression") {
    return unwrapped;
  }
  if (unwrapped.type !== "Identifier") {
    return void 0;
  }
  const binding = (0, import_static_values.resolveAstAliasBinding)(
    unwrapped.name,
    unwrapped.start || 0,
    staticFilterValueBindings,
    identifierBindings
  );
  if (!binding || seen.has(binding)) {
    return void 0;
  }
  const nextSeen = new Set(seen);
  nextSeen.add(binding);
  return resolveRunJsStaticFilterArrayNode(binding.valueNode, identifierBindings, staticFilterValueBindings, nextSeen);
}
function resolveRunJsStaticFilterValue(node, source, identifierBindings, staticFilterValueBindings, seen = /* @__PURE__ */ new Set()) {
  var _a, _b, _c, _d;
  const unwrapped = (0, import_bindings.unwrapAstChainExpression)(node);
  if (!unwrapped) {
    return { status: "unresolved" };
  }
  if (unwrapped.type === "Identifier") {
    const index = unwrapped.start || 0;
    if (unwrapped.name === "undefined" && !(0, import_static_values.hasAstActiveBinding)("undefined", index, identifierBindings)) {
      return { status: "resolved", value: void 0 };
    }
    const binding = (0, import_static_values.resolveAstAliasBinding)(unwrapped.name, index, staticFilterValueBindings, identifierBindings);
    if (!binding || seen.has(binding)) {
      return { status: "unresolved" };
    }
    const nextSeen = new Set(seen);
    nextSeen.add(binding);
    return resolveRunJsStaticFilterValue(
      binding.valueNode,
      source,
      identifierBindings,
      staticFilterValueBindings,
      nextSeen
    );
  }
  if (unwrapped.type === "Literal") {
    const value = unwrapped.value;
    if (value === null || ["string", "number", "boolean"].includes(typeof value)) {
      return { status: "resolved", value };
    }
    return { status: "unresolved" };
  }
  if (unwrapped.type === "TemplateLiteral" && !((_a = unwrapped.expressions) == null ? void 0 : _a.length)) {
    return {
      status: "resolved",
      value: ((_d = (_c = (_b = unwrapped.quasis) == null ? void 0 : _b[0]) == null ? void 0 : _c.value) == null ? void 0 : _d.cooked) ?? source.slice(unwrapped.start + 1, unwrapped.end - 1)
    };
  }
  if (unwrapped.type === "UnaryExpression") {
    const argument = resolveRunJsStaticFilterValue(
      unwrapped.argument,
      source,
      identifierBindings,
      staticFilterValueBindings,
      seen
    );
    if (argument.status !== "resolved") {
      return argument;
    }
    if (unwrapped.operator === "-") {
      return typeof argument.value === "number" ? { status: "resolved", value: -argument.value } : { status: "unresolved" };
    }
    if (unwrapped.operator === "+") {
      return typeof argument.value === "number" ? { status: "resolved", value: argument.value } : { status: "unresolved" };
    }
    if (unwrapped.operator === "!") {
      return { status: "resolved", value: !argument.value };
    }
    if (unwrapped.operator === "void") {
      return { status: "resolved", value: void 0 };
    }
    return { status: "unresolved" };
  }
  if (unwrapped.type === "ArrayExpression") {
    const values = [];
    for (const element of unwrapped.elements || []) {
      if (!element) {
        values.push(void 0);
        continue;
      }
      if (element.type === "SpreadElement") {
        return { status: "unresolved" };
      }
      const item = resolveRunJsStaticFilterValue(element, source, identifierBindings, staticFilterValueBindings, seen);
      if (item.status !== "resolved") {
        return item;
      }
      values.push(item.value);
    }
    return { status: "resolved", value: values };
  }
  if (unwrapped.type === "ObjectExpression") {
    const value = {};
    for (const property of unwrapped.properties || []) {
      if (!property || property.type === "SpreadElement") {
        return { status: "unresolved" };
      }
      const key = (0, import_static_values.getAstStaticPropertyName)(property);
      if (!key) {
        return { status: "unresolved" };
      }
      const propertyValue = resolveRunJsStaticFilterValue(
        property.value,
        source,
        identifierBindings,
        staticFilterValueBindings,
        seen
      );
      if (propertyValue.status !== "resolved") {
        return propertyValue;
      }
      value[key] = propertyValue.value;
    }
    return { status: "resolved", value };
  }
  return { status: "unresolved" };
}
function isRunJsFilterGroupObjectNode(objectExpression, identifierBindings, staticFilterValueBindings) {
  let hasLogic = false;
  for (const property of (objectExpression == null ? void 0 : objectExpression.properties) || []) {
    if (!property || property.type === "SpreadElement") {
      return false;
    }
    const key = (0, import_static_values.getAstStaticPropertyName)(property);
    if (!key) {
      return false;
    }
    if (key === "logic") {
      hasLogic = true;
    }
  }
  return hasLogic && !!resolveRunJsFilterGroupItemsArrayNode(objectExpression, identifierBindings, staticFilterValueBindings);
}
function buildRunJsResourceFilterShapeError(input) {
  const invalidValue = resolveRunJsStaticFilterValue(
    input.filterNode.node,
    input.source,
    input.identifierBindings,
    input.staticFilterValueBindings
  );
  const resolvedInvalidValue = invalidValue.status === "resolved" ? invalidValue.value : void 0;
  const shapeData = getRunJsResourceFilterShapeData(input, resolvedInvalidValue);
  return {
    capability: input.capability,
    collectionName: input.collectionName,
    dataSourceKey: input.dataSourceKey,
    index: input.index,
    invalidShape: input.invalidShape,
    invalidValue: resolvedInvalidValue,
    message: `flowSurfaces authoring ${input.capability}(...) received ${input.invalidShape === "filter-group" ? "a FlowSurface FilterGroup" : "an array"}, but FlowResource filters must be backend query filter objects such as { stage: { $eq: '\u65B0\u7EBF\u7D22' } }`,
    repairExample: buildRunJsResourceFilterShapeRepairExample(shapeData),
    repairHint: 'FlowResource setFilter/addFilterGroup does not accept FlowSurface FilterGroup items. Convert { field/path, operator, value } items to backend query filter object syntax and prefix operators with "$".',
    resourceType: input.resourceType,
    ruleId: "runjs-resource-filter-shape-invalid",
    suggestedOperator: getSuggestedRunJsResourceFilterShapeOperator(shapeData)
  };
}
function buildRunJsResourceFilterShapeRepairExample(source) {
  const items = source.items.map((item) => buildRunJsResourceFilterConditionFromItem(item)).filter(Boolean);
  if (items.length === 1) {
    return items[0];
  }
  if (items.length > 1) {
    return {
      [source.logic === "$or" ? "$or" : "$and"]: items
    };
  }
  return {
    stage: {
      $eq: "\u65B0\u7EBF\u7D22"
    }
  };
}
function getRunJsResourceFilterShapeData(input, resolvedValue) {
  const astShapeData = getRunJsResourceFilterAstShapeData(input);
  if (astShapeData.items.length) {
    return astShapeData;
  }
  return getRunJsResourceFilterResolvedShapeData(resolvedValue);
}
function getRunJsResourceFilterAstShapeData(input) {
  if (input.filterNode.kind === "array") {
    return {
      items: getRunJsResourceFilterShapeItemsFromArrayNode(input.filterNode.node, input)
    };
  }
  const itemsNode = resolveRunJsFilterGroupItemsArrayNode(
    input.filterNode.node,
    input.identifierBindings,
    input.staticFilterValueBindings
  );
  if (itemsNode) {
    return {
      items: getRunJsResourceFilterShapeItemsFromArrayNode(itemsNode, input),
      logic: resolveRunJsFilterGroupLogic(input.filterNode.node, input)
    };
  }
  return {
    items: []
  };
}
function getRunJsResourceFilterResolvedShapeData(value) {
  if (Array.isArray(value)) {
    return {
      items: value.map((item) => buildRunJsResourceFilterShapeItemFromValue(item)).filter(Boolean)
    };
  }
  if (_.isPlainObject(value)) {
    const valueObject = value;
    if (Array.isArray(valueObject.items)) {
      return {
        items: valueObject.items.map((item) => buildRunJsResourceFilterShapeItemFromValue(item)).filter(Boolean),
        logic: typeof valueObject.logic === "string" ? valueObject.logic : void 0
      };
    }
  }
  return {
    items: []
  };
}
function getRunJsResourceFilterShapeItemsFromArrayNode(arrayNode, input) {
  return ((arrayNode == null ? void 0 : arrayNode.elements) || []).map((element) => buildRunJsResourceFilterShapeItemFromNode(element, input)).filter(Boolean);
}
function buildRunJsResourceFilterShapeItemFromNode(node, input) {
  const objectNode = resolveRunJsStaticFilterObjectNode(
    node,
    input.identifierBindings,
    input.staticFilterValueBindings
  );
  if (!objectNode) {
    return null;
  }
  const fieldPath = resolveRunJsFilterShapeItemStringProperty(objectNode, "path", input) || resolveRunJsFilterShapeItemStringProperty(objectNode, "field", input);
  if (!fieldPath) {
    return null;
  }
  const operator = resolveRunJsFilterShapeItemStringProperty(objectNode, "operator", input);
  const value = resolveRunJsFilterShapeItemValue(objectNode, input);
  return {
    fieldPath,
    operator,
    ...value
  };
}
function buildRunJsResourceFilterShapeItemFromValue(value) {
  if (!_.isPlainObject(value)) {
    return null;
  }
  const input = value;
  const fieldPath = String(input.path || input.field || "").trim();
  if (!fieldPath) {
    return null;
  }
  return {
    fieldPath,
    hasValue: Object.prototype.hasOwnProperty.call(input, "value"),
    operator: typeof input.operator === "string" ? input.operator : void 0,
    value: input.value
  };
}
function buildRunJsResourceFilterConditionFromItem(item) {
  if (!item.fieldPath) {
    return null;
  }
  const operator = normalizeRunJsResourceFilterShapeOperator(item.operator);
  return {
    [item.fieldPath]: {
      [operator]: item.hasValue ? item.value : "<value>"
    }
  };
}
function resolveRunJsFilterShapeItemStringProperty(objectExpression, key, input) {
  const property = getRunJsFilterShapeObjectProperty(objectExpression, key);
  if (!property) {
    return "";
  }
  const resolved = resolveRunJsStaticFilterValue(
    property.value,
    input.source,
    input.identifierBindings,
    input.staticFilterValueBindings
  );
  return resolved.status === "resolved" && typeof resolved.value === "string" ? resolved.value.trim() : "";
}
function resolveRunJsFilterShapeItemValue(objectExpression, input) {
  const property = getRunJsFilterShapeObjectProperty(objectExpression, "value");
  if (!property) {
    return {
      hasValue: false
    };
  }
  const resolved = resolveRunJsStaticFilterValue(
    property.value,
    input.source,
    input.identifierBindings,
    input.staticFilterValueBindings
  );
  return {
    hasValue: true,
    value: resolved.status === "resolved" ? resolved.value : "<value>"
  };
}
function resolveRunJsFilterGroupItemsArrayNode(objectExpression, identifierBindings, staticFilterValueBindings) {
  const property = getRunJsFilterShapeObjectProperty(objectExpression, "items");
  return property ? resolveRunJsStaticFilterArrayNode(property.value, identifierBindings, staticFilterValueBindings) : void 0;
}
function resolveRunJsFilterGroupLogic(objectExpression, input) {
  const property = getRunJsFilterShapeObjectProperty(objectExpression, "logic");
  if (!property) {
    return void 0;
  }
  const resolved = resolveRunJsStaticFilterValue(
    property.value,
    input.source,
    input.identifierBindings,
    input.staticFilterValueBindings
  );
  return resolved.status === "resolved" && typeof resolved.value === "string" ? resolved.value : void 0;
}
function getRunJsFilterShapeObjectProperty(objectExpression, name) {
  return ((objectExpression == null ? void 0 : objectExpression.properties) || []).find((property) => {
    if (!property || property.type === "SpreadElement") {
      return false;
    }
    return (0, import_static_values.getAstStaticPropertyName)(property) === name;
  });
}
function normalizeRunJsResourceFilterShapeOperator(operator) {
  const normalized = String(operator || "").trim();
  if (!normalized) {
    return "$eq";
  }
  return getSuggestedRunJsFilterOperator(normalized) || normalized;
}
function getSuggestedRunJsResourceFilterShapeOperator(input) {
  var _a;
  const operator = (_a = input.items.find((item) => typeof item.operator === "string")) == null ? void 0 : _a.operator;
  if (!operator) {
    return void 0;
  }
  return getSuggestedRunJsFilterOperator(operator) || void 0;
}
function isRunJsDateFilterOperator(operator) {
  return import_constants.RUNJS_DATE_FILTER_OPERATORS.has(String(operator || "").trim());
}
function collectRunJsUnsupportedDateRangeValueKeys(objectExpression) {
  const keys = [];
  for (const property of objectExpression.properties || []) {
    if (!property || property.type === "SpreadElement") {
      continue;
    }
    const key = (0, import_static_values.getAstStaticPropertyName)(property);
    if (import_constants.RUNJS_UNSUPPORTED_DATE_RANGE_VALUE_KEYS.has(key)) {
      keys.push(key);
    }
  }
  return keys;
}
function buildSuggestedRunJsDateRangeValue(objectExpression, source) {
  const values = {};
  for (const property of objectExpression.properties || []) {
    if (!property || property.type === "SpreadElement") {
      continue;
    }
    const key = (0, import_static_values.getAstStaticPropertyName)(property);
    if (!import_constants.RUNJS_UNSUPPORTED_DATE_RANGE_VALUE_KEYS.has(key)) {
      continue;
    }
    const value = (0, import_static_values.resolveAstStaticStringValue)(property.value, source);
    if (typeof value === "string") {
      values[key] = value.trim();
    }
  }
  const from = values.$dateFrom;
  const to = values.$dateTo;
  const fromOffset = parseRunJsRelativeDateOffset(from);
  const toOffset = parseRunJsRelativeDateOffset(to);
  if (to === "now" && (fromOffset == null ? void 0 : fromOffset.direction) === "past") {
    return {
      type: "past",
      number: fromOffset.number,
      unit: fromOffset.unit
    };
  }
  if (from === "now" && (toOffset == null ? void 0 : toOffset.direction) === "next") {
    return {
      type: "next",
      number: toOffset.number,
      unit: toOffset.unit
    };
  }
  return void 0;
}
function parseRunJsRelativeDateOffset(value) {
  const match = String(value || "").trim().match(/^([+-])\s*(\d+)\s*(d|day|days|w|week|weeks|m|mo|month|months|q|quarter|quarters|y|year|years)$/i);
  if (!match) {
    return null;
  }
  const unit = normalizeRunJsRelativeDateUnit(match[3]);
  if (!unit) {
    return null;
  }
  return {
    direction: match[1] === "-" ? "past" : "next",
    number: Number(match[2]),
    unit
  };
}
function normalizeRunJsRelativeDateUnit(value) {
  const normalized = String(value || "").toLowerCase();
  if (normalized === "d" || normalized === "day" || normalized === "days") {
    return "day";
  }
  if (normalized === "w" || normalized === "week" || normalized === "weeks") {
    return "week";
  }
  if (normalized === "m" || normalized === "mo" || normalized === "month" || normalized === "months") {
    return "month";
  }
  if (normalized === "q" || normalized === "quarter" || normalized === "quarters") {
    return "quarter";
  }
  if (normalized === "y" || normalized === "year" || normalized === "years") {
    return "year";
  }
  return "";
}
function buildRunJsResourceFilterFieldError(input) {
  const collectionName = (0, import_service_helpers.getCollectionName)(input.collection) || input.collectionName;
  return {
    availableFields: (0, import_service_helpers.getCollectionFields)(input.collection).map((field) => String((0, import_service_helpers.getFieldName)(field) || "").trim()).filter(Boolean),
    capability: input.capability,
    collectionName,
    dataSourceKey: input.dataSourceKey,
    fieldPath: input.fieldPath,
    index: input.index,
    message: `flowSurfaces authoring ${input.capability}(...) references unknown filter field '${input.fieldPath}' on collection '${collectionName}'`,
    resourceType: input.resourceType,
    ruleId: "runjs-resource-filter-field-unknown"
  };
}
function resolveRunJsResourceFilterFieldPath(collection, dataSourceKey, fieldPath, context) {
  if (typeof (collection == null ? void 0 : collection.getFieldByPath) === "function") {
    const direct2 = collection.getFieldByPath(fieldPath);
    if (direct2) {
      return { collection, field: direct2 };
    }
  }
  const direct = (0, import_service_helpers.resolveFieldFromCollection)(collection, fieldPath);
  if (direct) {
    return { collection, field: direct };
  }
  let currentCollection = collection;
  let currentDataSourceKey = dataSourceKey || (collection == null ? void 0 : collection.dataSourceKey) || "main";
  const parts = String(fieldPath || "").split(".").filter(Boolean);
  for (let index = 0; index < parts.length; index += 1) {
    const part = parts[index];
    const field = getRunJsCollectionField(currentCollection, part);
    if (!field) {
      return { collection: currentCollection, field: null };
    }
    if (index === parts.length - 1) {
      return { collection: currentCollection, field };
    }
    if (!(0, import_service_helpers.isAssociationField)(field)) {
      return { collection: currentCollection, field: null };
    }
    const targetCollection = (0, import_service_helpers.resolveFieldTargetCollection)(
      field,
      (currentCollection == null ? void 0 : currentCollection.dataSourceKey) || currentDataSourceKey,
      (nextDataSourceKey, collectionName) => {
        var _a;
        return (_a = context.getCollection) == null ? void 0 : _a.call(context, nextDataSourceKey, collectionName);
      }
    );
    if (!targetCollection) {
      return { collection: currentCollection, field: null };
    }
    currentCollection = targetCollection;
    currentDataSourceKey = (targetCollection == null ? void 0 : targetCollection.dataSourceKey) || currentDataSourceKey;
  }
  return { collection: currentCollection, field: null };
}
function getRunJsCollectionField(collection, fieldName) {
  var _a, _b, _c, _d, _e, _f, _g;
  const normalized = String(fieldName || "").trim();
  if (!normalized) {
    return null;
  }
  const field = ((_a = collection == null ? void 0 : collection.getField) == null ? void 0 : _a.call(collection, normalized)) || ((_c = (_b = collection == null ? void 0 : collection.fields) == null ? void 0 : _b.get) == null ? void 0 : _c.call(_b, normalized));
  if (field) {
    return field;
  }
  const modelAttributes = (typeof ((_d = collection == null ? void 0 : collection.model) == null ? void 0 : _d.getAttributes) === "function" ? collection.model.getAttributes() : null) || ((_e = collection == null ? void 0 : collection.model) == null ? void 0 : _e.rawAttributes) || ((_f = collection == null ? void 0 : collection.model) == null ? void 0 : _f.attributes) || {};
  const modelAttribute = modelAttributes == null ? void 0 : modelAttributes[normalized];
  if (modelAttribute) {
    return {
      name: normalized,
      type: ((_g = modelAttribute.type) == null ? void 0 : _g.key) || modelAttribute.type,
      interface: modelAttribute.interface
    };
  }
  if (normalized === "id") {
    return {
      name: "id",
      type: "bigInt",
      interface: "id"
    };
  }
  return null;
}
function isRunJsJsonLikeField(field) {
  const type = String((0, import_service_helpers.getFieldType)(field) || "").trim().toLowerCase();
  return type === "json" || type === "jsonb" || type === "array";
}
function isRunJsSupportedFilterOperator(operator) {
  return import_constants.RUNJS_SUPPORTED_FILTER_OPERATORS.has(String(operator || "").trim());
}
function getSuggestedRunJsFilterOperator(operator) {
  const normalized = String(operator || "").trim();
  if (normalized === "$dataOn" || normalized === "dataOn") {
    return "$dateOn";
  }
  if (!normalized || normalized.startsWith("$")) {
    return "";
  }
  const suggested = `$${normalized}`;
  return isRunJsSupportedFilterOperator(suggested) ? suggested : "";
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  collectAstInvalidResourceFilterCalls
});
