#!/usr/bin/env python3
"""Document metadata and file attachments for PDFs (pypdf).

Modes (one required):
  --set-meta            set metadata keys given via --title/--author/...
  --clear-meta          drop all document info metadata
  --attach FILE         embed a file attachment
  --list-attachments    list embedded attachment names
  --extract-attachments DIR  write all attachments into DIR

Metadata note: values are stored in the classic DocInfo dictionary
(Title/Author/Subject/Keywords). XMP metadata, if present, is not
rewritten and may disagree in sophisticated viewers.
"""
from __future__ import annotations

import argparse
import json
import os
import sys
from pathlib import Path


def main() -> int:
    for stream in (sys.stdout, sys.stderr):
        try:
            stream.reconfigure(encoding="utf-8")
        except Exception:
            pass
    parser = argparse.ArgumentParser(description="Set/clear PDF metadata; manage attachments.")
    parser.add_argument("pdf", help="Input PDF path")
    mode = parser.add_mutually_exclusive_group(required=True)
    mode.add_argument("--set-meta", action="store_true", help="Set metadata fields")
    mode.add_argument("--clear-meta", action="store_true", help="Remove all DocInfo metadata")
    mode.add_argument("--attach", metavar="FILE", help="Embed FILE as an attachment")
    mode.add_argument("--list-attachments", action="store_true", help="List attachment names")
    mode.add_argument("--extract-attachments", metavar="DIR", help="Extract attachments into DIR")
    parser.add_argument("-o", "--output", help="Output PDF (required for write modes)")
    parser.add_argument("--title")
    parser.add_argument("--author")
    parser.add_argument("--subject")
    parser.add_argument("--keywords")
    parser.add_argument("--password", help="Password if the input is encrypted")
    args = parser.parse_args()

    try:
        from pypdf import PdfReader, PdfWriter
    except ImportError:
        print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)
        return 2

    reader = PdfReader(args.pdf)
    if reader.is_encrypted:
        if args.password is None or not reader.decrypt(args.password):
            print("Error: input is encrypted; pass --password", file=sys.stderr)
            return 3

    if args.list_attachments:
        names = list(reader.attachments.keys())
        json.dump({"attachment_count": len(names), "attachments": names}, sys.stdout,
                  ensure_ascii=False, indent=2)
        print()
        return 0

    if args.extract_attachments:
        out_dir = Path(args.extract_attachments)
        out_dir.mkdir(parents=True, exist_ok=True)
        written = []
        for name, contents in reader.attachments.items():
            data = contents[0] if isinstance(contents, list) else contents
            safe = os.path.basename(name) or "attachment.bin"
            target = out_dir / safe
            with open(target, "wb") as fh:
                fh.write(bytes(data))
            written.append(str(target))
        json.dump({"extracted": written}, sys.stdout, ensure_ascii=False, indent=2)
        print()
        return 0

    if not args.output:
        print("Error: -o/--output is required for write modes", file=sys.stderr)
        return 4

    writer = PdfWriter()
    writer.append(reader)

    if args.set_meta:
        meta = {}
        for key, value in ((f"/{k.capitalize()}", getattr(args, k))
                           for k in ("title", "author", "subject", "keywords")):
            if value is not None:
                meta[key] = value
        if not meta:
            print("Error: --set-meta needs at least one of --title/--author/--subject/--keywords",
                  file=sys.stderr)
            return 4
        writer.add_metadata(meta)
        result = {"output": args.output, "set": {k.lstrip("/"): v for k, v in meta.items()}}
    elif args.clear_meta:
        writer.metadata = None
        result = {"output": args.output, "cleared": True}
    else:  # --attach
        attach_path = Path(args.attach)
        with open(attach_path, "rb") as fh:
            writer.add_attachment(attach_path.name, fh.read())
        result = {"output": args.output, "attached": attach_path.name}

    with open(args.output, "wb") as fh:
        writer.write(fh)
    print(json.dumps(result, ensure_ascii=False))
    return 0


if __name__ == "__main__":
    sys.exit(main())
