import { commandRegistry } from "./registry.js";
import { defaultUndoToken } from "./types.js";
import {
  canonicalizeResourceTag,
  deriveResourceFromCommandId,
  invalidateCrudCache,
  pickFirstIdentifier,
  isCrudCacheDebugEnabled
} from "@open-mercato/shared/lib/crud/cache";
import { normalizeCustomFieldKey } from "@open-mercato/shared/lib/custom-fields/keys";
import { getAllCommandInterceptorInstances } from "./command-interceptor-store.js";
import {
  runCommandInterceptorsBefore,
  runCommandInterceptorsAfter,
  runCommandInterceptorsBeforeUndo,
  runCommandInterceptorsAfterUndo
} from "./command-interceptor-runner.js";
import { CommandInterceptorError } from "./errors.js";
import { isReadProjectionAlwaysConsistent } from "@open-mercato/shared/lib/data/consistency";
import { createLogger } from "../logger/index.js";
const logger = createLogger("shared").child({ component: "commands" });
const SKIPPED_ACTION_LOG_RESOURCE_KINDS = /* @__PURE__ */ new Set([
  "audit_logs.access",
  "audit_logs.action",
  "dashboards.layout",
  "dashboards.user_widgets",
  "dashboards.role_widgets"
]);
function asRecord(input) {
  if (!input || typeof input !== "object" || Array.isArray(input)) return null;
  return input;
}
function extractPrimaryIdFromCommandResult(result) {
  const r = asRecord(result);
  if (!r) return null;
  const direct = pickFirstIdentifier(r.id, r.entityId, r.recordId);
  if (direct) return direct;
  for (const key of [
    "warehouseId",
    "zoneId",
    "locationId",
    "lotId",
    "reservationId",
    "profileId",
    "movementId",
    "balanceId"
  ]) {
    const v = r[key];
    if (typeof v === "string" && v.trim().length > 0) return v.trim();
  }
  return null;
}
function toISOString(value) {
  if (value instanceof Date) {
    const iso = value.toISOString();
    return Number.isNaN(value.getTime()) ? null : iso;
  }
  if (typeof value === "string") {
    const parsed = new Date(value);
    return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
  }
  return null;
}
function deepEqual(a, b, seen) {
  if (Object.is(a, b)) return true;
  if (a instanceof Date || b instanceof Date) {
    const aIso = toISOString(a);
    const bIso = toISOString(b);
    if (aIso != null && bIso != null) return aIso === bIso;
    return false;
  }
  if (Array.isArray(a) && Array.isArray(b)) {
    if (a.length !== b.length) return false;
    return a.every((value, index) => deepEqual(value, b[index], seen));
  }
  if (a && b && typeof a === "object" && typeof b === "object") {
    if (!seen) seen = /* @__PURE__ */ new Set();
    if (seen.has(a) || seen.has(b)) return false;
    seen.add(a);
    seen.add(b);
    const aRec = a;
    const bRec = b;
    const keysA = Object.keys(aRec);
    const keysB = Object.keys(bRec);
    if (keysA.length !== keysB.length) return false;
    return keysA.every((key) => deepEqual(aRec[key], bRec[key], seen));
  }
  return false;
}
const CUSTOM_FIELD_CONTAINER_KEYS = /* @__PURE__ */ new Set(["custom", "customFields", "customValues", "cf"]);
const SKIPPED_CHANGE_KEYS = /* @__PURE__ */ new Set(["updatedAt", "updated_at"]);
function appendCustomFieldChanges(changes, before, after) {
  const beforeRec = asRecord(before);
  const afterRec = asRecord(after);
  if (!beforeRec && !afterRec) return false;
  const left = beforeRec ?? {};
  const right = afterRec ?? {};
  const keys = /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)]);
  for (const key of keys) {
    const from = left[key];
    const to = right[key];
    if (!deepEqual(from, to)) {
      changes[normalizeCustomFieldKey(key)] = { from, to };
    }
  }
  return true;
}
function buildRecordChanges(before, after) {
  return buildRecordChangesDeep(before, after);
}
function buildRecordChangesDeep(before, after, prefix, seen) {
  const changes = {};
  if (!seen) seen = /* @__PURE__ */ new Set();
  if (seen.has(before) || seen.has(after)) return changes;
  seen.add(before);
  seen.add(after);
  const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
  for (const key of keys) {
    if (SKIPPED_CHANGE_KEYS.has(key)) continue;
    if (CUSTOM_FIELD_CONTAINER_KEYS.has(key)) {
      const handled = appendCustomFieldChanges(changes, before[key], after[key]);
      if (handled) continue;
    }
    const from = before[key];
    const to = after[key];
    const path = prefix ? `${prefix}.${key}` : key;
    const fromRec = asRecord(from);
    const toRec = asRecord(to);
    if (fromRec && toRec) {
      const nested = buildRecordChangesDeep(fromRec, toRec, path, seen);
      if (Object.keys(nested).length) {
        Object.assign(changes, nested);
        continue;
      }
    }
    if (!deepEqual(from, to)) {
      changes[path] = { from, to };
    }
  }
  return changes;
}
function deriveChangesFromSnapshots(before, after) {
  const beforeRec = asRecord(before);
  const afterRec = asRecord(after);
  if (!beforeRec || !afterRec) return null;
  const changes = buildRecordChanges(beforeRec, afterRec);
  return Object.keys(changes).length ? changes : null;
}
function invertRecordedChanges(changes) {
  const source = asRecord(changes);
  if (!source) return null;
  const inverted = {};
  for (const [key, value] of Object.entries(source)) {
    const entry = asRecord(value);
    if (!entry || !("from" in entry) && !("to" in entry)) continue;
    inverted[key] = {
      from: entry.to,
      to: entry.from
    };
  }
  return Object.keys(inverted).length ? inverted : null;
}
function extractAliasList(source) {
  if (!source || typeof source !== "object" || Array.isArray(source)) return [];
  const record = source;
  const raw = record.cacheAliases;
  if (!Array.isArray(raw)) return [];
  const aliases = /* @__PURE__ */ new Set();
  for (const value of raw) {
    if (typeof value !== "string") continue;
    const normalized = canonicalizeResourceTag(value);
    if (normalized) aliases.add(normalized);
  }
  return Array.from(aliases);
}
class CommandBus {
  async execute(commandId, options) {
    const handler = await this.resolveHandler(commandId);
    const allInterceptors = getAllCommandInterceptorInstances();
    let interceptorMetadata = /* @__PURE__ */ new Map();
    let effectiveOptions = options;
    const userFeatures = allInterceptors.length ? await this.resolveUserFeaturesForInterceptors(options.ctx) : [];
    if (allInterceptors.length) {
      const interceptorCtx = {
        commandId,
        auth: options.ctx.auth ?? null,
        selectedOrganizationId: options.ctx.selectedOrganizationId ?? options.ctx.auth?.orgId ?? null,
        container: options.ctx.container
      };
      const beforeResult = await runCommandInterceptorsBefore(
        allInterceptors,
        commandId,
        options.input,
        interceptorCtx,
        userFeatures
      );
      if (!beforeResult.ok) {
        const blocked = beforeResult.error;
        throw new CommandInterceptorError(blocked.message, { status: blocked.status, body: blocked.body });
      }
      interceptorMetadata = beforeResult.metadataByInterceptor;
      if (beforeResult.modifiedInput) {
        effectiveOptions = {
          ...options,
          input: { ...options.input, ...beforeResult.modifiedInput }
        };
      }
    }
    const snapshots = await this.prepareSnapshots(handler, effectiveOptions);
    const redoLogEntry = effectiveOptions.redoLogEntry ?? null;
    const result = redoLogEntry && typeof handler.redo === "function" ? await handler.redo({ input: effectiveOptions.input, ctx: effectiveOptions.ctx, logEntry: redoLogEntry }) : await handler.execute(effectiveOptions.input, effectiveOptions.ctx);
    const afterSnapshot = await this.captureAfter(handler, effectiveOptions, result);
    const snapshotsWithAfter = { ...snapshots, after: afterSnapshot };
    const logMeta = await this.buildLog(handler, effectiveOptions, result, snapshotsWithAfter);
    let mergedMeta = this.mergeMetadata(effectiveOptions.metadata, logMeta);
    let interceptorContextMerged = {};
    for (const meta of interceptorMetadata.values()) {
      const logContextRecord = asRecord(asRecord(meta)?.logContext);
      if (!logContextRecord) continue;
      interceptorContextMerged = {
        ...interceptorContextMerged,
        ...logContextRecord
      };
    }
    const baseContext = asRecord(effectiveOptions.metadata?.context) ?? {};
    const logMetaContext = asRecord(logMeta?.context) ?? {};
    if (Object.keys(interceptorContextMerged).length > 0 || Object.keys(baseContext).length > 0 || Object.keys(logMetaContext).length > 0) {
      mergedMeta = mergedMeta ?? {};
      mergedMeta.context = {
        ...baseContext,
        ...interceptorContextMerged,
        ...logMetaContext
      };
    }
    const undoable = this.isUndoable(handler);
    if (undoable) {
      mergedMeta = mergedMeta ?? {};
      if (!mergedMeta.undoToken) mergedMeta.undoToken = defaultUndoToken();
      if (mergedMeta.actorUserId === void 0) mergedMeta.actorUserId = effectiveOptions.ctx.auth?.sub ?? null;
    }
    if (afterSnapshot !== void 0 && afterSnapshot !== null) {
      if (!mergedMeta) {
        mergedMeta = { snapshotAfter: afterSnapshot };
      } else if (!mergedMeta.snapshotAfter) {
        mergedMeta.snapshotAfter = afterSnapshot;
      }
    }
    if (snapshots.before) {
      if (!mergedMeta) {
        mergedMeta = { snapshotBefore: snapshots.before };
      } else if (!mergedMeta.snapshotBefore) {
        mergedMeta.snapshotBefore = snapshots.before;
      }
    }
    if (mergedMeta?.snapshotBefore !== void 0 && mergedMeta?.snapshotAfter !== void 0) {
      const currentChanges = mergedMeta.changes;
      const shouldInfer = currentChanges === void 0 || currentChanges === null || typeof currentChanges === "object" && !Array.isArray(currentChanges) && Object.keys(currentChanges).length === 0;
      if (shouldInfer) {
        const inferred = deriveChangesFromSnapshots(mergedMeta.snapshotBefore, mergedMeta.snapshotAfter);
        if (inferred) mergedMeta.changes = inferred;
      }
    }
    const logEntry = await this.persistLog(commandId, effectiveOptions, mergedMeta);
    let finalResult = result;
    if (allInterceptors.length) {
      const interceptorCtx = {
        commandId,
        auth: effectiveOptions.ctx.auth ?? null,
        selectedOrganizationId: effectiveOptions.ctx.selectedOrganizationId ?? effectiveOptions.ctx.auth?.orgId ?? null,
        container: effectiveOptions.ctx.container
      };
      const afterResult = await runCommandInterceptorsAfter(
        allInterceptors,
        commandId,
        effectiveOptions.input,
        result,
        interceptorCtx,
        userFeatures,
        interceptorMetadata
      );
      if (afterResult.modifiedResult && typeof result === "object" && result) {
        finalResult = { ...result, ...afterResult.modifiedResult };
      }
    }
    if (!effectiveOptions.skipCacheInvalidation) {
      await this.invalidateCacheAfterExecute(commandId, effectiveOptions, finalResult, mergedMeta);
    }
    await this.flushCrudSideEffects(effectiveOptions.ctx.container, effectiveOptions.ctx?.bulkImport);
    return { result: finalResult, logEntry };
  }
  async undo(undoToken, ctx) {
    const service = ctx.container.resolve("actionLogService");
    const log = await service.findByUndoToken(undoToken);
    if (!log) throw new Error("Undo token expired or not found");
    const handler = await this.resolveHandler(log.commandId);
    if (!handler.undo || this.isUndoable(handler) === false) {
      throw new Error(`Command ${log.commandId} is not undoable`);
    }
    const claimed = await service.claimForUndo(log.id);
    if (!claimed) throw new Error("Undo token already consumed");
    try {
      const allInterceptors = getAllCommandInterceptorInstances();
      let undoInterceptorMetadata = /* @__PURE__ */ new Map();
      const userFeatures = allInterceptors.length ? await this.resolveUserFeaturesForInterceptors(ctx) : [];
      if (allInterceptors.length) {
        const undoCtx = { input: log.commandPayload, logEntry: log, undoToken };
        const interceptorCtx = {
          commandId: log.commandId,
          auth: ctx.auth ?? null,
          selectedOrganizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,
          container: ctx.container
        };
        const beforeResult = await runCommandInterceptorsBeforeUndo(
          allInterceptors,
          log.commandId,
          undoCtx,
          interceptorCtx,
          userFeatures
        );
        if (!beforeResult.ok) {
          const blocked = beforeResult.error;
          throw new CommandInterceptorError(blocked.message, { status: blocked.status, body: blocked.body });
        }
        undoInterceptorMetadata = beforeResult.metadataByInterceptor;
      }
      await handler.undo({
        input: log.commandPayload,
        ctx,
        logEntry: log
      });
      await service.markUndone(log.id, this.buildUndoTraceLog(log, ctx));
      if (allInterceptors.length) {
        const undoCtx = { input: log.commandPayload, logEntry: log, undoToken };
        const interceptorCtx = {
          commandId: log.commandId,
          auth: ctx.auth ?? null,
          selectedOrganizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,
          container: ctx.container
        };
        await runCommandInterceptorsAfterUndo(
          allInterceptors,
          log.commandId,
          undoCtx,
          interceptorCtx,
          userFeatures,
          undoInterceptorMetadata
        );
      }
      await this.invalidateCacheAfterUndo(log, ctx);
      await this.flushCrudSideEffects(ctx.container);
    } catch (err) {
      await service.releaseUndoClaim(log.id).catch(() => {
      });
      throw err;
    }
  }
  buildUndoTraceLog(log, ctx) {
    const snapshotBefore = log.snapshotAfter ?? null;
    const snapshotAfter = log.snapshotBefore ?? null;
    const changes = deriveChangesFromSnapshots(snapshotBefore, snapshotAfter) ?? invertRecordedChanges(log.changesJson) ?? void 0;
    const baseContext = asRecord(log.contextJson) ?? {};
    const context = {
      ...baseContext,
      historyAction: "undo",
      sourceLogId: log.id,
      sourceCommandId: log.commandId
    };
    return {
      tenantId: log.tenantId ?? ctx.auth?.tenantId ?? null,
      organizationId: log.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,
      actorUserId: ctx.auth?.sub ?? log.actorUserId ?? null,
      commandId: log.commandId,
      actionLabel: log.actionLabel ?? void 0,
      resourceKind: log.resourceKind ?? void 0,
      resourceId: log.resourceId ?? void 0,
      parentResourceKind: log.parentResourceKind ?? null,
      parentResourceId: log.parentResourceId ?? null,
      relatedResourceKind: log.relatedResourceKind ?? null,
      relatedResourceId: log.relatedResourceId ?? null,
      snapshotBefore,
      snapshotAfter,
      changes,
      context
    };
  }
  async resolveUserFeaturesForInterceptors(ctx) {
    if (!ctx.auth) return [];
    try {
      const rbac = ctx.container.resolve("rbacService");
      if (rbac?.getGrantedFeatures) {
        return await rbac.getGrantedFeatures(ctx.auth.sub, {
          tenantId: ctx.auth.tenantId,
          organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId
        });
      }
    } catch {
    }
    return [];
  }
  async resolveHandler(commandId) {
    const handler = commandRegistry.get(commandId) ?? await commandRegistry.load(commandId);
    if (!handler) {
      const moduleName = commandId.split(".")[0];
      const registered = commandRegistry.list();
      const sameModule = registered.filter((id) => id.split(".")[0] === moduleName);
      const registeredLoaders = commandRegistry.listLoaders();
      const sameModuleLoaders = registeredLoaders.filter((id) => id === commandId || id.startsWith(`${moduleName}:`));
      const hint = sameModule.length > 0 ? ` Registered commands for module "${moduleName}": [${sameModule.join(", ")}].` : sameModuleLoaders.length > 0 ? ` Command loaders for module "${moduleName}" were registered but none loaded "${commandId}".` : ` No commands or command loaders registered for module "${moduleName}". Ensure the command file is imported or generated lazy command loaders are registered.`;
      throw new Error(`Command handler not registered for id ${commandId}.${hint}`);
    }
    return handler;
  }
  async prepareSnapshots(handler, options) {
    if (!handler.prepare) return {};
    try {
      return await handler.prepare(options.input, options.ctx) || {};
    } catch (err) {
      throw err;
    }
  }
  async captureAfter(handler, options, result) {
    if (!handler.captureAfter) return void 0;
    return handler.captureAfter(options.input, result, options.ctx);
  }
  async buildLog(handler, options, result, snapshots) {
    if (!handler.buildLog) return null;
    const args = {
      input: options.input,
      result,
      ctx: options.ctx,
      snapshots
    };
    return await handler.buildLog(args) || null;
  }
  mergeMetadata(primary, secondary) {
    if (!primary && !secondary) return null;
    return {
      skipLog: secondary?.skipLog ?? primary?.skipLog ?? false,
      tenantId: secondary?.tenantId ?? primary?.tenantId ?? null,
      organizationId: secondary?.organizationId ?? primary?.organizationId ?? null,
      actorUserId: secondary?.actorUserId ?? primary?.actorUserId ?? null,
      actionLabel: secondary?.actionLabel ?? primary?.actionLabel ?? null,
      resourceKind: secondary?.resourceKind ?? primary?.resourceKind ?? null,
      resourceId: secondary?.resourceId ?? primary?.resourceId ?? null,
      parentResourceKind: secondary?.parentResourceKind ?? primary?.parentResourceKind ?? null,
      parentResourceId: secondary?.parentResourceId ?? primary?.parentResourceId ?? null,
      relatedResourceKind: secondary?.relatedResourceKind ?? primary?.relatedResourceKind ?? null,
      relatedResourceId: secondary?.relatedResourceId ?? primary?.relatedResourceId ?? null,
      undoToken: secondary?.undoToken ?? primary?.undoToken ?? null,
      payload: secondary?.payload ?? primary?.payload ?? null,
      snapshotBefore: secondary?.snapshotBefore ?? primary?.snapshotBefore ?? null,
      snapshotAfter: secondary?.snapshotAfter ?? primary?.snapshotAfter ?? null,
      changes: secondary?.changes ?? primary?.changes ?? null,
      context: secondary?.context ?? primary?.context ?? null
    };
  }
  async persistLog(commandId, options, metadata) {
    if (!metadata) return null;
    if (metadata.skipLog) return null;
    const resourceKind = typeof metadata.resourceKind === "string" ? metadata.resourceKind : null;
    if (resourceKind && SKIPPED_ACTION_LOG_RESOURCE_KINDS.has(resourceKind)) {
      return null;
    }
    let service = null;
    try {
      service = options.ctx.container.resolve("actionLogService");
    } catch {
      service = null;
    }
    if (!service) return null;
    const tenantId = metadata.tenantId ?? options.ctx.auth?.tenantId ?? null;
    const organizationId = metadata.organizationId ?? options.ctx.selectedOrganizationId ?? options.ctx.auth?.orgId ?? null;
    const actorUserId = metadata.actorUserId ?? options.ctx.auth?.sub ?? null;
    const payload = {
      tenantId: tenantId ?? void 0,
      organizationId: organizationId ?? void 0,
      actorUserId: actorUserId ?? void 0,
      commandId
    };
    if (metadata) {
      if ("actionLabel" in metadata && metadata.actionLabel != null) payload.actionLabel = metadata.actionLabel;
      if ("resourceKind" in metadata && metadata.resourceKind != null) payload.resourceKind = metadata.resourceKind;
      if ("resourceId" in metadata && metadata.resourceId != null) payload.resourceId = metadata.resourceId;
      if ("parentResourceKind" in metadata && metadata.parentResourceKind != null) payload.parentResourceKind = metadata.parentResourceKind;
      if ("parentResourceId" in metadata && metadata.parentResourceId != null) payload.parentResourceId = metadata.parentResourceId;
      if ("relatedResourceKind" in metadata && metadata.relatedResourceKind != null) payload.relatedResourceKind = metadata.relatedResourceKind;
      if ("relatedResourceId" in metadata && metadata.relatedResourceId != null) payload.relatedResourceId = metadata.relatedResourceId;
      if ("undoToken" in metadata && metadata.undoToken != null) payload.undoToken = metadata.undoToken;
      if ("payload" in metadata && metadata.payload !== void 0) payload.commandPayload = metadata.payload;
      if ("snapshotBefore" in metadata && metadata.snapshotBefore !== void 0) payload.snapshotBefore = metadata.snapshotBefore;
      if ("snapshotAfter" in metadata && metadata.snapshotAfter !== void 0) payload.snapshotAfter = metadata.snapshotAfter;
      if ("changes" in metadata && metadata.changes !== void 0 && metadata.changes !== null) payload.changes = metadata.changes;
      if ("context" in metadata && metadata.context !== void 0 && metadata.context !== null) payload.context = metadata.context;
    }
    const redoEnvelope = wrapRedoPayload("commandPayload" in payload ? payload.commandPayload : void 0, options.input);
    payload.commandPayload = redoEnvelope;
    return await service.log(payload);
  }
  isUndoable(handler) {
    return handler.isUndoable !== false && typeof handler.undo === "function";
  }
  async invalidateCacheAfterExecute(commandId, options, result, metadata) {
    const resource = typeof metadata?.resourceKind === "string" ? metadata.resourceKind : null;
    if (!resource) return;
    try {
      const ctx = options.ctx;
      const resultRecord = asRecord(result);
      const resultEntity = asRecord(resultRecord?.entity);
      const inputRecord = asRecord(options.input);
      const inputEntity = asRecord(inputRecord?.entity);
      const recordId = pickFirstIdentifier(
        metadata?.resourceId,
        extractPrimaryIdFromCommandResult(result),
        resultRecord?.entityId,
        resultRecord?.id,
        resultRecord?.recordId,
        resultEntity?.id,
        inputRecord?.id,
        inputRecord?.entityId,
        inputRecord?.recordId,
        inputEntity?.id
      );
      const organizationId = pickFirstIdentifier(
        metadata?.organizationId,
        resultRecord?.organizationId,
        resultEntity?.organizationId,
        inputRecord?.organizationId,
        inputEntity?.organizationId,
        ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
      );
      const tenantId = pickFirstIdentifier(
        metadata?.tenantId,
        resultRecord?.tenantId,
        resultEntity?.tenantId,
        inputRecord?.tenantId,
        inputEntity?.tenantId,
        ctx.auth?.tenantId ?? null
      );
      const fallbackTenant = pickFirstIdentifier(metadata?.tenantId, ctx.auth?.tenantId ?? null);
      const aliasSet = /* @__PURE__ */ new Set();
      for (const alias of extractAliasList(metadata?.context ?? null)) {
        aliasSet.add(alias);
      }
      const derived = deriveResourceFromCommandId(commandId);
      if (derived) aliasSet.add(derived);
      const aliasExtras = Array.from(aliasSet);
      await invalidateCrudCache(
        ctx.container,
        resource,
        { id: recordId, organizationId, tenantId },
        fallbackTenant,
        `command:${commandId}:execute`,
        aliasExtras
      );
    } catch (err) {
      if (isCrudCacheDebugEnabled()) {
        try {
          logger.debug("Cache execute-invalidation failed", { commandId, err });
        } catch {
        }
      }
    }
  }
  async invalidateCacheAfterUndo(log, ctx) {
    const resource = typeof log.resourceKind === "string" ? log.resourceKind : null;
    if (!resource) return;
    try {
      const recordId = pickFirstIdentifier(log.resourceId);
      const organizationId = pickFirstIdentifier(log.organizationId, ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null);
      const tenantId = pickFirstIdentifier(log.tenantId, ctx.auth?.tenantId ?? null);
      const fallbackTenant = pickFirstIdentifier(log.tenantId, ctx.auth?.tenantId ?? null);
      const aliasSet = /* @__PURE__ */ new Set();
      for (const alias of extractAliasList(log.contextJson ?? null)) {
        aliasSet.add(alias);
      }
      const derived = deriveResourceFromCommandId(log.commandId);
      if (derived) aliasSet.add(derived);
      const aliasExtras = Array.from(aliasSet);
      await invalidateCrudCache(
        ctx.container,
        resource,
        { id: recordId, organizationId, tenantId },
        fallbackTenant,
        `command:${log.commandId}:undo`,
        aliasExtras
      );
    } catch (err) {
      if (isCrudCacheDebugEnabled()) {
        try {
          logger.debug("Cache undo-invalidation failed", { commandId: log.commandId, err });
        } catch {
        }
      }
    }
  }
  async flushCrudSideEffects(container, suppress) {
    try {
      const dataEngine = container.resolve("dataEngine");
      await dataEngine.flushOrmEntityChanges(suppress);
    } catch (error) {
      if (isReadProjectionAlwaysConsistent()) {
        throw error;
      }
    }
  }
}
function wrapRedoPayload(existing, input) {
  if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
    const envelope = { __redoInput: input };
    if (existing !== void 0) envelope.value = existing;
    return envelope;
  }
  const current = existing;
  if ("__redoInput" in current && current.__redoInput !== void 0) {
    return current;
  }
  return { __redoInput: input, ...current };
}
export {
  CommandBus
};
//# sourceMappingURL=command-bus.js.map
