"""Merge PDFs in a folder into a single document.

Fixes over the naive version: it sorts numerically so page 10 does not land
between 1 and 2, it skips encrypted files instead of raising halfway through,
and it always closes the merger even on failure.

    python3 pdf_merger.py ./pdfs --out combined.pdf

Requires: pypdf
"""

from __future__ import annotations

import argparse
import re
import sys
from pathlib import Path

from pypdf import PdfReader, PdfWriter


def natural_key(path: Path) -> list[object]:
    """Sort 'doc2.pdf' before 'doc10.pdf'."""
    return [int(part) if part.isdigit() else part.lower()
            for part in re.split(r"(\d+)", path.name)]


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Merge PDFs into one document.")
    parser.add_argument("folder", type=Path, help="Folder containing the PDFs")
    parser.add_argument("--out", type=Path, default=Path("combined.pdf"), help="Output file")
    args = parser.parse_args(argv)

    folder: Path = args.folder.expanduser()
    if not folder.is_dir():
        print(f"{folder} is not a directory", file=sys.stderr)
        return 1

    paths = sorted(
        (p for p in folder.iterdir() if p.suffix.lower() == ".pdf" and not p.name.startswith(".")),
        key=natural_key,
    )
    if not paths:
        print(f"No PDFs in {folder}", file=sys.stderr)
        return 1

    writer = PdfWriter()
    merged, pages = 0, 0
    try:
        for path in paths:
            try:
                reader = PdfReader(path)
                if reader.is_encrypted:
                    print(f"Skipping {path.name}: encrypted", file=sys.stderr)
                    continue
                for page in reader.pages:
                    writer.add_page(page)
                    pages += 1
                merged += 1
                print(f"Added {path.name} ({len(reader.pages)} pages)")
            except Exception as exc:
                print(f"Skipping {path.name}: {exc}", file=sys.stderr)

        if merged == 0:
            print("Nothing could be merged.", file=sys.stderr)
            return 1

        with args.out.open("wb") as handle:
            writer.write(handle)
    finally:
        writer.close()

    print(f"\nMerged {merged} file(s), {pages} pages -> {args.out}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
