import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { CustomerInteraction } from "../data/entities.js";
import { buildEmailVisibilityMikroFilter } from "./visibilityFilter.js";
const DEFAULT_MAX_THREADS = 50;
const DEFAULT_MAX_MESSAGES_PER_THREAD = 200;
const PREVIEW_LENGTH = 140;
function extractAddresses(value) {
  const out = [];
  const pushOne = (item) => {
    if (typeof item === "string") {
      const trimmed = item.trim();
      if (trimmed) out.push({ email: trimmed, name: null });
      return;
    }
    if (item && typeof item === "object") {
      const rec = item;
      const address = typeof rec.address === "string" ? rec.address.trim() : null;
      const name = typeof rec.name === "string" && rec.name.trim() ? rec.name.trim() : null;
      if (address) out.push({ email: address, name });
    }
  };
  if (Array.isArray(value)) {
    for (const item of value) pushOne(item);
  } else {
    pushOne(value);
  }
  return out;
}
function firstString(...values) {
  for (const value of values) {
    if (typeof value === "string" && value.trim().length > 0) return value;
  }
  return null;
}
function toStringArray(...values) {
  for (const value of values) {
    if (Array.isArray(value)) {
      const strings = value.filter((item) => typeof item === "string" && item.trim().length > 0);
      if (strings.length > 0) return strings;
    }
  }
  return [];
}
function truncate(value) {
  if (!value) return value;
  const collapsed = value.replace(/\s+/g, " ").trim();
  if (collapsed.length <= PREVIEW_LENGTH) return collapsed;
  return `${collapsed.slice(0, PREVIEW_LENGTH - 1)}\u2026`;
}
async function buildPersonEmailThreads(em, opts) {
  const {
    personId,
    tenantId,
    organizationId,
    viewerUserId,
    userFeatures,
    maxThreads = DEFAULT_MAX_THREADS,
    maxMessagesPerThread = DEFAULT_MAX_MESSAGES_PER_THREAD
  } = opts;
  const interactionWhere = {
    entity: personId,
    interactionType: "email",
    deletedAt: null,
    tenantId
  };
  if (organizationId) interactionWhere.organizationId = organizationId;
  interactionWhere.$or = buildEmailVisibilityMikroFilter({
    currentUserId: viewerUserId,
    userFeatures
  }).$or;
  const dscope = { tenantId, organizationId: organizationId ?? null };
  const interactions = await findWithDecryption(
    em,
    CustomerInteraction,
    interactionWhere,
    { orderBy: { occurredAt: "desc", createdAt: "desc" }, limit: maxThreads * 20 },
    dscope
  );
  const linkIdByInteraction = /* @__PURE__ */ new Map();
  const linkIds = [];
  for (const interaction of interactions) {
    const linkId = interaction.externalMessageId;
    if (!linkId || linkIdByInteraction.has(linkId)) continue;
    linkIdByInteraction.set(linkId, { occurredAt: interaction.occurredAt ?? interaction.createdAt });
    linkIds.push(linkId);
  }
  if (linkIds.length === 0) return [];
  const linkWhere = { id: { $in: linkIds }, tenantId };
  if (organizationId) linkWhere.organizationId = organizationId;
  const links = await findWithDecryption(
    em,
    "MessageChannelLink",
    linkWhere,
    void 0,
    dscope
  );
  const messageIds = Array.from(
    new Set(
      links.map((link) => typeof link.messageId === "string" ? link.messageId : null).filter((value) => !!value)
    )
  );
  const messageById = /* @__PURE__ */ new Map();
  if (messageIds.length > 0) {
    const messageWhere = { id: { $in: messageIds }, tenantId };
    if (organizationId) messageWhere.organizationId = organizationId;
    const messages = await findWithDecryption(
      em,
      "Message",
      messageWhere,
      void 0,
      dscope
    );
    for (const message of messages) {
      if (typeof message.id === "string") messageById.set(message.id, message);
    }
  }
  const threadsByKey = /* @__PURE__ */ new Map();
  for (const link of links) {
    const linkId = typeof link.id === "string" ? link.id : null;
    if (!linkId) continue;
    const direction = link.direction === "outbound" ? "outbound" : "inbound";
    const providerKey = typeof link.providerKey === "string" ? link.providerKey : null;
    const payload = link.channelPayload ?? null;
    const meta = link.channelMetadata ?? null;
    const messageId = typeof link.messageId === "string" ? link.messageId : null;
    const message = messageId ? messageById.get(messageId) ?? null : null;
    const primary = direction === "outbound" ? meta : payload;
    const secondary = direction === "outbound" ? payload : meta;
    const fromList = extractAddresses(primary?.from ?? secondary?.from);
    const toList = extractAddresses(primary?.to ?? secondary?.to);
    const ccList = extractAddresses(primary?.cc ?? secondary?.cc);
    const subject = firstString(
      primary?.subject,
      secondary?.subject,
      typeof message?.subject === "string" ? message.subject : null
    );
    const bodyText = firstString(
      direction === "outbound" ? meta?.bodyText : payload?.text,
      direction === "outbound" ? payload?.text : meta?.bodyText,
      typeof message?.body === "string" ? message.body : null
    );
    const sentAtRaw = (typeof message?.sentAt === "string" || message?.sentAt instanceof Date ? message.sentAt : null) ?? (link.createdAt instanceof Date || typeof link.createdAt === "string" ? link.createdAt : null) ?? linkIdByInteraction.get(linkId)?.occurredAt ?? /* @__PURE__ */ new Date();
    const sentAt = (sentAtRaw instanceof Date ? sentAtRaw : new Date(sentAtRaw)).toISOString();
    const threadKey = (typeof message?.threadId === "string" && message.threadId ? message.threadId : null) ?? (messageId ? `message:${messageId}` : `link:${linkId}`);
    const dto = {
      id: linkId,
      messageId,
      rfcMessageId: firstString(primary?.messageId, secondary?.messageId),
      references: toStringArray(primary?.references, secondary?.references),
      direction,
      fromName: fromList[0]?.name ?? null,
      fromEmail: fromList[0]?.email ?? null,
      to: toList.map((a) => a.email),
      cc: ccList.map((a) => a.email),
      subject,
      bodyText,
      sentAt,
      providerKey
    };
    const existing = threadsByKey.get(threadKey);
    if (existing) {
      existing.messages.push(dto);
    } else {
      threadsByKey.set(threadKey, {
        threadKey,
        subject,
        preview: null,
        participants: [],
        lastMessageAt: sentAt,
        messageCount: 0,
        providerKey,
        lastDirection: direction,
        messages: [dto]
      });
    }
  }
  const threads = [];
  for (const thread of threadsByKey.values()) {
    thread.messages.sort((a, b) => a.sentAt.localeCompare(b.sentAt));
    if (thread.messages.length > maxMessagesPerThread) {
      thread.messages = thread.messages.slice(thread.messages.length - maxMessagesPerThread);
    }
    const last = thread.messages[thread.messages.length - 1];
    const firstWithSubject = thread.messages.find((m) => m.subject);
    const participantSet = /* @__PURE__ */ new Set();
    for (const message of thread.messages) {
      if (message.direction === "inbound" && message.fromEmail) participantSet.add(message.fromEmail);
      if (message.direction === "outbound") message.to.forEach((email) => participantSet.add(email));
    }
    thread.subject = firstWithSubject?.subject ?? thread.subject;
    thread.preview = truncate(last?.bodyText ?? null);
    thread.participants = Array.from(participantSet);
    thread.lastMessageAt = last?.sentAt ?? thread.lastMessageAt;
    thread.lastDirection = last?.direction ?? thread.lastDirection;
    thread.providerKey = last?.providerKey ?? thread.providerKey;
    thread.messageCount = thread.messages.length;
    threads.push(thread);
  }
  threads.sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt));
  return threads.slice(0, maxThreads);
}
export {
  buildPersonEmailThreads
};
//# sourceMappingURL=personEmailThreads.js.map
