/**
 * 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 contract_guard_exports = {};
__export(contract_guard_exports, {
  FlowSurfaceContractGuard: () => FlowSurfaceContractGuard
});
module.exports = __toCommonJS(contract_guard_exports);
var import_lodash = __toESM(require("lodash"));
var import_catalog = require("./catalog");
var import_errors = require("./errors");
var import_filter_group = require("./filter-group");
const EMPTY_GRID_ITEM_UID = "__EMPTY__";
class FlowSurfaceContractGuard {
  mergeDomainValue(domain, currentValue, nextValue, contract, use) {
    if (!import_lodash.default.isPlainObject(nextValue)) {
      (0, import_errors.throwBadRequest)(`flowSurfaces updateSettings domain '${domain}' on '${use}' requires an object payload`);
    }
    if (contract.wildcard) {
      if (!Object.keys(nextValue).length) {
        return {};
      }
      return contract.mergeStrategy === "replace" ? import_lodash.default.cloneDeep(nextValue) : mergeFlowSurfaceSettingsValue(currentValue || {}, nextValue);
    }
    if (contract.groups) {
      const unknownGroups = Object.keys(nextValue).filter((key) => !contract.allowedKeys.includes(key));
      if (unknownGroups.length) {
        (0, import_errors.throwBadRequest)(
          `flowSurfaces updateSettings domain '${domain}' on '${use}' does not allow groups: ${unknownGroups.join(
            ", "
          )}`
        );
      }
      const next2 = import_lodash.default.cloneDeep(currentValue || {});
      Object.entries(nextValue).forEach(([groupKey, value]) => {
        var _a;
        const groupContract = (_a = contract.groups) == null ? void 0 : _a[groupKey];
        if (!groupContract) {
          return;
        }
        if (value === null || import_lodash.default.isPlainObject(value) && !Object.keys(value).length) {
          if (!groupContract.clearable) {
            (0, import_errors.throwBadRequest)(
              `flowSurfaces updateSettings domain '${domain}.${groupKey}' on '${use}' does not support clearing`
            );
          }
          next2[groupKey] = {};
          return;
        }
        if (!import_lodash.default.isPlainObject(value)) {
          (0, import_errors.throwBadRequest)(
            `flowSurfaces updateSettings domain '${domain}.${groupKey}' on '${use}' requires an object payload`
          );
        }
        const normalizedValue2 = normalizeGroupValue(value, groupContract, {
          domain,
          groupKey,
          use
        });
        const invalidPaths = collectLeafPaths(normalizedValue2).filter(
          (path) => !isAllowedGroupPath(groupContract, path)
        );
        if (invalidPaths.length) {
          (0, import_errors.throwBadRequest)(
            `flowSurfaces updateSettings domain '${domain}.${groupKey}' on '${use}' does not allow paths: ${invalidPaths.join(
              ", "
            )}`
          );
        }
        const invalidValueTypes2 = collectTypedValueErrors(normalizedValue2, groupContract.pathSchemas || {});
        if (invalidValueTypes2.length) {
          (0, import_errors.throwBadRequest)(
            `flowSurfaces updateSettings domain '${domain}.${groupKey}' on '${use}' has invalid values: ${invalidValueTypes2.join(
              ", "
            )}`
          );
        }
        next2[groupKey] = groupContract.mergeStrategy === "replace" ? import_lodash.default.cloneDeep(normalizedValue2) : mergeFlowSurfaceSettingsValue((currentValue == null ? void 0 : currentValue[groupKey]) || {}, normalizedValue2);
      });
      return next2;
    }
    const unknownKeys = Object.keys(nextValue).filter((key) => !contract.allowedKeys.includes(key));
    if (unknownKeys.length) {
      (0, import_errors.throwBadRequest)(
        `flowSurfaces updateSettings domain '${domain}' on '${use}' does not allow keys: ${unknownKeys.join(", ")}`
      );
    }
    const normalizedValue = normalizeDomainValue(nextValue, contract, {
      domain,
      use
    });
    if (!Object.keys(normalizedValue).length) {
      return {};
    }
    const invalidValueTypes = collectTypedValueErrors(normalizedValue, contract.pathSchemas || {});
    if (invalidValueTypes.length) {
      (0, import_errors.throwBadRequest)(
        `flowSurfaces updateSettings domain '${domain}' on '${use}' has invalid values: ${invalidValueTypes.join(
          ", "
        )}`
      );
    }
    const next = import_lodash.default.cloneDeep(currentValue || {});
    Object.entries(normalizedValue).forEach(([key, value]) => {
      next[key] = contract.mergeStrategy === "replace" || !import_lodash.default.isPlainObject(value) || !Object.keys(value).length ? import_lodash.default.cloneDeep(value) : mergeFlowSurfaceSettingsValue((currentValue == null ? void 0 : currentValue[key]) || {}, value);
    });
    return next;
  }
  validateFlowRegistry(node, flowRegistry) {
    var _a, _b, _c;
    if (!import_lodash.default.isPlainObject(flowRegistry)) {
      (0, import_errors.throwBadRequest)("flowSurfaces setEventFlows requires an object flowRegistry");
    }
    const contract = (0, import_catalog.getNodeContract)(node == null ? void 0 : node.use);
    const allowedDirectEvents = new Set(((_a = contract.eventCapabilities) == null ? void 0 : _a.direct) || []);
    const allowedObjectEvents = new Set(((_b = contract.eventCapabilities) == null ? void 0 : _b.object) || []);
    const stepParamGroups = ((_c = contract.domains.stepParams) == null ? void 0 : _c.groups) || {};
    Object.entries(flowRegistry).forEach(([key, flow]) => {
      var _a2, _b2;
      if (!import_lodash.default.isPlainObject(flow)) {
        (0, import_errors.throwBadRequest)(`flowSurfaces flow '${key}' must be an object`);
      }
      const on = flow.on;
      if (!on) {
        return;
      }
      const onObj = typeof on === "string" ? { eventName: on } : on;
      const eventName = onObj == null ? void 0 : onObj.eventName;
      if (!eventName || typeof eventName !== "string") {
        (0, import_errors.throwBadRequest)(`flowSurfaces flow '${key}' has invalid eventName`);
      }
      if (typeof on === "string" && !allowedDirectEvents.has(eventName)) {
        (0, import_errors.throwBadRequest)(`flowSurfaces flow '${key}' event '${eventName}' is not allowed on '${node == null ? void 0 : node.use}'`);
      }
      if (typeof on !== "string" && !allowedObjectEvents.has(eventName) && !allowedDirectEvents.has(eventName)) {
        (0, import_errors.throwBadRequest)(`flowSurfaces flow '${key}' event '${eventName}' is not allowed on '${node == null ? void 0 : node.use}'`);
      }
      const phase = onObj == null ? void 0 : onObj.phase;
      const allowedPhases = ["beforeAllFlows", "afterAllFlows", "beforeFlow", "afterFlow", "beforeStep", "afterStep"];
      if (phase && !allowedPhases.includes(phase)) {
        (0, import_errors.throwBadRequest)(`flowSurfaces flow '${key}' has invalid phase '${phase}'`);
      }
      if (["beforeFlow", "afterFlow", "beforeStep", "afterStep"].includes(phase) && !onObj.flowKey) {
        (0, import_errors.throwBadRequest)(`flowSurfaces flow '${key}' requires flowKey for phase '${phase}'`);
      }
      if (["beforeStep", "afterStep"].includes(phase) && !onObj.stepKey) {
        (0, import_errors.throwBadRequest)(`flowSurfaces flow '${key}' requires stepKey for phase '${phase}'`);
      }
      if (onObj.flowKey && !stepParamGroups[onObj.flowKey]) {
        (0, import_errors.throwBadRequest)(`flowSurfaces flow '${key}' references unknown flowKey '${onObj.flowKey}' on '${node == null ? void 0 : node.use}'`);
      }
      const allowedStepKeys = (_a2 = stepParamGroups[onObj.flowKey]) == null ? void 0 : _a2.eventBindingSteps;
      if (onObj.stepKey && allowedStepKeys !== "*" && Array.isArray(allowedStepKeys) && !allowedStepKeys.includes(onObj.stepKey)) {
        (0, import_errors.throwBadRequest)(
          `flowSurfaces flow '${key}' references unknown stepKey '${onObj.stepKey}' for flowKey '${onObj.flowKey}' on '${node == null ? void 0 : node.use}'`
        );
      }
      if (onObj.flowKey) {
        const flowGroupContract = stepParamGroups[onObj.flowKey];
        const flowGroup = (_b2 = node == null ? void 0 : node.stepParams) == null ? void 0 : _b2[onObj.flowKey];
        const contractDefinesFlow = isContractDefinedFlowGroup(flowGroupContract);
        const implicitStepAvailable = isImplicitContractStep(flowGroupContract, onObj.stepKey);
        if ((!import_lodash.default.isPlainObject(flowGroup) || !Object.keys(flowGroup).length) && !contractDefinesFlow) {
          (0, import_errors.throwBadRequest)(
            `flowSurfaces flow '${key}' references unavailable flowKey '${onObj.flowKey}' on '${node == null ? void 0 : node.use}'`
          );
        }
        if (onObj.stepKey && import_lodash.default.isNil(flowGroup == null ? void 0 : flowGroup[onObj.stepKey]) && !implicitStepAvailable) {
          (0, import_errors.throwBadRequest)(
            `flowSurfaces flow '${key}' references unavailable stepKey '${onObj.stepKey}' for flowKey '${onObj.flowKey}' on '${node == null ? void 0 : node.use}'`
          );
        }
      }
    });
  }
  validateLayout(grid, layout) {
    var _a;
    const itemUids = new Set(import_lodash.default.castArray(((_a = grid == null ? void 0 : grid.subModels) == null ? void 0 : _a.items) || []).map((item) => item.uid));
    const seen = /* @__PURE__ */ new Set();
    Object.entries(layout.rows || {}).forEach(([rowId, cells]) => {
      var _a2;
      import_lodash.default.castArray(cells).forEach((cell) => {
        import_lodash.default.castArray(cell).forEach((itemUid) => {
          if (itemUid === EMPTY_GRID_ITEM_UID) {
            return;
          }
          if (!itemUids.has(itemUid)) {
            (0, import_errors.throwBadRequest)(`flowSurfaces layout item '${itemUid}' does not exist under grid '${grid.uid}'`);
          }
          if (seen.has(itemUid)) {
            (0, import_errors.throwBadRequest)(`flowSurfaces layout item '${itemUid}' is duplicated under grid '${grid.uid}'`);
          }
          seen.add(itemUid);
        });
      });
      const sizeArray = (_a2 = layout.sizes) == null ? void 0 : _a2[rowId];
      if (!sizeArray) {
        (0, import_errors.throwBadRequest)(`flowSurfaces layout row '${rowId}' requires sizes definition`);
      }
      if (sizeArray.length !== import_lodash.default.castArray(cells).length) {
        (0, import_errors.throwBadRequest)(`flowSurfaces layout sizes length of row '${rowId}' does not match columns length`);
      }
    });
    const declaredRows = Object.keys(layout.rows || {});
    if (import_lodash.default.difference(declaredRows, layout.rowOrder || []).length || import_lodash.default.difference(layout.rowOrder || [], declaredRows).length) {
      (0, import_errors.throwBadRequest)("flowSurfaces layout rowOrder must match rows");
    }
    if ((layout.rowOrder || []).length !== new Set(layout.rowOrder || []).size) {
      (0, import_errors.throwBadRequest)("flowSurfaces layout rowOrder contains duplicate row ids");
    }
    if (seen.size !== itemUids.size) {
      const missing = Array.from(itemUids).filter((uid) => !seen.has(uid));
      (0, import_errors.throwBadRequest)(`flowSurfaces layout must cover all grid children, missing: ${missing.join(", ")}`);
    }
  }
  validateNodeTreeAgainstContract(node) {
    if (!(node == null ? void 0 : node.use)) {
      return;
    }
    const contract = (0, import_catalog.getNodeContract)(node.use);
    ["props", "decoratorProps", "stepParams", "flowRegistry"].forEach((domain) => {
      if (import_lodash.default.isUndefined(node[domain])) {
        return;
      }
      if (import_lodash.default.isPlainObject(node[domain]) && !Object.keys(node[domain]).length) {
        return;
      }
      if (!contract.editableDomains.includes(domain)) {
        (0, import_errors.throwBadRequest)(`flowSurfaces create tree domain '${domain}' is not editable on '${node.use}'`);
      }
      const domainContract = contract.domains[domain];
      if (!domainContract) {
        (0, import_errors.throwBadRequest)(`flowSurfaces create tree domain '${domain}' is not supported by '${node.use}'`);
      }
      node[domain] = this.mergeDomainValue(domain, void 0, node[domain], domainContract, node.use);
    });
    if (import_lodash.default.isPlainObject(node.flowRegistry) && Object.keys(node.flowRegistry).length) {
      this.validateFlowRegistry(node, node.flowRegistry);
    }
    Object.values(node.subModels || {}).forEach((value) => {
      import_lodash.default.castArray(value).forEach((child) => this.validateNodeTreeAgainstContract(child));
    });
  }
}
function mergeFlowSurfaceSettingsValue(currentValue, nextValue) {
  return import_lodash.default.mergeWith({}, currentValue || {}, nextValue, (_current, next) => {
    if (Array.isArray(next)) {
      return import_lodash.default.cloneDeep(next);
    }
    return void 0;
  });
}
function matchesContractPath(pattern, path) {
  if (pattern === "*") {
    return true;
  }
  if (pattern === path) {
    return true;
  }
  if (pattern.endsWith(".*")) {
    const prefix = pattern.slice(0, -2);
    return path === prefix || path.startsWith(`${prefix}.`);
  }
  return false;
}
function isAllowedGroupPath(groupContract, path) {
  return groupContract.allowedPaths.some((pattern) => {
    var _a;
    if (matchesContractPath(pattern, path)) {
      return true;
    }
    const schema = (_a = groupContract.pathSchemas) == null ? void 0 : _a[pattern];
    return (schema == null ? void 0 : schema.type) === "object" && path.startsWith(`${pattern}.`);
  });
}
function collectLeafPaths(value, prefix = "") {
  if (!import_lodash.default.isPlainObject(value) || !Object.keys(value).length) {
    return prefix ? [prefix] : [];
  }
  return Object.entries(value).flatMap(([key, nested]) => collectLeafPaths(nested, prefix ? `${prefix}.${key}` : key));
}
function collectTypedValueErrors(value, pathSchemas) {
  return Object.entries(pathSchemas).flatMap(([path, schema]) => {
    if (!import_lodash.default.has(value, path)) {
      return [];
    }
    const candidate = import_lodash.default.get(value, path);
    return collectSchemaValueErrors(candidate, schema, path);
  });
}
function collectSchemaValueErrors(value, schema, path) {
  if (value === null) {
    return (schema == null ? void 0 : schema.nullable) === true ? [] : [`${path} expected ${(schema == null ? void 0 : schema.type) || "value"}`];
  }
  switch (schema == null ? void 0 : schema.type) {
    case "string":
      return typeof value === "string" ? [] : [`${path} expected string`];
    case "boolean":
      return typeof value === "boolean" ? [] : [`${path} expected boolean`];
    case "number":
      return typeof value === "number" && Number.isFinite(value) ? [] : [`${path} expected number`];
    case "object":
      return import_lodash.default.isPlainObject(value) ? [] : [`${path} expected object`];
    case "array":
      if (!Array.isArray(value)) {
        return [`${path} expected array`];
      }
      if (!schema.items) {
        return [];
      }
      return value.flatMap((item, index) => collectSchemaValueErrors(item, schema.items, `${path}[${index}]`));
    default:
      return [];
  }
}
function normalizeDomainValue(value, contract, context) {
  const normalized = import_lodash.default.cloneDeep(value);
  Object.entries(contract.pathSchemas || {}).forEach(([path, schema]) => {
    if (!import_lodash.default.has(normalized, path)) {
      return;
    }
    if (isFilterGroupSchema(schema)) {
      import_lodash.default.set(normalized, path, normalizeFilterGroupValue(import_lodash.default.get(normalized, path), context, path));
    } else if (isLinkageRulesSchema(schema)) {
      import_lodash.default.set(normalized, path, normalizeLinkageRulesValue(import_lodash.default.get(normalized, path), context, path));
    }
  });
  return normalized;
}
function normalizeGroupValue(value, groupContract, context) {
  const normalized = import_lodash.default.cloneDeep(value);
  Object.entries(groupContract.pathSchemas || {}).forEach(([path, schema]) => {
    if (!import_lodash.default.has(normalized, path)) {
      return;
    }
    if (isFilterGroupSchema(schema)) {
      import_lodash.default.set(normalized, path, normalizeFilterGroupValue(import_lodash.default.get(normalized, path), context, path));
    } else if (isLinkageRulesSchema(schema)) {
      import_lodash.default.set(normalized, path, normalizeLinkageRulesValue(import_lodash.default.get(normalized, path), context, path));
    }
  });
  return normalized;
}
function isFilterGroupSchema(schema) {
  return (schema == null ? void 0 : schema["x-flowSurfaceFormat"]) === "filter-group";
}
function isLinkageRulesSchema(schema) {
  return (schema == null ? void 0 : schema["x-flowSurfaceFormat"]) === "linkage-rules";
}
function normalizeFilterGroupValue(value, context, path) {
  const domainPath = context.groupKey ? `${context.domain}.${context.groupKey}.${path}` : `${context.domain}.${path}`;
  return (0, import_filter_group.normalizeFlowSurfaceFilterGroupValue)(
    value,
    `flowSurfaces updateSettings domain '${domainPath}' on '${context.use}' expects FilterGroup like ${import_filter_group.FLOW_SURFACE_FILTER_GROUP_EXAMPLE}`,
    { strictDateValues: true }
  );
}
function normalizeLinkageRulesValue(value, context, path) {
  if (!Array.isArray(value)) {
    return value;
  }
  return value.map((rule, index) => {
    if (!import_lodash.default.isPlainObject(rule)) {
      (0, import_errors.throwBadRequest)(
        `flowSurfaces updateSettings domain '${getLinkageRuleDomainPath(context, path, index)}' on '${context.use}' expects each linkage rule to be an object`
      );
    }
    const normalizedRule = import_lodash.default.cloneDeep(rule);
    const hasCondition = import_lodash.default.has(normalizedRule, "condition");
    const hasWhen = import_lodash.default.has(normalizedRule, "when");
    const conditionLeaf = hasCondition ? "condition" : hasWhen ? "when" : "condition";
    const conditionInput = hasCondition ? import_lodash.default.get(normalizedRule, "condition") : hasWhen ? import_lodash.default.get(normalizedRule, "when") : import_filter_group.FLOW_SURFACE_EMPTY_FILTER_GROUP;
    import_lodash.default.set(
      normalizedRule,
      "condition",
      (0, import_filter_group.normalizeFlowSurfaceCompatibleFilterGroupValue)(
        conditionInput,
        `flowSurfaces updateSettings domain '${getLinkageRuleDomainPath(context, path, index, conditionLeaf)}' on '${context.use}' expects FilterGroup or backend query filter like ${import_filter_group.FLOW_SURFACE_FILTER_GROUP_EXAMPLE}`,
        { strictDateValues: true }
      )
    );
    const hasActions = import_lodash.default.has(normalizedRule, "actions");
    const hasThen = import_lodash.default.has(normalizedRule, "then");
    const actionsLeaf = hasActions ? "actions" : hasThen ? "then" : "actions";
    const actionsInput = hasActions ? import_lodash.default.get(normalizedRule, "actions") : hasThen ? import_lodash.default.get(normalizedRule, "then") : [];
    if (!Array.isArray(actionsInput)) {
      (0, import_errors.throwBadRequest)(
        `flowSurfaces updateSettings domain '${getLinkageRuleDomainPath(context, path, index, actionsLeaf)}' on '${context.use}' expects actions/then to be an array`
      );
    }
    import_lodash.default.set(normalizedRule, "actions", actionsInput);
    if (!import_lodash.default.has(normalizedRule, "key")) {
      import_lodash.default.set(normalizedRule, "key", `linkage-rule-${index + 1}`);
    }
    if (!import_lodash.default.has(normalizedRule, "title")) {
      import_lodash.default.set(normalizedRule, "title", `Linkage rule ${index + 1}`);
    }
    if (!import_lodash.default.has(normalizedRule, "enable")) {
      const enabled = import_lodash.default.get(normalizedRule, "enabled");
      import_lodash.default.set(normalizedRule, "enable", typeof enabled === "boolean" ? enabled : true);
    }
    import_lodash.default.unset(normalizedRule, "enabled");
    import_lodash.default.unset(normalizedRule, "when");
    import_lodash.default.unset(normalizedRule, "then");
    return normalizedRule;
  });
}
function getLinkageRuleDomainPath(context, path, index, leaf) {
  const basePath = context.groupKey ? `${context.domain}.${context.groupKey}.${path}[${index}]` : `${context.domain}.${path}[${index}]`;
  return leaf ? `${basePath}.${leaf}` : basePath;
}
function isContractDefinedFlowGroup(groupContract) {
  if (!groupContract) {
    return false;
  }
  if (!Array.isArray(groupContract.allowedPaths) || groupContract.allowedPaths.length === 0) {
    return true;
  }
  return hasImplicitContractSteps(groupContract);
}
function hasImplicitContractSteps(groupContract) {
  if (groupContract.eventBindingSteps === "*") {
    return false;
  }
  return import_lodash.default.castArray(groupContract.eventBindingSteps || []).some(
    (stepKey) => isImplicitContractStep(groupContract, stepKey)
  );
}
function isImplicitContractStep(groupContract, stepKey) {
  if (!groupContract || !stepKey) {
    return false;
  }
  if (groupContract.eventBindingSteps !== "*" && !import_lodash.default.castArray(groupContract.eventBindingSteps || []).includes(stepKey)) {
    return false;
  }
  return !import_lodash.default.castArray(groupContract.allowedPaths || []).some(
    (pattern) => pattern === stepKey || pattern === `${stepKey}.*` || pattern.startsWith(`${stepKey}.`)
  );
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  FlowSurfaceContractGuard
});
