import { CrudHttpError, conflict, isUniqueViolation } from "@open-mercato/shared/lib/crud/errors";
import { extractUndoPayload } from "./undo.js";
import { emitCrudSideEffects } from "./helpers.js";
import { withAtomicFlush } from "./flush.js";
const DEFAULT_SNAPSHOT_DATE_FIELDS = ["createdAt", "updatedAt", "deletedAt"];
function reviveSnapshotSeed(snapshot, dateFields = DEFAULT_SNAPSHOT_DATE_FIELDS) {
  const seed = { ...snapshot };
  for (const field of dateFields) {
    const value = seed[field];
    if (typeof value === "string") seed[field] = new Date(value);
  }
  return seed;
}
function serializeRowSnapshot(entity, fields, dateFields = DEFAULT_SNAPSHOT_DATE_FIELDS) {
  const snapshot = {};
  const dateFieldSet = new Set(dateFields);
  for (const field of fields) {
    const value = entity[field];
    if (dateFieldSet.has(field)) {
      snapshot[field] = value instanceof Date ? value.toISOString() : value ?? null;
    } else {
      snapshot[field] = value ?? null;
    }
  }
  return snapshot;
}
function resolveRedoSnapshot(logEntry) {
  if (!logEntry) return null;
  const undo = extractUndoPayload(logEntry);
  if (undo && undo.after != null) return undo.after;
  if (logEntry.snapshotAfter != null) return logEntry.snapshotAfter;
  return null;
}
async function restoreCreatedRow(em, entityClass, id, seedFromSnapshot, findRow) {
  const existing = findRow ? await findRow(em, id) : await em.findOne(entityClass, { id });
  if (existing) {
    existing.deletedAt = null;
    const seed = seedFromSnapshot();
    if (typeof seed.isActive === "boolean") existing.isActive = seed.isActive;
    return existing;
  }
  const record = em.create(entityClass, { ...seedFromSnapshot(), deletedAt: null });
  em.persist(record);
  return record;
}
function makeCreateRedo(config) {
  const getSnapshotId = config.getSnapshotId ?? ((snapshot) => snapshot.id ?? null);
  const seedFromSnapshot = config.seedFromSnapshot ?? ((snapshot) => reviveSnapshotSeed(snapshot, config.dateFields));
  return async ({ ctx, logEntry }) => {
    const snapshot = resolveRedoSnapshot(logEntry);
    const id = snapshot ? getSnapshotId(snapshot) : logEntry.resourceId ?? null;
    if (!snapshot || !id) {
      throw new CrudHttpError(400, { error: "[internal] redo snapshot unavailable for create command" });
    }
    const em = ctx.container.resolve("em").fork();
    const overrides = config.beforeRestore ? await config.beforeRestore({ em, ctx, snapshot }) : void 0;
    const buildSeed = () => overrides ? { ...seedFromSnapshot(snapshot), ...overrides } : seedFromSnapshot(snapshot);
    const findRow = config.findRow ? (forkedEm, rowId) => config.findRow({ em: forkedEm, ctx, id: rowId, snapshot }) : void 0;
    let entity;
    const restorePhase = async () => {
      entity = await restoreCreatedRow(em, config.entityClass, id, buildSeed, findRow);
    };
    const afterRestorePhase = async () => {
      if (config.afterRestore) {
        await config.afterRestore({ em, ctx, entity, snapshot, logEntry });
      }
    };
    try {
      if (config.transaction) {
        const phases = config.afterRestore ? [restorePhase, afterRestorePhase] : [restorePhase];
        await withAtomicFlush(em, phases, { transaction: true });
      } else {
        await restorePhase();
        await em.flush();
        await afterRestorePhase();
      }
    } catch (err) {
      if (isUniqueViolation(err)) {
        throw conflict("[internal] Cannot redo: a record with the same unique key already exists.");
      }
      throw err;
    }
    const dataEngine = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine,
      action: "created",
      entity,
      identifiers: {
        id,
        organizationId: entity.organizationId ?? null,
        tenantId: entity.tenantId ?? null
      },
      events: config.events,
      indexer: config.indexer
    });
    return config.buildResult(entity, snapshot);
  };
}
export {
  makeCreateRedo,
  resolveRedoSnapshot,
  restoreCreatedRow,
  reviveSnapshotSeed,
  serializeRowSnapshot
};
//# sourceMappingURL=redo.js.map
