import { registerCommand } from "@open-mercato/shared/lib/commands";
import { emitCrudSideEffects } from "@open-mercato/shared/lib/commands/helpers";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import {
  documentAddressCreateSchema,
  documentAddressDeleteSchema,
  documentAddressUpdateSchema
} from "../data/validators.js";
import { SalesDocumentAddress, SalesOrder, SalesQuote } from "../data/entities.js";
import { ensureOrganizationScope, ensureSameScope, ensureTenantScope, assertFound, extractUndoPayload } from "./shared.js";
import { loadSalesSettings } from "./settings.js";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { E } from "../../../generated/entities.ids.generated.js";
const DOCUMENT_ADDRESS_ENTITY_TYPE = E.sales.sales_document_address;
function snapshotDocumentAddress(entity) {
  return {
    id: entity.id,
    organizationId: entity.organizationId,
    tenantId: entity.tenantId,
    documentId: entity.documentId,
    documentKind: entity.documentKind,
    customerAddressId: entity.customerAddressId ?? null,
    name: entity.name ?? null,
    purpose: entity.purpose ?? null,
    companyName: entity.companyName ?? null,
    addressLine1: entity.addressLine1,
    addressLine2: entity.addressLine2 ?? null,
    buildingNumber: entity.buildingNumber ?? null,
    flatNumber: entity.flatNumber ?? null,
    city: entity.city ?? null,
    region: entity.region ?? null,
    postalCode: entity.postalCode ?? null,
    country: entity.country ?? null,
    latitude: entity.latitude ?? null,
    longitude: entity.longitude ?? null
  };
}
async function loadDocumentAddressSnapshot(em, id) {
  const entity = await findOneWithDecryption(em, SalesDocumentAddress, { id }, {});
  return entity ? snapshotDocumentAddress(entity) : null;
}
function applyDocumentAddressSnapshot(em, entity, snapshot) {
  entity.organizationId = snapshot.organizationId;
  entity.tenantId = snapshot.tenantId;
  entity.documentId = snapshot.documentId;
  entity.documentKind = snapshot.documentKind;
  entity.customerAddressId = snapshot.customerAddressId;
  entity.name = snapshot.name;
  entity.purpose = snapshot.purpose;
  entity.companyName = snapshot.companyName;
  entity.addressLine1 = snapshot.addressLine1;
  entity.addressLine2 = snapshot.addressLine2;
  entity.buildingNumber = snapshot.buildingNumber;
  entity.flatNumber = snapshot.flatNumber;
  entity.city = snapshot.city;
  entity.region = snapshot.region;
  entity.postalCode = snapshot.postalCode;
  entity.country = snapshot.country;
  entity.latitude = snapshot.latitude;
  entity.longitude = snapshot.longitude;
  entity.order = snapshot.documentKind === "order" ? em.getReference(SalesOrder, snapshot.documentId) : null;
  entity.quote = snapshot.documentKind === "quote" ? em.getReference(SalesQuote, snapshot.documentId) : null;
}
function assertDocumentAddressParent(entity, input) {
  if (entity.documentId !== input.documentId || entity.documentKind !== input.documentKind) {
    throw new CrudHttpError(404, { error: "sales.document.address.not_found" });
  }
}
async function emitDocumentAddressIndexSideEffects(ctx, action, snapshot) {
  let dataEngine = null;
  try {
    dataEngine = ctx.container.resolve("dataEngine");
  } catch {
    dataEngine = null;
  }
  if (!dataEngine) return;
  await emitCrudSideEffects({
    dataEngine,
    action,
    entity: snapshot,
    identifiers: {
      id: snapshot.id,
      organizationId: snapshot.organizationId,
      tenantId: snapshot.tenantId
    },
    indexer: { entityType: DOCUMENT_ADDRESS_ENTITY_TYPE }
  });
}
async function requireDocument(em, kind, id, organizationId, tenantId) {
  const repo = kind === "order" ? SalesOrder : SalesQuote;
  const doc = await findOneWithDecryption(em, repo, { id, organizationId, tenantId }, {}, { tenantId, organizationId });
  if (!doc) {
    throw new CrudHttpError(404, { error: "sales.document.not_found" });
  }
  return doc;
}
async function assertAddressEditable(em, params) {
  const settings = await loadSalesSettings(em, {
    tenantId: params.tenantId,
    organizationId: params.organizationId
  });
  const allowed = settings?.orderAddressEditableStatuses ?? null;
  if (!Array.isArray(allowed)) return;
  const { translate } = await resolveTranslations();
  if (allowed.length === 0) {
    throw new CrudHttpError(400, { error: translate("sales.orders.edit_addresses_blocked", "Addresses cannot be changed for the current status.") });
  }
  if (!params.status || !allowed.includes(params.status)) {
    throw new CrudHttpError(400, { error: translate("sales.orders.edit_addresses_blocked", "Addresses cannot be changed for the current status.") });
  }
}
const createDocumentAddress = {
  id: "sales.document-addresses.create",
  async execute(rawInput, ctx) {
    const input = documentAddressCreateSchema.parse(rawInput);
    ensureTenantScope(ctx, input.tenantId);
    ensureOrganizationScope(ctx, input.organizationId);
    const em = ctx.container.resolve("em").fork();
    const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId);
    if (input.documentKind === "order") {
      await assertAddressEditable(em, {
        organizationId: input.organizationId,
        tenantId: input.tenantId,
        status: document.status ?? null
      });
    }
    const entity = em.create(SalesDocumentAddress, {
      organizationId: input.organizationId,
      tenantId: input.tenantId,
      documentId: input.documentId,
      documentKind: input.documentKind,
      customerAddressId: input.customerAddressId ?? null,
      name: input.name ?? null,
      purpose: input.purpose ?? null,
      companyName: input.companyName ?? null,
      addressLine1: input.addressLine1,
      addressLine2: input.addressLine2 ?? null,
      buildingNumber: input.buildingNumber ?? null,
      flatNumber: input.flatNumber ?? null,
      city: input.city ?? null,
      region: input.region ?? null,
      postalCode: input.postalCode ?? null,
      country: input.country ?? null,
      latitude: input.latitude ?? null,
      longitude: input.longitude ?? null,
      order: input.documentKind === "order" ? document : null,
      quote: input.documentKind === "quote" ? document : null
    });
    await em.persist(entity).flush();
    return { id: entity.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return result?.id ? loadDocumentAddressSnapshot(em, result.id) : null;
  },
  buildLog: async ({ result, snapshots }) => {
    const after = snapshots.after;
    if (!after) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("sales.audit.document_addresses.create", "Add document address"),
      resourceKind: "sales.document_address",
      resourceId: result.id,
      parentResourceKind: after.documentKind === "order" ? "sales.order" : "sales.quote",
      parentResourceId: after.documentId,
      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;
    ensureTenantScope(ctx, after.tenantId);
    ensureOrganizationScope(ctx, after.organizationId);
    const em = ctx.container.resolve("em").fork();
    const entity = await findOneWithDecryption(em, SalesDocumentAddress, { id: after.id }, {}, { tenantId: after.tenantId, organizationId: after.organizationId });
    if (!entity) return;
    await em.remove(entity).flush();
    await emitDocumentAddressIndexSideEffects(ctx, "deleted", after);
  }
};
const updateDocumentAddress = {
  id: "sales.document-addresses.update",
  async prepare(rawInput, ctx) {
    const parsed = documentAddressUpdateSchema.parse(rawInput);
    const em = ctx.container.resolve("em");
    const snapshot = await loadDocumentAddressSnapshot(em, parsed.id);
    return snapshot ? { before: snapshot } : {};
  },
  async execute(rawInput, ctx) {
    const input = documentAddressUpdateSchema.parse(rawInput);
    ensureTenantScope(ctx, input.tenantId);
    ensureOrganizationScope(ctx, input.organizationId);
    const em = ctx.container.resolve("em").fork();
    const entity = assertFound(
      await findOneWithDecryption(em, SalesDocumentAddress, { id: input.id }, {}, { tenantId: input.tenantId, organizationId: input.organizationId }),
      "sales.document.address.not_found"
    );
    ensureSameScope(entity, input.organizationId, input.tenantId);
    assertDocumentAddressParent(entity, input);
    const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId);
    if (input.documentKind === "order") {
      await assertAddressEditable(em, {
        organizationId: input.organizationId,
        tenantId: input.tenantId,
        status: document.status ?? null
      });
    }
    entity.documentId = input.documentId;
    entity.documentKind = input.documentKind;
    entity.customerAddressId = input.customerAddressId ?? null;
    entity.name = input.name ?? null;
    entity.purpose = input.purpose ?? null;
    entity.companyName = input.companyName ?? null;
    entity.addressLine1 = input.addressLine1;
    entity.addressLine2 = input.addressLine2 ?? null;
    entity.buildingNumber = input.buildingNumber ?? null;
    entity.flatNumber = input.flatNumber ?? null;
    entity.city = input.city ?? null;
    entity.region = input.region ?? null;
    entity.postalCode = input.postalCode ?? null;
    entity.country = input.country ?? null;
    entity.latitude = input.latitude ?? null;
    entity.longitude = input.longitude ?? null;
    await em.flush();
    return { id: entity.id };
  },
  captureAfter: async (_input, result, ctx) => {
    const em = ctx.container.resolve("em").fork();
    return result?.id ? loadDocumentAddressSnapshot(em, result.id) : null;
  },
  buildLog: async ({ result, snapshots }) => {
    const before = snapshots.before;
    const after = snapshots.after;
    if (!after) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("sales.audit.document_addresses.update", "Update document address"),
      resourceKind: "sales.document_address",
      resourceId: result.id,
      parentResourceKind: after.documentKind === "order" ? "sales.order" : "sales.quote",
      parentResourceId: after.documentId,
      tenantId: after.tenantId,
      organizationId: after.organizationId,
      snapshotBefore: before ?? null,
      snapshotAfter: after,
      payload: { undo: { before: before ?? null, after } }
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const payload = extractUndoPayload(logEntry);
    const before = payload?.before;
    if (!before) return;
    ensureTenantScope(ctx, before.tenantId);
    ensureOrganizationScope(ctx, before.organizationId);
    const em = ctx.container.resolve("em").fork();
    const entity = await findOneWithDecryption(em, SalesDocumentAddress, { id: before.id }, {}, { tenantId: before.tenantId, organizationId: before.organizationId }) ?? em.create(SalesDocumentAddress, { id: before.id });
    applyDocumentAddressSnapshot(em, entity, before);
    await em.persist(entity).flush();
    await emitDocumentAddressIndexSideEffects(ctx, "updated", before);
  }
};
const deleteDocumentAddress = {
  id: "sales.document-addresses.delete",
  async prepare(rawInput, ctx) {
    const parsed = documentAddressDeleteSchema.parse(rawInput);
    const em = ctx.container.resolve("em");
    const snapshot = await loadDocumentAddressSnapshot(em, parsed.id);
    return snapshot ? { before: snapshot } : {};
  },
  async execute(rawInput, ctx) {
    const input = documentAddressDeleteSchema.parse(rawInput);
    ensureTenantScope(ctx, input.tenantId);
    ensureOrganizationScope(ctx, input.organizationId);
    const em = ctx.container.resolve("em").fork();
    const entity = assertFound(
      await findOneWithDecryption(em, SalesDocumentAddress, { id: input.id }, {}, { tenantId: input.tenantId, organizationId: input.organizationId }),
      "sales.document.address.not_found"
    );
    ensureSameScope(entity, input.organizationId, input.tenantId);
    assertDocumentAddressParent(entity, input);
    const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId);
    if (input.documentKind === "order") {
      await assertAddressEditable(em, {
        organizationId: input.organizationId,
        tenantId: input.tenantId,
        status: document.status ?? null
      });
    }
    await em.remove(entity).flush();
    return { ok: true, id: input.id };
  },
  buildLog: async ({ result, snapshots }) => {
    const before = snapshots.before;
    if (!before) return null;
    const { translate } = await resolveTranslations();
    return {
      actionLabel: translate("sales.audit.document_addresses.delete", "Remove document address"),
      resourceKind: "sales.document_address",
      resourceId: result.id,
      parentResourceKind: before.documentKind === "order" ? "sales.order" : "sales.quote",
      parentResourceId: before.documentId,
      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;
    ensureTenantScope(ctx, before.tenantId);
    ensureOrganizationScope(ctx, before.organizationId);
    const em = ctx.container.resolve("em").fork();
    const existing = await findOneWithDecryption(em, SalesDocumentAddress, { id: before.id }, {}, { tenantId: before.tenantId, organizationId: before.organizationId });
    const entity = existing ?? em.create(SalesDocumentAddress, { id: before.id });
    applyDocumentAddressSnapshot(em, entity, before);
    await em.persist(entity).flush();
    await emitDocumentAddressIndexSideEffects(ctx, "created", before);
  }
};
const documentAddressCommands = [createDocumentAddress, updateDocumentAddress, deleteDocumentAddress];
registerCommand(createDocumentAddress);
registerCommand(updateDocumentAddress);
registerCommand(deleteDocumentAddress);
export {
  documentAddressCommands
};
//# sourceMappingURL=documentAddresses.js.map
