import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { User } from "../../auth/data/entities.js";
import { Message, MessageObject, MessageRecipient } from "../data/entities.js";
import { emitMessagesEvent } from "../events.js";
import { getMessageEmailAttachments } from "../lib/attachments.js";
import {
  sendMessageEmailToExternal,
  sendMessageEmailToRecipient
} from "../lib/email-sender.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("messages").child({ component: "send-email" });
const MESSAGES_EMAIL_QUEUE_NAME = "messages-email";
const metadata = {
  queue: MESSAGES_EMAIL_QUEUE_NAME,
  id: "messages:send-email",
  concurrency: 10
};
async function emitEmailDeliveryEvent(ctx, eventId, payload) {
  const eventBus = ctx.resolve("eventBus");
  if (!eventBus || typeof eventBus !== "object" || typeof eventBus.emit !== "function") {
    return;
  }
  await emitMessagesEvent(eventId, payload, { persistent: true });
}
async function resolveSender(em, message) {
  const sender = await findOneWithDecryption(
    em,
    User,
    {
      id: message.senderUserId,
      tenantId: message.tenantId,
      deletedAt: null
    },
    void 0,
    { tenantId: message.tenantId, organizationId: message.organizationId ?? null }
  );
  return {
    name: sender?.name ?? null,
    email: sender?.email ?? null
  };
}
async function resolveMessageScope(em, payload) {
  return await findOneWithDecryption(
    em,
    Message,
    {
      id: payload.messageId,
      tenantId: payload.tenantId,
      organizationId: payload.organizationId ?? null,
      deletedAt: null
    },
    void 0,
    { tenantId: payload.tenantId, organizationId: payload.organizationId ?? null }
  );
}
async function claimRecipientDelivery(em, payload) {
  const claimTimestamp = /* @__PURE__ */ new Date();
  const updatedRows = await em.nativeUpdate(
    MessageRecipient,
    {
      messageId: payload.messageId,
      recipientUserId: payload.recipientUserId,
      emailSentAt: null
    },
    {
      emailSentAt: claimTimestamp,
      emailFailedAt: null,
      emailError: null
    }
  );
  return updatedRows > 0 ? claimTimestamp : null;
}
async function releaseRecipientClaim(em, payload, claimTimestamp, errorMessage) {
  await em.nativeUpdate(
    MessageRecipient,
    {
      messageId: payload.messageId,
      recipientUserId: payload.recipientUserId,
      emailSentAt: claimTimestamp
    },
    {
      emailSentAt: null,
      emailFailedAt: /* @__PURE__ */ new Date(),
      emailError: errorMessage
    }
  );
}
async function claimExternalDelivery(em, payload) {
  const claimTimestamp = /* @__PURE__ */ new Date();
  const updatedRows = await em.nativeUpdate(
    Message,
    {
      id: payload.messageId,
      tenantId: payload.tenantId,
      organizationId: payload.organizationId ?? null,
      externalEmailSentAt: null
    },
    {
      externalEmailSentAt: claimTimestamp,
      externalEmailFailedAt: null,
      externalEmailError: null
    }
  );
  return updatedRows > 0 ? claimTimestamp : null;
}
async function releaseExternalClaim(em, payload, claimTimestamp, errorMessage) {
  await em.nativeUpdate(
    Message,
    {
      id: payload.messageId,
      tenantId: payload.tenantId,
      organizationId: payload.organizationId ?? null,
      externalEmailSentAt: claimTimestamp
    },
    {
      externalEmailSentAt: null,
      externalEmailFailedAt: /* @__PURE__ */ new Date(),
      externalEmailError: errorMessage
    }
  );
}
async function handle(job, ctx) {
  const { payload } = job;
  const em = ctx.resolve("em").fork();
  const message = await resolveMessageScope(em, payload);
  if (!message) {
    logger.error("Message not found", { messageId: payload.messageId });
    return;
  }
  const objects = await em.find(MessageObject, { messageId: message.id });
  const attachments = await getMessageEmailAttachments(
    em,
    message.id,
    message.organizationId ?? null,
    message.tenantId
  );
  const sender = await resolveSender(em, message);
  if (payload.type === "external") {
    const externalClaimTimestamp = await claimExternalDelivery(em, payload);
    if (!externalClaimTimestamp) {
      return;
    }
    try {
      await sendMessageEmailToExternal({
        message,
        email: payload.email,
        sender,
        objects,
        attachments
      });
      await emitEmailDeliveryEvent(ctx, "messages.message.email_sent", {
        messageId: message.id,
        target: "external",
        email: payload.email,
        recipientUserId: message.senderUserId,
        tenantId: message.tenantId,
        organizationId: message.organizationId ?? null
      });
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : "Unknown error";
      await releaseExternalClaim(
        em,
        payload,
        externalClaimTimestamp,
        errorMessage
      );
      await emitEmailDeliveryEvent(ctx, "messages.message.email_failed", {
        messageId: message.id,
        target: "external",
        email: payload.email,
        error: errorMessage,
        recipientUserId: message.senderUserId,
        tenantId: message.tenantId,
        organizationId: message.organizationId ?? null
      });
      logger.error("External email send failed", { err: error });
    }
    return;
  }
  const recipientRecord = await em.findOne(MessageRecipient, {
    messageId: payload.messageId,
    recipientUserId: payload.recipientUserId
  });
  if (!recipientRecord) {
    logger.error("Recipient row not found", { messageId: payload.messageId, recipientUserId: payload.recipientUserId });
    return;
  }
  if (recipientRecord.emailSentAt) {
    return;
  }
  const recipientClaimTimestamp = await claimRecipientDelivery(em, payload);
  if (!recipientClaimTimestamp) {
    return;
  }
  const recipientUser = await findOneWithDecryption(
    em,
    User,
    {
      id: payload.recipientUserId,
      tenantId: message.tenantId,
      deletedAt: null
    },
    void 0,
    { tenantId: message.tenantId, organizationId: message.organizationId ?? null }
  );
  const recipientEmail = recipientUser?.email?.trim();
  if (!recipientEmail) {
    await releaseRecipientClaim(
      em,
      payload,
      recipientClaimTimestamp,
      "Recipient has no email address"
    );
    return;
  }
  try {
    await sendMessageEmailToRecipient({
      em,
      message,
      recipientUserId: payload.recipientUserId,
      recipientEmail,
      sender,
      objects,
      attachments
    });
    await emitEmailDeliveryEvent(ctx, "messages.message.email_sent", {
      messageId: message.id,
      target: "recipient",
      recipientUserId: payload.recipientUserId,
      email: recipientEmail,
      tenantId: message.tenantId,
      organizationId: message.organizationId ?? null
    });
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : "Unknown error";
    await releaseRecipientClaim(
      em,
      payload,
      recipientClaimTimestamp,
      errorMessage
    );
    await emitEmailDeliveryEvent(ctx, "messages.message.email_failed", {
      messageId: message.id,
      target: "recipient",
      recipientUserId: payload.recipientUserId,
      email: recipientEmail,
      error: errorMessage,
      tenantId: message.tenantId,
      organizationId: message.organizationId ?? null
    });
    logger.error("Recipient email send failed", { err: error });
  }
}
export {
  MESSAGES_EMAIL_QUEUE_NAME,
  claimExternalDelivery,
  claimRecipientDelivery,
  handle as default,
  metadata,
  releaseExternalClaim,
  releaseRecipientClaim
};
//# sourceMappingURL=send-email.worker.js.map
