import { randomUUID } from "crypto";
import { registerCommand } from "@open-mercato/shared/lib/commands";
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
import { LockMode } from "@mikro-orm/core";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { invalidateCrudCache } from "@open-mercato/shared/lib/crud/cache";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { emitCrudSideEffects } from "@open-mercato/shared/lib/commands/helpers";
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { SalesDocumentNumberGenerator } from "../services/salesDocumentNumberGenerator.js";
import { cloneJson, deriveLineNetFromGross, ensureOrganizationScope, ensureSameScope, ensureTenantScope, extractUndoPayload, toNumericString, enforceSalesDocumentOptimisticLock, SALES_RESOURCE_KIND_ORDER, SALES_RESOURCE_KIND_RETURN } from "./shared.js";
import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
import { SalesOrder, SalesOrderAdjustment, SalesOrderLine, SalesReturn, SalesReturnLine, SalesShipment, SalesShipmentItem } from "../data/entities.js";
import { coerceShipmentQuantity } from "../lib/shipments/snapshots.js";
import { computeAvailableReturnQuantity } from "../lib/returnQuantity.js";
import {
  returnCreateSchema,
  returnUpdateSchema,
  returnDeleteSchema
} from "../data/validators.js";
import { E } from "../../../generated/entities.ids.generated.js";
const returnCrudEvents = {
  module: "sales",
  entity: "return",
  persistent: true,
  buildPayload: (ctx) => ({
    id: ctx.identifiers.id,
    organizationId: ctx.identifiers.organizationId,
    tenantId: ctx.identifiers.tenantId
  })
};
async function invalidateOrderCache(container, order, fallbackTenant) {
  await invalidateCrudCache(
    container,
    SALES_RESOURCE_KIND_ORDER,
    { id: order.id, organizationId: order.organizationId, tenantId: order.tenantId },
    fallbackTenant,
    "updated"
  );
}
function toNumeric(value) {
  if (typeof value === "number" && Number.isFinite(value)) return value;
  if (typeof value === "string" && value.trim().length) {
    const parsed = Number(value);
    if (Number.isFinite(parsed)) return parsed;
  }
  return 0;
}
function round(value) {
  return Math.round((value + Number.EPSILON) * 1e4) / 1e4;
}
function resolveExistingPaymentTotals(order) {
  return {
    paidTotalAmount: toNumeric(order.paidTotalAmount),
    refundedTotalAmount: toNumeric(order.refundedTotalAmount)
  };
}
function applyOrderTotals(order, totals, lineCount) {
  order.subtotalNetAmount = toNumericString(totals.subtotalNetAmount) ?? "0";
  order.subtotalGrossAmount = toNumericString(totals.subtotalGrossAmount) ?? "0";
  order.discountTotalAmount = toNumericString(totals.discountTotalAmount) ?? "0";
  order.taxTotalAmount = toNumericString(totals.taxTotalAmount) ?? "0";
  order.shippingNetAmount = toNumericString(totals.shippingNetAmount) ?? "0";
  order.shippingGrossAmount = toNumericString(totals.shippingGrossAmount) ?? "0";
  order.surchargeTotalAmount = toNumericString(totals.surchargeTotalAmount) ?? "0";
  order.grandTotalNetAmount = toNumericString(totals.grandTotalNetAmount) ?? "0";
  order.grandTotalGrossAmount = toNumericString(totals.grandTotalGrossAmount) ?? "0";
  order.paidTotalAmount = toNumericString(totals.paidTotalAmount) ?? "0";
  order.refundedTotalAmount = toNumericString(totals.refundedTotalAmount) ?? "0";
  order.outstandingAmount = toNumericString(totals.outstandingAmount) ?? "0";
  order.totalsSnapshot = cloneJson(totals);
  order.lineItemCount = lineCount;
}
function mapOrderLineEntityToSnapshot(line) {
  return {
    id: line.id,
    lineNumber: line.lineNumber,
    kind: line.kind,
    productId: line.productId ?? null,
    productVariantId: line.productVariantId ?? null,
    name: line.name ?? null,
    description: line.description ?? null,
    comment: line.comment ?? null,
    quantity: toNumeric(line.quantity),
    quantityUnit: line.quantityUnit ?? null,
    normalizedQuantity: toNumeric(line.normalizedQuantity ?? line.quantity),
    normalizedUnit: line.normalizedUnit ?? line.quantityUnit ?? null,
    uomSnapshot: line.uomSnapshot ? cloneJson(line.uomSnapshot) : null,
    currencyCode: line.currencyCode,
    unitPriceNet: toNumeric(line.unitPriceNet),
    unitPriceGross: toNumeric(line.unitPriceGross),
    discountAmount: toNumeric(line.discountAmount),
    discountPercent: toNumeric(line.discountPercent),
    taxRate: toNumeric(line.taxRate),
    taxAmount: toNumeric(line.taxAmount),
    totalNetAmount: toNumeric(line.totalNetAmount),
    totalGrossAmount: toNumeric(line.totalGrossAmount),
    configuration: line.configuration ? cloneJson(line.configuration) : null,
    promotionCode: line.promotionCode ?? null,
    metadata: line.metadata ? cloneJson(line.metadata) : null,
    customFieldSetId: line.customFieldSetId ?? null
  };
}
function mapOrderAdjustmentToDraft(adjustment) {
  return {
    id: adjustment.id,
    scope: adjustment.scope ?? "order",
    kind: adjustment.kind,
    code: adjustment.code ?? null,
    label: adjustment.label ?? null,
    calculatorKey: adjustment.calculatorKey ?? null,
    promotionId: adjustment.promotionId ?? null,
    rate: toNumeric(adjustment.rate),
    amountNet: toNumeric(adjustment.amountNet),
    amountGross: toNumeric(adjustment.amountGross),
    currencyCode: adjustment.currencyCode ?? null,
    metadata: adjustment.metadata ? cloneJson(adjustment.metadata) : null,
    position: adjustment.position ?? 0
  };
}
function buildCalculationContext(order) {
  return {
    tenantId: order.tenantId,
    organizationId: order.organizationId,
    currencyCode: order.currencyCode,
    metadata: {
      shippingMethod: order.shippingMethodSnapshot ? cloneJson(order.shippingMethodSnapshot) : null,
      paymentMethod: order.paymentMethodSnapshot ? cloneJson(order.paymentMethodSnapshot) : null
    }
  };
}
async function recalculateOrderTotalsForDisplay(em, container, orderId, scope) {
  const order = await findOneWithDecryption(
    em,
    SalesOrder,
    { id: orderId, deletedAt: null },
    {},
    scope
  );
  if (!order) return null;
  const [orderLines, adjustments] = await Promise.all([
    findWithDecryption(em, SalesOrderLine, { order: order.id, deletedAt: null }, {}, scope),
    findWithDecryption(
      em,
      SalesOrderAdjustment,
      { order: order.id, deletedAt: null },
      { orderBy: { position: "asc" } },
      scope
    )
  ]);
  const lineSnapshots = orderLines.map(mapOrderLineEntityToSnapshot);
  const adjustmentDrafts = adjustments.map(mapOrderAdjustmentToDraft);
  const salesCalculationService = container.resolve("salesCalculationService");
  const calculation = await salesCalculationService.calculateDocumentTotals({
    documentKind: "order",
    lines: lineSnapshots,
    adjustments: adjustmentDrafts,
    context: buildCalculationContext(order),
    existingTotals: resolveExistingPaymentTotals(order)
  });
  return calculation.totals;
}
async function loadReturnSnapshot(em, id) {
  const header = await findOneWithDecryption(
    em,
    SalesReturn,
    { id, deletedAt: null },
    { populate: ["order"] },
    {}
  );
  if (!header || !header.order) return null;
  const orderId = typeof header.order === "string" ? header.order : header.order.id;
  const lines = await findWithDecryption(
    em,
    SalesReturnLine,
    { salesReturn: header.id, deletedAt: null },
    { populate: ["orderLine"] },
    { tenantId: header.tenantId, organizationId: header.organizationId }
  );
  const adjustmentIds = [];
  const adjustments = await findWithDecryption(
    em,
    SalesOrderAdjustment,
    { order: orderId, kind: "return", deletedAt: null },
    {},
    { tenantId: header.tenantId, organizationId: header.organizationId }
  );
  adjustments.forEach((adj) => {
    const meta = adj.metadata;
    if (meta && meta.returnId === header.id) adjustmentIds.push(adj.id);
  });
  return {
    id: header.id,
    orderId,
    organizationId: header.organizationId,
    tenantId: header.tenantId,
    returnNumber: header.returnNumber,
    returnedAt: header.returnedAt ? header.returnedAt.toISOString() : null,
    reason: header.reason ?? null,
    notes: header.notes ?? null,
    lines: lines.map((line) => ({
      id: line.id,
      orderLineId: typeof line.orderLine === "string" ? line.orderLine : line.orderLine?.id ?? null,
      quantityReturned: toNumeric(line.quantityReturned),
      unitPriceNet: toNumeric(line.unitPriceNet),
      unitPriceGross: toNumeric(line.unitPriceGross),
      totalNetAmount: toNumeric(line.totalNetAmount),
      totalGrossAmount: toNumeric(line.totalGrossAmount)
    })),
    adjustmentIds
  };
}
async function loadReturnHeaderSnapshot(em, id) {
  const header = await findOneWithDecryption(em, SalesReturn, { id, deletedAt: null }, { populate: ["order"] }, {});
  if (!header || !header.order) return null;
  const orderId = typeof header.order === "string" ? header.order : header.order.id;
  return {
    id: header.id,
    orderId,
    organizationId: header.organizationId,
    tenantId: header.tenantId,
    reason: header.reason ?? null,
    notes: header.notes ?? null,
    returnedAt: header.returnedAt ? header.returnedAt.toISOString() : null
  };
}
async function reverseReturnEffects(em, salesCalculationService, snapshot) {
  const order = await findOneWithDecryption(
    em,
    SalesOrder,
    { id: snapshot.orderId, deletedAt: null },
    {},
    { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
  );
  if (!order) return;
  let lines = [];
  await withAtomicFlush(
    em,
    [
      async () => {
        lines = await findWithDecryption(
          em,
          SalesOrderLine,
          { order: order.id, deletedAt: null },
          {},
          { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
        );
        const lineMap = new Map(lines.map((line) => [line.id, line]));
        snapshot.lines.forEach((entry) => {
          const line = lineMap.get(entry.orderLineId);
          if (!line) return;
          const next = Math.max(0, toNumeric(line.returnedQuantity) - entry.quantityReturned);
          line.returnedQuantity = next.toString();
          line.updatedAt = /* @__PURE__ */ new Date();
          em.persist(line);
        });
      },
      async () => {
        if (snapshot.adjustmentIds.length) {
          const adjustments = await findWithDecryption(
            em,
            SalesOrderAdjustment,
            { id: { $in: snapshot.adjustmentIds }, deletedAt: null },
            {},
            { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
          );
          adjustments.forEach((adj) => em.remove(adj));
        }
        const header = await findOneWithDecryption(
          em,
          SalesReturn,
          { id: snapshot.id, deletedAt: null },
          {},
          { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
        );
        const returnLines = await findWithDecryption(
          em,
          SalesReturnLine,
          { salesReturn: snapshot.id, deletedAt: null },
          {},
          { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
        );
        returnLines.forEach((line) => em.remove(line));
        if (header) em.remove(header);
        const existingAdjustments = await findWithDecryption(
          em,
          SalesOrderAdjustment,
          { order: order.id, deletedAt: null },
          { orderBy: { position: "asc" } },
          { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
        );
        const lineSnapshots = lines.map(mapOrderLineEntityToSnapshot);
        const adjustmentDrafts = existingAdjustments.map(mapOrderAdjustmentToDraft);
        const calculation = await salesCalculationService.calculateDocumentTotals({
          documentKind: "order",
          lines: lineSnapshots,
          adjustments: adjustmentDrafts,
          context: buildCalculationContext(order),
          existingTotals: resolveExistingPaymentTotals(order)
        });
        applyOrderTotals(order, calculation.totals, calculation.lines.length);
        order.updatedAt = /* @__PURE__ */ new Date();
        em.persist(order);
      }
    ],
    { transaction: true }
  );
}
async function restoreReturnEffects(em, salesCalculationService, snapshot) {
  const returnId = snapshot.id;
  const createdLines = [];
  await withAtomicFlush(
    em,
    [
      async () => {
        const order = await findOneWithDecryption(
          em,
          SalesOrder,
          { id: snapshot.orderId, deletedAt: null },
          {},
          { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
        );
        if (!order) {
          throw new CrudHttpError(404, { error: "sales.returns.orderMissing" });
        }
        ensureSameScope(order, snapshot.organizationId, snapshot.tenantId);
        const orderLines = await findWithDecryption(
          em,
          SalesOrderLine,
          { order: order.id, deletedAt: null },
          { lockMode: LockMode.PESSIMISTIC_WRITE },
          { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
        );
        const lineMap = new Map(orderLines.map((line) => [line.id, line]));
        const existingAdjustments = await findWithDecryption(
          em,
          SalesOrderAdjustment,
          { order: order.id, deletedAt: null },
          { orderBy: { position: "asc" } },
          { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
        );
        const positionStart = existingAdjustments.reduce((acc, adj) => Math.max(acc, adj.position ?? 0), 0) + 1;
        const restoredHeader = await findOneWithDecryption(
          em,
          SalesReturn,
          { id: snapshot.id },
          {},
          { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }
        ) ?? em.create(SalesReturn, {
          id: snapshot.id,
          order,
          organizationId: snapshot.organizationId,
          tenantId: snapshot.tenantId,
          returnNumber: snapshot.returnNumber,
          reason: snapshot.reason ?? null,
          notes: snapshot.notes ?? null,
          returnedAt: snapshot.returnedAt ? new Date(snapshot.returnedAt) : /* @__PURE__ */ new Date(),
          createdAt: /* @__PURE__ */ new Date(),
          updatedAt: /* @__PURE__ */ new Date()
        });
        restoredHeader.order = order;
        restoredHeader.deletedAt = null;
        restoredHeader.organizationId = snapshot.organizationId;
        restoredHeader.tenantId = snapshot.tenantId;
        restoredHeader.returnNumber = snapshot.returnNumber;
        restoredHeader.reason = snapshot.reason ?? null;
        restoredHeader.notes = snapshot.notes ?? null;
        restoredHeader.returnedAt = snapshot.returnedAt ? new Date(snapshot.returnedAt) : /* @__PURE__ */ new Date();
        restoredHeader.updatedAt = /* @__PURE__ */ new Date();
        em.persist(restoredHeader);
        const createdAdjustments = [];
        snapshot.lines.forEach((lineSnapshot, index) => {
          const line = lineMap.get(lineSnapshot.orderLineId);
          if (!line) return;
          const totalNet = lineSnapshot.totalNetAmount;
          const totalGross = lineSnapshot.totalGrossAmount;
          const adjustmentId = snapshot.adjustmentIds[index] ?? randomUUID();
          const returnLine = em.create(SalesReturnLine, {
            id: lineSnapshot.id,
            salesReturn: restoredHeader,
            orderLine: em.getReference(SalesOrderLine, line.id),
            organizationId: snapshot.organizationId,
            tenantId: snapshot.tenantId,
            quantityReturned: lineSnapshot.quantityReturned.toString(),
            unitPriceNet: lineSnapshot.unitPriceNet.toString(),
            unitPriceGross: lineSnapshot.unitPriceGross.toString(),
            totalNetAmount: totalNet.toString(),
            totalGrossAmount: totalGross.toString(),
            createdAt: /* @__PURE__ */ new Date(),
            updatedAt: /* @__PURE__ */ new Date()
          });
          createdLines.push(returnLine);
          em.persist(returnLine);
          const adjustment = em.create(SalesOrderAdjustment, {
            id: adjustmentId,
            order,
            orderLine: em.getReference(SalesOrderLine, line.id),
            organizationId: snapshot.organizationId,
            tenantId: snapshot.tenantId,
            scope: "line",
            kind: "return",
            rate: "0",
            amountNet: totalNet.toString(),
            amountGross: totalGross.toString(),
            currencyCode: order.currencyCode,
            metadata: { returnId, returnLineId: lineSnapshot.id },
            position: positionStart + index,
            createdAt: /* @__PURE__ */ new Date(),
            updatedAt: /* @__PURE__ */ new Date()
          });
          createdAdjustments.push(adjustment);
          em.persist(adjustment);
          line.returnedQuantity = (toNumeric(line.returnedQuantity) + lineSnapshot.quantityReturned).toString();
          line.updatedAt = /* @__PURE__ */ new Date();
          em.persist(line);
        });
        const lineSnapshots = orderLines.map(mapOrderLineEntityToSnapshot);
        const adjustmentDrafts = [...existingAdjustments, ...createdAdjustments].map(
          mapOrderAdjustmentToDraft
        );
        const calculation = await salesCalculationService.calculateDocumentTotals({
          documentKind: "order",
          lines: lineSnapshots,
          adjustments: adjustmentDrafts,
          context: buildCalculationContext(order),
          existingTotals: resolveExistingPaymentTotals(order)
        });
        applyOrderTotals(order, calculation.totals, calculation.lines.length);
        order.updatedAt = /* @__PURE__ */ new Date();
        em.persist(order);
      }
    ],
    { transaction: true }
  );
  return createdLines;
}
async function loadShippedQuantityByLine(em, orderId, scope) {
  const shipments = await findWithDecryption(
    em,
    SalesShipment,
    { order: orderId, deletedAt: null },
    {},
    scope
  );
  const shippedByLine = /* @__PURE__ */ new Map();
  if (!shipments.length) return shippedByLine;
  const items = await findWithDecryption(
    em,
    SalesShipmentItem,
    { shipment: { $in: shipments.map((shipment) => shipment.id) } },
    {},
    scope
  );
  items.forEach((item) => {
    const orderLineId = typeof item.orderLine === "string" ? item.orderLine : item.orderLine?.id ?? null;
    if (!orderLineId) return;
    const next = (shippedByLine.get(orderLineId) ?? 0) + coerceShipmentQuantity(item.quantity);
    shippedByLine.set(orderLineId, next);
  });
  return shippedByLine;
}
function normalizeLinesInput(lines) {
  const seen = /* @__PURE__ */ new Set();
  const result = [];
  for (const line of lines) {
    const orderLineId = line.orderLineId;
    if (!orderLineId || seen.has(orderLineId)) continue;
    const quantity = toNumeric(line.quantity);
    if (!Number.isFinite(quantity) || quantity <= 0) continue;
    seen.add(orderLineId);
    result.push({ orderLineId, quantity });
  }
  return result;
}
const createReturnCommand = {
  id: "sales.returns.create",
  async execute(rawInput, ctx) {
    const input = returnCreateSchema.parse(rawInput ?? {});
    ensureTenantScope(ctx, input.tenantId);
    ensureOrganizationScope(ctx, input.organizationId);
    const { translate } = await resolveTranslations();
    const em = ctx.container.resolve("em").fork();
    const requested = normalizeLinesInput(input.lines);
    if (!requested.length) {
      throw new CrudHttpError(400, { error: translate("sales.returns.linesRequired", "Select at least one line to return.") });
    }
    const salesCalculationService = ctx.container.resolve("salesCalculationService");
    const { header, createdLines, order } = await em.transactional(async (tx) => {
      const order2 = await findOneWithDecryption(
        tx,
        SalesOrder,
        { id: input.orderId, deletedAt: null },
        {},
        { tenantId: input.tenantId, organizationId: input.organizationId }
      );
      if (!order2) {
        throw new CrudHttpError(404, { error: translate("sales.returns.orderMissing", "Order not found.") });
      }
      ensureSameScope(order2, input.organizationId, input.tenantId);
      await enforceSalesDocumentOptimisticLock(ctx, order2, SALES_RESOURCE_KIND_ORDER);
      const orderLines = await findWithDecryption(
        tx,
        SalesOrderLine,
        { order: order2.id, deletedAt: null },
        { lockMode: LockMode.PESSIMISTIC_WRITE },
        { tenantId: input.tenantId, organizationId: input.organizationId }
      );
      const lineMap = new Map(orderLines.map((line) => [line.id, line]));
      const shippedByLine = await loadShippedQuantityByLine(tx, order2.id, {
        tenantId: input.tenantId,
        organizationId: input.organizationId
      });
      requested.forEach(({ orderLineId, quantity }) => {
        const line = lineMap.get(orderLineId);
        if (!line) {
          throw new CrudHttpError(404, { error: translate("sales.returns.lineMissing", "Order line not found.") });
        }
        const available = computeAvailableReturnQuantity({
          quantity: toNumeric(line.quantity),
          returnedQuantity: toNumeric(line.returnedQuantity),
          shippedQuantity: shippedByLine.get(orderLineId) ?? 0
        });
        if (quantity - 1e-6 > available) {
          throw new CrudHttpError(400, { error: translate("sales.returns.quantityExceedsShipped", "Cannot return more than the shipped quantity. Ship the items before recording a return.") });
        }
      });
      const existingAdjustments = await findWithDecryption(
        tx,
        SalesOrderAdjustment,
        { order: order2.id, deletedAt: null },
        { orderBy: { position: "asc" } },
        { tenantId: input.tenantId, organizationId: input.organizationId }
      );
      const positionStart = existingAdjustments.reduce((acc, adj) => Math.max(acc, adj.position ?? 0), 0) + 1;
      const numberGenerator = new SalesDocumentNumberGenerator(tx);
      const generated = await numberGenerator.generate({
        kind: "return",
        tenantId: input.tenantId,
        organizationId: input.organizationId
      });
      const returnId = randomUUID();
      const entity = tx.create(SalesReturn, {
        id: returnId,
        order: order2,
        organizationId: input.organizationId,
        tenantId: input.tenantId,
        returnNumber: generated.number,
        reason: input.reason ?? null,
        notes: input.notes ?? null,
        returnedAt: input.returnedAt ?? /* @__PURE__ */ new Date(),
        createdAt: /* @__PURE__ */ new Date(),
        updatedAt: /* @__PURE__ */ new Date()
      });
      tx.persist(entity);
      const createdAdjustments = [];
      const createdReturnLines = [];
      requested.forEach((lineInput, index) => {
        const line = lineMap.get(lineInput.orderLineId);
        if (!line) return;
        const quantity = lineInput.quantity;
        const lineQuantity = Math.max(toNumeric(line.quantity), 0);
        const lineTotalNet = deriveLineNetFromGross(line.totalNetAmount, line.totalGrossAmount, line.taxRate);
        const unitNet = lineQuantity > 0 ? lineTotalNet / lineQuantity : toNumeric(line.unitPriceNet);
        const unitGross = lineQuantity > 0 ? toNumeric(line.totalGrossAmount) / lineQuantity : toNumeric(line.unitPriceGross);
        const totalNet = -round(Math.max(unitNet, 0) * quantity);
        const totalGross = -round(Math.max(unitGross, 0) * quantity);
        const returnLineId = randomUUID();
        const returnLine = tx.create(SalesReturnLine, {
          id: returnLineId,
          salesReturn: entity,
          orderLine: tx.getReference(SalesOrderLine, line.id),
          organizationId: input.organizationId,
          tenantId: input.tenantId,
          quantityReturned: quantity.toString(),
          unitPriceNet: round(unitNet).toString(),
          unitPriceGross: round(unitGross).toString(),
          totalNetAmount: totalNet.toString(),
          totalGrossAmount: totalGross.toString(),
          createdAt: /* @__PURE__ */ new Date(),
          updatedAt: /* @__PURE__ */ new Date()
        });
        createdReturnLines.push(returnLine);
        tx.persist(returnLine);
        const adjustment = tx.create(SalesOrderAdjustment, {
          id: randomUUID(),
          order: order2,
          orderLine: tx.getReference(SalesOrderLine, line.id),
          organizationId: input.organizationId,
          tenantId: input.tenantId,
          scope: "line",
          kind: "return",
          rate: "0",
          amountNet: totalNet.toString(),
          amountGross: totalGross.toString(),
          currencyCode: order2.currencyCode,
          metadata: { returnId, returnLineId },
          position: positionStart + index,
          createdAt: /* @__PURE__ */ new Date(),
          updatedAt: /* @__PURE__ */ new Date()
        });
        createdAdjustments.push(adjustment);
        tx.persist(adjustment);
        line.returnedQuantity = (toNumeric(line.returnedQuantity) + quantity).toString();
        line.updatedAt = /* @__PURE__ */ new Date();
        tx.persist(line);
      });
      const lineSnapshots = orderLines.map(mapOrderLineEntityToSnapshot);
      const adjustmentDrafts = [...existingAdjustments, ...createdAdjustments].map(mapOrderAdjustmentToDraft);
      const calculation = await salesCalculationService.calculateDocumentTotals({
        documentKind: "order",
        lines: lineSnapshots,
        adjustments: adjustmentDrafts,
        context: buildCalculationContext(order2),
        existingTotals: resolveExistingPaymentTotals(order2)
      });
      applyOrderTotals(order2, calculation.totals, calculation.lines.length);
      order2.updatedAt = /* @__PURE__ */ new Date();
      tx.persist(order2);
      await tx.flush();
      return { header: entity, createdLines: createdReturnLines, order: order2 };
    });
    await invalidateOrderCache(ctx.container, order, ctx.auth?.tenantId ?? null);
    const dataEngine = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine,
      action: "created",
      entity: header,
      identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },
      indexer: { entityType: E.sales.sales_return },
      events: returnCrudEvents
    });
    if (createdLines.length) {
      await Promise.all(
        createdLines.map(
          (line) => emitCrudSideEffects({
            dataEngine,
            action: "created",
            entity: line,
            identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },
            indexer: { entityType: E.sales.sales_return_line }
          })
        )
      );
    }
    return { returnId: header.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return loadReturnSnapshot(em, result.returnId);
  },
  buildLog: async ({ result, snapshots }) => {
    const after = snapshots.after;
    if (!after) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("sales.audit.returns.create", "Create return"),
      resourceKind: "sales.return",
      resourceId: result.returnId,
      parentResourceKind: "sales.order",
      parentResourceId: after.orderId ?? null,
      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 salesCalculationService = ctx.container.resolve("salesCalculationService");
    await reverseReturnEffects(em, salesCalculationService, after);
    await invalidateOrderCache(ctx.container, {
      id: after.orderId,
      organizationId: after.organizationId,
      tenantId: after.tenantId
    }, ctx.auth?.tenantId ?? null);
  },
  redo: async ({ ctx, logEntry }) => {
    const after = resolveRedoSnapshot(logEntry);
    if (!after || !after.id) {
      throw new CrudHttpError(400, { error: "[internal] redo snapshot unavailable for sales.returns.create" });
    }
    const em = ctx.container.resolve("em").fork();
    const salesCalculationService = ctx.container.resolve("salesCalculationService");
    const createdLines = await restoreReturnEffects(em, salesCalculationService, after);
    const header = await findOneWithDecryption(
      em,
      SalesReturn,
      { id: after.id, deletedAt: null },
      {},
      { tenantId: after.tenantId, organizationId: after.organizationId }
    );
    if (!header) {
      throw new CrudHttpError(404, { error: "sales.returns.orderMissing" });
    }
    await invalidateOrderCache(ctx.container, {
      id: after.orderId,
      organizationId: after.organizationId,
      tenantId: after.tenantId
    }, ctx.auth?.tenantId ?? null);
    const dataEngine = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine,
      action: "created",
      entity: header,
      identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },
      indexer: { entityType: E.sales.sales_return },
      events: returnCrudEvents
    });
    if (createdLines.length) {
      await Promise.all(
        createdLines.map(
          (line) => emitCrudSideEffects({
            dataEngine,
            action: "created",
            entity: line,
            identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },
            indexer: { entityType: E.sales.sales_return_line }
          })
        )
      );
    }
    return { returnId: header.id };
  }
};
const updateReturnCommand = {
  id: "sales.returns.update",
  async prepare(rawInput, ctx) {
    const parsed = returnUpdateSchema.parse(rawInput ?? {});
    const em = ctx.container.resolve("em");
    const snapshot = await loadReturnHeaderSnapshot(em, parsed.id);
    if (snapshot) {
      ensureTenantScope(ctx, snapshot.tenantId);
      ensureOrganizationScope(ctx, snapshot.organizationId);
    }
    return snapshot ? { before: snapshot } : {};
  },
  async execute(rawInput, ctx) {
    const input = returnUpdateSchema.parse(rawInput ?? {});
    ensureTenantScope(ctx, input.tenantId);
    ensureOrganizationScope(ctx, input.organizationId);
    const { translate } = await resolveTranslations();
    const em = ctx.container.resolve("em").fork();
    const header = await em.transactional(async (tx) => {
      const entity = await findOneWithDecryption(
        tx,
        SalesReturn,
        { id: input.id, deletedAt: null },
        { populate: ["order"] },
        { tenantId: input.tenantId, organizationId: input.organizationId }
      );
      if (!entity || !entity.order) {
        throw new CrudHttpError(404, { error: translate("sales.returns.notFound", "Return not found.") });
      }
      ensureSameScope(entity, input.organizationId, input.tenantId);
      const orderId = typeof entity.order === "string" ? entity.order : entity.order.id;
      if (input.orderId !== orderId) {
        throw new CrudHttpError(400, { error: translate("sales.returns.orderMismatch", "Return does not belong to this order.") });
      }
      await enforceSalesDocumentOptimisticLock(ctx, entity, SALES_RESOURCE_KIND_RETURN);
      if (input.reason !== void 0) entity.reason = input.reason.length ? input.reason : null;
      if (input.notes !== void 0) entity.notes = input.notes.length ? input.notes : null;
      if (input.returnedAt !== void 0) entity.returnedAt = input.returnedAt ?? null;
      entity.updatedAt = /* @__PURE__ */ new Date();
      tx.persist(entity);
      await tx.flush();
      return entity;
    });
    const dataEngine = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine,
      action: "updated",
      entity: header,
      identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },
      indexer: { entityType: E.sales.sales_return },
      events: returnCrudEvents
    });
    return { returnId: header.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return loadReturnHeaderSnapshot(em, result.returnId);
  },
  buildLog: async ({ snapshots, result }) => {
    const { translate } = await resolveTranslations();
    const before = snapshots.before;
    const after = snapshots.after;
    return {
      actionLabel: translate("sales.audit.returns.update", "Update return"),
      resourceKind: "sales.return",
      resourceId: result.returnId,
      parentResourceKind: "sales.order",
      parentResourceId: after?.orderId ?? before?.orderId ?? null,
      tenantId: after?.tenantId ?? before?.tenantId ?? null,
      organizationId: after?.organizationId ?? before?.organizationId ?? null,
      snapshotBefore: before ?? null,
      snapshotAfter: after ?? null,
      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();
    await em.transactional(async (tx) => {
      const entity = await findOneWithDecryption(
        tx,
        SalesReturn,
        { id: before.id, deletedAt: null },
        {},
        { tenantId: before.tenantId, organizationId: before.organizationId }
      );
      if (!entity) return;
      entity.reason = before.reason;
      entity.notes = before.notes;
      entity.returnedAt = before.returnedAt ? new Date(before.returnedAt) : null;
      entity.updatedAt = /* @__PURE__ */ new Date();
      tx.persist(entity);
      await tx.flush();
    });
    const dataEngine = ctx.container.resolve("dataEngine");
    const restored = await findOneWithDecryption(
      em,
      SalesReturn,
      { id: before.id, deletedAt: null },
      {},
      { tenantId: before.tenantId, organizationId: before.organizationId }
    );
    if (restored) {
      await emitCrudSideEffects({
        dataEngine,
        action: "updated",
        entity: restored,
        identifiers: { id: restored.id, organizationId: restored.organizationId, tenantId: restored.tenantId },
        indexer: { entityType: E.sales.sales_return },
        events: returnCrudEvents
      });
    }
  }
};
const deleteReturnCommand = {
  id: "sales.returns.delete",
  async prepare(rawInput, ctx) {
    const parsed = returnDeleteSchema.parse(rawInput ?? {});
    const em = ctx.container.resolve("em");
    const snapshot = await loadReturnSnapshot(em, parsed.id);
    if (snapshot) {
      ensureTenantScope(ctx, snapshot.tenantId);
      ensureOrganizationScope(ctx, snapshot.organizationId);
    }
    return snapshot ? { before: snapshot } : {};
  },
  async execute(rawInput, ctx) {
    const input = returnDeleteSchema.parse(rawInput ?? {});
    ensureTenantScope(ctx, input.tenantId);
    ensureOrganizationScope(ctx, input.organizationId);
    const { translate } = await resolveTranslations();
    const em = ctx.container.resolve("em").fork();
    const salesCalculationService = ctx.container.resolve("salesCalculationService");
    const snapshot = await loadReturnSnapshot(em, input.id);
    if (!snapshot) {
      throw new CrudHttpError(404, { error: translate("sales.returns.notFound", "Return not found.") });
    }
    ensureSameScope(snapshot, input.organizationId, input.tenantId);
    if (input.orderId !== snapshot.orderId) {
      throw new CrudHttpError(400, { error: translate("sales.returns.orderMismatch", "Return does not belong to this order.") });
    }
    const header = await findOneWithDecryption(
      em,
      SalesReturn,
      { id: input.id, deletedAt: null },
      {},
      { tenantId: input.tenantId, organizationId: input.organizationId }
    );
    if (!header) {
      throw new CrudHttpError(404, { error: translate("sales.returns.notFound", "Return not found.") });
    }
    ensureSameScope(header, input.organizationId, input.tenantId);
    await enforceSalesDocumentOptimisticLock(ctx, header, SALES_RESOURCE_KIND_RETURN);
    await reverseReturnEffects(em, salesCalculationService, snapshot);
    await invalidateOrderCache(ctx.container, {
      id: snapshot.orderId,
      organizationId: snapshot.organizationId,
      tenantId: snapshot.tenantId
    }, ctx.auth?.tenantId ?? null);
    const dataEngine = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine,
      action: "deleted",
      entity: header,
      identifiers: { id: snapshot.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },
      indexer: { entityType: E.sales.sales_return },
      events: returnCrudEvents
    });
    if (snapshot.lines.length) {
      await Promise.all(
        snapshot.lines.map(
          (line) => emitCrudSideEffects({
            dataEngine,
            action: "deleted",
            entity: { id: line.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },
            identifiers: { id: line.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },
            indexer: { entityType: E.sales.sales_return_line }
          })
        )
      );
    }
    return { returnId: snapshot.id };
  },
  buildLog: async ({ snapshots, result }) => {
    const before = snapshots.before;
    if (!before) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("sales.audit.returns.delete", "Delete return"),
      resourceKind: "sales.return",
      resourceId: result.returnId,
      parentResourceKind: "sales.order",
      parentResourceId: before.orderId ?? null,
      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();
    const salesCalculationService = ctx.container.resolve("salesCalculationService");
    const createdLines = await restoreReturnEffects(em, salesCalculationService, before);
    const header = await findOneWithDecryption(
      em,
      SalesReturn,
      { id: before.id, deletedAt: null },
      {},
      { tenantId: before.tenantId, organizationId: before.organizationId }
    );
    if (!header) return;
    await invalidateOrderCache(ctx.container, {
      id: before.orderId,
      organizationId: before.organizationId,
      tenantId: before.tenantId
    }, ctx.auth?.tenantId ?? null);
    const dataEngine = ctx.container.resolve("dataEngine");
    await emitCrudSideEffects({
      dataEngine,
      action: "created",
      entity: header,
      identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },
      indexer: { entityType: E.sales.sales_return },
      events: returnCrudEvents
    });
    if (createdLines.length) {
      await Promise.all(
        createdLines.map(
          (line) => emitCrudSideEffects({
            dataEngine,
            action: "created",
            entity: line,
            identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },
            indexer: { entityType: E.sales.sales_return_line }
          })
        )
      );
    }
  }
};
registerCommand(createReturnCommand);
registerCommand(updateReturnCommand);
registerCommand(deleteReturnCommand);
const returnCommands = [createReturnCommand, updateReturnCommand, deleteReturnCommand];
export {
  loadReturnSnapshot,
  recalculateOrderTotalsForDisplay,
  returnCommands
};
//# sourceMappingURL=returns.js.map
