#!/usr/bin/env python3
"""
Render a controlled statement-template probe PDF through the real PHP renderer
and inspect the output with PyMuPDF.

Requirements:
  - python3
  - pymupdf (`pip install pymupdf`)
  - php with PDFlib and gd enabled

Usage:
  python3 scripts/statement_template_precision_probe.py
"""

from __future__ import annotations

import json
import subprocess
import sys
import tempfile
from pathlib import Path

try:
    import fitz  # type: ignore
except Exception as exc:  # pragma: no cover - runtime dependency check
    sys.stderr.write(
        "PyMuPDF is required for this probe. Install it with `pip install pymupdf`.\n"
    )
    raise SystemExit(2) from exc


PROJECT_ROOT = Path(__file__).resolve().parents[1]
RENDERER_PATH = PROJECT_ROOT / "php" / "src" / "Services" / "StatementTemplatePdfRenderer.php"


def build_probe_pdf(output_pdf: Path, baseline_offset_pt: float | None = None) -> None:
    baseline_line = ""
    if baseline_offset_pt is not None:
        baseline_line = f"'baseline_offset_pt' => {baseline_offset_pt},"
    php_script = f"""require {str(RENDERER_PATH)!r};

$assetPath = {str(output_pdf.with_suffix('.png'))!r};
$im = imagecreatetruecolor(20, 10);
$white = imagecolorallocate($im, 255, 255, 255);
$black = imagecolorallocate($im, 0, 0, 0);
imagefill($im, 0, 0, $white);
imagefilledrectangle($im, 0, 0, 19, 9, $black);
imagepng($im, $assetPath);
imagedestroy($im);

$layout = [
    'meta' => [
        'page_width_pt' => 220.0,
        'page_height_pt' => 120.0,
        'template_pdf' => '',
    ],
    'fields' => [
        'probe_text' => [
            'object_type' => 'text',
            'object_subtype' => 'text_block',
            'x_pt' => 10.0,
            'y_pt' => 10.0,
            'w_pt' => 80.0,
            'h_pt' => 20.0,
            'pad_pt' => 0.0,
            'font' => 'Helvetica',
            'font_key' => 'helvetica',
            'fontsize_pt' => 10.0,
            'align' => 'left',
            'valign' => 'top',
            'fit' => 'nofit',
            'render' => 'textline',
            'literal_text' => 'Hello',
            {baseline_line}
        ],
        'probe_line' => [
            'object_type' => 'line',
            'object_subtype' => 'line',
            'x_pt' => 10.0,
            'y_pt' => 40.0,
            'w_pt' => 30.0,
            'h_pt' => 1.0,
            'line_orientation' => 'horizontal',
            'line_style' => 'solid',
            'line_length_pt' => 30.0,
            'line_weight_pt' => 1.0,
        ],
        'probe_image' => [
            'object_type' => 'image',
            'object_subtype' => 'image',
            'x_pt' => 10.0,
            'y_pt' => 50.0,
            'w_pt' => 40.0,
            'h_pt' => 20.0,
            'asset_storage_path' => $assetPath,
        ],
    ],
];

$renderer = new StatementTemplatePdfRenderer({str(PROJECT_ROOT)!r}, []);
$pdf = $renderer->renderFromLayoutDefinition($layout, [], false, false, false, false);
file_put_contents({str(output_pdf)!r}, $pdf);
"""
    subprocess.run(["php", "-r", php_script], check=True, cwd=str(PROJECT_ROOT))


def scan_raster_bounds(page: fitz.Page, y_min: int, y_max: int) -> dict[str, int]:
    pix = page.get_pixmap(matrix=fitz.Matrix(1, 1), alpha=False)
    width, height = pix.width, pix.height
    bounds = {"minx": width, "miny": height, "maxx": -1, "maxy": -1}
    for y in range(max(0, y_min), min(height, y_max + 1)):
        for x in range(width):
            idx = (y * width + x) * 3
            r = pix.samples[idx]
            g = pix.samples[idx + 1]
            b = pix.samples[idx + 2]
            if r < 240 or g < 240 or b < 240:
                bounds["minx"] = min(bounds["minx"], x)
                bounds["miny"] = min(bounds["miny"], y)
                bounds["maxx"] = max(bounds["maxx"], x)
                bounds["maxy"] = max(bounds["maxy"], y)
    return bounds


def inspect_probe_pdf(output_pdf: Path) -> dict[str, object]:
    doc = fitz.open(output_pdf)
    page = doc[0]
    text_hits = page.search_for("Hello")
    text_bbox = text_hits[0] if text_hits else None
    line_bounds = scan_raster_bounds(page, 38, 42)
    image_bounds = scan_raster_bounds(page, 50, 69)
    return {
        "pdf_path": str(output_pdf),
        "page_rect": [page.rect.x0, page.rect.y0, page.rect.x1, page.rect.y1],
        "text_probe": {
            "expected_top_left_pt": [10.0, 10.0],
            "bbox": None
            if text_bbox is None
            else [text_bbox.x0, text_bbox.y0, text_bbox.x1, text_bbox.y1],
            "top_drift_pt": None if text_bbox is None else round(text_bbox.y0 - 10.0, 6),
        },
        "line_probe": {
            "expected": {"x": 10.0, "y": 40.0, "w": 30.0, "h": 1.0},
            "raster_bounds_at_72dpi": line_bounds,
        },
        "image_probe": {
            "expected": {"x": 10.0, "y": 50.0, "w": 40.0, "h": 20.0},
            "raster_bounds_at_72dpi": image_bounds,
        },
    }


def main() -> int:
    with tempfile.TemporaryDirectory(prefix="statement-precision-probe-") as tmp_dir:
        auto_pdf = Path(tmp_dir) / "statement_probe_auto.pdf"
        build_probe_pdf(auto_pdf)
        auto_result = inspect_probe_pdf(auto_pdf)

        manual_zero_pdf = Path(tmp_dir) / "statement_probe_manual_zero.pdf"
        build_probe_pdf(manual_zero_pdf, baseline_offset_pt=0.0)
        manual_zero_result = inspect_probe_pdf(manual_zero_pdf)
        manual_zero_result["applied_baseline_offset_pt"] = 0.0

        manual_override_pdf = Path(tmp_dir) / "statement_probe_manual_override.pdf"
        build_probe_pdf(manual_override_pdf, baseline_offset_pt=-2.237839)
        manual_override_result = inspect_probe_pdf(manual_override_pdf)
        manual_override_result["applied_baseline_offset_pt"] = -2.237839

        result = {
            "auto": auto_result,
            "manual_zero": manual_zero_result,
            "manual_override": manual_override_result,
        }
        print(json.dumps(result, indent=2))
    return 0


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