import { z } from "zod";
import { registerCommand } from "@open-mercato/shared/lib/commands";
import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
import { Message, MessageRecipient } from "../data/entities.js";
import { emitMessagesEvent } from "../events.js";
import { assertOrganizationAccess } from "./shared.js";
const recipientMutationSchema = z.object({
  messageId: z.string().uuid(),
  tenantId: z.string().uuid(),
  organizationId: z.string().uuid().nullable(),
  userId: z.string().uuid()
});
function toIso(value) {
  return value ? value.toISOString() : null;
}
function toDate(value) {
  if (!value) return null;
  return new Date(value);
}
async function loadRecipientForMutation(em, input) {
  const message = await em.findOne(Message, {
    id: input.messageId,
    tenantId: input.tenantId,
    deletedAt: null
  });
  if (!message) throw new Error("Message not found");
  assertOrganizationAccess(input, message);
  const recipient = await em.findOne(MessageRecipient, {
    messageId: input.messageId,
    recipientUserId: input.userId,
    deletedAt: null
  });
  if (!recipient) throw new Error("Access denied");
  return { message, recipient };
}
function snapshotRecipient(recipient) {
  return {
    id: recipient.id,
    messageId: recipient.messageId,
    recipientUserId: recipient.recipientUserId,
    recipientType: recipient.recipientType,
    status: recipient.status,
    readAt: toIso(recipient.readAt),
    archivedAt: toIso(recipient.archivedAt),
    deletedAt: toIso(recipient.deletedAt)
  };
}
function buildLog(actionLabel, payload, input) {
  return {
    actionLabel,
    resourceKind: "messages.message",
    resourceId: input.messageId,
    tenantId: input.tenantId,
    organizationId: input.organizationId,
    payload: { undo: payload },
    snapshotBefore: payload.before ?? null,
    snapshotAfter: payload.after ?? null
  };
}
function applyRecipientSnapshot(recipient, snapshot) {
  recipient.status = snapshot.status;
  recipient.readAt = toDate(snapshot.readAt);
  recipient.archivedAt = toDate(snapshot.archivedAt);
  recipient.deletedAt = toDate(snapshot.deletedAt);
}
async function emitRecipientMutationEvent(eventId, input) {
  await emitMessagesEvent(
    eventId,
    {
      messageId: input.messageId,
      recipientUserId: input.userId,
      userId: input.userId,
      tenantId: input.tenantId,
      organizationId: input.organizationId
    },
    { persistent: true }
  );
}
const markReadCommand = {
  id: "messages.recipients.mark_read",
  async prepare(rawInput, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    return { before: snapshotRecipient(recipient) };
  },
  async execute(rawInput, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    if (recipient.status !== "read") {
      recipient.status = "read";
      recipient.readAt = /* @__PURE__ */ new Date();
      await em.flush();
      await emitRecipientMutationEvent("messages.message.read", input);
    }
    return { ok: true };
  },
  async captureAfter(rawInput, _result, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    return snapshotRecipient(recipient);
  },
  async buildLog({ input, snapshots }) {
    const parsed = recipientMutationSchema.parse(input);
    return buildLog("Mark message as read", {
      before: snapshots.before ?? null,
      after: snapshots.after ?? null
    }, parsed);
  },
  undo: async ({ logEntry, ctx }) => {
    const undo = extractUndoPayload(logEntry);
    const before = undo?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const recipient = await em.findOne(MessageRecipient, { id: before.id });
    if (!recipient) return;
    applyRecipientSnapshot(recipient, before);
    await em.flush();
  }
};
const markUnreadCommand = {
  id: "messages.recipients.mark_unread",
  async prepare(rawInput, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    return { before: snapshotRecipient(recipient) };
  },
  async execute(rawInput, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    if (recipient.status !== "unread" || recipient.readAt !== null) {
      recipient.status = "unread";
      recipient.readAt = null;
      await em.flush();
      await emitRecipientMutationEvent("messages.message.marked_unread", input);
    }
    return { ok: true };
  },
  async captureAfter(rawInput, _result, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    return snapshotRecipient(recipient);
  },
  async buildLog({ input, snapshots }) {
    const parsed = recipientMutationSchema.parse(input);
    return buildLog("Mark message as unread", {
      before: snapshots.before ?? null,
      after: snapshots.after ?? null
    }, parsed);
  },
  undo: async ({ logEntry, ctx }) => {
    const undo = extractUndoPayload(logEntry);
    const before = undo?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const recipient = await em.findOne(MessageRecipient, { id: before.id });
    if (!recipient) return;
    applyRecipientSnapshot(recipient, before);
    await em.flush();
  }
};
const archiveRecipientCommand = {
  id: "messages.recipients.archive",
  async prepare(rawInput, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    return { before: snapshotRecipient(recipient) };
  },
  async execute(rawInput, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    if (recipient.status !== "archived" || recipient.archivedAt === null) {
      recipient.archivedAt = /* @__PURE__ */ new Date();
      recipient.status = "archived";
      await em.flush();
      await emitRecipientMutationEvent("messages.message.archived", input);
    }
    return { ok: true };
  },
  async captureAfter(rawInput, _result, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    return snapshotRecipient(recipient);
  },
  async buildLog({ input, snapshots }) {
    const parsed = recipientMutationSchema.parse(input);
    return buildLog("Archive message", {
      before: snapshots.before ?? null,
      after: snapshots.after ?? null
    }, parsed);
  },
  undo: async ({ logEntry, ctx }) => {
    const undo = extractUndoPayload(logEntry);
    const before = undo?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const recipient = await em.findOne(MessageRecipient, { id: before.id });
    if (!recipient) return;
    applyRecipientSnapshot(recipient, before);
    await em.flush();
  }
};
const unarchiveRecipientCommand = {
  id: "messages.recipients.unarchive",
  async prepare(rawInput, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    return { before: snapshotRecipient(recipient) };
  },
  async execute(rawInput, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    if (recipient.archivedAt !== null || recipient.status === "archived") {
      recipient.archivedAt = null;
      recipient.status = recipient.readAt ? "read" : "unread";
      await em.flush();
      await emitRecipientMutationEvent("messages.message.unarchived", input);
    }
    return { ok: true };
  },
  async captureAfter(rawInput, _result, ctx) {
    const input = recipientMutationSchema.parse(rawInput);
    const em = ctx.container.resolve("em").fork();
    const { recipient } = await loadRecipientForMutation(em, input);
    return snapshotRecipient(recipient);
  },
  async buildLog({ input, snapshots }) {
    const parsed = recipientMutationSchema.parse(input);
    return buildLog("Unarchive message", {
      before: snapshots.before ?? null,
      after: snapshots.after ?? null
    }, parsed);
  },
  undo: async ({ logEntry, ctx }) => {
    const undo = extractUndoPayload(logEntry);
    const before = undo?.before;
    if (!before) return;
    const em = ctx.container.resolve("em").fork();
    const recipient = await em.findOne(MessageRecipient, { id: before.id });
    if (!recipient) return;
    applyRecipientSnapshot(recipient, before);
    await em.flush();
  }
};
registerCommand(markReadCommand);
registerCommand(markUnreadCommand);
registerCommand(archiveRecipientCommand);
registerCommand(unarchiveRecipientCommand);
//# sourceMappingURL=recipients.js.map
