import { randomUUID } from "node:crypto";
import { registerCommand } from "@open-mercato/shared/lib/commands";
import {
  buildChanges,
  requireId,
  parseWithCustomFields,
  setCustomFieldsIfAny,
  emitCrudSideEffects,
  emitCrudUndoSideEffects
} from "@open-mercato/shared/lib/commands/helpers";
import { UniqueConstraintViolationException } from "@mikro-orm/core";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import {
  loadCustomFieldSnapshot,
  buildCustomFieldResetMap
} from "@open-mercato/shared/lib/commands/customFieldSnapshots";
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
import { resolveRedoSnapshot, restoreCreatedRow } from "@open-mercato/shared/lib/commands/redo";
import { E } from "../../../generated/entities.ids.generated.js";
import { slugifyTagLabel } from "@open-mercato/shared/lib/utils";
import { parseObjectLike } from "@open-mercato/shared/lib/json/parseObjectLike";
import {
  CatalogOffer,
  CatalogProduct,
  CatalogProductVariant,
  CatalogProductPrice,
  CatalogProductUnitConversion,
  CatalogOptionSchemaTemplate,
  CatalogProductCategory,
  CatalogProductCategoryAssignment,
  CatalogProductTag,
  CatalogProductTagAssignment
} from "../data/entities.js";
import { SalesTaxRate } from "@open-mercato/core/modules/sales/data/entities";
import {
  productCreateSchema,
  productUpdateSchema
} from "../data/validators.js";
import {
  cloneJson,
  ensureOrganizationScope,
  ensureSameScope,
  ensureTenantScope,
  extractUndoPayload,
  requireOptionSchemaTemplate,
  resolveOptionSchemaCode,
  emitCatalogQueryIndexEvent,
  randomSuffix,
  toNumericString,
  getErrorConstraint,
  getErrorMessage
} from "./shared.js";
import {
  findWithDecryption,
  findOneWithDecryption
} from "@open-mercato/shared/lib/encryption/find";
import { canonicalizeUnitCode } from "../lib/unitCodes.js";
import {
  resolveCanonicalUnitCode
} from "../lib/unitResolution.js";
async function resolveProductUnitDefaults(em, params) {
  const defaultUnitInput = canonicalizeUnitCode(params.defaultUnit);
  const defaultSalesUnitInput = canonicalizeUnitCode(params.defaultSalesUnit);
  if (!defaultUnitInput && defaultSalesUnitInput) {
    throw new CrudHttpError(400, { error: "uom.default_unit_missing" });
  }
  const defaultUnit = defaultUnitInput ? await resolveCanonicalUnitCode(em, {
    organizationId: params.organizationId,
    tenantId: params.tenantId,
    unitCode: defaultUnitInput
  }) : null;
  const defaultSalesUnit = defaultSalesUnitInput ? await resolveCanonicalUnitCode(em, {
    organizationId: params.organizationId,
    tenantId: params.tenantId,
    unitCode: defaultSalesUnitInput
  }) : null;
  return { defaultUnit, defaultSalesUnit };
}
async function ensureBaseUnitCanBeRemoved(em, params) {
  if (params.defaultUnit) return;
  if (params.defaultSalesUnit) {
    throw new CrudHttpError(400, { error: "uom.default_unit_missing" });
  }
  const activeConversionCount = await em.count(CatalogProductUnitConversion, {
    product: params.productId,
    organizationId: params.organizationId,
    tenantId: params.tenantId,
    deletedAt: null,
    isActive: true
  });
  if (activeConversionCount > 0) {
    throw new CrudHttpError(400, { error: "uom.default_unit_missing" });
  }
}
function resolveUnitPriceInput(parsed) {
  const enabledFromNested = parsed.unitPrice?.enabled;
  const enabledFromFlat = parsed.unitPriceEnabled;
  const referenceFromNested = parsed.unitPrice?.referenceUnit;
  const referenceFromFlat = parsed.unitPriceReferenceUnit;
  const baseFromNested = parsed.unitPrice?.baseQuantity;
  const baseFromFlat = parsed.unitPriceBaseQuantity;
  const enabledProvided = enabledFromNested !== void 0 || enabledFromFlat !== void 0;
  const referenceProvided = referenceFromNested !== void 0 || referenceFromFlat !== void 0;
  const baseProvided = baseFromNested !== void 0 || baseFromFlat !== void 0;
  const enabled = enabledFromNested ?? enabledFromFlat;
  const referenceUnit = canonicalizeUnitCode(
    referenceFromNested ?? referenceFromFlat ?? null
  );
  const baseQuantitySource = baseFromNested ?? baseFromFlat;
  const baseQuantity = baseQuantitySource === void 0 ? void 0 : toNumericString(baseQuantitySource) ?? null;
  return {
    enabled,
    referenceUnit,
    baseQuantity,
    enabledProvided,
    referenceProvided,
    baseProvided
  };
}
const productCrudEvents = {
  module: "catalog",
  entity: "product",
  persistent: true,
  buildPayload: (ctx) => ({
    id: ctx.identifiers.id,
    tenantId: ctx.identifiers.tenantId,
    organizationId: ctx.identifiers.organizationId,
    productType: ctx.entity.productType,
    statusEntryId: ctx.entity.statusEntryId ?? null,
    isActive: ctx.entity.isActive
  })
};
const productCrudIndexer = {
  entityType: E.catalog.catalog_product,
  buildUpsertPayload: (ctx) => ({
    entityType: E.catalog.catalog_product,
    recordId: ctx.identifiers.id,
    tenantId: ctx.identifiers.tenantId,
    organizationId: ctx.identifiers.organizationId
  }),
  buildDeletePayload: (ctx) => ({
    entityType: E.catalog.catalog_product,
    recordId: ctx.identifiers.id,
    tenantId: ctx.identifiers.tenantId,
    organizationId: ctx.identifiers.organizationId
  })
};
function buildProductCrudIdentifiers(product) {
  return {
    id: product.id,
    organizationId: product.organizationId,
    tenantId: product.tenantId
  };
}
async function emitProductCrudChange(opts) {
  const { dataEngine, action, product } = opts;
  await emitCrudSideEffects({
    dataEngine,
    action,
    entity: product,
    identifiers: buildProductCrudIdentifiers(product),
    events: productCrudEvents,
    indexer: productCrudIndexer
  });
}
async function emitProductCrudUndoChange(opts) {
  const { dataEngine, action, product } = opts;
  await emitCrudUndoSideEffects({
    dataEngine,
    action,
    entity: product,
    identifiers: buildProductCrudIdentifiers(product),
    events: productCrudEvents,
    indexer: productCrudIndexer
  });
}
async function resolveScopedTaxRate(em, taxRateId, taxRateInput, organizationId, tenantId) {
  const normalizedRate = taxRateInput === null || taxRateInput === void 0 ? null : (() => {
    const numeric = typeof taxRateInput === "string" ? Number(taxRateInput) : taxRateInput;
    return Number.isFinite(numeric) ? toNumericString(numeric) : null;
  })();
  if (!taxRateId) {
    return { taxRateId: null, taxRate: normalizedRate };
  }
  const record = await findOneWithDecryption(em, SalesTaxRate, {
    id: taxRateId,
    organizationId,
    tenantId,
    deletedAt: null
  });
  if (!record) {
    const { translate } = await resolveTranslations();
    throw new CrudHttpError(400, {
      error: translate(
        "catalog.products.errors.taxClassNotFound",
        "Tax class not found"
      )
    });
  }
  return { taxRateId, taxRate: record.rate ?? normalizedRate };
}
function slugifyCode(input) {
  return input.toLowerCase().trim().replace(/[^a-z0-9\-]+/g, "-").replace(/(?:^-+|-+$)/g, "");
}
function normalizeCatalogOptionSchema(input) {
  if (!input || !Array.isArray(input.options) || !input.options.length)
    return null;
  const options = input.options.map((option) => {
    if (!option) return null;
    const label = typeof option.label === "string" && option.label.trim().length ? option.label.trim() : null;
    const codeSource = typeof option.code === "string" && option.code.trim().length ? option.code.trim() : label;
    const code = slugifyCode(codeSource ?? "");
    if (!label && !code) return null;
    const choices = Array.isArray(option.choices) ? option.choices.map((choice) => {
      if (!choice) return null;
      const choiceLabel = typeof choice.label === "string" && choice.label.trim().length ? choice.label.trim() : null;
      const choiceCodeSource = typeof choice.code === "string" && choice.code.trim().length ? choice.code.trim() : choiceLabel;
      const choiceCode = slugifyCode(choiceCodeSource ?? "");
      if (!choiceLabel && !choiceCode) return null;
      return {
        code: choiceCode || `choice-${randomSuffix()}`,
        label: choiceLabel ?? (choiceCode || `Choice ${randomSuffix()}`)
      };
    }).filter(
      (entry) => !!entry && entry.code.trim().length > 0 && entry.label.trim().length > 0
    ) : [];
    return {
      code: code || `option-${randomSuffix()}`,
      label: label ?? (code || `Option ${randomSuffix()}`),
      description: typeof option.description === "string" && option.description.trim().length ? option.description.trim() : null,
      inputType: option.inputType === "text" || option.inputType === "textarea" || option.inputType === "number" ? option.inputType : "select",
      isRequired: option.isRequired ?? false,
      isMultiple: option.isMultiple ?? false,
      choices
    };
  }).filter((entry) => !!entry && entry.code.trim().length > 0);
  if (!options.length) return null;
  return {
    version: typeof input.version === "number" && input.version > 0 ? input.version : 1,
    name: typeof input.name === "string" && input.name.trim().length ? input.name.trim() : void 0,
    description: typeof input.description === "string" && input.description.trim().length ? input.description.trim() : void 0,
    options
  };
}
function convertLegacyOptionSchema(raw) {
  if (!Array.isArray(raw)) return null;
  const options = raw.map((entry) => {
    if (!entry || typeof entry !== "object") return null;
    const source = entry;
    const title = typeof source["title"] === "string" && source["title"].trim().length ? source["title"].trim() : null;
    if (!title) return null;
    const values = Array.isArray(source["values"]) ? source["values"].map((value) => {
      if (!value || typeof value !== "object") return null;
      const choice = value;
      const label = typeof choice.label === "string" && choice.label.trim().length ? choice.label.trim() : null;
      if (!label) return null;
      return { code: slugifyCode(label), label };
    }).filter(
      (choice) => !!choice
    ) : [];
    return {
      code: slugifyCode(title),
      label: title,
      inputType: "select",
      choices: values
    };
  }).filter((option) => !!option);
  if (!options.length) return null;
  return {
    version: 1,
    options
  };
}
function extractOptionSchemaInput(source) {
  const metadata = source.metadata && typeof source.metadata === "object" ? { ...source.metadata } : null;
  let schema = normalizeCatalogOptionSchema(source.optionSchema);
  if (!schema && metadata) {
    const legacy = convertLegacyOptionSchema(
      metadata["optionSchema"] ?? metadata["option_schema"]
    );
    schema = normalizeCatalogOptionSchema(legacy);
  }
  if (metadata) {
    delete metadata["optionSchema"];
    delete metadata["option_schema"];
    delete metadata["dimensions"];
    delete metadata["weight"];
  }
  return {
    schema,
    metadata: metadata && Object.keys(metadata).length ? metadata : null
  };
}
function parseNumeric(value) {
  const numeric = typeof value === "number" ? value : Number(value);
  if (!Number.isFinite(numeric) || numeric < 0) return null;
  return numeric;
}
function normalizeDimensionsInput(raw) {
  const source = parseObjectLike(raw);
  if (!source) return null;
  const clean = {};
  const width = parseNumeric(source.width);
  const height = parseNumeric(source.height);
  const depth = parseNumeric(source.depth);
  const unit = typeof source.unit === "string" && source.unit.trim().length ? source.unit.trim() : null;
  if (width !== null) clean.width = width;
  if (height !== null) clean.height = height;
  if (depth !== null) clean.depth = depth;
  if (unit) clean.unit = unit;
  return Object.keys(clean).length ? clean : null;
}
function normalizeWeightInput(raw) {
  const source = parseObjectLike(raw);
  if (!source) return null;
  const value = parseNumeric(source.value);
  const unit = typeof source.unit === "string" && source.unit.trim().length ? source.unit.trim() : null;
  if (value === null && !unit) return null;
  const clean = {};
  if (value !== null) clean.value = value;
  if (unit) clean.unit = unit;
  return clean;
}
function extractMeasurementsFromMetadata(metadata) {
  if (!metadata || typeof metadata !== "object") {
    return {
      metadata: null,
      dimensions: null,
      weightValue: null,
      weightUnit: null
    };
  }
  const clone = { ...metadata };
  const dimensions = normalizeDimensionsInput(clone.dimensions);
  const weight = normalizeWeightInput(clone.weight);
  delete clone.dimensions;
  delete clone.weight;
  const cleanedMetadata = Object.keys(clone).length ? clone : null;
  return {
    metadata: cleanedMetadata,
    dimensions,
    weightValue: weight?.value ?? null,
    weightUnit: weight?.unit ?? null
  };
}
function ensureSchemaName(name, fallback) {
  if (name && name.trim().length) return name.trim();
  if (fallback && fallback.trim().length) return fallback.trim();
  return "Product option schema";
}
async function assignOptionSchemaTemplate(em, product, schema, preferredName) {
  const resolvedName = ensureSchemaName(
    schema.name,
    preferredName ?? product.title
  );
  const templateCode = resolveOptionSchemaCode({
    name: schema.name ?? resolvedName,
    fallback: `${resolvedName}-${product.id}`,
    uniqueHint: product.id?.slice(0, 8)
  });
  let template = product.optionSchemaTemplate ?? null;
  if (!template) {
    template = await em.findOne(CatalogOptionSchemaTemplate, {
      organizationId: product.organizationId,
      tenantId: product.tenantId,
      code: templateCode,
      deletedAt: null
    });
  }
  if (!template) {
    template = em.create(CatalogOptionSchemaTemplate, {
      organizationId: product.organizationId,
      tenantId: product.tenantId,
      name: resolvedName,
      code: templateCode,
      description: schema.description ?? null,
      schema: cloneJson(schema),
      metadata: { source: "product" },
      isActive: true
    });
    em.persist(template);
  } else {
    template.code = templateCode;
    template.name = resolvedName;
    template.description = schema.description ?? template.description ?? null;
    template.schema = cloneJson(schema);
  }
  product.optionSchemaTemplate = template;
  return template;
}
function serializeOffer(record) {
  return {
    id: record.id,
    channelId: record.channelId,
    title: record.title,
    description: record.description ?? null,
    defaultMediaId: record.defaultMediaId ?? null,
    defaultMediaUrl: record.defaultMediaUrl ?? null,
    metadata: record.metadata ? cloneJson(record.metadata) : null,
    isActive: record.isActive
  };
}
async function loadOfferSnapshots(em, productId) {
  const offerRecords = await em.find(
    CatalogOffer,
    { product: productId },
    { orderBy: { createdAt: "asc" } }
  );
  return offerRecords.map((offer) => serializeOffer(offer));
}
async function restoreOffersFromSnapshot(em, product, snapshot) {
  const existing = await em.find(CatalogOffer, { product });
  const keepIds = /* @__PURE__ */ new Set();
  const list = Array.isArray(snapshot) ? snapshot : [];
  for (const offer of existing) {
    if (!list.some((snap) => snap.id === offer.id)) {
      em.remove(offer);
    } else {
      keepIds.add(offer.id);
    }
  }
  for (const snap of list) {
    let target = existing.find((entry) => entry.id === snap.id);
    if (!target) {
      target = em.create(CatalogOffer, {
        id: snap.id,
        product,
        organizationId: product.organizationId,
        tenantId: product.tenantId,
        channelId: snap.channelId,
        title: snap.title,
        isActive: snap.isActive
      });
      em.persist(target);
    }
    target.channelId = snap.channelId;
    target.title = snap.title;
    target.description = snap.description ?? null;
    target.defaultMediaId = snap.defaultMediaId ?? null;
    target.defaultMediaUrl = snap.defaultMediaUrl ?? null;
    target.metadata = snap.metadata ? cloneJson(snap.metadata) : null;
    target.isActive = snap.isActive;
    keepIds.add(target.id);
  }
  const toRemove = existing.filter((offer) => !keepIds.has(offer.id));
  if (toRemove.length) {
    for (const offer of toRemove) {
      em.remove(offer);
    }
  }
}
async function syncOffers(em, product, inputs) {
  if (!inputs) return;
  const normalized = inputs.map((input) => ({
    ...input,
    title: input.title?.trim().length ? input.title.trim() : product.title,
    description: input.description != null && input.description.trim().length ? input.description.trim() : product.description ?? null,
    defaultMediaId: typeof input.defaultMediaId === "string" && input.defaultMediaId.trim().length ? input.defaultMediaId.trim() : null,
    defaultMediaUrl: typeof input.defaultMediaUrl === "string" && input.defaultMediaUrl.trim().length ? input.defaultMediaUrl.trim() : null,
    metadata: input.metadata ? cloneJson(input.metadata) : null,
    isActive: input.isActive !== false
  }));
  const existing = await em.find(CatalogOffer, { product });
  const claimed = /* @__PURE__ */ new Set();
  const channelMap = /* @__PURE__ */ new Map();
  for (const offer of existing) {
    channelMap.set(offer.channelId, offer);
  }
  const updates = [];
  for (const input of normalized) {
    if (!input.channelId) continue;
    let target;
    if (input.id) {
      target = existing.find((item) => item.id === input.id);
    }
    if (!target) {
      const existingByChannel = channelMap.get(input.channelId);
      if (existingByChannel && !claimed.has(existingByChannel.id)) {
        target = existingByChannel;
      }
    }
    if (!target) {
      target = em.create(CatalogOffer, {
        product,
        organizationId: product.organizationId,
        tenantId: product.tenantId,
        channelId: input.channelId,
        title: input.title || product.title,
        isActive: input.isActive !== false
      });
      em.persist(target);
      existing.push(target);
      channelMap.set(input.channelId, target);
    }
    target.channelId = input.channelId;
    target.title = input.title || product.title;
    target.description = input.description ?? null;
    target.defaultMediaId = input.defaultMediaId ?? null;
    target.defaultMediaUrl = input.defaultMediaUrl ?? null;
    target.metadata = input.metadata ? cloneJson(input.metadata) : null;
    target.isActive = input.isActive !== false;
    claimed.add(target.id);
    updates.push(target);
  }
  const toRemove = existing.filter((offer) => !claimed.has(offer.id));
  for (const offer of toRemove) {
    em.remove(offer);
  }
}
async function syncCategoryAssignments(em, product, categoryIds) {
  if (categoryIds === void 0) return;
  const normalized = Array.from(
    new Set(
      (Array.isArray(categoryIds) ? categoryIds : []).map((id) => typeof id === "string" ? id.trim() : "").filter((id) => id.length)
    )
  );
  const existing = await em.find(CatalogProductCategoryAssignment, { product });
  if (!normalized.length) {
    if (existing.length) {
      for (const assignment of existing) {
        em.remove(assignment);
      }
    }
    return;
  }
  const categories = await em.find(CatalogProductCategory, {
    id: { $in: normalized },
    organizationId: product.organizationId,
    tenantId: product.tenantId
  });
  const categoryMap = new Map(
    categories.map((category) => [category.id, category])
  );
  const claimed = /* @__PURE__ */ new Set();
  normalized.forEach((categoryId, index) => {
    const category = categoryMap.get(categoryId);
    if (!category) return;
    let assignment = existing.find((item) => {
      const value = typeof item.category === "string" ? item.category : item.category?.id;
      return value === categoryId;
    });
    if (!assignment) {
      assignment = em.create(CatalogProductCategoryAssignment, {
        product,
        category,
        organizationId: product.organizationId,
        tenantId: product.tenantId,
        position: index
      });
      em.persist(assignment);
      existing.push(assignment);
    }
    assignment.position = index;
    claimed.add(assignment.id);
  });
  for (const assignment of existing) {
    if (!claimed.has(assignment.id)) {
      em.remove(assignment);
    }
  }
}
async function syncProductTags(em, product, tags) {
  if (tags === void 0) return;
  const labelMap = /* @__PURE__ */ new Map();
  if (Array.isArray(tags)) {
    tags.forEach((raw) => {
      const label = typeof raw === "string" ? raw.trim() : "";
      if (!label) return;
      const slug = slugifyTagLabel(label);
      if (!labelMap.has(slug)) {
        labelMap.set(slug, label);
      }
    });
  }
  const slugs = Array.from(labelMap.keys());
  const existingAssignments = await findWithDecryption(
    em,
    CatalogProductTagAssignment,
    { product },
    { populate: ["tag"] },
    { tenantId: product.tenantId, organizationId: product.organizationId }
  );
  if (!slugs.length) {
    if (existingAssignments.length) {
      for (const assignment of existingAssignments) {
        em.remove(assignment);
      }
    }
    return;
  }
  const existingTags = await em.find(CatalogProductTag, {
    organizationId: product.organizationId,
    tenantId: product.tenantId,
    slug: { $in: slugs }
  });
  const tagsBySlug = new Map(existingTags.map((tag) => [tag.slug, tag]));
  for (const slug of slugs) {
    if (tagsBySlug.has(slug)) continue;
    const label = labelMap.get(slug) ?? slug;
    const tag = em.create(CatalogProductTag, {
      organizationId: product.organizationId,
      tenantId: product.tenantId,
      slug,
      label
    });
    em.persist(tag);
    tagsBySlug.set(slug, tag);
  }
  const assignmentByTagId = new Map(
    existingAssignments.map((assignment) => [
      typeof assignment.tag === "string" ? assignment.tag : assignment.tag.id,
      assignment
    ])
  );
  const keepIds = /* @__PURE__ */ new Set();
  for (const slug of slugs) {
    const tag = tagsBySlug.get(slug);
    if (!tag) continue;
    const tagId = tag.id;
    let assignment = assignmentByTagId.get(tagId);
    if (!assignment) {
      assignment = em.create(CatalogProductTagAssignment, {
        product,
        tag,
        organizationId: product.organizationId,
        tenantId: product.tenantId
      });
      em.persist(assignment);
    }
    keepIds.add(assignment.id);
  }
  for (const assignment of existingAssignments) {
    if (!keepIds.has(assignment.id)) {
      em.remove(assignment);
    }
  }
}
async function collectProductVariantCleanup(em, product) {
  const variants = await em.find(CatalogProductVariant, { product });
  if (!variants.length) return { variants: [], cleanupEntries: [] };
  const cleanupEntries = await Promise.all(
    variants.map(async (variant) => {
      const custom = await loadCustomFieldSnapshot(em, {
        entityId: E.catalog.catalog_product_variant,
        recordId: variant.id,
        organizationId: variant.organizationId,
        tenantId: variant.tenantId
      });
      return {
        id: variant.id,
        organizationId: variant.organizationId,
        tenantId: variant.tenantId,
        custom: Object.keys(custom).length ? custom : null
      };
    })
  );
  return { variants, cleanupEntries };
}
async function removeProductVariants(em, variants) {
  if (!variants.length) return;
  const variantIds = variants.map((variant) => variant.id);
  if (variantIds.length) {
    await em.nativeDelete(CatalogProductPrice, {
      variant: { $in: variantIds }
    });
  }
  for (const variant of variants) {
    em.remove(variant);
  }
}
async function emitProductVariantCleanupSideEffects(opts) {
  const { dataEngine, ctx, cleanupEntries } = opts;
  for (const cleanup of cleanupEntries) {
    if (!cleanup.custom) continue;
    const resetValues = buildCustomFieldResetMap(cleanup.custom, void 0);
    if (!Object.keys(resetValues).length) continue;
    await setCustomFieldsIfAny({
      dataEngine,
      entityId: E.catalog.catalog_product_variant,
      recordId: cleanup.id,
      organizationId: cleanup.organizationId,
      tenantId: cleanup.tenantId,
      values: resetValues
    });
  }
  for (const cleanup of cleanupEntries) {
    await emitCatalogQueryIndexEvent(ctx, {
      entityType: E.catalog.catalog_product_variant,
      recordId: cleanup.id,
      organizationId: cleanup.organizationId,
      tenantId: cleanup.tenantId,
      action: "deleted"
    });
  }
}
function isProductOwnedOptionSchemaTemplate(template) {
  if (!template || typeof template === "string") return false;
  const metadata = template.metadata;
  if (!metadata || typeof metadata !== "object") return false;
  const source = metadata.source;
  return source === "product";
}
async function resolveOptionSchemaTemplateForRemoval(em, product) {
  const template = product.optionSchemaTemplate;
  if (!isProductOwnedOptionSchemaTemplate(template)) {
    return null;
  }
  const otherUsage = await em.count(CatalogProduct, {
    optionSchemaTemplate: template,
    id: { $ne: product.id },
    deletedAt: null
  });
  if (otherUsage > 0) return null;
  return template;
}
async function loadProductSnapshot(em, id) {
  const record = await findOneWithDecryption(
    em,
    CatalogProduct,
    { id, deletedAt: null },
    { populate: ["optionSchemaTemplate"] }
  );
  if (!record) return null;
  const [offers, tagAssignments, categoryAssignments] = await Promise.all([
    loadOfferSnapshots(em, record.id),
    findWithDecryption(
      em,
      CatalogProductTagAssignment,
      { product: record.id },
      { populate: ["tag"] },
      { tenantId: record.tenantId, organizationId: record.organizationId }
    ),
    findWithDecryption(
      em,
      CatalogProductCategoryAssignment,
      { product: record.id },
      { populate: ["category"] },
      { tenantId: record.tenantId, organizationId: record.organizationId }
    )
  ]);
  const tags = tagAssignments.map((assignment) => {
    const tag = typeof assignment.tag === "string" ? null : assignment.tag ?? null;
    const label = tag?.label ?? null;
    return typeof label === "string" && label.trim().length ? label : null;
  }).filter((label) => !!label).sort((a, b) => a.localeCompare(b));
  const categoryIds = categoryAssignments.slice().sort((a, b) => (a.position ?? 0) - (b.position ?? 0)).map((assignment) => {
    if (typeof assignment.category === "string") return assignment.category;
    return assignment.category?.id ?? null;
  }).filter((value) => !!value);
  const custom = await loadCustomFieldSnapshot(em, {
    entityId: E.catalog.catalog_product,
    recordId: record.id,
    tenantId: record.tenantId,
    organizationId: record.organizationId
  });
  const optionSchemaTemplate = record.optionSchemaTemplate;
  const optionTemplateId = typeof optionSchemaTemplate === "string" ? optionSchemaTemplate : optionSchemaTemplate?.id ?? null;
  const measurements = extractMeasurementsFromMetadata(
    record.metadata ? cloneJson(record.metadata) : null
  );
  const dimensions = record.dimensions && Object.keys(record.dimensions).length ? cloneJson(record.dimensions) : measurements.dimensions ? cloneJson(measurements.dimensions) : null;
  const weightValue = record.weightValue ?? (measurements.weightValue !== null ? toNumericString(measurements.weightValue) : null);
  const weightUnit = record.weightUnit ?? measurements.weightUnit ?? null;
  const metadata = measurements.metadata ? cloneJson(measurements.metadata) : null;
  return {
    id: record.id,
    organizationId: record.organizationId,
    tenantId: record.tenantId,
    title: record.title,
    subtitle: record.subtitle ?? null,
    description: record.description ?? null,
    sku: record.sku ?? null,
    handle: record.handle ?? null,
    taxRateId: record.taxRateId ?? null,
    taxRate: record.taxRate ?? null,
    productType: record.productType,
    statusEntryId: record.statusEntryId ?? null,
    primaryCurrencyCode: record.primaryCurrencyCode ?? null,
    defaultUnit: record.defaultUnit ?? null,
    defaultSalesUnit: record.defaultSalesUnit ?? null,
    defaultSalesUnitQuantity: record.defaultSalesUnitQuantity ?? "1",
    uomRoundingScale: record.uomRoundingScale ?? 4,
    uomRoundingMode: record.uomRoundingMode ?? "half_up",
    unitPriceEnabled: record.unitPriceEnabled ?? false,
    unitPriceReferenceUnit: record.unitPriceReferenceUnit ?? null,
    unitPriceBaseQuantity: record.unitPriceBaseQuantity ?? null,
    defaultMediaId: record.defaultMediaId ?? null,
    defaultMediaUrl: record.defaultMediaUrl ?? null,
    weightValue,
    weightUnit,
    dimensions,
    countryOfOriginCode: record.countryOfOriginCode ?? null,
    pkwiuCode: record.pkwiuCode ?? null,
    cnCode: record.cnCode ?? null,
    hsCode: record.hsCode ?? null,
    taxClassificationCode: record.taxClassificationCode ?? null,
    gtuCodes: record.gtuCodes?.length ? [...record.gtuCodes] : null,
    ageMin: record.ageMin ?? null,
    isExciseGood: record.isExciseGood ?? false,
    exciseCategory: record.exciseCategory ?? null,
    requiresPrescription: record.requiresPrescription ?? false,
    hazmatClass: record.hazmatClass ?? null,
    unNumber: record.unNumber ?? null,
    hazmatPackingGroup: record.hazmatPackingGroup ?? null,
    containsLithiumBattery: record.containsLithiumBattery ?? false,
    launchAt: record.launchAt ? record.launchAt.toISOString() : null,
    endOfLifeAt: record.endOfLifeAt ? record.endOfLifeAt.toISOString() : null,
    availableFrom: record.availableFrom ? record.availableFrom.toISOString() : null,
    availableUntil: record.availableUntil ? record.availableUntil.toISOString() : null,
    minOrderQty: record.minOrderQty ?? null,
    maxOrderQty: record.maxOrderQty ?? null,
    orderQtyIncrement: record.orderQtyIncrement ?? null,
    requiresShipping: record.requiresShipping ?? true,
    isQuoteOnly: record.isQuoteOnly ?? false,
    seoTitle: record.seoTitle ?? null,
    seoDescription: record.seoDescription ?? null,
    canonicalUrl: record.canonicalUrl ?? null,
    customFieldsetCode: record.customFieldsetCode ?? null,
    metadata,
    isConfigurable: record.isConfigurable,
    isActive: record.isActive,
    optionSchemaId: optionTemplateId,
    createdAt: record.createdAt.toISOString(),
    updatedAt: record.updatedAt.toISOString(),
    offers,
    tags,
    categoryIds,
    custom: Object.keys(custom).length ? custom : null
  };
}
function productSeedFromSnapshot(snapshot) {
  return {
    id: snapshot.id,
    organizationId: snapshot.organizationId,
    tenantId: snapshot.tenantId,
    title: snapshot.title,
    subtitle: snapshot.subtitle ?? null,
    description: snapshot.description ?? null,
    sku: snapshot.sku ?? null,
    handle: snapshot.handle ?? null,
    taxRateId: snapshot.taxRateId ?? null,
    taxRate: snapshot.taxRate ?? null,
    productType: snapshot.productType ?? "simple",
    statusEntryId: snapshot.statusEntryId ?? null,
    primaryCurrencyCode: snapshot.primaryCurrencyCode ?? null,
    defaultUnit: snapshot.defaultUnit ?? null,
    defaultSalesUnit: snapshot.defaultSalesUnit ?? null,
    defaultSalesUnitQuantity: snapshot.defaultSalesUnitQuantity ?? "1",
    uomRoundingScale: snapshot.uomRoundingScale,
    uomRoundingMode: snapshot.uomRoundingMode,
    unitPriceEnabled: snapshot.unitPriceEnabled,
    unitPriceReferenceUnit: snapshot.unitPriceReferenceUnit ?? null,
    unitPriceBaseQuantity: snapshot.unitPriceBaseQuantity ?? null,
    defaultMediaId: snapshot.defaultMediaId ?? null,
    defaultMediaUrl: snapshot.defaultMediaUrl ?? null,
    weightValue: snapshot.weightValue ?? null,
    weightUnit: snapshot.weightUnit ?? null,
    dimensions: snapshot.dimensions ? cloneJson(snapshot.dimensions) : null,
    countryOfOriginCode: snapshot.countryOfOriginCode ?? null,
    pkwiuCode: snapshot.pkwiuCode ?? null,
    cnCode: snapshot.cnCode ?? null,
    hsCode: snapshot.hsCode ?? null,
    taxClassificationCode: snapshot.taxClassificationCode ?? null,
    gtuCodes: snapshot.gtuCodes ? [...snapshot.gtuCodes] : null,
    ageMin: snapshot.ageMin ?? null,
    isExciseGood: snapshot.isExciseGood ?? false,
    exciseCategory: snapshot.exciseCategory ?? null,
    requiresPrescription: snapshot.requiresPrescription ?? false,
    hazmatClass: snapshot.hazmatClass ?? null,
    unNumber: snapshot.unNumber ?? null,
    hazmatPackingGroup: snapshot.hazmatPackingGroup ?? null,
    containsLithiumBattery: snapshot.containsLithiumBattery ?? false,
    launchAt: snapshot.launchAt ? new Date(snapshot.launchAt) : null,
    endOfLifeAt: snapshot.endOfLifeAt ? new Date(snapshot.endOfLifeAt) : null,
    availableFrom: snapshot.availableFrom ? new Date(snapshot.availableFrom) : null,
    availableUntil: snapshot.availableUntil ? new Date(snapshot.availableUntil) : null,
    minOrderQty: snapshot.minOrderQty ?? null,
    maxOrderQty: snapshot.maxOrderQty ?? null,
    orderQtyIncrement: snapshot.orderQtyIncrement ?? null,
    requiresShipping: snapshot.requiresShipping ?? true,
    isQuoteOnly: snapshot.isQuoteOnly ?? false,
    seoTitle: snapshot.seoTitle ?? null,
    seoDescription: snapshot.seoDescription ?? null,
    canonicalUrl: snapshot.canonicalUrl ?? null,
    metadata: snapshot.metadata ? cloneJson(snapshot.metadata) : null,
    customFieldsetCode: snapshot.customFieldsetCode ?? null,
    isConfigurable: snapshot.isConfigurable,
    isActive: snapshot.isActive,
    createdAt: new Date(snapshot.createdAt),
    updatedAt: new Date(snapshot.updatedAt)
  };
}
function applyProductSnapshot(em, record, snapshot) {
  record.organizationId = snapshot.organizationId;
  record.tenantId = snapshot.tenantId;
  record.title = snapshot.title;
  record.subtitle = snapshot.subtitle ?? null;
  record.description = snapshot.description ?? null;
  record.sku = snapshot.sku ?? null;
  record.handle = snapshot.handle ?? null;
  record.taxRateId = snapshot.taxRateId ?? null;
  record.taxRate = snapshot.taxRate ?? null;
  record.productType = snapshot.productType;
  record.statusEntryId = snapshot.statusEntryId ?? null;
  record.primaryCurrencyCode = snapshot.primaryCurrencyCode ?? null;
  record.defaultUnit = snapshot.defaultUnit ?? null;
  record.defaultSalesUnit = snapshot.defaultSalesUnit ?? null;
  record.defaultSalesUnitQuantity = snapshot.defaultSalesUnitQuantity ?? "1";
  record.uomRoundingScale = snapshot.uomRoundingScale;
  record.uomRoundingMode = snapshot.uomRoundingMode;
  record.unitPriceEnabled = snapshot.unitPriceEnabled;
  record.unitPriceReferenceUnit = snapshot.unitPriceReferenceUnit ?? null;
  record.unitPriceBaseQuantity = snapshot.unitPriceBaseQuantity ?? null;
  record.defaultMediaId = snapshot.defaultMediaId ?? null;
  record.defaultMediaUrl = snapshot.defaultMediaUrl ?? null;
  record.weightValue = snapshot.weightValue ?? null;
  record.weightUnit = snapshot.weightUnit ?? null;
  record.dimensions = snapshot.dimensions ? cloneJson(snapshot.dimensions) : null;
  record.countryOfOriginCode = snapshot.countryOfOriginCode ?? null;
  record.pkwiuCode = snapshot.pkwiuCode ?? null;
  record.cnCode = snapshot.cnCode ?? null;
  record.hsCode = snapshot.hsCode ?? null;
  record.taxClassificationCode = snapshot.taxClassificationCode ?? null;
  record.gtuCodes = snapshot.gtuCodes ? [...snapshot.gtuCodes] : null;
  record.ageMin = snapshot.ageMin ?? null;
  record.isExciseGood = snapshot.isExciseGood ?? false;
  record.exciseCategory = snapshot.exciseCategory ?? null;
  record.requiresPrescription = snapshot.requiresPrescription ?? false;
  record.hazmatClass = snapshot.hazmatClass ?? null;
  record.unNumber = snapshot.unNumber ?? null;
  record.hazmatPackingGroup = snapshot.hazmatPackingGroup ?? null;
  record.containsLithiumBattery = snapshot.containsLithiumBattery ?? false;
  record.launchAt = snapshot.launchAt ? new Date(snapshot.launchAt) : null;
  record.endOfLifeAt = snapshot.endOfLifeAt ? new Date(snapshot.endOfLifeAt) : null;
  record.availableFrom = snapshot.availableFrom ? new Date(snapshot.availableFrom) : null;
  record.availableUntil = snapshot.availableUntil ? new Date(snapshot.availableUntil) : null;
  record.minOrderQty = snapshot.minOrderQty ?? null;
  record.maxOrderQty = snapshot.maxOrderQty ?? null;
  record.orderQtyIncrement = snapshot.orderQtyIncrement ?? null;
  record.requiresShipping = snapshot.requiresShipping ?? true;
  record.isQuoteOnly = snapshot.isQuoteOnly ?? false;
  record.seoTitle = snapshot.seoTitle ?? null;
  record.seoDescription = snapshot.seoDescription ?? null;
  record.canonicalUrl = snapshot.canonicalUrl ?? null;
  record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null;
  record.customFieldsetCode = snapshot.customFieldsetCode ?? null;
  record.optionSchemaTemplate = snapshot.optionSchemaId ? em.getReference(CatalogOptionSchemaTemplate, snapshot.optionSchemaId) : null;
  record.isConfigurable = snapshot.isConfigurable;
  record.isActive = snapshot.isActive;
  record.createdAt = new Date(snapshot.createdAt);
  record.updatedAt = new Date(snapshot.updatedAt);
}
const createProductCommand = {
  id: "catalog.products.create",
  async execute(rawInput, ctx) {
    const { parsed, custom } = parseWithCustomFields(
      productCreateSchema,
      rawInput
    );
    ensureTenantScope(ctx, parsed.tenantId);
    ensureOrganizationScope(ctx, parsed.organizationId);
    const em = ctx.container.resolve("em").fork();
    const { translate } = await resolveTranslations();
    const now = /* @__PURE__ */ new Date();
    const { taxRateId, taxRate } = await resolveScopedTaxRate(
      em,
      parsed.taxRateId ?? null,
      parsed.taxRate,
      parsed.organizationId,
      parsed.tenantId
    );
    const { schema: optionSchemaDefinition, metadata: sanitizedMetadata } = extractOptionSchemaInput(parsed);
    const measurements = extractMeasurementsFromMetadata(sanitizedMetadata);
    const dimensions = normalizeDimensionsInput(parsed.dimensions) ?? measurements.dimensions;
    const weightValue = parsed.weightValue !== void 0 ? toNumericString(parsed.weightValue) : measurements.weightValue !== null ? toNumericString(measurements.weightValue) : null;
    const weightUnit = parsed.weightUnit !== void 0 ? parsed.weightUnit ?? null : measurements.weightUnit ?? null;
    const metadata = measurements.metadata ? cloneJson(measurements.metadata) : null;
    const unitPriceInput = resolveUnitPriceInput(parsed);
    const unitPriceEnabled = unitPriceInput.enabled ?? false;
    const resolvedUnits = await resolveProductUnitDefaults(em, {
      organizationId: parsed.organizationId,
      tenantId: parsed.tenantId,
      defaultUnit: parsed.defaultUnit ?? null,
      defaultSalesUnit: parsed.defaultSalesUnit ?? parsed.defaultUnit ?? null
    });
    const productId = randomUUID();
    const record = em.create(CatalogProduct, {
      id: productId,
      organizationId: parsed.organizationId,
      tenantId: parsed.tenantId,
      title: parsed.title,
      subtitle: parsed.subtitle ?? null,
      description: parsed.description ?? null,
      sku: parsed.sku ?? null,
      handle: parsed.handle ?? null,
      taxRateId,
      taxRate,
      productType: parsed.productType ?? "simple",
      statusEntryId: parsed.statusEntryId ?? null,
      primaryCurrencyCode: parsed.primaryCurrencyCode ?? null,
      defaultUnit: resolvedUnits.defaultUnit,
      defaultSalesUnit: resolvedUnits.defaultSalesUnit ?? resolvedUnits.defaultUnit,
      defaultSalesUnitQuantity: toNumericString(parsed.defaultSalesUnitQuantity ?? 1) ?? "1",
      uomRoundingScale: parsed.uomRoundingScale ?? 4,
      uomRoundingMode: parsed.uomRoundingMode ?? "half_up",
      unitPriceEnabled,
      unitPriceReferenceUnit: unitPriceEnabled ? unitPriceInput.referenceUnit ?? null : null,
      unitPriceBaseQuantity: unitPriceEnabled ? unitPriceInput.baseQuantity ?? null : null,
      defaultMediaId: parsed.defaultMediaId ?? null,
      defaultMediaUrl: parsed.defaultMediaUrl ?? null,
      weightValue,
      weightUnit,
      dimensions,
      countryOfOriginCode: parsed.countryOfOriginCode ?? null,
      pkwiuCode: parsed.pkwiuCode ?? null,
      cnCode: parsed.cnCode ?? null,
      hsCode: parsed.hsCode ?? null,
      taxClassificationCode: parsed.taxClassificationCode ?? null,
      gtuCodes: parsed.gtuCodes ?? null,
      ageMin: parsed.ageMin ?? null,
      isExciseGood: parsed.isExciseGood ?? false,
      exciseCategory: parsed.exciseCategory ?? null,
      requiresPrescription: parsed.requiresPrescription ?? false,
      hazmatClass: parsed.hazmatClass ?? null,
      unNumber: parsed.unNumber ?? null,
      hazmatPackingGroup: parsed.hazmatPackingGroup ?? null,
      containsLithiumBattery: parsed.containsLithiumBattery ?? false,
      launchAt: parsed.launchAt ?? null,
      endOfLifeAt: parsed.endOfLifeAt ?? null,
      availableFrom: parsed.availableFrom ?? null,
      availableUntil: parsed.availableUntil ?? null,
      minOrderQty: parsed.minOrderQty ?? null,
      maxOrderQty: parsed.maxOrderQty ?? null,
      orderQtyIncrement: parsed.orderQtyIncrement ?? null,
      requiresShipping: parsed.requiresShipping ?? true,
      isQuoteOnly: parsed.isQuoteOnly ?? false,
      seoTitle: parsed.seoTitle ?? null,
      seoDescription: parsed.seoDescription ?? null,
      canonicalUrl: parsed.canonicalUrl ?? null,
      metadata,
      customFieldsetCode: parsed.customFieldsetCode ?? null,
      isConfigurable: parsed.isConfigurable ?? false,
      isActive: parsed.isActive ?? true,
      createdAt: now,
      updatedAt: now
    });
    let optionSchemaTemplate = null;
    if (parsed.optionSchemaId) {
      optionSchemaTemplate = await requireOptionSchemaTemplate(
        em,
        parsed.optionSchemaId,
        { tenantId: parsed.tenantId, organizationId: parsed.organizationId },
        translate("catalog.errors.optionSchemaNotFound", "Option schema not found")
      );
      ensureSameScope(
        optionSchemaTemplate,
        parsed.organizationId,
        parsed.tenantId
      );
      record.optionSchemaTemplate = optionSchemaTemplate;
    } else if (optionSchemaDefinition) {
      optionSchemaTemplate = await assignOptionSchemaTemplate(
        em,
        record,
        optionSchemaDefinition,
        optionSchemaDefinition.name ?? parsed.title
      );
    }
    em.persist(record);
    try {
      await withAtomicFlush(
        em,
        [
          () => em.flush(),
          () => syncOffers(em, record, parsed.offers),
          () => syncCategoryAssignments(em, record, parsed.categoryIds),
          () => syncProductTags(em, record, parsed.tags)
        ],
        { transaction: true }
      );
    } catch (error) {
      await rethrowProductUniqueConstraint(error);
    }
    const dataEngine = ctx.container.resolve("dataEngine");
    await setCustomFieldsIfAny({
      dataEngine,
      entityId: E.catalog.catalog_product,
      recordId: record.id,
      organizationId: record.organizationId,
      tenantId: record.tenantId,
      values: custom
    });
    await emitProductCrudChange({
      dataEngine,
      action: "created",
      product: record
    });
    return { productId: record.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return loadProductSnapshot(em, result.productId);
  },
  buildLog: async ({ result, snapshots }) => {
    const after = snapshots.after;
    if (!after) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate(
        "catalog.audit.products.create",
        "Create catalog product"
      ),
      resourceKind: "catalog.product",
      resourceId: result.productId,
      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 findOneWithDecryption(em, CatalogProduct, { id: after.id });
    if (!record) return;
    ensureTenantScope(ctx, record.tenantId);
    ensureOrganizationScope(ctx, record.organizationId);
    em.remove(record);
    await em.flush();
    const dataEngine = ctx.container.resolve("dataEngine");
    const resetValues = buildCustomFieldResetMap(
      void 0,
      after.custom ?? void 0
    );
    if (Object.keys(resetValues).length) {
      await setCustomFieldsIfAny({
        dataEngine,
        entityId: E.catalog.catalog_product,
        recordId: after.id,
        organizationId: after.organizationId,
        tenantId: after.tenantId,
        values: resetValues
      });
    }
    await emitProductCrudUndoChange({
      dataEngine,
      action: "deleted",
      product: record
    });
  },
  redo: async ({ ctx, logEntry }) => {
    const after = resolveRedoSnapshot(logEntry);
    if (!after) {
      throw new CrudHttpError(400, {
        error: "[internal] redo snapshot unavailable for product create"
      });
    }
    const em = ctx.container.resolve("em").fork();
    const record = await restoreCreatedRow(
      em,
      CatalogProduct,
      after.id,
      () => productSeedFromSnapshot(after)
    );
    applyProductSnapshot(em, record, after);
    try {
      await withAtomicFlush(
        em,
        [
          () => em.flush(),
          () => restoreOffersFromSnapshot(em, record, after.offers),
          () => syncCategoryAssignments(em, record, after.categoryIds),
          () => syncProductTags(em, record, after.tags)
        ],
        { transaction: true }
      );
    } catch (error) {
      await rethrowProductUniqueConstraint(error);
    }
    const dataEngine = ctx.container.resolve("dataEngine");
    if (after.custom && Object.keys(after.custom).length) {
      await setCustomFieldsIfAny({
        dataEngine,
        entityId: E.catalog.catalog_product,
        recordId: record.id,
        organizationId: record.organizationId,
        tenantId: record.tenantId,
        values: after.custom
      });
    }
    await emitProductCrudChange({
      dataEngine,
      action: "created",
      product: record
    });
    return { productId: record.id };
  }
};
const updateProductCommand = {
  id: "catalog.products.update",
  async prepare(input, ctx) {
    const id = requireId(input, "Product id is required");
    const em = ctx.container.resolve("em");
    const snapshot = await loadProductSnapshot(em, id);
    if (snapshot) {
      ensureTenantScope(ctx, snapshot.tenantId);
      ensureOrganizationScope(ctx, snapshot.organizationId);
    }
    return snapshot ? { before: snapshot } : {};
  },
  async execute(rawInput, ctx) {
    const { parsed, custom } = parseWithCustomFields(
      productUpdateSchema,
      rawInput
    );
    const rawPayload = rawInput && typeof rawInput === "object" ? rawInput : null;
    const hasDefaultUnit = Boolean(
      rawPayload && Object.prototype.hasOwnProperty.call(rawPayload, "defaultUnit")
    );
    const hasDefaultSalesUnit = Boolean(
      rawPayload && Object.prototype.hasOwnProperty.call(rawPayload, "defaultSalesUnit")
    );
    const requestedDefaultUnit = hasDefaultUnit ? rawPayload?.defaultUnit : parsed.defaultUnit;
    const requestedDefaultSalesUnit = hasDefaultSalesUnit ? rawPayload?.defaultSalesUnit : parsed.defaultSalesUnit;
    const em = ctx.container.resolve("em").fork();
    const { translate } = await resolveTranslations();
    const record = await findOneWithDecryption(em, CatalogProduct, {
      id: parsed.id,
      deletedAt: null
    });
    if (!record)
      throw new CrudHttpError(404, {
        error: translate("catalog.errors.productNotFound", "Catalog product not found")
      });
    const organizationId = parsed.organizationId ?? record.organizationId;
    const tenantId = parsed.tenantId ?? record.tenantId;
    ensureTenantScope(ctx, tenantId);
    ensureOrganizationScope(ctx, organizationId);
    ensureSameScope(record, organizationId, tenantId);
    const mergedRangeViolation = (() => {
      const minOrderQty = parsed.minOrderQty !== void 0 ? parsed.minOrderQty ?? null : record.minOrderQty ?? null;
      const maxOrderQty = parsed.maxOrderQty !== void 0 ? parsed.maxOrderQty ?? null : record.maxOrderQty ?? null;
      if (minOrderQty != null && maxOrderQty != null && maxOrderQty < minOrderQty) {
        return {
          field: "maxOrderQty",
          key: "catalog.products.validation.orderQtyRange",
          fallback: "Maximum order quantity cannot be lower than the minimum."
        };
      }
      const launchAt = parsed.launchAt !== void 0 ? parsed.launchAt ?? null : record.launchAt ?? null;
      const endOfLifeAt = parsed.endOfLifeAt !== void 0 ? parsed.endOfLifeAt ?? null : record.endOfLifeAt ?? null;
      if (launchAt && endOfLifeAt && endOfLifeAt < launchAt) {
        return {
          field: "endOfLifeAt",
          key: "catalog.products.validation.lifecycleDateRange",
          fallback: "End of life cannot be before the launch date."
        };
      }
      const availableFrom = parsed.availableFrom !== void 0 ? parsed.availableFrom ?? null : record.availableFrom ?? null;
      const availableUntil = parsed.availableUntil !== void 0 ? parsed.availableUntil ?? null : record.availableUntil ?? null;
      if (availableFrom && availableUntil && availableUntil < availableFrom) {
        return {
          field: "availableUntil",
          key: "catalog.products.validation.availabilityDateRange",
          fallback: "Available until cannot be before available from."
        };
      }
      return null;
    })();
    if (mergedRangeViolation) {
      const message = translate(
        mergedRangeViolation.key,
        mergedRangeViolation.fallback
      );
      throw new CrudHttpError(400, {
        error: message,
        fieldErrors: { [mergedRangeViolation.field]: message },
        details: [
          {
            path: [mergedRangeViolation.field],
            message,
            code: "invalid",
            origin: "validation"
          }
        ]
      });
    }
    const dataEngine = ctx.container.resolve("dataEngine");
    const lookupEm = em.fork();
    const taxRateProvided = parsed.taxRateId !== void 0 || parsed.taxRate !== void 0;
    const resolvedTaxRate = taxRateProvided ? await resolveScopedTaxRate(
      lookupEm,
      parsed.taxRateId ?? null,
      parsed.taxRate,
      organizationId,
      tenantId
    ) : null;
    record.organizationId = organizationId;
    record.tenantId = tenantId;
    if (parsed.title !== void 0) record.title = parsed.title;
    if (parsed.subtitle !== void 0)
      record.subtitle = parsed.subtitle ?? null;
    if (parsed.description !== void 0)
      record.description = parsed.description ?? null;
    if (parsed.sku !== void 0) record.sku = parsed.sku ?? null;
    if (parsed.handle !== void 0) record.handle = parsed.handle ?? null;
    if (taxRateProvided) {
      record.taxRateId = resolvedTaxRate?.taxRateId ?? null;
      record.taxRate = resolvedTaxRate?.taxRate ?? null;
    }
    if (parsed.productType !== void 0)
      record.productType = parsed.productType;
    if (parsed.statusEntryId !== void 0)
      record.statusEntryId = parsed.statusEntryId ?? null;
    if (parsed.primaryCurrencyCode !== void 0) {
      record.primaryCurrencyCode = parsed.primaryCurrencyCode ?? null;
    }
    const uomDefaultsTouched = hasDefaultUnit || hasDefaultSalesUnit || parsed.defaultUnit !== void 0 || parsed.defaultSalesUnit !== void 0 || parsed.organizationId !== void 0 || parsed.tenantId !== void 0;
    if (uomDefaultsTouched) {
      const resolvedUnits = await resolveProductUnitDefaults(lookupEm, {
        organizationId,
        tenantId,
        defaultUnit: hasDefaultUnit ? requestedDefaultUnit : parsed.defaultUnit !== void 0 ? parsed.defaultUnit : record.defaultUnit,
        defaultSalesUnit: hasDefaultSalesUnit ? requestedDefaultSalesUnit : parsed.defaultSalesUnit !== void 0 ? parsed.defaultSalesUnit : record.defaultSalesUnit
      });
      await ensureBaseUnitCanBeRemoved(lookupEm, {
        productId: record.id,
        organizationId,
        tenantId,
        defaultUnit: resolvedUnits.defaultUnit,
        defaultSalesUnit: resolvedUnits.defaultSalesUnit
      });
      record.defaultUnit = resolvedUnits.defaultUnit;
      record.defaultSalesUnit = resolvedUnits.defaultSalesUnit;
    }
    if (parsed.defaultSalesUnitQuantity !== void 0) {
      record.defaultSalesUnitQuantity = toNumericString(parsed.defaultSalesUnitQuantity) ?? "1";
    }
    if (parsed.uomRoundingScale !== void 0) {
      record.uomRoundingScale = parsed.uomRoundingScale;
    }
    if (parsed.uomRoundingMode !== void 0) {
      record.uomRoundingMode = parsed.uomRoundingMode;
    }
    const unitPriceInput = resolveUnitPriceInput(parsed);
    if (unitPriceInput.enabledProvided) {
      record.unitPriceEnabled = unitPriceInput.enabled ?? false;
      if (!record.unitPriceEnabled) {
        record.unitPriceReferenceUnit = null;
        record.unitPriceBaseQuantity = null;
      }
    }
    if (unitPriceInput.referenceProvided && record.unitPriceEnabled) {
      record.unitPriceReferenceUnit = unitPriceInput.referenceUnit ?? null;
    }
    if (unitPriceInput.baseProvided && record.unitPriceEnabled) {
      record.unitPriceBaseQuantity = unitPriceInput.baseQuantity ?? null;
    }
    if (parsed.defaultMediaId !== void 0) {
      record.defaultMediaId = parsed.defaultMediaId ?? null;
    }
    if (parsed.defaultMediaUrl !== void 0) {
      record.defaultMediaUrl = parsed.defaultMediaUrl ?? null;
    }
    const metadataProvided = rawInput && typeof rawInput === "object" && Object.prototype.hasOwnProperty.call(rawInput, "metadata");
    const { schema: optionSchemaDefinition, metadata: sanitizedMetadata } = extractOptionSchemaInput(parsed);
    const measurements = extractMeasurementsFromMetadata(sanitizedMetadata);
    const normalizedDimensions = parsed.dimensions !== void 0 ? normalizeDimensionsInput(parsed.dimensions) : measurements.dimensions;
    const weightValueFromInput = parsed.weightValue === null ? null : parsed.weightValue !== void 0 ? toNumericString(parsed.weightValue) : measurements.weightValue !== null ? toNumericString(measurements.weightValue) : null;
    const weightUnitFromInput = parsed.weightUnit !== void 0 ? parsed.weightUnit ?? null : measurements.weightUnit ?? null;
    const weightProvided = parsed.weightValue !== void 0 || parsed.weightUnit !== void 0 || measurements.weightValue !== null || measurements.weightUnit !== null;
    if (normalizedDimensions !== null || parsed.dimensions !== void 0) {
      record.dimensions = normalizedDimensions ? cloneJson(normalizedDimensions) : null;
    }
    if (weightProvided) {
      record.weightValue = weightValueFromInput;
      record.weightUnit = weightUnitFromInput;
    }
    if (metadataProvided) {
      record.metadata = measurements.metadata ? cloneJson(measurements.metadata) : null;
    }
    if (parsed.optionSchemaId !== void 0) {
      if (!parsed.optionSchemaId) {
        record.optionSchemaTemplate = null;
      } else {
        const optionTemplate = await requireOptionSchemaTemplate(
          lookupEm,
          parsed.optionSchemaId,
          { tenantId, organizationId },
          translate("catalog.errors.optionSchemaNotFound", "Option schema not found")
        );
        ensureSameScope(optionTemplate, organizationId, tenantId);
        record.optionSchemaTemplate = optionTemplate;
      }
    }
    if (optionSchemaDefinition) {
      await assignOptionSchemaTemplate(
        em,
        record,
        optionSchemaDefinition,
        optionSchemaDefinition.name ?? parsed.title ?? record.title
      );
    }
    if (parsed.customFieldsetCode !== void 0) {
      record.customFieldsetCode = parsed.customFieldsetCode ?? null;
    }
    if (parsed.isConfigurable !== void 0)
      record.isConfigurable = parsed.isConfigurable;
    if (parsed.isActive !== void 0) record.isActive = parsed.isActive;
    if (parsed.countryOfOriginCode !== void 0)
      record.countryOfOriginCode = parsed.countryOfOriginCode ?? null;
    if (parsed.pkwiuCode !== void 0)
      record.pkwiuCode = parsed.pkwiuCode ?? null;
    if (parsed.cnCode !== void 0) record.cnCode = parsed.cnCode ?? null;
    if (parsed.hsCode !== void 0) record.hsCode = parsed.hsCode ?? null;
    if (parsed.taxClassificationCode !== void 0)
      record.taxClassificationCode = parsed.taxClassificationCode ?? null;
    if (parsed.gtuCodes !== void 0)
      record.gtuCodes = parsed.gtuCodes ?? null;
    if (parsed.ageMin !== void 0) record.ageMin = parsed.ageMin ?? null;
    if (parsed.isExciseGood !== void 0)
      record.isExciseGood = parsed.isExciseGood;
    if (parsed.exciseCategory !== void 0)
      record.exciseCategory = parsed.exciseCategory ?? null;
    if (parsed.requiresPrescription !== void 0)
      record.requiresPrescription = parsed.requiresPrescription;
    if (parsed.hazmatClass !== void 0)
      record.hazmatClass = parsed.hazmatClass ?? null;
    if (parsed.unNumber !== void 0)
      record.unNumber = parsed.unNumber ?? null;
    if (parsed.hazmatPackingGroup !== void 0)
      record.hazmatPackingGroup = parsed.hazmatPackingGroup ?? null;
    if (parsed.containsLithiumBattery !== void 0)
      record.containsLithiumBattery = parsed.containsLithiumBattery;
    if (parsed.launchAt !== void 0)
      record.launchAt = parsed.launchAt ?? null;
    if (parsed.endOfLifeAt !== void 0)
      record.endOfLifeAt = parsed.endOfLifeAt ?? null;
    if (parsed.availableFrom !== void 0)
      record.availableFrom = parsed.availableFrom ?? null;
    if (parsed.availableUntil !== void 0)
      record.availableUntil = parsed.availableUntil ?? null;
    if (parsed.minOrderQty !== void 0)
      record.minOrderQty = parsed.minOrderQty ?? null;
    if (parsed.maxOrderQty !== void 0)
      record.maxOrderQty = parsed.maxOrderQty ?? null;
    if (parsed.orderQtyIncrement !== void 0)
      record.orderQtyIncrement = parsed.orderQtyIncrement ?? null;
    if (parsed.requiresShipping !== void 0)
      record.requiresShipping = parsed.requiresShipping;
    if (parsed.isQuoteOnly !== void 0)
      record.isQuoteOnly = parsed.isQuoteOnly;
    if (parsed.seoTitle !== void 0)
      record.seoTitle = parsed.seoTitle ?? null;
    if (parsed.seoDescription !== void 0)
      record.seoDescription = parsed.seoDescription ?? null;
    if (parsed.canonicalUrl !== void 0)
      record.canonicalUrl = parsed.canonicalUrl ?? null;
    try {
      await withAtomicFlush(
        em,
        [
          () => em.flush(),
          () => syncOffers(em, record, parsed.offers),
          () => syncCategoryAssignments(em, record, parsed.categoryIds),
          () => syncProductTags(em, record, parsed.tags)
        ],
        { transaction: true }
      );
    } catch (error) {
      await rethrowProductUniqueConstraint(error);
    }
    if (custom && Object.keys(custom).length) {
      await setCustomFieldsIfAny({
        dataEngine,
        entityId: E.catalog.catalog_product,
        recordId: record.id,
        organizationId: record.organizationId,
        tenantId: record.tenantId,
        values: custom
      });
    }
    await emitProductCrudChange({
      dataEngine,
      action: "updated",
      product: record
    });
    return { productId: record.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return loadProductSnapshot(em, result.productId);
  },
  buildLog: async ({ snapshots }) => {
    const before = snapshots.before;
    const after = snapshots.after;
    if (!before || !after) return null;
    const { translate } = await resolveTranslations();
    const beforeGtuCodes = (before.gtuCodes ?? []).join(",");
    const afterGtuCodes = (after.gtuCodes ?? []).join(",");
    const gtuCodesChange = beforeGtuCodes !== afterGtuCodes ? {
      gtuCodes: {
        from: before.gtuCodes ?? null,
        to: after.gtuCodes ?? null
      }
    } : null;
    return {
      actionLabel: translate(
        "catalog.audit.products.update",
        "Update catalog product"
      ),
      resourceKind: "catalog.product",
      resourceId: before.id,
      tenantId: before.tenantId,
      organizationId: before.organizationId,
      changes: {
        ...buildChanges(before, after, [
          "title",
          "sku",
          "productType",
          "defaultUnit",
          "defaultSalesUnit",
          "defaultSalesUnitQuantity",
          "uomRoundingScale",
          "uomRoundingMode",
          "unitPriceEnabled",
          "unitPriceReferenceUnit",
          "unitPriceBaseQuantity",
          "isActive",
          "countryOfOriginCode",
          "pkwiuCode",
          "cnCode",
          "hsCode",
          "taxClassificationCode",
          "ageMin",
          "isExciseGood",
          "exciseCategory",
          "requiresPrescription",
          "hazmatClass",
          "unNumber",
          "hazmatPackingGroup",
          "containsLithiumBattery",
          "launchAt",
          "endOfLifeAt",
          "availableFrom",
          "availableUntil",
          "minOrderQty",
          "maxOrderQty",
          "orderQtyIncrement",
          "requiresShipping",
          "isQuoteOnly",
          "seoTitle",
          "seoDescription",
          "canonicalUrl"
        ]),
        ...gtuCodesChange
      },
      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 findOneWithDecryption(em, CatalogProduct, { id: before.id });
    if (!record) {
      record = em.create(CatalogProduct, {
        id: before.id,
        organizationId: before.organizationId,
        tenantId: before.tenantId,
        title: before.title,
        subtitle: before.subtitle ?? null,
        description: before.description ?? null,
        sku: before.sku ?? null,
        handle: before.handle ?? null,
        taxRateId: before.taxRateId ?? null,
        taxRate: before.taxRate ?? null,
        statusEntryId: before.statusEntryId ?? null,
        primaryCurrencyCode: before.primaryCurrencyCode ?? null,
        defaultUnit: before.defaultUnit ?? null,
        defaultSalesUnit: before.defaultSalesUnit ?? null,
        defaultSalesUnitQuantity: before.defaultSalesUnitQuantity ?? "1",
        uomRoundingScale: before.uomRoundingScale,
        uomRoundingMode: before.uomRoundingMode,
        unitPriceEnabled: before.unitPriceEnabled,
        unitPriceReferenceUnit: before.unitPriceReferenceUnit ?? null,
        unitPriceBaseQuantity: before.unitPriceBaseQuantity ?? null,
        weightValue: before.weightValue ?? null,
        weightUnit: before.weightUnit ?? null,
        dimensions: before.dimensions ? cloneJson(before.dimensions) : null,
        metadata: before.metadata ? cloneJson(before.metadata) : null,
        customFieldsetCode: before.customFieldsetCode ?? null,
        optionSchemaTemplate: before.optionSchemaId ? em.getReference(CatalogOptionSchemaTemplate, before.optionSchemaId) : null,
        productType: before.productType ?? "simple",
        isConfigurable: before.isConfigurable,
        isActive: before.isActive,
        createdAt: new Date(before.createdAt),
        updatedAt: new Date(before.updatedAt)
      });
      em.persist(record);
    }
    ensureTenantScope(ctx, before.tenantId);
    ensureOrganizationScope(ctx, before.organizationId);
    applyProductSnapshot(em, record, before);
    await withAtomicFlush(
      em,
      [
        () => em.flush(),
        () => restoreOffersFromSnapshot(em, record, before.offers),
        () => syncCategoryAssignments(em, record, before.categoryIds),
        () => syncProductTags(em, record, before.tags)
      ],
      { transaction: true }
    );
    const dataEngine = ctx.container.resolve("dataEngine");
    const resetValues = buildCustomFieldResetMap(
      before.custom ?? void 0,
      payload?.after?.custom ?? void 0
    );
    if (Object.keys(resetValues).length) {
      await setCustomFieldsIfAny({
        dataEngine,
        entityId: E.catalog.catalog_product,
        recordId: before.id,
        organizationId: before.organizationId,
        tenantId: before.tenantId,
        values: resetValues
      });
    }
    await emitProductCrudUndoChange({
      dataEngine,
      action: "updated",
      product: record
    });
  }
};
const deleteProductCommand = {
  id: "catalog.products.delete",
  async prepare(input, ctx) {
    const id = requireId(input, "Product id is required");
    const em = ctx.container.resolve("em");
    const snapshot = await loadProductSnapshot(em, id);
    if (snapshot) {
      ensureTenantScope(ctx, snapshot.tenantId);
      ensureOrganizationScope(ctx, snapshot.organizationId);
    }
    return snapshot ? { before: snapshot } : {};
  },
  async execute(input, ctx) {
    const id = requireId(input, "Product id is required");
    const em = ctx.container.resolve("em").fork();
    const record = await findOneWithDecryption(
      em,
      CatalogProduct,
      { id },
      { populate: ["optionSchemaTemplate"] }
    );
    if (!record) {
      const { translate } = await resolveTranslations();
      throw new CrudHttpError(404, {
        error: translate(
          "catalog.products.errors.notFound",
          "Catalog product not found"
        )
      });
    }
    const baseEm = ctx.container.resolve("em");
    const snapshot = await loadProductSnapshot(baseEm, id);
    ensureTenantScope(ctx, record.tenantId);
    ensureOrganizationScope(ctx, record.organizationId);
    const dataEngine = ctx.container.resolve("dataEngine");
    const { variants, cleanupEntries } = await collectProductVariantCleanup(
      em,
      record
    );
    await withAtomicFlush(
      em,
      [
        () => removeProductVariants(em, variants),
        async () => {
          await em.nativeDelete(CatalogProductPrice, { product: record.id });
        },
        async () => {
          const templateToRemove = await resolveOptionSchemaTemplateForRemoval(
            em,
            record
          );
          if (templateToRemove) {
            record.optionSchemaTemplate = null;
            em.remove(templateToRemove);
          }
        },
        () => {
          em.remove(record);
        }
      ],
      { transaction: true }
    );
    await emitProductVariantCleanupSideEffects({
      dataEngine,
      ctx,
      cleanupEntries
    });
    if (snapshot?.custom && Object.keys(snapshot.custom).length) {
      const resetValues = buildCustomFieldResetMap(snapshot.custom, void 0);
      if (Object.keys(resetValues).length) {
        await setCustomFieldsIfAny({
          dataEngine,
          entityId: E.catalog.catalog_product,
          recordId: id,
          organizationId: record.organizationId,
          tenantId: record.tenantId,
          values: resetValues
        });
      }
    }
    await emitProductCrudChange({
      dataEngine,
      action: "deleted",
      product: record
    });
    return { productId: id };
  },
  buildLog: async ({ snapshots }) => {
    const before = snapshots.before;
    if (!before) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate(
        "catalog.audit.products.delete",
        "Delete catalog product"
      ),
      resourceKind: "catalog.product",
      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 findOneWithDecryption(em, CatalogProduct, { id: before.id });
    if (!record) {
      record = em.create(CatalogProduct, {
        id: before.id,
        organizationId: before.organizationId,
        tenantId: before.tenantId,
        title: before.title,
        subtitle: before.subtitle ?? null,
        description: before.description ?? null,
        sku: before.sku ?? null,
        handle: before.handle ?? null,
        taxRateId: before.taxRateId ?? null,
        taxRate: before.taxRate ?? null,
        statusEntryId: before.statusEntryId ?? null,
        primaryCurrencyCode: before.primaryCurrencyCode ?? null,
        defaultUnit: before.defaultUnit ?? null,
        defaultSalesUnit: before.defaultSalesUnit ?? null,
        defaultSalesUnitQuantity: before.defaultSalesUnitQuantity ?? "1",
        uomRoundingScale: before.uomRoundingScale,
        uomRoundingMode: before.uomRoundingMode,
        unitPriceEnabled: before.unitPriceEnabled,
        unitPriceReferenceUnit: before.unitPriceReferenceUnit ?? null,
        unitPriceBaseQuantity: before.unitPriceBaseQuantity ?? null,
        weightValue: before.weightValue ?? null,
        weightUnit: before.weightUnit ?? null,
        dimensions: before.dimensions ? cloneJson(before.dimensions) : null,
        metadata: before.metadata ? cloneJson(before.metadata) : null,
        customFieldsetCode: before.customFieldsetCode ?? null,
        optionSchemaTemplate: before.optionSchemaId ? em.getReference(CatalogOptionSchemaTemplate, before.optionSchemaId) : null,
        productType: before.productType ?? "simple",
        isConfigurable: before.isConfigurable,
        isActive: before.isActive,
        createdAt: new Date(before.createdAt),
        updatedAt: new Date(before.updatedAt)
      });
      em.persist(record);
    }
    ensureTenantScope(ctx, before.tenantId);
    ensureOrganizationScope(ctx, before.organizationId);
    applyProductSnapshot(em, record, before);
    await withAtomicFlush(
      em,
      [
        () => em.flush(),
        () => restoreOffersFromSnapshot(em, record, before.offers),
        () => syncCategoryAssignments(em, record, before.categoryIds),
        () => syncProductTags(em, record, before.tags)
      ],
      { transaction: true }
    );
    const dataEngine = ctx.container.resolve("dataEngine");
    if (before.custom && Object.keys(before.custom).length) {
      await setCustomFieldsIfAny({
        dataEngine,
        entityId: E.catalog.catalog_product,
        recordId: before.id,
        organizationId: before.organizationId,
        tenantId: before.tenantId,
        values: before.custom
      });
    }
    await emitProductCrudUndoChange({
      dataEngine,
      action: "created",
      product: record
    });
  }
};
registerCommand(createProductCommand);
registerCommand(updateProductCommand);
registerCommand(deleteProductCommand);
function resolveProductUniqueConstraint(error) {
  if (!(error instanceof UniqueConstraintViolationException)) return null;
  const constraint = getErrorConstraint(error);
  if (constraint === "catalog_products_handle_scope_unique") return "handle";
  if (constraint === "catalog_products_sku_scope_unique") return "sku";
  const message = getErrorMessage(error).toLowerCase();
  if (message.includes("catalog_products_handle_scope_unique") || message.includes(" handle")) {
    return "handle";
  }
  if (message.includes("catalog_products_sku_scope_unique") || message.includes(" sku")) {
    return "sku";
  }
  return null;
}
async function rethrowProductUniqueConstraint(error) {
  const target = resolveProductUniqueConstraint(error);
  if (target === "handle") await throwDuplicateHandleError();
  if (target === "sku") await throwDuplicateSkuError();
  throw error;
}
async function throwDuplicateHandleError() {
  const { translate } = await resolveTranslations();
  const message = translate(
    "catalog.products.errors.handleExists",
    "Handle already in use."
  );
  throw new CrudHttpError(400, {
    error: message,
    fieldErrors: { handle: message },
    details: [
      { path: ["handle"], message, code: "duplicate", origin: "validation" }
    ]
  });
}
async function throwDuplicateSkuError() {
  const { translate } = await resolveTranslations();
  const message = translate(
    "catalog.products.errors.skuExists",
    "SKU already in use."
  );
  throw new CrudHttpError(400, {
    error: message,
    fieldErrors: { sku: message },
    details: [
      { path: ["sku"], message, code: "duplicate", origin: "validation" }
    ]
  });
}
//# sourceMappingURL=products.js.map
