"""
Magic-byte (file signature) verification for onboarding document uploads.

SECURITY (M-2): `file.content_type` on a multipart upload is a client-supplied
header — trivially spoofable (e.g. `curl -F "file=@evil.exe;type=application/pdf"`).
Trusting it alone for the PDF/JPG/XLS/XLSX allowlist in
`field_mapping.ALLOWED_DOCUMENT_CONTENT_TYPES` means an attacker can smuggle any
byte stream past the declared-type check. This module verifies the *actual*
file bytes match the declared content-type's known signature before the upload
is persisted/forwarded to Global Payments.

Deliberately dependency-free: `python-magic` (libmagic) is not in
requirements.txt and pulling it in for four known signatures is unnecessary
weight. Everything here uses only the stdlib (`zipfile`) plus raw byte
comparisons.
"""

from __future__ import annotations

import zipfile
from io import BytesIO

# Signature constants
_PDF_MAGIC = b"%PDF-"
_JPEG_MAGIC = b"\xFF\xD8\xFF"
_OLE2_MAGIC = b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"  # legacy XLS (Compound File Binary Format)
_ZIP_MAGICS = (b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08")  # local file header / empty / spanned


def _looks_like_pdf(file_bytes: bytes) -> bool:
    return file_bytes.startswith(_PDF_MAGIC)


def _looks_like_jpeg(file_bytes: bytes) -> bool:
    return file_bytes.startswith(_JPEG_MAGIC)


def _looks_like_xls(file_bytes: bytes) -> bool:
    return file_bytes.startswith(_OLE2_MAGIC)


def _looks_like_xlsx(file_bytes: bytes) -> bool:
    """
    XLSX is a zip archive. A bare zip signature is necessary but not
    sufficient (docx/pptx/generic zips share it), so also confirm the
    archive actually contains the `xl/` member that only Excel's OOXML
    package produces.
    """
    if not file_bytes.startswith(_ZIP_MAGICS):
        return False
    try:
        with zipfile.ZipFile(BytesIO(file_bytes)) as zf:
            names = zf.namelist()
    except zipfile.BadZipFile:
        return False
    return any(name.startswith("xl/") for name in names)


# Maps a declared MIME type to the signature check(s) that must pass for it.
_CONTENT_TYPE_VALIDATORS = {
    "application/pdf": (_looks_like_pdf,),
    "image/jpeg": (_looks_like_jpeg,),
    "image/jpg": (_looks_like_jpeg,),
    "application/vnd.ms-excel": (_looks_like_xls,),
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": (_looks_like_xlsx,),
}


def content_matches_declared_type(file_bytes: bytes, declared_content_type: str) -> bool:
    """
    Verify the file's actual magic bytes match the client-declared content-type.

    Returns False for:
      - a declared type we don't recognize at all (caller should already have
        rejected this via the ALLOWED_DOCUMENT_CONTENT_TYPES allowlist first)
      - a recognized declared type whose signature check fails

    Returns True only when at least one signature check for the declared type
    passes.
    """
    validators = _CONTENT_TYPE_VALIDATORS.get(declared_content_type)
    if not validators:
        return False
    return any(check(file_bytes) for check in validators)
