export const ALLOWED_MIME_TYPES = new Set<string>([
  'image/jpeg',
  'image/png',
  'image/gif',
  'image/webp',
  'application/pdf',
  'application/msword',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  'application/vnd.ms-excel',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  'text/plain',
  'text/csv',
]);

export const MIME_TO_EXT: Record<string, string> = {
  'image/jpeg': 'jpg',
  'image/png': 'png',
  'image/gif': 'gif',
  'image/webp': 'webp',
  'application/pdf': 'pdf',
  'application/msword': 'doc',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
  'application/vnd.ms-excel': 'xls',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
  'text/plain': 'txt',
  'text/csv': 'csv',
};

export const MAX_FILE_SIZE = 52_428_800; // 50MB

export const PREVIEWABLE_MIMES = new Set<string>([
  'image/jpeg',
  'image/png',
  'image/gif',
  'image/webp',
  'application/pdf',
  // Plain text + CSV render via the in-app TextPreview.
  'text/plain',
  'text/csv',
  // Office formats: .docx renders client-side via docx-preview; the
  // legacy/binary + spreadsheet formats fall through to a download CTA in
  // the preview dialog. They're allow-listed here so the preview endpoint
  // returns a URL instead of rejecting the file outright (which surfaced
  // as a misleading "Failed to load preview"). We deliberately do NOT use
  // Microsoft's hosted Office viewer — it can't reach our private storage.
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx
  'application/msword', // .doc
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
  'application/vnd.ms-excel', // .xls
]);

/** True when the file is an OOXML Word document we can render in-browser. */
export function isWordDocx(mimeType: string | undefined, fileName: string | undefined): boolean {
  if (mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
    return true;
  return (fileName ?? '').toLowerCase().endsWith('.docx');
}

/**
 * Magic-byte signatures keyed by claimed MIME type. Used by the file
 * upload handler to reject files whose first few bytes don't match the
 * MIME the browser declared. Without this, a `<form>` could lie about
 * Content-Type and pass arbitrary bytes through ALLOWED_MIME_TYPES.
 *
 * Each signature is the leading prefix of the file. When multiple variants
 * exist (e.g. JPEG SOI + APPn marker), we accept any of them.
 */
export const MAGIC_BYTE_SIGNATURES: Record<string, Uint8Array[]> = {
  'image/jpeg': [new Uint8Array([0xff, 0xd8, 0xff])],
  'image/png': [new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])],
  'image/gif': [
    new Uint8Array([0x47, 0x49, 0x46, 0x38, 0x37, 0x61]), // GIF87a
    new Uint8Array([0x47, 0x49, 0x46, 0x38, 0x39, 0x61]), // GIF89a
  ],
  'image/webp': [new Uint8Array([0x52, 0x49, 0x46, 0x46])], // RIFF; WEBP signature follows at offset 8
  'application/pdf': [new Uint8Array([0x25, 0x50, 0x44, 0x46])], // %PDF
  // Office formats are zip-based (modern: docx/xlsx) or OLE (legacy: doc/xls).
  // Both share well-known magic bytes - match either family for a given MIME.
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document': [
    new Uint8Array([0x50, 0x4b, 0x03, 0x04]), // PK\3\4 (zip)
    new Uint8Array([0x50, 0x4b, 0x05, 0x06]), // empty archive
  ],
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': [
    new Uint8Array([0x50, 0x4b, 0x03, 0x04]),
    new Uint8Array([0x50, 0x4b, 0x05, 0x06]),
  ],
  'application/msword': [
    new Uint8Array([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1]), // OLE compound
  ],
  'application/vnd.ms-excel': [new Uint8Array([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1])],
  // text/plain and text/csv have no magic bytes - leave unconstrained;
  // size cap + ALLOWED_MIME_TYPES allow-list is the only gate.
};

/** Returns true when the buffer starts with one of the registered prefixes
 *  for the given MIME, or when the MIME has no signature requirement. */
export function bufferMatchesMime(buffer: Buffer, mime: string): boolean {
  const sigs = MAGIC_BYTE_SIGNATURES[mime];
  if (!sigs) return true; // text/plain, text/csv, or unrecognised allow-list entry
  return sigs.some((sig) => {
    if (buffer.length < sig.length) return false;
    for (let i = 0; i < sig.length; i++) {
      if (buffer[i] !== sig[i]) return false;
    }
    return true;
  });
}
