/**
 * Validates that a base64 image data URL is a self-consistent image of its
 * declared MIME type, and contains no polyglot markers (HTML/SVG/script tails
 * masquerading under a valid image header). Note: this is validation, not
 * sanitisation — we do not re-encode pixel data. The security goal is to
 * prevent a user-uploaded data URL from ever passing if it contains anything
 * a browser could later interpret as markup when the data URL is served
 * somewhere less strict than `<img src>`.
 */

interface MagicSignature {
  mimeType: string;
  bytes: number[];
}

// Full PNG magic (8 bytes) and JPEG SOI (3 bytes). Older implementations used
// shorter prefixes which allowed polyglot payloads whose non-header bytes
// differed from the declared format.
const SIGNATURES: MagicSignature[] = [
  { mimeType: "image/png", bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a] },
  { mimeType: "image/jpeg", bytes: [0xff, 0xd8, 0xff] },
  { mimeType: "image/webp", bytes: [0x52, 0x49, 0x46, 0x46] }, // RIFF (WebP starts with RIFF....WEBP)
  { mimeType: "image/gif", bytes: [0x47, 0x49, 0x46, 0x38] },
  { mimeType: "image/bmp", bytes: [0x42, 0x4d] },
  { mimeType: "image/tiff", bytes: [0x49, 0x49, 0x2a, 0x00] },
  { mimeType: "image/tiff", bytes: [0x4d, 0x4d, 0x00, 0x2a] },
];

// Polyglot markers — byte sequences that must never appear inside a bona-fide
// raster image. If any of these appears, the decoded content contains a
// tail/comment section that a browser or downstream parser could interpret as
// markup, giving us a stored-XSS vector if the bytes are ever served with a
// non-strict MIME. All comparisons are lowercased.
const POLYGLOT_MARKERS = [
  "<!doctype",
  "<script",
  "<svg",
  "<html",
  "<iframe",
  "<object",
  "<embed",
  "javascript:",
  "onerror=",
  "onload=",
];

const MAX_IMAGE_BYTES_FOR_VALIDATION = 16 * 1024 * 1024; // refuse to decode anything silly-large

/**
 * Detects the actual MIME type of a binary buffer by checking magic bytes.
 * Returns null if no known image signature matches.
 */
export function detectImageMime(buffer: Uint8Array): string | null {
  for (const sig of SIGNATURES) {
    if (buffer.length >= sig.bytes.length && sig.bytes.every((b, i) => buffer[i] === b)) {
      // Extra check for WebP: bytes 8-11 must be "WEBP"
      if (sig.mimeType === "image/webp") {
        if (buffer.length < 12) continue;
        const webpTag = String.fromCharCode(buffer[8]!, buffer[9]!, buffer[10]!, buffer[11]!);
        if (webpTag !== "WEBP") continue;
      }
      return sig.mimeType;
    }
  }
  return null;
}

function endsWith(buffer: Uint8Array, tail: number[]): boolean {
  if (buffer.length < tail.length) return false;
  const offset = buffer.length - tail.length;
  return tail.every((b, i) => buffer[offset + i] === b);
}

function validateTrailer(
  mime: string,
  buffer: Uint8Array,
): { valid: true } | { valid: false; reason: string } {
  if (mime === "image/png") {
    // PNG ends with the IEND chunk: 0x49 0x45 0x4e 0x44 0xae 0x42 0x60 0x82.
    // Anything after IEND is a polyglot tail and is rejected.
    if (!endsWith(buffer, [0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82])) {
      return { valid: false, reason: "PNG does not end with a well-formed IEND chunk." };
    }
  }
  if (mime === "image/jpeg") {
    // JPEG must end with the EOI marker 0xFFD9.
    if (!endsWith(buffer, [0xff, 0xd9])) {
      return { valid: false, reason: "JPEG does not end with a well-formed EOI marker." };
    }
  }
  return { valid: true };
}

function scanForPolyglotMarkers(
  buffer: Uint8Array,
): { valid: true } | { valid: false; reason: string } {
  // Only the "textual" portion of an image — comments, EXIF text blocks, tail
  // after the declared trailer — could carry HTML. We do a full-buffer scan
  // because those regions can legitimately appear anywhere in the byte stream.
  // Buffers up to MAX_IMAGE_BYTES_FOR_VALIDATION are cheap to scan linearly.
  const asText = Buffer.from(buffer).toString("latin1").toLowerCase();
  for (const marker of POLYGLOT_MARKERS) {
    if (asText.includes(marker)) {
      return {
        valid: false,
        reason: `Image contains a polyglot marker ("${marker}") — likely a disguised markup payload.`,
      };
    }
  }
  return { valid: true };
}

function decodeBase64Safe(
  base64: string,
): { ok: true; buffer: Uint8Array } | { ok: false; reason: string } {
  try {
    const buffer = Buffer.from(base64, "base64");
    if (buffer.length === 0) return { ok: false, reason: "Decoded image is empty." };
    if (buffer.length > MAX_IMAGE_BYTES_FOR_VALIDATION) {
      return { ok: false, reason: "Decoded image exceeds validation size budget." };
    }
    return { ok: true, buffer };
  } catch {
    return { ok: false, reason: "Invalid base64 encoding." };
  }
}

/**
 * Validates a data URL by comparing its declared MIME type against the actual
 * magic bytes AND by decoding the full buffer to verify a consistent trailer
 * and the absence of polyglot markup markers.
 *
 * Returns { valid: true } or { valid: false, reason: string }.
 */
export function validateImageDataUrl(
  dataUrl: string,
): { valid: true } | { valid: false; reason: string } {
  const match = dataUrl.match(/^data:(image\/[a-z+]+);base64,(.+)$/i);
  if (!match) {
    return { valid: false, reason: "Not a valid base64 image data URL." };
  }

  const declaredMime = match[1]!.toLowerCase();
  const base64 = match[2]!;

  // Explicitly reject SVG — it is XML and can carry <script>. We do not accept
  // vector uploads here regardless of how cleanly the payload decodes.
  if (declaredMime === "image/svg+xml" || declaredMime === "image/svg") {
    return { valid: false, reason: "SVG uploads are not permitted." };
  }

  const decoded = decodeBase64Safe(base64);
  if (!decoded.ok) {
    return { valid: false, reason: decoded.reason };
  }

  const actualMime = detectImageMime(decoded.buffer);
  if (!actualMime) {
    return { valid: false, reason: "File content does not match any known image format." };
  }

  const normalize = (m: string) => m.replace("image/jpg", "image/jpeg");
  if (normalize(declaredMime) !== normalize(actualMime)) {
    return {
      valid: false,
      reason: `MIME type mismatch: declared "${declaredMime}" but actual content is "${actualMime}".`,
    };
  }

  const trailer = validateTrailer(actualMime, decoded.buffer);
  if (!trailer.valid) return trailer;

  const polyglot = scanForPolyglotMarkers(decoded.buffer);
  if (!polyglot.valid) return polyglot;

  return { valid: true };
}
