import { DEMO_EMAILS, DEMO_REFERENCE_DATE_UTC, DEMO_USER, type DemoEmail, } from "@/components/demo/demo-mail-data" import type { ApiMailAccount, ApiMessageFull, ApiMessageSummary, MessageSearchFilter, PaginatedResponse, Recipient, } from "@/lib/api/types" import { apiMessageToEmail } from "@/lib/demo/demo-mail-email-map" import { emailMatchesFolder } from "@/lib/mail-folder-filter" import type { Email } from "@/lib/email-data" import { normalizeListPageSize } from "@/lib/mail-list-page-size" export const DEMO_MAIL_ACCOUNT_ID = "demo-mail-account" export const DEMO_MAIL_ACCOUNT: ApiMailAccount = { id: DEMO_MAIL_ACCOUNT_ID, name: DEMO_USER.name, email: DEMO_USER.email, provider: "demo", imap_host: "demo.local", smtp_host: "demo.local", is_active: true, created_at: "2026-06-01T08:00:00+02:00", } function escapeHtml(text: string): string { return text .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) } function demoReferenceMidnightUtc(): Date { return new Date( Date.UTC( DEMO_REFERENCE_DATE_UTC.year, DEMO_REFERENCE_DATE_UTC.month, DEMO_REFERENCE_DATE_UTC.day ) ) } /** Map demo list labels to stable ISO timestamps (SSR-safe, no `new Date()`). */ function demoTimeToIso(time: string): string { const base = demoReferenceMidnightUtc() if (time.includes(":")) { const [h, m] = time.split(":").map(Number) base.setUTCHours(h, m ?? 0, 0, 0) return base.toISOString() } if (time === "Hier") { base.setUTCDate(base.getUTCDate() - 1) base.setUTCHours(14, 20, 0, 0) return base.toISOString() } if (time === "Dim.") { const day = base.getUTCDay() const diff = day === 0 ? 0 : day base.setUTCDate(base.getUTCDate() - diff) base.setUTCHours(3, 0, 0, 0) return base.toISOString() } if (time.startsWith("Lun")) { const day = base.getUTCDay() const diff = day === 0 ? 6 : day - 1 base.setUTCDate(base.getUTCDate() - diff) base.setUTCHours(11, 0, 0, 0) return base.toISOString() } const weekdayOffsets: Record = { "Mar.": 2, "Mer.": 3, "Jeu.": 4, "Ven.": 5, "Sam.": 6, } for (const [prefix, weekday] of Object.entries(weekdayOffsets)) { if (time.startsWith(prefix)) { const day = base.getUTCDay() const diff = day >= weekday ? day - weekday : day + (7 - weekday) base.setUTCDate(base.getUTCDate() - diff) base.setUTCHours(10, 30, 0, 0) return base.toISOString() } } base.setUTCHours(12, 0, 0, 0) return base.toISOString() } function dedupeLabels(labels: string[]): string[] { const seen = new Set() const out: string[] = [] for (const label of labels) { const key = label.toLowerCase() if (seen.has(key)) continue seen.add(key) out.push(label) } return out } function demoTagLabels(email: DemoEmail): string[] { const tags = [...(email.tags ?? [])] if ( email.label && !tags.some((tag) => tag.toLowerCase() === email.label!.text.toLowerCase()) ) { tags.push(email.label.text) } return tags } function demoFolderToLabels(email: DemoEmail): string[] { const tags = demoTagLabels(email) if (email.folder === "trash") return ["trash"] if (email.folder === "sent") return ["sent"] if (email.folder === "drafts") return ["drafts"] if (email.folder === "archive") return dedupeLabels(tags) return dedupeLabels(["inbox", ...tags]) } function demoBodyToHtml(body: string[]): string { return body .map( (paragraph) => `

${escapeHtml(paragraph).replace(/\n/g, "
")}

` ) .join("") } export function demoEmailToApiMessage(email: DemoEmail): ApiMessageFull { const flags: string[] = [] if (!email.unread) flags.push("\\Seen") if (email.starred) flags.push("\\Flagged") if (email.important) flags.push("important") return { id: email.id, message_id: email.id, thread_id: email.id, account_id: DEMO_MAIL_ACCOUNT_ID, subject: email.subject, from: [{ name: email.fromName, address: email.fromEmail }], to: [{ name: DEMO_USER.name, address: DEMO_USER.email }], date: demoTimeToIso(email.time), snippet: email.preview, flags, labels: demoFolderToLabels(email), has_attachments: email.hasAttachment ?? false, body_html: demoBodyToHtml(email.body), body_text: email.body.join("\n\n"), } } export function createInitialDemoMessages(): ApiMessageFull[] { return DEMO_EMAILS.map(demoEmailToApiMessage) } function toSummary(message: ApiMessageFull): ApiMessageSummary { const { body_html: _bodyHtml, body_text: _bodyText, cc: _cc, reply_to: _replyTo, auth_info: _auth, in_reply_to: _inReply, references: _refs, ...summary } = message return summary } function messageToEmail(message: ApiMessageSummary | ApiMessageFull): Email { return apiMessageToEmail(message) } function matchesSearch(message: ApiMessageSummary, filter: MessageSearchFilter): boolean { const email = messageToEmail(message) const q = filter.q?.trim().toLowerCase() if (q) { const haystack = [ email.sender, email.senderEmail ?? "", email.subject, email.preview, message.snippet, ] .join(" ") .toLowerCase() if (!haystack.includes(q)) return false } if (filter.from) { const from = filter.from.toLowerCase() const sender = message.from[0]?.address?.toLowerCase() ?? "" const name = message.from[0]?.name?.toLowerCase() ?? "" if (!sender.includes(from) && !name.includes(from)) return false } if (filter.label) { const needle = filter.label.toLowerCase() if (!message.labels.some((l) => l.toLowerCase() === needle)) return false } return true } export function listDemoMessages( messages: ApiMessageFull[], folder: string, page: number, pageSize: number ): PaginatedResponse { const safePageSize = normalizeListPageSize(pageSize) const ctx = { starredEmailIds: [], importantEmailIds: [] } const filtered = messages .map(toSummary) .filter((message) => emailMatchesFolder(messageToEmail(message), folder, ctx) ) .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) const start = (page - 1) * safePageSize const slice = filtered.slice(start, start + safePageSize) return { data: slice, pagination: { page, page_size: safePageSize, total: filtered.length, }, } } export function searchDemoMessages( messages: ApiMessageFull[], filter: MessageSearchFilter | null, page = 1, pageSize = 50 ): PaginatedResponse { const safePageSize = normalizeListPageSize(pageSize) const filtered = messages .map(toSummary) .filter((message) => (filter ? matchesSearch(message, filter) : true)) .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) const start = (page - 1) * safePageSize return { data: filtered.slice(start, start + safePageSize), pagination: { page, page_size: safePageSize, total: filtered.length, }, } } export function demoSentMessageFromPayload(payload: { account_id: string to: Recipient[] subject: string body_html: string }): ApiMessageFull { const id = `sent-${Date.now()}` const text = payload.body_html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim() return { id, message_id: id, thread_id: id, account_id: payload.account_id, subject: payload.subject || "(Sans objet)", from: [{ name: DEMO_USER.name, address: DEMO_USER.email }], to: payload.to, date: new Date().toISOString(), snippet: text.slice(0, 160) || "(message vide)", flags: ["\\Seen"], labels: ["sent"], has_attachments: false, body_html: payload.body_html, body_text: text, } } export function moveDemoMessageToTrash(message: ApiMessageFull): ApiMessageFull { const labels = message.labels.filter((l) => l.toLowerCase() !== "inbox") if (!labels.some((l) => l.toLowerCase() === "trash")) { labels.push("trash") } return { ...message, labels } }