from __future__ import annotations

import hashlib
import json
import shutil
import subprocess
import sys
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo

import numpy as np
from PIL import Image
from pypdf import PdfReader


ROOT = Path(__file__).resolve().parent
PDF = ROOT / "paper.pdf"
RENDER_DIR = ROOT / "tmp" / "pdfs" / "current"
REPORT = ROOT / "pdf-render-report.json"
BUNDLED_PDFTOPPM = Path(
    r"C:\Users\hands\.cache\codex-runtimes\codex-primary-runtime\dependencies\native\poppler\Library\bin\pdftoppm.exe"
)


def now_taipei() -> str:
    return datetime.now(ZoneInfo("Asia/Taipei")).replace(microsecond=0).isoformat()


def sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        for chunk in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def main() -> None:
    errors: list[str] = []
    if not PDF.is_file():
        raise FileNotFoundError(PDF)
    pdftoppm = str(BUNDLED_PDFTOPPM) if BUNDLED_PDFTOPPM.is_file() else shutil.which("pdftoppm")
    RENDER_DIR.mkdir(parents=True, exist_ok=True)
    for old in RENDER_DIR.glob("page-*.png"):
        old.unlink()
    render_engine = "pdftoppm"
    use_fallback = not bool(pdftoppm)
    if pdftoppm:
        completed = subprocess.run(
            [pdftoppm, "-png", "-r", "150", str(PDF), str(RENDER_DIR / "page")],
            capture_output=True,
            text=True,
            encoding="utf-8",
            errors="replace",
            timeout=300,
            check=False,
        )
        if completed.returncode != 0:
            use_fallback = True
    if use_fallback:
        import fitz

        render_engine = "PyMuPDF fallback because pdftoppm is unavailable"
        document = fitz.open(PDF)
        matrix = fitz.Matrix(150 / 72, 150 / 72)
        for page_index, page in enumerate(document, 1):
            pixmap = page.get_pixmap(matrix=matrix, alpha=False)
            pixmap.save(RENDER_DIR / f"page-{page_index:03d}.png")
        document.close()
    pages = sorted(RENDER_DIR.glob("page-*.png"))
    reader = PdfReader(str(PDF))
    if len(pages) != len(reader.pages):
        errors.append(f"Rendered page count {len(pages)} differs from PDF page count {len(reader.pages)}")
    records: list[dict[str, object]] = []
    for index, page in enumerate(pages, 1):
        with Image.open(page) as image:
            rgb = np.asarray(image.convert("RGB"), dtype=np.uint8)
        near_white = float((rgb.mean(axis=2) >= 250).mean())
        dark = float((rgb.mean(axis=2) <= 80).mean())
        if near_white > 0.9985:
            errors.append(f"Rendered page {index} appears blank")
        if dark > 0.80:
            errors.append(f"Rendered page {index} is unexpectedly dark")
        records.append({
            "page": index,
            "path": page.relative_to(ROOT).as_posix(),
            "width": int(rgb.shape[1]),
            "height": int(rgb.shape[0]),
            "nearWhiteFraction": round(near_white, 6),
            "darkFraction": round(dark, 6),
            "sha256": sha256(page),
        })
    report = {
        "schemaVersion": 1,
        "renderedAt": now_taipei(),
        "status": "PASSED" if not errors else "FAILED",
        "scope": "Automated 150-DPI rendering and blank/dark-page screening. Human visual review is recorded separately in pdf-visual-qa.json.",
        "pdf": "paper.pdf",
        "pdfSha256": sha256(PDF),
        "renderDpi": 150,
        "renderEngine": render_engine,
        "pages": records,
        "errors": errors,
    }
    REPORT.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(json.dumps({"status": report["status"], "pages": len(records), "renderDir": str(RENDER_DIR)}, ensure_ascii=False))
    if errors:
        sys.exit(1)


if __name__ == "__main__":
    main()
