#!/usr/bin/env python3
"""Generate synthetic redaction fixtures and a machine-readable audit.

The files contain only invented identifiers. The unsafe example deliberately
keeps the sentinel in visible text, metadata, and a text annotation, then draws
a black rectangle over the visible line. The safe example is rebuilt without
the sentinel and contains no annotation or sensitive metadata.
"""

from __future__ import annotations

import csv
import hashlib
import json
from pathlib import Path

from pypdf import PdfReader, PdfWriter
from pypdf.annotations import FreeText
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas


ROOT = Path(__file__).resolve().parent
SENTINEL = "BT-REDACT-9472"
FILES = {
    "source": ROOT / "source-unredacted.pdf",
    "unsafe": ROOT / "unsafe-black-box.pdf",
    "safe": ROOT / "safe-redacted-working-copy.pdf",
}


def draw_page(path: Path, *, include_sentinel: bool, draw_mask: bool) -> None:
    pdf = canvas.Canvas(str(path), pagesize=A4, pageCompression=0)
    width, height = A4
    pdf.setTitle("Synthetic Redaction Fixture")
    pdf.setFont("Helvetica-Bold", 18)
    pdf.drawString(72, height - 84, "Synthetic review memo")
    pdf.setFont("Helvetica", 11)
    pdf.drawString(72, height - 120, "Case: invented training record")
    pdf.drawString(72, height - 145, "Employee ID:")
    if include_sentinel:
        pdf.drawString(150, height - 145, SENTINEL)
    if draw_mask:
        pdf.setFillColorRGB(0, 0, 0)
        pdf.rect(144, height - 163, 190, 30, fill=1, stroke=0)
        pdf.setFillColorRGB(0, 0, 0)
    pdf.drawString(72, height - 175, "Status: internal review complete")
    pdf.drawString(72, height - 205, "This page uses invented data for a redaction audit.")
    pdf.setStrokeColorRGB(0.25, 0.25, 0.25)
    pdf.line(72, height - 222, width - 72, height - 222)
    pdf.setFont("Helvetica-Oblique", 9)
    pdf.drawString(72, 54, "Synthetic fixture — not a customer document")
    pdf.save()


def decorate_unsafe(path: Path) -> None:
    reader = PdfReader(str(path))
    writer = PdfWriter(clone_from=reader)
    writer.add_metadata(
        {
            "/Title": "Synthetic Redaction Fixture",
            "/Subject": f"Unsafe metadata leak: {SENTINEL}",
            "/Keywords": f"training,{SENTINEL}",
        }
    )
    writer.add_annotation(
        page_number=0,
        annotation=FreeText(
            text=f"Reviewer note still contains {SENTINEL}",
            rect=(72, 500, 300, 540),
            font="Helvetica",
            font_size="8pt",
            font_color="000000",
            border_color="ffffff",
            background_color="ffffff",
        ),
    )
    with path.open("wb") as handle:
        writer.write(handle)


def inspect_pdf(path: Path) -> dict[str, object]:
    reader = PdfReader(str(path))
    text = "\n".join(page.extract_text() or "" for page in reader.pages)
    metadata = json.dumps(dict(reader.metadata or {}), ensure_ascii=False)
    annotations: list[str] = []
    for page in reader.pages:
        for reference in page.get("/Annots", []):
            annotation = reference.get_object()
            annotations.append(str(annotation.get("/Contents", "")))
    raw_content = b"\n".join(
        (page.get_contents().get_data() if page.get_contents() else b"")
        for page in reader.pages
    ).decode("latin-1", errors="ignore")
    return {
        "file": path.name,
        "sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
        "pages": len(reader.pages),
        "text_contains_sentinel": SENTINEL in text,
        "metadata_contains_sentinel": SENTINEL in metadata,
        "annotations_contain_sentinel": SENTINEL in "\n".join(annotations),
        "content_stream_contains_sentinel": SENTINEL in raw_content,
        "sentinel_found_anywhere_in_audited_surfaces": any(
            [
                SENTINEL in text,
                SENTINEL in metadata,
                SENTINEL in "\n".join(annotations),
                SENTINEL in raw_content,
            ]
        ),
    }


def main() -> None:
    draw_page(FILES["source"], include_sentinel=True, draw_mask=False)
    draw_page(FILES["unsafe"], include_sentinel=True, draw_mask=True)
    decorate_unsafe(FILES["unsafe"])
    draw_page(FILES["safe"], include_sentinel=False, draw_mask=True)

    rows = [inspect_pdf(FILES[name]) for name in ("source", "unsafe", "safe")]
    (ROOT / "redaction-audit.json").write_text(
        json.dumps(
            {
                "sentinel": SENTINEL,
                "scope": "Synthetic fixtures only; audited text, metadata, annotations, and decoded page content streams.",
                "results": rows,
            },
            indent=2,
        )
        + "\n",
        encoding="utf-8",
    )
    with (ROOT / "redaction-audit.csv").open("w", newline="", encoding="utf-8") as handle:
        writer = csv.DictWriter(handle, fieldnames=rows[0].keys(), lineterminator="\n")
        writer.writeheader()
        writer.writerows(rows)


if __name__ == "__main__":
    main()
