import { z } from "zod";
import { registerCommand } from "@open-mercato/shared/lib/commands";
import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
import { Message } from "../data/entities.js";
import { attachmentIdsPayloadSchema, unlinkAttachmentPayloadSchema } from "../data/validators.js";
import { linkAttachmentsToMessage } from "../lib/attachments.js";
import { MESSAGE_ATTACHMENT_ENTITY_ID } from "../lib/constants.js";
import { assertOrganizationAccess, getAttachmentIdsForMessage } from "./shared.js";
const attachmentMutationScopeSchema = z.object({
  messageId: z.string().uuid(),
  tenantId: z.string().uuid(),
  organizationId: z.string().uuid().nullable(),
  userId: z.string().uuid()
});
const linkDraftAttachmentsSchema = attachmentMutationScopeSchema.extend({
  attachmentIds: attachmentIdsPayloadSchema.shape.attachmentIds
});
const unlinkDraftAttachmentsSchema = attachmentMutationScopeSchema.extend({
  attachmentIds: z.array(z.string().uuid()).min(1).max(100)
});
async function requireEditableDraftMessage(em, scope, messageId) {
  const message = await em.findOne(Message, {
    id: messageId,
    tenantId: scope.tenantId,
    deletedAt: null
  });
  if (!message) throw new Error("Message not found");
  assertOrganizationAccess(scope, message);
  if (message.senderUserId !== scope.userId) throw new Error("Access denied");
  if (!message.isDraft) throw new Error("Attachments can only be modified on drafts");
  return message;
}
const linkDraftAttachmentsCommand = {
  id: "messages.attachments.link_to_draft",
  async prepare(rawInput, ctx) {
    const input = linkDraftAttachmentsSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const message = await requireEditableDraftMessage(em, input, input.messageId);
    return {
      before: {
        attachmentIds: await getAttachmentIdsForMessage(em, message.id, {
          tenantId: input.tenantId,
          organizationId: input.organizationId
        })
      }
    };
  },
  async execute(rawInput, ctx) {
    const input = linkDraftAttachmentsSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    await requireEditableDraftMessage(em, input, input.messageId);
    await linkAttachmentsToMessage(
      em,
      input.messageId,
      input.attachmentIds,
      input.organizationId,
      input.tenantId
    );
    return { ok: true };
  },
  async captureAfter(rawInput, _result, ctx) {
    const input = linkDraftAttachmentsSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    return {
      attachmentIds: await getAttachmentIdsForMessage(em, input.messageId, {
        tenantId: input.tenantId,
        organizationId: input.organizationId
      })
    };
  },
  buildLog: async ({ input, snapshots }) => {
    const parsed = linkDraftAttachmentsSchema.parse(input);
    return {
      actionLabel: "Link message draft attachments",
      resourceKind: "messages.message",
      resourceId: parsed.messageId,
      tenantId: parsed.tenantId,
      organizationId: parsed.organizationId,
      payload: {
        undo: {
          before: snapshots.before ?? null,
          after: snapshots.after ?? null
        }
      },
      snapshotBefore: snapshots.before ?? null,
      snapshotAfter: snapshots.after ?? null
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const undo = extractUndoPayload(logEntry);
    const before = undo?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const messageId = logEntry?.resourceId;
    const tenantId = logEntry?.tenantId;
    const organizationId = logEntry?.organizationId ?? null;
    if (!messageId || !tenantId) return;
    const { Attachment } = await import("@open-mercato/core/modules/attachments/data/entities");
    await em.nativeDelete(Attachment, {
      entityId: MESSAGE_ATTACHMENT_ENTITY_ID,
      recordId: messageId,
      tenantId,
      organizationId,
      id: { $nin: before.attachmentIds.length ? before.attachmentIds : ["00000000-0000-0000-0000-000000000000"] }
    });
    if (before.attachmentIds.length > 0) {
      const attachments = await em.find(Attachment, {
        id: { $in: before.attachmentIds },
        tenantId,
        organizationId
      });
      for (const attachment of attachments) {
        attachment.entityId = MESSAGE_ATTACHMENT_ENTITY_ID;
        attachment.recordId = messageId;
      }
    }
    await em.flush();
  }
};
const unlinkDraftAttachmentsCommand = {
  id: "messages.attachments.unlink_from_draft",
  async prepare(rawInput, ctx) {
    const input = unlinkDraftAttachmentsSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const message = await requireEditableDraftMessage(em, input, input.messageId);
    return {
      before: {
        attachmentIds: await getAttachmentIdsForMessage(em, message.id, {
          tenantId: input.tenantId,
          organizationId: input.organizationId
        })
      }
    };
  },
  async execute(rawInput, ctx) {
    const input = unlinkDraftAttachmentsSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    await requireEditableDraftMessage(em, input, input.messageId);
    const { Attachment } = await import("@open-mercato/core/modules/attachments/data/entities");
    await em.nativeDelete(Attachment, {
      id: { $in: input.attachmentIds },
      entityId: MESSAGE_ATTACHMENT_ENTITY_ID,
      recordId: input.messageId,
      tenantId: input.tenantId,
      organizationId: input.organizationId
    });
    return { ok: true };
  },
  async captureAfter(rawInput, _result, ctx) {
    const input = unlinkDraftAttachmentsSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    return {
      attachmentIds: await getAttachmentIdsForMessage(em, input.messageId, {
        tenantId: input.tenantId,
        organizationId: input.organizationId
      })
    };
  },
  buildLog: async ({ input, snapshots }) => {
    const parsed = unlinkDraftAttachmentsSchema.parse(input);
    return {
      actionLabel: "Unlink message draft attachments",
      resourceKind: "messages.message",
      resourceId: parsed.messageId,
      tenantId: parsed.tenantId,
      organizationId: parsed.organizationId,
      payload: {
        undo: {
          before: snapshots.before ?? null,
          after: snapshots.after ?? null
        }
      },
      snapshotBefore: snapshots.before ?? null,
      snapshotAfter: snapshots.after ?? null
    };
  },
  undo: async ({ logEntry, ctx }) => {
    const undo = extractUndoPayload(logEntry);
    const before = undo?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const messageId = logEntry?.resourceId;
    const tenantId = logEntry?.tenantId;
    const organizationId = logEntry?.organizationId ?? null;
    if (!messageId || !tenantId) return;
    const { Attachment } = await import("@open-mercato/core/modules/attachments/data/entities");
    await em.nativeDelete(Attachment, {
      entityId: MESSAGE_ATTACHMENT_ENTITY_ID,
      recordId: messageId,
      tenantId,
      organizationId,
      id: { $nin: before.attachmentIds.length ? before.attachmentIds : ["00000000-0000-0000-0000-000000000000"] }
    });
    if (before.attachmentIds.length > 0) {
      const attachments = await em.find(Attachment, {
        id: { $in: before.attachmentIds },
        tenantId,
        organizationId
      });
      for (const attachment of attachments) {
        attachment.entityId = MESSAGE_ATTACHMENT_ENTITY_ID;
        attachment.recordId = messageId;
      }
    }
    await em.flush();
  }
};
function parseUnlinkAttachmentIds(rawInput) {
  const input = unlinkAttachmentPayloadSchema.parse(rawInput);
  return input.attachmentIds ?? (input.attachmentId ? [input.attachmentId] : []);
}
registerCommand(linkDraftAttachmentsCommand);
registerCommand(unlinkDraftAttachmentsCommand);
export {
  parseUnlinkAttachmentIds
};
//# sourceMappingURL=attachments.js.map
