import { registerCommand } from "@open-mercato/shared/lib/commands";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import {
  optionSchemaTemplateCreateSchema,
  optionSchemaTemplateUpdateSchema
} from "../data/validators.js";
import { CatalogOptionSchemaTemplate, CatalogProduct } from "../data/entities.js";
import {
  cloneJson,
  ensureOrganizationScope,
  ensureSameScope,
  ensureTenantScope,
  extractUndoPayload,
  resolveOptionSchemaCode
} from "./shared.js";
import { makeCreateRedo } from "@open-mercato/shared/lib/commands/redo";
async function loadOptionSchemaSnapshot(em, id) {
  const record = await em.findOne(CatalogOptionSchemaTemplate, { id, deletedAt: null });
  if (!record) return null;
  return {
    id: record.id,
    organizationId: record.organizationId,
    tenantId: record.tenantId,
    name: record.name,
    code: record.code,
    description: record.description ?? null,
    schema: cloneJson(record.schema),
    metadata: record.metadata ? cloneJson(record.metadata) : null,
    isActive: record.isActive,
    createdAt: record.createdAt.toISOString(),
    updatedAt: record.updatedAt.toISOString()
  };
}
function applyOptionSchemaSnapshot(record, snapshot) {
  record.organizationId = snapshot.organizationId;
  record.tenantId = snapshot.tenantId;
  record.name = snapshot.name;
  record.code = snapshot.code;
  record.description = snapshot.description ?? null;
  record.schema = cloneJson(snapshot.schema);
  record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null;
  record.isActive = snapshot.isActive;
  record.createdAt = new Date(snapshot.createdAt);
  record.updatedAt = new Date(snapshot.updatedAt);
}
const createOptionSchemaCommand = {
  id: "catalog.optionSchemas.create",
  async execute(input, ctx) {
    const parsed = optionSchemaTemplateCreateSchema.parse(input);
    ensureTenantScope(ctx, parsed.tenantId);
    ensureOrganizationScope(ctx, parsed.organizationId);
    const em = ctx.container.resolve("em").fork();
    const code = resolveOptionSchemaCode({
      code: parsed.code,
      name: parsed.name
    });
    const now = /* @__PURE__ */ new Date();
    const record = em.create(CatalogOptionSchemaTemplate, {
      organizationId: parsed.organizationId,
      tenantId: parsed.tenantId,
      name: parsed.name,
      code,
      description: parsed.description ?? null,
      schema: cloneJson(parsed.schema),
      metadata: parsed.metadata ? cloneJson(parsed.metadata) : null,
      isActive: parsed.isActive ?? true,
      createdAt: now,
      updatedAt: now
    });
    em.persist(record);
    await em.flush();
    return { schemaId: record.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return loadOptionSchemaSnapshot(em, result.schemaId);
  },
  buildLog: async ({ snapshots }) => {
    const after = snapshots.after;
    if (!after) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("catalog.audit.optionSchemas.create", "Create option schema"),
      resourceKind: "catalog.optionSchema",
      resourceId: after.id,
      tenantId: after.tenantId,
      organizationId: after.organizationId,
      snapshotAfter: after,
      payload: {
        undo: { after }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const after = payload?.after;
    if (!after) return;
    const em = ctx.container.resolve("em").fork();
    const record = await em.findOne(CatalogOptionSchemaTemplate, { id: after.id });
    if (!record) return;
    ensureTenantScope(ctx, record.tenantId);
    ensureOrganizationScope(ctx, record.organizationId);
    em.remove(record);
    await em.flush();
  },
  redo: makeCreateRedo({
    entityClass: CatalogOptionSchemaTemplate,
    buildResult: (entity) => ({ schemaId: entity.id })
  })
};
const updateOptionSchemaCommand = {
  id: "catalog.optionSchemas.update",
  async prepare(input, ctx) {
    const em = ctx.container.resolve("em");
    const snapshot = await loadOptionSchemaSnapshot(em, input.id);
    if (snapshot) {
      ensureTenantScope(ctx, snapshot.tenantId);
      ensureOrganizationScope(ctx, snapshot.organizationId);
    }
    return snapshot ? { before: snapshot } : {};
  },
  async execute(input, ctx) {
    const parsed = optionSchemaTemplateUpdateSchema.parse(input);
    const em = ctx.container.resolve("em").fork();
    const record = await em.findOne(CatalogOptionSchemaTemplate, {
      id: parsed.id,
      deletedAt: null
    });
    if (!record) throw new CrudHttpError(404, { error: "Option schema not found" });
    const organizationId = parsed.organizationId ?? record.organizationId;
    const tenantId = parsed.tenantId ?? record.tenantId;
    ensureTenantScope(ctx, tenantId);
    ensureOrganizationScope(ctx, organizationId);
    ensureSameScope(record, organizationId, tenantId);
    record.organizationId = organizationId;
    record.tenantId = tenantId;
    if (parsed.name !== void 0) record.name = parsed.name;
    if (parsed.code !== void 0) {
      record.code = resolveOptionSchemaCode({
        code: parsed.code,
        name: parsed.name ?? record.name
      });
    }
    if (parsed.description !== void 0) record.description = parsed.description ?? null;
    if (parsed.schema !== void 0) {
      record.schema = cloneJson(parsed.schema);
    }
    if (parsed.metadata !== void 0) {
      record.metadata = parsed.metadata ? cloneJson(parsed.metadata) : null;
    }
    if (parsed.isActive !== void 0) record.isActive = parsed.isActive;
    await em.flush();
    return { schemaId: record.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return loadOptionSchemaSnapshot(em, result.schemaId);
  },
  buildLog: async ({ snapshots }) => {
    const before = snapshots.before;
    const after = snapshots.after;
    if (!before || !after) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("catalog.audit.optionSchemas.update", "Update option schema"),
      resourceKind: "catalog.optionSchema",
      resourceId: after.id,
      tenantId: after.tenantId,
      organizationId: after.organizationId,
      snapshotBefore: before,
      snapshotAfter: after,
      payload: {
        undo: { before, after }
      }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    let record = await em.findOne(CatalogOptionSchemaTemplate, { id: before.id });
    if (!record) {
      record = em.create(CatalogOptionSchemaTemplate, {
        id: before.id,
        organizationId: before.organizationId,
        tenantId: before.tenantId,
        name: before.name,
        code: before.code,
        description: before.description ?? null,
        schema: cloneJson(before.schema),
        metadata: before.metadata ? cloneJson(before.metadata) : null,
        isActive: before.isActive,
        createdAt: new Date(before.createdAt),
        updatedAt: new Date(before.updatedAt)
      });
      em.persist(record);
    }
    ensureTenantScope(ctx, before.tenantId);
    ensureOrganizationScope(ctx, before.organizationId);
    applyOptionSchemaSnapshot(record, before);
    await em.flush();
  }
};
const deleteOptionSchemaCommand = {
  id: "catalog.optionSchemas.delete",
  async prepare(input, ctx) {
    const em = ctx.container.resolve("em");
    const snapshot = await loadOptionSchemaSnapshot(em, input.id);
    if (snapshot) {
      ensureTenantScope(ctx, snapshot.tenantId);
      ensureOrganizationScope(ctx, snapshot.organizationId);
    }
    return snapshot ? { before: snapshot } : {};
  },
  async execute(input, ctx) {
    const em = ctx.container.resolve("em").fork();
    const record = await em.findOne(CatalogOptionSchemaTemplate, { id: input.id, deletedAt: null });
    if (!record) throw new CrudHttpError(404, { error: "Option schema not found" });
    ensureTenantScope(ctx, record.tenantId);
    ensureOrganizationScope(ctx, record.organizationId);
    const assigned = await em.count(CatalogProduct, { optionSchemaTemplate: record, deletedAt: null });
    if (assigned > 0) {
      throw new CrudHttpError(400, { error: "Detach products from this schema before deleting it." });
    }
    record.deletedAt = /* @__PURE__ */ new Date();
    await em.flush();
    return { schemaId: record.id };
  },
  buildLog: async ({ snapshots }) => {
    const before = snapshots.before;
    if (!before) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("catalog.audit.optionSchemas.delete", "Delete option schema"),
      resourceKind: "catalog.optionSchema",
      resourceId: before.id,
      tenantId: before.tenantId,
      organizationId: before.organizationId,
      snapshotBefore: before,
      payload: { undo: { before } }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    let record = await em.findOne(CatalogOptionSchemaTemplate, { id: before.id });
    if (!record) {
      record = em.create(CatalogOptionSchemaTemplate, {
        id: before.id,
        organizationId: before.organizationId,
        tenantId: before.tenantId,
        name: before.name,
        code: before.code,
        description: before.description ?? null,
        schema: cloneJson(before.schema),
        metadata: before.metadata ? cloneJson(before.metadata) : null,
        isActive: before.isActive,
        createdAt: new Date(before.createdAt),
        updatedAt: new Date(before.updatedAt)
      });
      em.persist(record);
    }
    ensureTenantScope(ctx, before.tenantId);
    ensureOrganizationScope(ctx, before.organizationId);
    applyOptionSchemaSnapshot(record, before);
    record.deletedAt = null;
    await em.flush();
  }
};
registerCommand(createOptionSchemaCommand);
registerCommand(updateOptionSchemaCommand);
registerCommand(deleteOptionSchemaCommand);
//# sourceMappingURL=optionSchemas.js.map
