import { Request, Response, NextFunction } from 'express';
import fs from 'fs';
import path from 'path';
import { assertSafePdf } from '../utils/sanitize.js';
import { ApiError } from '../utils/apiError.js';

/**
 * Magic-Byte-Whitelist + canonical Extension Rename + PDF-Active-Content-
 * Scan in einer Middleware. Greift nach `multer.single(...)`, prüft die
 * geschriebene Datei auf erlaubten Typ (PDF/JPG/PNG/GIF/WebP) und benennt
 * sie auf eine kanonische Endung um – damit verschwindet die
 * `evil.gif.php`-Doppel-Endung und der client-gemeldete mimetype wird
 * durch den ERKANNTEN ersetzt.
 *
 * Historie:
 * - Pentest 39.3 / 39.4 (2026-05-30): Magic-Byte + canonical Rename.
 * - Pentest 68.1 (2026-06-03): PDF-Body-Scan auf JS/Launch/Embed/RichMedia.
 * - Pentest 69.3 (2026-06-03): Wiederverwendung in contract.routes.ts
 *   (Vertragsdokumente) – vorher waren JPG/PNG-Uploads dort ungeprüft,
 *   nur durch Download-Layer kompensiert.
 */

const PDF_MAGIC = Buffer.from('%PDF-', 'latin1');
const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
const GIF87 = Buffer.from('GIF87a', 'latin1');
const GIF89 = Buffer.from('GIF89a', 'latin1');

export function detectFileType(buf: Buffer): { mime: string; ext: string } | null {
  if (buf.length >= 5 && buf.subarray(0, 5).equals(PDF_MAGIC)) return { mime: 'application/pdf', ext: '.pdf' };
  if (buf.length >= 8 && buf.subarray(0, 8).equals(PNG_MAGIC)) return { mime: 'image/png', ext: '.png' };
  if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) return { mime: 'image/jpeg', ext: '.jpg' };
  if (buf.length >= 6 && (buf.subarray(0, 6).equals(GIF87) || buf.subarray(0, 6).equals(GIF89))) return { mime: 'image/gif', ext: '.gif' };
  if (buf.length >= 12
      && buf.subarray(0, 4).toString('latin1') === 'RIFF'
      && buf.subarray(8, 12).toString('latin1') === 'WEBP') return { mime: 'image/webp', ext: '.webp' };
  return null;
}

export function validateUploadedFile(req: Request, res: Response, next: NextFunction): void {
  const file = (req as Request & { file?: Express.Multer.File }).file;
  if (!file) {
    next();
    return;
  }
  try {
    const fd = fs.openSync(file.path, 'r');
    const head = Buffer.alloc(12);
    fs.readSync(fd, head, 0, 12, 0);
    fs.closeSync(fd);

    const detected = detectFileType(head);
    if (!detected) {
      try { fs.unlinkSync(file.path); } catch { /* ignore */ }
      res.status(415).json({
        success: false,
        error: 'Datei-Inhalt entspricht keinem zulässigen Typ (PDF, JPG, PNG, GIF, WebP).',
      });
      return;
    }

    if (detected.mime === 'application/pdf') {
      try {
        const fullBuf = fs.readFileSync(file.path);
        assertSafePdf(fullBuf);
      } catch (e) {
        try { fs.unlinkSync(file.path); } catch { /* ignore */ }
        const status = e instanceof ApiError ? e.statusCode : 415;
        const msg = e instanceof Error ? e.message : 'PDF ungültig';
        res.status(status).json({ success: false, error: msg });
        return;
      }
    }

    const dir = path.dirname(file.path);
    const base = path.basename(file.path).replace(/\.[^./]+(\.[^./]+)*$/, '');
    const newName = base + detected.ext;
    const newPath = path.join(dir, newName);
    if (newPath !== file.path) {
      try {
        fs.renameSync(file.path, newPath);
        file.path = newPath;
        file.filename = newName;
      } catch (e) {
        try { fs.unlinkSync(file.path); } catch { /* ignore */ }
        console.error('Upload-Rename fehlgeschlagen:', e);
        res.status(500).json({ success: false, error: 'Upload konnte nicht abgeschlossen werden' });
        return;
      }
    }

    file.mimetype = detected.mime;
    next();
  } catch (e) {
    console.error('Magic-Byte-Check fehlgeschlagen:', e);
    try { fs.unlinkSync(file.path); } catch { /* ignore */ }
    res.status(500).json({ success: false, error: 'Upload konnte nicht geprüft werden' });
  }
}
