"""
Detect password-protected Office documents.

Password-protected Office files cannot be parsed and should fail early
with a clear error message rather than producing cryptic conversion errors.

Detection strategy:
- Modern formats (.docx, .xlsx, .pptx, .docm, .xlsm, .pptm):
  Normal files are ZIP archives (magic: PK). Encrypted files are
  OLE2 Compound Binary containers (magic: D0 CF 11 E0 A1 B1 1A E1).
- Legacy formats (.doc, .xls, .ppt):
  Always OLE2. Check for EncryptionInfo or encryption streams.
"""

import logging

log = logging.getLogger(__name__)

# OLE2 Compound Binary File magic bytes
_OLE2_MAGIC = b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1'

# Modern Office Open XML extensions (should be ZIP archives)
_OOXML_EXTENSIONS = {'.docx', '.docm', '.xlsx', '.xlsm', '.pptx', '.pptm'}

# Legacy OLE2-based extensions
_LEGACY_EXTENSIONS = {'.doc', '.xls', '.ppt'}


class PasswordProtectedError(Exception):
    """Raised when a document is password-protected and cannot be processed."""
    pass


def check_password_protected(file_path: str) -> None:
    """
    Check if an Office document is password-protected.

    Raises PasswordProtectedError if the file is encrypted/locked.
    Does nothing if the file is not protected.

    Args:
        file_path: Path to the Office document.

    Raises:
        PasswordProtectedError: If the document is password-protected.
    """
    from pathlib import Path

    ext = Path(file_path).suffix.lower()

    if ext in _OOXML_EXTENSIONS:
        _check_ooxml(file_path, ext)
    elif ext in _LEGACY_EXTENSIONS:
        _check_legacy_ole2(file_path, ext)


def _check_ooxml(file_path: str, ext: str) -> None:
    """
    Check modern Office Open XML files (.docx, .xlsx, .pptx, etc.).

    Normal OOXML files are ZIP archives. Password-protected ones are
    wrapped in an OLE2 EncryptedPackage container.
    """
    with open(file_path, 'rb') as f:
        header = f.read(8)

    if header[:8] == _OLE2_MAGIC:
        log.warning(f"Password-protected document detected (OLE2 container): {file_path}")
        raise PasswordProtectedError(
            f"Document is password-protected and cannot be processed: {file_path}. "
            f"Please remove the password and re-upload the file."
        )


def _check_legacy_ole2(file_path: str, ext: str) -> None:
    """
    Check legacy OLE2 Office files (.doc, .xls, .ppt).

    These are always OLE2 containers, so we need to inspect the
    internal streams for encryption markers.

    Detection methods:
    - EncryptionInfo stream (common across all legacy formats)
    - .doc: FIB (File Information Block) fEncrypted bit at offset 0x0A, bit 8
    - .xls: Workbook stream FilePass record check
    """
    import struct

    try:
        import olefile
    except ImportError:
        log.debug("olefile not available, skipping legacy password detection")
        return

    if not olefile.isOleFile(file_path):
        return

    try:
        ole = olefile.OleFileIO(file_path)
    except Exception as e:
        log.warning(f"Failed to open OLE2 file for password check: {e}")
        return

    try:
        # Check 1: EncryptionInfo stream (common across formats)
        if ole.exists('EncryptionInfo') or ole.exists('encryption'):
            log.warning(f"Password-protected legacy document detected: {file_path}")
            raise PasswordProtectedError(
                f"Document is password-protected and cannot be processed: {file_path}. "
                f"Please remove the password and re-upload the file."
            )

        # Check 2: .doc — FIB fEncrypted bit in WordDocument stream
        if ext == '.doc' and ole.exists('WordDocument'):
            word_data = ole.openstream('WordDocument').read(12)
            if len(word_data) >= 12:
                fib_flags = struct.unpack_from('<H', word_data, 0x0A)[0]
                f_encrypted = bool(fib_flags & 0x0100)
                if f_encrypted:
                    log.warning(f"Encrypted .doc detected (FIB fEncrypted): {file_path}")
                    raise PasswordProtectedError(
                        f"Document is password-protected and cannot be processed: {file_path}. "
                        f"Please remove the password and re-upload the file."
                    )

        # Check 3: .xls — FilePass record in Workbook stream
        if ext == '.xls' and ole.exists('Workbook'):
            workbook_data = ole.openstream('Workbook').read(128)
            # FilePass record type = 0x002F
            if b'\x2f\x00' in workbook_data[:128]:
                log.warning(f"Encrypted .xls workbook detected: {file_path}")
                raise PasswordProtectedError(
                    f"Document is password-protected and cannot be processed: {file_path}. "
                    f"Please remove the password and re-upload the file."
                )
    finally:
        ole.close()
