import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
import { Message, MessageObject, MessageRecipient } from "../data/entities.js";
import { MESSAGE_ATTACHMENT_ENTITY_ID } from "../lib/constants.js";
function toIso(value) {
  return value ? value.toISOString() : null;
}
function toDate(value) {
  if (!value) return null;
  return new Date(value);
}
function hasOrganizationAccess(scopeOrganizationId, messageOrganizationId) {
  if (scopeOrganizationId) {
    return messageOrganizationId === scopeOrganizationId;
  }
  return messageOrganizationId == null;
}
function assertOrganizationAccess(scope, message) {
  if (!hasOrganizationAccess(scope.organizationId, message.organizationId)) {
    throw new Error("Access denied");
  }
}
async function getAttachmentIdsForMessage(em, messageId, scope) {
  const { Attachment } = await import("@open-mercato/core/modules/attachments/data/entities");
  const attachments = await em.find(Attachment, {
    entityId: MESSAGE_ATTACHMENT_ENTITY_ID,
    recordId: messageId,
    tenantId: scope.tenantId,
    organizationId: scope.organizationId
  });
  return attachments.map((item) => item.id);
}
async function loadMessageAggregateSnapshot(em, messageId, scope) {
  const where = { id: messageId };
  if (scope) {
    where.tenantId = scope.tenantId;
    where.organizationId = scope.organizationId;
  }
  const message = await em.findOne(Message, where);
  if (!message) return null;
  const recipients = await em.find(MessageRecipient, { messageId });
  const objects = await em.find(MessageObject, { messageId });
  const attachmentIds = await getAttachmentIdsForMessage(em, messageId, {
    tenantId: message.tenantId,
    organizationId: message.organizationId ?? null
  });
  return {
    message: {
      id: message.id,
      type: message.type,
      visibility: message.visibility ?? null,
      sourceEntityType: message.sourceEntityType ?? null,
      sourceEntityId: message.sourceEntityId ?? null,
      externalEmail: message.externalEmail ?? null,
      externalName: message.externalName ?? null,
      threadId: message.threadId ?? null,
      parentMessageId: message.parentMessageId ?? null,
      senderUserId: message.senderUserId,
      subject: message.subject,
      body: message.body,
      bodyFormat: message.bodyFormat,
      priority: message.priority,
      status: message.status,
      isDraft: message.isDraft,
      sentAt: toIso(message.sentAt),
      actionData: message.actionData ?? null,
      actionResult: message.actionResult ?? null,
      actionTaken: message.actionTaken ?? null,
      actionTakenByUserId: message.actionTakenByUserId ?? null,
      actionTakenAt: toIso(message.actionTakenAt),
      sendViaEmail: message.sendViaEmail,
      tenantId: message.tenantId,
      organizationId: message.organizationId ?? null,
      deletedAt: toIso(message.deletedAt)
    },
    recipients: recipients.map((item) => ({
      id: item.id,
      messageId: item.messageId,
      recipientUserId: item.recipientUserId,
      recipientType: item.recipientType,
      status: item.status,
      readAt: toIso(item.readAt),
      archivedAt: toIso(item.archivedAt),
      deletedAt: toIso(item.deletedAt)
    })),
    objects: objects.map((item) => ({
      id: item.id,
      messageId: item.messageId,
      entityModule: item.entityModule,
      entityType: item.entityType,
      entityId: item.entityId,
      actionRequired: item.actionRequired,
      actionType: item.actionType ?? null,
      actionLabel: item.actionLabel ?? null,
      entitySnapshot: item.entitySnapshot ?? null
    })),
    attachmentIds
  };
}
async function restoreMessageAggregateSnapshot(em, snapshot) {
  await withAtomicFlush(em, [async () => {
    const { Attachment } = await import("@open-mercato/core/modules/attachments/data/entities");
    const existingMessage = await em.findOne(Message, { id: snapshot.message.id });
    const existingRecipients = await em.find(MessageRecipient, { messageId: snapshot.message.id });
    const existingObjects = await em.find(MessageObject, { messageId: snapshot.message.id });
    const attachmentsToRelink = snapshot.attachmentIds.length > 0 ? await em.find(Attachment, {
      id: { $in: snapshot.attachmentIds },
      tenantId: snapshot.message.tenantId,
      organizationId: snapshot.message.organizationId
    }) : [];
    if (!existingMessage) {
      const created = em.create(Message, {
        id: snapshot.message.id,
        type: snapshot.message.type,
        visibility: snapshot.message.visibility,
        sourceEntityType: snapshot.message.sourceEntityType,
        sourceEntityId: snapshot.message.sourceEntityId,
        externalEmail: snapshot.message.externalEmail,
        externalName: snapshot.message.externalName,
        threadId: snapshot.message.threadId,
        parentMessageId: snapshot.message.parentMessageId,
        senderUserId: snapshot.message.senderUserId,
        subject: snapshot.message.subject,
        body: snapshot.message.body,
        bodyFormat: snapshot.message.bodyFormat,
        priority: snapshot.message.priority,
        status: snapshot.message.status,
        isDraft: snapshot.message.isDraft,
        sentAt: toDate(snapshot.message.sentAt),
        actionData: snapshot.message.actionData,
        actionResult: snapshot.message.actionResult,
        actionTaken: snapshot.message.actionTaken,
        actionTakenByUserId: snapshot.message.actionTakenByUserId,
        actionTakenAt: toDate(snapshot.message.actionTakenAt),
        sendViaEmail: snapshot.message.sendViaEmail,
        tenantId: snapshot.message.tenantId,
        organizationId: snapshot.message.organizationId,
        deletedAt: toDate(snapshot.message.deletedAt)
      });
      em.persist(created);
    } else {
      existingMessage.type = snapshot.message.type;
      existingMessage.visibility = snapshot.message.visibility;
      existingMessage.sourceEntityType = snapshot.message.sourceEntityType;
      existingMessage.sourceEntityId = snapshot.message.sourceEntityId;
      existingMessage.externalEmail = snapshot.message.externalEmail;
      existingMessage.externalName = snapshot.message.externalName;
      existingMessage.threadId = snapshot.message.threadId;
      existingMessage.parentMessageId = snapshot.message.parentMessageId;
      existingMessage.senderUserId = snapshot.message.senderUserId;
      existingMessage.subject = snapshot.message.subject;
      existingMessage.body = snapshot.message.body;
      existingMessage.bodyFormat = snapshot.message.bodyFormat;
      existingMessage.priority = snapshot.message.priority;
      existingMessage.status = snapshot.message.status;
      existingMessage.isDraft = snapshot.message.isDraft;
      existingMessage.sentAt = toDate(snapshot.message.sentAt);
      existingMessage.actionData = snapshot.message.actionData;
      existingMessage.actionResult = snapshot.message.actionResult;
      existingMessage.actionTaken = snapshot.message.actionTaken;
      existingMessage.actionTakenByUserId = snapshot.message.actionTakenByUserId;
      existingMessage.actionTakenAt = toDate(snapshot.message.actionTakenAt);
      existingMessage.sendViaEmail = snapshot.message.sendViaEmail;
      existingMessage.tenantId = snapshot.message.tenantId;
      existingMessage.organizationId = snapshot.message.organizationId;
      existingMessage.deletedAt = toDate(snapshot.message.deletedAt);
    }
    const recipientById = new Map(existingRecipients.map((item) => [item.id, item]));
    const snapshotRecipientIds = new Set(snapshot.recipients.map((item) => item.id));
    for (const current of existingRecipients) {
      if (!snapshotRecipientIds.has(current.id)) {
        em.remove(current);
      }
    }
    for (const recipient of snapshot.recipients) {
      const existing = recipientById.get(recipient.id);
      if (!existing) {
        em.persist(em.create(MessageRecipient, {
          id: recipient.id,
          messageId: recipient.messageId,
          recipientUserId: recipient.recipientUserId,
          recipientType: recipient.recipientType,
          status: recipient.status,
          readAt: toDate(recipient.readAt),
          archivedAt: toDate(recipient.archivedAt),
          deletedAt: toDate(recipient.deletedAt)
        }));
        continue;
      }
      existing.messageId = recipient.messageId;
      existing.recipientUserId = recipient.recipientUserId;
      existing.recipientType = recipient.recipientType;
      existing.status = recipient.status;
      existing.readAt = toDate(recipient.readAt);
      existing.archivedAt = toDate(recipient.archivedAt);
      existing.deletedAt = toDate(recipient.deletedAt);
    }
    const objectById = new Map(existingObjects.map((item) => [item.id, item]));
    const snapshotObjectIds = new Set(snapshot.objects.map((item) => item.id));
    for (const current of existingObjects) {
      if (!snapshotObjectIds.has(current.id)) {
        em.remove(current);
      }
    }
    for (const object of snapshot.objects) {
      const existing = objectById.get(object.id);
      if (!existing) {
        em.persist(em.create(MessageObject, {
          id: object.id,
          messageId: object.messageId,
          entityModule: object.entityModule,
          entityType: object.entityType,
          entityId: object.entityId,
          actionRequired: object.actionRequired,
          actionType: object.actionType,
          actionLabel: object.actionLabel,
          entitySnapshot: object.entitySnapshot
        }));
        continue;
      }
      existing.messageId = object.messageId;
      existing.entityModule = object.entityModule;
      existing.entityType = object.entityType;
      existing.entityId = object.entityId;
      existing.actionRequired = object.actionRequired;
      existing.actionType = object.actionType;
      existing.actionLabel = object.actionLabel;
      existing.entitySnapshot = object.entitySnapshot;
    }
    await em.nativeDelete(Attachment, {
      entityId: MESSAGE_ATTACHMENT_ENTITY_ID,
      recordId: snapshot.message.id,
      tenantId: snapshot.message.tenantId,
      organizationId: snapshot.message.organizationId,
      id: { $nin: snapshot.attachmentIds.length > 0 ? snapshot.attachmentIds : ["00000000-0000-0000-0000-000000000000"] }
    });
    for (const attachment of attachmentsToRelink) {
      attachment.entityId = MESSAGE_ATTACHMENT_ENTITY_ID;
      attachment.recordId = snapshot.message.id;
    }
  }], { transaction: true });
}
function buildCommandLogBase(actionLabel, resourceId, snapshot) {
  return {
    actionLabel,
    resourceKind: "messages.message",
    resourceId,
    tenantId: snapshot.tenantId,
    organizationId: snapshot.organizationId
  };
}
export {
  assertOrganizationAccess,
  buildCommandLogBase,
  getAttachmentIdsForMessage,
  loadMessageAggregateSnapshot,
  restoreMessageAggregateSnapshot
};
//# sourceMappingURL=shared.js.map
