/**
 * 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 meta_exports = {};
__export(meta_exports, {
  buildGetReactionMetaResult: () => buildGetReactionMetaResult,
  buildReactionMetaCapabilities: () => buildReactionMetaCapabilities
});
module.exports = __toCommonJS(meta_exports);
var import_node_crypto = require("node:crypto");
var import_lodash = __toESM(require("lodash"));
var import_service_helpers = require("../service-helpers");
var import_errors = require("./errors");
var import_registry = require("./registry");
var import_resolver = require("./resolver");
const STRING_OPERATORS = ["$includes", "$notIncludes", "$eq", "$ne", "$empty", "$notEmpty"];
const ARRAY_OPERATORS = ["$match", "$notMatch", "$anyOf", "$noneOf", "$empty", "$notEmpty"];
const OBJECT_OPERATORS = ["$eq", "$ne"];
const DATETIME_OPERATORS = [
  "$dateOn",
  "$dateNotOn",
  "$dateBefore",
  "$dateAfter",
  "$dateNotBefore",
  "$dateNotAfter",
  "$dateBetween",
  "$empty",
  "$notEmpty"
];
const NUMBER_OPERATORS = ["$eq", "$ne", "$gt", "$gte", "$lt", "$lte", "$empty", "$notEmpty"];
const ID_OPERATORS = ["$eq", "$ne", "$exists", "$notExists"];
const ENUM_OPERATORS = ["$eq", "$ne", "$in", "$notIn", "$empty", "$notEmpty"];
const TIME_OPERATORS = ["$eq", "$neq", "$empty", "$notEmpty"];
const BOOLEAN_OPERATORS = ["$isTruly", "$isFalsy", "$empty", "$notEmpty"];
const FIELD_STATE_BY_SCENE = {
  form: ["visible", "hidden", "hiddenReservedValue", "required", "notRequired", "disabled", "enabled"],
  details: ["visible", "hidden", "hiddenReservedValue"],
  subForm: ["visible", "hidden", "hiddenReservedValue", "required", "notRequired", "disabled", "enabled"]
};
const AUTHORABLE_LINKAGE_SCALAR_CONTEXT_PATHS = /* @__PURE__ */ new Set(["role", "locale", "token", "deviceType"]);
const AUTHORABLE_LINKAGE_CONTEXT_ROOTS = /* @__PURE__ */ new Set([
  "user",
  "collection",
  "formValues",
  "record",
  "item",
  "popup",
  "urlSearchParams"
]);
const CONTEXT_ROOT_BY_SCENE = {
  form: "formValues",
  details: "record",
  subForm: "formValues"
};
function getAllReactionKinds() {
  return Object.keys(import_registry.FLOW_SURFACE_REACTION_SLOT_REGISTRY);
}
function buildStableFingerprintString(value) {
  if (import_lodash.default.isNil(value)) {
    return "null";
  }
  if (Array.isArray(value)) {
    return `[${value.map((item) => buildStableFingerprintString(item)).join(",")}]`;
  }
  if (import_lodash.default.isPlainObject(value)) {
    const entries = Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${buildStableFingerprintString(value[key])}`);
    return `{${entries.join(",")}}`;
  }
  return JSON.stringify(value);
}
function buildReactionFingerprint(value) {
  return (0, import_node_crypto.createHash)("sha1").update(buildStableFingerprintString(value)).digest("hex");
}
function buildConditionOperatorsByPath(context) {
  const operatorsByPath = {};
  for (const [path, info] of collectContextPathEntries(context)) {
    if (!isAuthorableLinkageContextPath(path)) {
      continue;
    }
    operatorsByPath[path] = getOperatorsForContextInfo(info);
  }
  return operatorsByPath;
}
function buildConditionFieldMetaByPath(context) {
  const fieldMetaByPath = {};
  for (const [path, info] of collectContextPathEntries(context)) {
    if (!isAuthorableLinkageContextPath(path)) {
      continue;
    }
    fieldMetaByPath[path] = {
      ...(info == null ? void 0 : info.type) ? { type: info.type } : {},
      ...(info == null ? void 0 : info.interface) ? { interface: info.interface } : {}
    };
  }
  return fieldMetaByPath;
}
function isAuthorableLinkageContextPath(path) {
  const segments = String(path || "").split(".").filter(Boolean);
  const root = segments[0];
  if (!root) {
    return false;
  }
  if (AUTHORABLE_LINKAGE_SCALAR_CONTEXT_PATHS.has(root)) {
    return segments.length === 1;
  }
  return AUTHORABLE_LINKAGE_CONTEXT_ROOTS.has(root);
}
function collectContextPathEntries(context) {
  const result = /* @__PURE__ */ new Map();
  const visit = (prefix, info) => {
    if (!prefix || !info) {
      return;
    }
    result.set(prefix, info);
    if (info.dynamicProperties) {
      result.set(`${prefix}.*`, info.dynamicProperties);
    }
    for (const [childKey, childInfo] of Object.entries(info.properties || {})) {
      visit(`${prefix}.${childKey}`, childInfo);
    }
  };
  for (const [key, info] of Object.entries((context == null ? void 0 : context.vars) || {})) {
    visit(key, info);
  }
  return result;
}
function getOperatorsForContextInfo(info) {
  const normalizedInterface = String((info == null ? void 0 : info.interface) || "").trim();
  if (normalizedInterface === "checkbox") {
    return BOOLEAN_OPERATORS;
  }
  if (normalizedInterface === "time") {
    return TIME_OPERATORS;
  }
  if (normalizedInterface === "id") {
    return ID_OPERATORS;
  }
  if (normalizedInterface === "multipleSelect" || normalizedInterface === "checkboxGroup") {
    return ARRAY_OPERATORS;
  }
  if (normalizedInterface === "select" || normalizedInterface === "radioGroup" || ((info == null ? void 0 : info.enumValues) || []).length) {
    return ENUM_OPERATORS;
  }
  switch (info == null ? void 0 : info.type) {
    case "number":
      return NUMBER_OPERATORS;
    case "boolean":
      return BOOLEAN_OPERATORS;
    case "array":
      return ARRAY_OPERATORS;
    case "object":
      return OBJECT_OPERATORS;
    default:
      break;
  }
  if (["date", "dateOnly", "datetime", "datetimeNoTz", "createdAt", "updatedAt", "unixTimestamp"].includes(
    normalizedInterface
  )) {
    return DATETIME_OPERATORS;
  }
  return STRING_OPERATORS;
}
function getCanonicalRulesFromNode(node, capability) {
  const stepRoot = import_lodash.default.get(node, ["stepParams", capability.resolvedSlot.flowKey, capability.resolvedSlot.stepKey]);
  const rawValue = capability.resolvedSlot.valuePath == null ? stepRoot : import_lodash.default.get(stepRoot, capability.resolvedSlot.valuePath || "");
  return Array.isArray(rawValue) ? import_lodash.default.cloneDeep(rawValue) : [];
}
function normalizeCapabilityRules(input, capability, canonicalRules) {
  var _a;
  const normalizer = (_a = input.normalizeRules) == null ? void 0 : _a[capability.kind];
  if (!normalizer) {
    return import_lodash.default.cloneDeep(canonicalRules);
  }
  return normalizer({
    kind: capability.kind,
    resolvedScene: capability.resolvedScene,
    resolvedSlot: capability.resolvedSlot,
    canonicalRules,
    node: input.resolvedTarget.node,
    context: input.context
  });
}
function inferFieldOptionSupport(scene) {
  switch (scene) {
    case "form":
      return {
        supportsDefault: true,
        supportsAssign: true,
        supportsState: FIELD_STATE_BY_SCENE.form
      };
    case "details":
      return {
        supportsDefault: false,
        supportsAssign: false,
        supportsState: FIELD_STATE_BY_SCENE.details
      };
    case "subForm":
      return {
        supportsDefault: false,
        supportsAssign: true,
        supportsState: FIELD_STATE_BY_SCENE.subForm
      };
    default:
      return {
        supportsDefault: false,
        supportsAssign: false,
        supportsState: []
      };
  }
}
function inferNodeFieldLabel(node, fallback) {
  var _a, _b, _c, _d;
  const labelCandidates = [
    (_a = node == null ? void 0 : node.props) == null ? void 0 : _a.label,
    (_b = node == null ? void 0 : node.decoratorProps) == null ? void 0 : _b.label,
    (_c = node == null ? void 0 : node.props) == null ? void 0 : _c.title,
    (_d = node == null ? void 0 : node.decoratorProps) == null ? void 0 : _d.title,
    import_lodash.default.get(node, ["stepParams", "cardSettings", "titleDescription", "title"])
  ];
  for (const candidate of labelCandidates) {
    const normalized = String(candidate || "").trim();
    if (normalized) {
      return normalized;
    }
  }
  return fallback;
}
function buildContextPathIndex(context) {
  const entries = collectContextPathEntries(context);
  return new Map(entries);
}
function enrichFieldOptionFromContext(option, contextPathIndex, scene) {
  const contextRoot = CONTEXT_ROOT_BY_SCENE[scene];
  if (!contextRoot) {
    return option;
  }
  const contextInfo = contextPathIndex.get(`${contextRoot}.${option.path}`);
  return {
    ...option,
    ...(contextInfo == null ? void 0 : contextInfo.interface) ? { interface: contextInfo.interface } : {},
    ...(contextInfo == null ? void 0 : contextInfo.type) ? { type: contextInfo.type } : {}
  };
}
function collectFieldOptionsFromNode(node, context, scene) {
  const dedupe = /* @__PURE__ */ new Map();
  const support = inferFieldOptionSupport(scene);
  const contextPathIndex = buildContextPathIndex(context);
  const visit = (current) => {
    var _a, _b, _c, _d, _e, _f;
    if (!current || typeof current !== "object") {
      return;
    }
    const fieldInit = ((_b = (_a = current == null ? void 0 : current.stepParams) == null ? void 0 : _a.fieldSettings) == null ? void 0 : _b.init) || ((_f = (_e = (_d = (_c = current == null ? void 0 : current.subModels) == null ? void 0 : _c.field) == null ? void 0 : _d.stepParams) == null ? void 0 : _e.fieldSettings) == null ? void 0 : _f.init);
    const fieldPath = String((fieldInit == null ? void 0 : fieldInit.fieldPath) || "").trim();
    if (fieldPath) {
      const normalizedPath = (0, import_service_helpers.normalizeFieldPath)(fieldPath, fieldInit == null ? void 0 : fieldInit.associationPathName);
      if (normalizedPath && !dedupe.has(normalizedPath)) {
        const baseOption = {
          path: normalizedPath,
          label: inferNodeFieldLabel(current, normalizedPath),
          supportsDefault: support.supportsDefault,
          supportsAssign: support.supportsAssign,
          supportsState: support.supportsState
        };
        dedupe.set(normalizedPath, enrichFieldOptionFromContext(baseOption, contextPathIndex, scene));
      }
    }
    for (const child of Object.values(current.subModels || {})) {
      const children = Array.isArray(child) ? child : [child];
      children.forEach((item) => visit(item));
    }
  };
  visit(node);
  return [...dedupe.values()].sort((a, b) => a.path.localeCompare(b.path));
}
function collectFieldOptionsFromContext(context, scene) {
  var _a;
  const contextRoot = CONTEXT_ROOT_BY_SCENE[scene];
  const support = inferFieldOptionSupport(scene);
  if (!contextRoot) {
    return [];
  }
  const root = (_a = context == null ? void 0 : context.vars) == null ? void 0 : _a[contextRoot];
  const result = /* @__PURE__ */ new Map();
  const visit = (prefix, info) => {
    if (!prefix || !info) {
      return;
    }
    if (!info.properties || !Object.keys(info.properties).length) {
      result.set(prefix, {
        path: prefix,
        label: info.title || prefix,
        interface: info.interface,
        type: info.type,
        supportsDefault: support.supportsDefault,
        supportsAssign: support.supportsAssign,
        supportsState: support.supportsState
      });
      return;
    }
    for (const [childKey, childInfo] of Object.entries(info.properties)) {
      visit(prefix ? `${prefix}.${childKey}` : childKey, childInfo);
    }
  };
  for (const [childKey, childInfo] of Object.entries((root == null ? void 0 : root.properties) || {})) {
    visit(childKey, childInfo);
  }
  return [...result.values()].sort((a, b) => a.path.localeCompare(b.path));
}
function collectFieldOptions(node, context, scene) {
  const fromNode = collectFieldOptionsFromNode(node, context, scene);
  return fromNode.length ? fromNode : collectFieldOptionsFromContext(context, scene);
}
function buildSupportedActions(capability) {
  switch (capability.kind) {
    case "blockLinkage":
      return [{ type: "setBlockState", states: ["visible", "hidden"] }, { type: "runjs" }];
    case "actionLinkage":
      return [
        { type: "setActionState", states: ["visible", "hidden", "hiddenText", "enabled", "disabled"] },
        { type: "runjs" }
      ];
    case "fieldLinkage":
      if (capability.resolvedScene === "details") {
        return [{ type: "setFieldState", states: FIELD_STATE_BY_SCENE.details }, { type: "runjs" }];
      }
      if (capability.resolvedScene === "subForm") {
        return [
          { type: "setFieldState", states: FIELD_STATE_BY_SCENE.subForm },
          { type: "assignField" },
          { type: "runjs" }
        ];
      }
      return [
        { type: "setFieldState", states: FIELD_STATE_BY_SCENE.form },
        { type: "assignField" },
        { type: "setFieldDefaultValue" },
        { type: "runjs" }
      ];
    default:
      return [];
  }
}
function buildUnavailableCapability(kind) {
  if (kind === "fieldValue") {
    return {
      kind,
      code: import_errors.FLOW_SURFACE_REACTION_FORM_ONLY,
      reason: "Field value rules are only available on create/edit form blocks"
    };
  }
  return {
    kind,
    code: import_errors.FLOW_SURFACE_REACTION_UNSUPPORTED_TARGET_KIND,
    reason: `The current target does not support ${kind}`
  };
}
function buildCapabilityFingerprint(capability, canonicalRules) {
  return buildReactionFingerprint({
    kind: capability.kind,
    scene: capability.resolvedScene,
    slot: capability.resolvedSlot,
    canonicalRules
  });
}
function buildFieldValueCapability(input, capability) {
  const storageNode = (0, import_resolver.resolveReactionStorageNode)(input.resolvedTarget, capability);
  const canonicalRules = getCanonicalRulesFromNode(storageNode, capability);
  return {
    kind: "fieldValue",
    resolvedScene: capability.resolvedScene,
    resolvedSlot: capability.resolvedSlot,
    fingerprint: buildCapabilityFingerprint(capability, canonicalRules),
    normalizedRules: normalizeCapabilityRules(input, capability, canonicalRules),
    canonicalRules,
    context: input.context,
    targetFields: collectFieldOptions(input.resolvedTarget.node, input.context, capability.resolvedScene),
    conditionMeta: {
      operatorsByPath: buildConditionOperatorsByPath(input.context),
      fieldMetaByPath: buildConditionFieldMetaByPath(input.context)
    },
    valueExprMeta: {
      supportedSources: ["literal", "path", "runjs"],
      runjsScene: "fieldValue"
    }
  };
}
function buildLinkageCapability(input, capability) {
  const storageNode = (0, import_resolver.resolveReactionStorageNode)(input.resolvedTarget, capability);
  const canonicalRules = getCanonicalRulesFromNode(storageNode, capability);
  const base = {
    resolvedScene: capability.resolvedScene,
    resolvedSlot: capability.resolvedSlot,
    fingerprint: buildCapabilityFingerprint(capability, canonicalRules),
    canonicalRules,
    context: input.context,
    supportedActions: buildSupportedActions(capability),
    conditionMeta: {
      operatorsByPath: buildConditionOperatorsByPath(input.context),
      fieldMetaByPath: buildConditionFieldMetaByPath(input.context)
    }
  };
  if (capability.kind === "fieldLinkage") {
    return {
      ...base,
      kind: "fieldLinkage",
      normalizedRules: normalizeCapabilityRules(input, capability, canonicalRules),
      targetFields: collectFieldOptions(input.resolvedTarget.node, input.context, capability.resolvedScene),
      ...capability.resolvedScene === "details" ? {} : {
        valueExprMeta: {
          supportedSources: ["literal", "path", "runjs"],
          runjsScene: "linkage"
        }
      }
    };
  }
  if (capability.kind === "blockLinkage") {
    return {
      ...base,
      kind: "blockLinkage",
      normalizedRules: normalizeCapabilityRules(input, capability, canonicalRules)
    };
  }
  return {
    ...base,
    kind: "actionLinkage",
    normalizedRules: normalizeCapabilityRules(input, capability, canonicalRules)
  };
}
function buildReactionMetaCapabilities(input) {
  const capabilities = [];
  const unavailable = [];
  const capabilityMap = new Map(input.resolvedTarget.capabilities.map((item) => [item.kind, item]));
  for (const kind of getAllReactionKinds()) {
    const capability = capabilityMap.get(kind);
    if (!capability) {
      unavailable.push(buildUnavailableCapability(kind));
      continue;
    }
    if (kind === "fieldValue") {
      capabilities.push(buildFieldValueCapability(input, capability));
      continue;
    }
    capabilities.push(buildLinkageCapability(input, capability));
  }
  return {
    capabilities,
    unavailable
  };
}
function buildGetReactionMetaResult(input) {
  const { capabilities, unavailable } = buildReactionMetaCapabilities(input);
  return {
    target: input.resolvedTarget.target,
    capabilities,
    unavailable
  };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  buildGetReactionMetaResult,
  buildReactionMetaCapabilities
});
