/**
 * Upload-Whitelist Suite
 *
 * Verifiziert das serverseitige Whitelist-Gate `assertAllowedUploadKind`:
 * Nur ausdrücklich erlaubte Dateitypen (Belege: PDF/JPG/PNG/WebP/HEIC, Profilbilder:
 * JPG/PNG/WebP) werden akzeptiert — entschieden wird ausschließlich über die
 * Magic Bytes des Buffers. Der deklarierte MIME-Type ist vom Client steuerbar
 * und darf NIE Zugriff gewähren. Kein beliebiger Dateityp darf die
 * Verarbeitungspipeline erreichen.
 */

import { describe, test, expect } from "./runner";
import {
  AVATAR_ALLOWED_KINDS,
  RECEIPT_ALLOWED_KINDS,
  UnsupportedFileTypeError,
  assertAllowedUploadKind,
} from "../../src/lib/ingest/acceptedTypes";

function bufferFromHex(hex: string): Buffer {
  return Buffer.from(hex.replace(/\s+/g, ""), "hex");
}

/* ----------------------- Echte Magic-Byte-Buffer (Inline) ------------------ */

// JPEG: FF D8 FF
const JPEG_BUFFER = bufferFromHex("FF D8 FF E0 00 10 4A 46 49 46 00 01 01 00 00 01 00 01 00 00");
// PNG: 89 50 4E 47 0D 0A 1A 0A
const PNG_BUFFER = bufferFromHex(
  "89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 01 00 00 00 01 08 06 00 00 00 1F 15 C4 89"
);
// WebP: RIFF....WEBP
const WEBP_BUFFER = Buffer.from("RIFF" + "\x10\x00\x00\x00" + "WEBPVP8 ", "latin1");
// PDF: %PDF- im Kopf
const PDF_BUFFER = Buffer.from(
  "%PDF-1.7\n%\u00e2\u00e3\u00cf\u00d3\n1 0 obj\n<< /Type /Catalog >>\nendobj\n",
  "latin1"
);

// Erkannte, aber für Belege NICHT erlaubte Typen:
const GIF_BUFFER = Buffer.from("GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00", "latin1");
const TIFF_BUFFER = bufferFromHex("49 49 2A 00 08 00 00 00 00 00 00 00");
const BMP_BUFFER = bufferFromHex("42 4D 36 00 00 00 00 00 00 00 36 00 00 00");
const AVIF_BUFFER = Buffer.from("\x00\x00\x00\x1Cftypavif\x00\x00\x00\x00", "latin1");
const HEIC_BUFFER = Buffer.from("\x00\x00\x00\x18ftypheic\x00\x00\x00\x00", "latin1");

// Keine (erkennbare) Signatur:
const TEXT_BUFFER = Buffer.from("Dies ist nur Textinhalt und keine Datei.", "utf8");
// MZ-Header (Windows-EXE) — von der Erkennung nicht als zulässiger Typ klassifiziert:
const EXE_BUFFER = bufferFromHex("4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00");

const RECEIPT_MESSAGE = "Dateityp nicht erlaubt. Erlaubt sind: PDF, JPG, PNG, WebP, HEIC.";
const AVATAR_MESSAGE = "Dateityp nicht erlaubt. Erlaubt sind: JPG, PNG, WebP.";

function captureError(fn: () => unknown): unknown {
  try {
    fn();
    return null;
  } catch (err) {
    return err;
  }
}

/* -------------------------------------------------------------------------- */

describe("UploadWhitelist — Beleg-Scans (PDF/JPG/PNG/WebP/HEIC)", () => {
  test("JPEG-Magic-Bytes werden akzeptiert", () => {
    expect(assertAllowedUploadKind(JPEG_BUFFER, "image/jpeg", RECEIPT_ALLOWED_KINDS)).toBe(
      "jpeg"
    );
  });

  test("PNG-Magic-Bytes werden akzeptiert", () => {
    expect(assertAllowedUploadKind(PNG_BUFFER, "image/png", RECEIPT_ALLOWED_KINDS)).toBe("png");
  });

  test("WebP-Magic-Bytes werden akzeptiert", () => {
    expect(assertAllowedUploadKind(WEBP_BUFFER, "image/webp", RECEIPT_ALLOWED_KINDS)).toBe(
      "webp"
    );
  });

  test("PDF-Magic-Bytes werden akzeptiert", () => {
    expect(assertAllowedUploadKind(PDF_BUFFER, "application/pdf", RECEIPT_ALLOWED_KINDS)).toBe(
      "pdf"
    );
  });

  test("deklarierter MIME-Type ist optional (Magic Bytes allein entscheiden)", () => {
    expect(assertAllowedUploadKind(JPEG_BUFFER, undefined, RECEIPT_ALLOWED_KINDS)).toBe("jpeg");
  });

  test("deklarierter MIME-Type kann eine echte Datei nicht ausbremsen", () => {
    // Echte JPEG-Signatur, aber unplausibler/geloggener MIME-Type → Magic Bytes gewinnen.
    expect(
      assertAllowedUploadKind(JPEG_BUFFER, "application/octet-stream", RECEIPT_ALLOWED_KINDS)
    ).toBe("jpeg");
    expect(
      assertAllowedUploadKind(PDF_BUFFER, "text/plain", RECEIPT_ALLOWED_KINDS)
    ).toBe("pdf");
  });

  test("deklarierter MIME-Type kann eine echte Datei nicht auf einen anderen erlaubten Typ umbiegen", () => {
    // JPEG-Signatur mit deklariertem application/pdf → weiterhin "jpeg", nicht "pdf".
    expect(
      assertAllowedUploadKind(JPEG_BUFFER, "application/pdf", RECEIPT_ALLOWED_KINDS)
    ).toBe("jpeg");
  });

  test("GIF wird abgelehnt (erkannt, aber nicht in der Beleg-Whitelist)", () => {
    const err = captureError(() =>
      assertAllowedUploadKind(GIF_BUFFER, "image/gif", RECEIPT_ALLOWED_KINDS)
    );
    expect(err).toBeInstanceOf(UnsupportedFileTypeError);
    expect((err as UnsupportedFileTypeError).kind).toBe("gif");
    expect((err as UnsupportedFileTypeError).message).toBe(RECEIPT_MESSAGE);
  });

  test("HEIC-Magic-Bytes werden akzeptiert", () => {
    expect(assertAllowedUploadKind(HEIC_BUFFER, "image/heic", RECEIPT_ALLOWED_KINDS)).toBe("heic");
  });

  test("TIFF/BMP/AVIF werden abgelehnt (erkannt, aber nicht in der Beleg-Whitelist)", () => {
    const cases: Array<[Buffer, string, string]> = [
      [TIFF_BUFFER, "image/tiff", "tiff"],
      [BMP_BUFFER, "image/bmp", "bmp"],
      [AVIF_BUFFER, "image/avif", "avif"],
    ];
    for (const [buffer, mime, kind] of cases) {
      const err = captureError(() => assertAllowedUploadKind(buffer, mime, RECEIPT_ALLOWED_KINDS));
      expect(err).toBeInstanceOf(UnsupportedFileTypeError);
      expect((err as UnsupportedFileTypeError).kind).toBe(kind);
      expect((err as UnsupportedFileTypeError).message).toBe(RECEIPT_MESSAGE);
    }
  });

  test("Text-Buffer ohne Signatur wird abgelehnt", () => {
    const err = captureError(() =>
      assertAllowedUploadKind(TEXT_BUFFER, "text/plain", RECEIPT_ALLOWED_KINDS)
    );
    expect(err).toBeInstanceOf(UnsupportedFileTypeError);
    expect((err as UnsupportedFileTypeError).kind).toBe("unknown");
    expect((err as UnsupportedFileTypeError).message).toBe(RECEIPT_MESSAGE);
  });

  test("EXE-Buffer (MZ-Header) wird abgelehnt", () => {
    const err = captureError(() =>
      assertAllowedUploadKind(EXE_BUFFER, "application/octet-stream", RECEIPT_ALLOWED_KINDS)
    );
    expect(err).toBeInstanceOf(UnsupportedFileTypeError);
    expect((err as UnsupportedFileTypeError).kind).toBe("unknown");
  });

  test("gespoofte deklarierte MIME-Types gewähren nie Zugriff", () => {
    // Text-Inhalt, deklariert als image/jpeg → abgelehnt.
    const jpegSpoof = captureError(() =>
      assertAllowedUploadKind(TEXT_BUFFER, "image/jpeg", RECEIPT_ALLOWED_KINDS)
    );
    expect(jpegSpoof).toBeInstanceOf(UnsupportedFileTypeError);

    // Text-Inhalt, deklariert als application/pdf → abgelehnt (kein Zugriff über
    // den deklarierten Typ — nur die Magic Bytes entscheiden).
    const pdfSpoof = captureError(() =>
      assertAllowedUploadKind(TEXT_BUFFER, "application/pdf", RECEIPT_ALLOWED_KINDS)
    );
    expect(pdfSpoof).toBeInstanceOf(UnsupportedFileTypeError);
    expect((pdfSpoof as UnsupportedFileTypeError).kind).toBe("unknown");
  });

  test("leerer Buffer wird abgelehnt", () => {
    const err = captureError(() =>
      assertAllowedUploadKind(Buffer.alloc(0), "image/jpeg", RECEIPT_ALLOWED_KINDS)
    );
    expect(err).toBeInstanceOf(UnsupportedFileTypeError);
  });
});

describe("UploadWhitelist — Profilbilder (JPG/PNG/WebP, kein PDF)", () => {
  test("JPEG/PNG/WebP werden für Profilbilder akzeptiert", () => {
    expect(assertAllowedUploadKind(JPEG_BUFFER, "image/jpeg", AVATAR_ALLOWED_KINDS)).toBe(
      "jpeg"
    );
    expect(assertAllowedUploadKind(PNG_BUFFER, "image/png", AVATAR_ALLOWED_KINDS)).toBe("png");
    expect(assertAllowedUploadKind(WEBP_BUFFER, "image/webp", AVATAR_ALLOWED_KINDS)).toBe(
      "webp"
    );
  });

  test("PDF wird für Profilbilder abgelehnt", () => {
    const err = captureError(() =>
      assertAllowedUploadKind(PDF_BUFFER, "application/pdf", AVATAR_ALLOWED_KINDS)
    );
    expect(err).toBeInstanceOf(UnsupportedFileTypeError);
    expect((err as UnsupportedFileTypeError).kind).toBe("pdf");
    expect((err as UnsupportedFileTypeError).message).toBe(AVATAR_MESSAGE);
  });

  test("GIF wird für Profilbilder abgelehnt", () => {
    const err = captureError(() =>
      assertAllowedUploadKind(GIF_BUFFER, "image/gif", AVATAR_ALLOWED_KINDS)
    );
    expect(err).toBeInstanceOf(UnsupportedFileTypeError);
    expect((err as UnsupportedFileTypeError).kind).toBe("gif");
  });

  test("Text-Inhalt mit deklariertem image/png wird auch für Profilbilder abgelehnt", () => {
    const err = captureError(() =>
      assertAllowedUploadKind(TEXT_BUFFER, "image/png", AVATAR_ALLOWED_KINDS)
    );
    expect(err).toBeInstanceOf(UnsupportedFileTypeError);
    expect((err as UnsupportedFileTypeError).kind).toBe("unknown");
  });
});
