"""Unit coverage for app/core/ocr.py — the OpenCV + OCR "vision to text" step
Page Designer's AI Assist (target_type "page") runs on an attached screenshot
before it ever reaches the (text-only) AI model. Runs against the REAL
`tesseract` binary when one is available (same "hit the real thing" posture
this suite already takes with Postgres) — skipped, not failed, when it isn't,
since Tesseract is a system binary this repo can't install for CI itself."""

import cv2
import numpy as np
import pytest

from app.core.config import settings
from app.core.ocr import OcrUnavailableError, screenshot_to_layout_json


def _tesseract_available() -> bool:
    try:
        import pytesseract

        if settings.tesseract_cmd:
            pytesseract.pytesseract.tesseract_cmd = settings.tesseract_cmd
        pytesseract.get_tesseract_version()
        return True
    except Exception:
        return False


requires_tesseract = pytest.mark.skipif(not _tesseract_available(), reason="tesseract binary not available in this environment")


def _synthetic_screenshot() -> bytes:
    """A plain white 800x400 canvas with three text labels placed the way a
    real form screenshot would lay them out — two stacked field labels on the
    left, a button-like label bottom-right — encoded as PNG bytes, the same
    shape POST /uploads stores for a real screenshot."""
    img = np.full((400, 800, 3), 255, dtype=np.uint8)
    cv2.putText(img, "Application Number", (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 0), 2)
    cv2.putText(img, "Applicant Name", (30, 120), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 0), 2)
    cv2.putText(img, "Submit", (600, 350), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 0), 2)
    ok, buf = cv2.imencode(".png", img)
    assert ok
    return buf.tobytes()


@requires_tesseract
def test_extracts_text_with_normalized_reading_order_boxes():
    result = screenshot_to_layout_json(_synthetic_screenshot())
    assert result["engine"] == "tesseract"
    assert result["image_width"] == 800 and result["image_height"] == 400

    texts = [e["text"] for e in result["elements"]]
    assert "Application Number" in texts
    assert "Applicant Name" in texts
    assert "Submit" in texts

    # Every box is a real fraction of the image, and "Application Number" (drawn
    # near the top) sits above "Applicant Name" (drawn further down), i.e. the
    # OCR's own reading order matches the image's actual top-to-bottom layout.
    by_text = {e["text"]: e for e in result["elements"]}
    for e in result["elements"]:
        assert 0 <= e["left"] <= 1 and 0 <= e["top"] <= 1
        assert 0 < e["width"] <= 1 and 0 < e["height"] <= 1
    assert by_text["Application Number"]["top"] < by_text["Applicant Name"]["top"]
    # "Submit" was drawn on the right half of the canvas.
    assert by_text["Submit"]["left"] > 0.5


@requires_tesseract
def test_a_blank_image_yields_no_elements_not_an_error():
    blank = np.full((200, 200, 3), 255, dtype=np.uint8)
    ok, buf = cv2.imencode(".png", blank)
    assert ok
    result = screenshot_to_layout_json(buf.tobytes())
    assert result["elements"] == []


def test_garbage_bytes_raise_a_clear_error_not_a_crash():
    with pytest.raises(OcrUnavailableError):
        screenshot_to_layout_json(b"this is not an image")


def test_unknown_engine_raises_a_clear_error(monkeypatch):
    monkeypatch.setattr(settings, "ocr_engine", "magic")
    with pytest.raises(OcrUnavailableError):
        screenshot_to_layout_json(_synthetic_screenshot())


def test_missing_tesseract_binary_raises_ocr_unavailable(monkeypatch):
    """Simulates a deployment with no `tesseract` on PATH and no
    TESSERACT_CMD set — the failure users actually hit most often — without
    depending on whether this environment happens to have Tesseract."""
    monkeypatch.setattr(settings, "ocr_engine", "tesseract")
    monkeypatch.setattr(settings, "tesseract_cmd", "")

    import pytesseract

    def _raise(*args, **kwargs):
        raise pytesseract.TesseractNotFoundError()

    monkeypatch.setattr(pytesseract, "image_to_data", _raise)
    with pytest.raises(OcrUnavailableError, match="Tesseract"):
        screenshot_to_layout_json(_synthetic_screenshot())
