import {
  CatalogProduct,
  CatalogOffer,
  CatalogProductVariant,
  CatalogOptionSchemaTemplate,
  CatalogPriceKind
} from "../data/entities.js";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { ensureOrganizationScope, ensureSameScope, ensureTenantScope } from "@open-mercato/shared/lib/commands/scope";
import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
function ensureSameTenant(entity, tenantId) {
  if (entity.tenantId !== tenantId) {
    throw new CrudHttpError(403, { error: "Cross-tenant relation forbidden" });
  }
}
import { assertFound } from "@open-mercato/shared/lib/crud/errors";
function cloneJson(value) {
  if (value === null || value === void 0) return value;
  return JSON.parse(JSON.stringify(value));
}
const OPTION_SCHEMA_CODE_MAX_LENGTH = 150;
function randomSuffix(length = 6) {
  return Math.random().toString(36).slice(2, 2 + length);
}
function normalizeOptionSchemaCode(value) {
  if (!value || typeof value !== "string") return "";
  const ascii = value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
  const slug = ascii.toLowerCase().trim().replace(/[^a-z0-9\-_]+/g, "-").replace(/-+/g, "-").replace(/(?:^-+|-+$)/g, "");
  return slug.slice(0, OPTION_SCHEMA_CODE_MAX_LENGTH);
}
function resolveOptionSchemaCode(opts) {
  const baseCandidate = normalizeOptionSchemaCode(opts.code) || normalizeOptionSchemaCode(opts.name) || normalizeOptionSchemaCode(opts.fallback);
  let resolved = baseCandidate || "";
  if (!resolved) {
    resolved = `schema-${randomSuffix()}`;
  }
  if (opts.uniqueHint) {
    const hinted = normalizeOptionSchemaCode(`${resolved}-${opts.uniqueHint}`);
    if (hinted) {
      resolved = hinted;
    }
  }
  return resolved || `schema-${randomSuffix()}`;
}
function toNumericString(value) {
  if (value === void 0 || value === null) return null;
  return value.toString();
}
function commandActorScope(ctx) {
  const orgUnrestricted = ctx.auth?.isSuperAdmin === true || ctx.organizationScope?.allowedIds === null;
  return {
    tenantId: ctx.auth?.tenantId ?? null,
    organizationId: orgUnrestricted ? null : ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
  };
}
function applyScopeToWhere(where, scope) {
  if (scope.tenantId != null) where.tenantId = scope.tenantId;
  if (scope.organizationId != null) where.organizationId = scope.organizationId;
}
async function requireProduct(em, id, scope, message = "Catalog product not found") {
  const where = { id, deletedAt: null };
  applyScopeToWhere(where, scope);
  const product = await findOneWithDecryption(
    em,
    CatalogProduct,
    where,
    void 0,
    { tenantId: scope.tenantId, organizationId: scope.organizationId }
  );
  if (!product) throw new CrudHttpError(404, { error: message });
  return product;
}
async function requireVariant(em, id, scope, message = "Catalog variant not found") {
  const where = { id, deletedAt: null };
  applyScopeToWhere(where, scope);
  const variant = await findOneWithDecryption(
    em,
    CatalogProductVariant,
    where,
    { populate: ["product"] },
    { tenantId: scope.tenantId, organizationId: scope.organizationId }
  );
  if (!variant) throw new CrudHttpError(404, { error: message });
  return variant;
}
async function requireOffer(em, id, scope, message = "Catalog offer not found") {
  const where = { id };
  applyScopeToWhere(where, scope);
  const offer = await findOneWithDecryption(
    em,
    CatalogOffer,
    where,
    void 0,
    { tenantId: scope.tenantId, organizationId: scope.organizationId }
  );
  if (!offer) throw new CrudHttpError(404, { error: message });
  return offer;
}
async function requirePriceKind(em, id, scope, message = "Catalog price kind not found") {
  const where = { id, deletedAt: null };
  applyScopeToWhere(where, { tenantId: scope.tenantId, organizationId: null });
  const priceKind = await findOneWithDecryption(
    em,
    CatalogPriceKind,
    where,
    void 0,
    { tenantId: scope.tenantId, organizationId: null }
  );
  if (!priceKind) throw new CrudHttpError(404, { error: message });
  return priceKind;
}
async function requireOptionSchemaTemplate(em, id, scope, message = "Option schema not found") {
  const where = { id, deletedAt: null };
  applyScopeToWhere(where, scope);
  const schema = await findOneWithDecryption(
    em,
    CatalogOptionSchemaTemplate,
    where,
    void 0,
    { tenantId: scope.tenantId, organizationId: scope.organizationId }
  );
  if (!schema) throw new CrudHttpError(404, { error: message });
  return schema;
}
function getErrorConstraint(error) {
  const errObj = error;
  if (typeof errObj.constraint === "string") return errObj.constraint;
  if (typeof errObj.message === "string") {
    return null;
  }
  return null;
}
function getErrorMessage(error) {
  const errObj = error;
  return typeof errObj.message === "string" ? errObj.message : "";
}
async function emitCatalogQueryIndexEvent(ctx, params) {
  const entityType = String(params.entityType || "");
  const recordId = String(params.recordId || "");
  if (!entityType || !recordId) return;
  let bus = null;
  try {
    bus = ctx.container.resolve("eventBus");
  } catch {
    bus = null;
  }
  if (!bus?.emitEvent) return;
  const payload = {
    entityType,
    recordId,
    organizationId: params.organizationId ?? null,
    tenantId: params.tenantId ?? null,
    crudAction: params.action
  };
  if (params.coverageBaseDelta !== void 0) {
    payload.coverageBaseDelta = params.coverageBaseDelta;
  } else if (params.action === "created") {
    payload.coverageBaseDelta = 1;
  } else if (params.action === "deleted") {
    payload.coverageBaseDelta = -1;
  }
  const eventName = params.action === "deleted" ? "query_index.delete_one" : "query_index.upsert_one";
  await bus.emitEvent(eventName, payload, {
    tenantId: params.tenantId ?? null,
    organizationId: params.organizationId ?? null
  }).catch(() => void 0);
}
export {
  assertFound,
  cloneJson,
  commandActorScope,
  emitCatalogQueryIndexEvent,
  ensureOrganizationScope,
  ensureSameScope,
  ensureSameTenant,
  ensureTenantScope,
  extractUndoPayload,
  getErrorConstraint,
  getErrorMessage,
  normalizeOptionSchemaCode,
  randomSuffix,
  requireOffer,
  requireOptionSchemaTemplate,
  requirePriceKind,
  requireProduct,
  requireVariant,
  resolveOptionSchemaCode,
  toNumericString
};
//# sourceMappingURL=shared.js.map
