/**
 * Lightweight upload content guard — rejects HTML/SVG/XML active content
 * regardless of declared Content-Type (S8-i2-005 mitigation).
 */

export class UploadContentRejectedError extends Error {
  constructor(message: string) {
    super(message)
    this.name = 'UploadContentRejectedError'
  }
}

function headAsLowerText(bytes: Uint8Array, max = 512): string {
  return new TextDecoder('utf-8', { fatal: false, ignoreBOM: true }).decode(bytes.slice(0, max)).trimStart().toLowerCase()
}

// Polyglot detection is partial (e.g. JPEG header + SVG payload); Content-Disposition: attachment is the primary XSS barrier.
export function rejectDangerousUploadContent(bytes: Uint8Array): void {
  const head = headAsLowerText(bytes)
  if (head.startsWith('<svg')) {
    throw new UploadContentRejectedError('File content is not allowed')
  }
  if (head.startsWith('<?xml')) {
    throw new UploadContentRejectedError('File content is not allowed')
  }
  if (head.startsWith('<!doctype html') || head.startsWith('<html')) {
    throw new UploadContentRejectedError('File content is not allowed')
  }
  if (head.startsWith('<script')) {
    throw new UploadContentRejectedError('File content is not allowed')
  }
}

function verifyImageMagicBytes(mimeType: string, bytes: Uint8Array): boolean {
  if (bytes.length < 4) return false

  switch (mimeType) {
    case 'image/jpeg':
      return bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff
    case 'image/png':
      return bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47
    case 'image/gif':
      return bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46
    case 'image/webp':
      return (
        bytes.length >= 12 &&
        bytes[0] === 0x52 &&
        bytes[1] === 0x49 &&
        bytes[2] === 0x46 &&
        bytes[3] === 0x46 &&
        bytes[8] === 0x57 &&
        bytes[9] === 0x45 &&
        bytes[10] === 0x42 &&
        bytes[11] === 0x50
      )
    default:
      return true
  }
}

export function validateUploadContent(mimeType: string, bytes: Uint8Array): void {
  rejectDangerousUploadContent(bytes)
  if (mimeType.startsWith('image/') && !verifyImageMagicBytes(mimeType, bytes)) {
    throw new UploadContentRejectedError('File content does not match declared MIME type')
  }
}
