import { validateDossierAttachments, type DossierAttachment, } from './project-dossier-report.ts'; export type DossierExportOperation = | 'word' | 'package' | 'pdf' | 'csv' | 'attachments'; const messages: Record = { word: 'Não foi possível gerar o relatório Word.', package: 'Não foi possível preparar o pacote de anexos.', pdf: 'Não foi possível preparar a versão para PDF.', csv: 'Não foi possível preparar o ficheiro CSV.', attachments: 'Não foi possível ler os anexos seleccionados.', }; let failureSequence = 0; export function dossierExportFailure( operation: DossierExportOperation, cause: unknown, log: (message: string, detail: unknown) => void = console.error, ): string { const reference = `EXP-${Date.now().toString(36)}-${++failureSequence}`; // Local diagnostics only: never send dossier values or file contents. log('[InvesteMoz exportação]', { reference, operation, cause }); return `${messages[operation]} Os dados, as secções e os anexos foram mantidos. Tente novamente pelo mesmo botão. Referência: ${reference}.`; } /** Synchronous acquisition prevents a second click before React renders busy. */ export class DossierExportGate { private active = false; async run(task: () => Promise): Promise { if (this.active) return false; this.active = true; try { await task(); return true; } finally { this.active = false; } } } export async function prepareDossierAttachments( current: DossierAttachment[], incoming: File[], ): Promise<{ attachments: DossierAttachment[]; duplicateCount: number; problem?: string; }> { for (const file of incoming) { const problem = validateDossierAttachments([file]); if (problem) return { attachments: current, duplicateCount: 0, problem }; } const identity = async (file: File) => { const hash = await crypto.subtle.digest( 'SHA-256', await file.arrayBuffer(), ); return `${file.name}:${Array.from(new Uint8Array(hash), (b) => b.toString(16).padStart(2, '0')).join('')}`; }; const known = new Set( await Promise.all(current.map((item) => identity(item.file))), ); const added: DossierAttachment[] = []; let duplicateCount = 0; for (const file of incoming) { const key = await identity(file); if (known.has(key)) { duplicateCount++; continue; } known.add(key); added.push({ file, include: true, caption: '' }); } const problem = validateDossierAttachments( [...current, ...added].map((item) => item.file), ); return { attachments: problem ? current : [...current, ...added], duplicateCount, problem: problem ?? undefined, }; }