"""Test the extraction-service contract → app ghost-row mapping (P2).

Mocks the PDF geometry so the mapper is tested in isolation: page-fraction → 780-canvas conversion,
per-template uuid remap, FK-safety (orphan regions / dangling parents), and rich meta passthrough.
"""
import routers.exam.templates as T

TID = "11111111-1111-1111-1111-111111111111"

# two pages, each rendered 780 wide × 1000 tall; page 2 stacked below page 1
_GEOM = [
    {"rendered_w": 780.0, "rendered_h": 1000.0, "page_top": 0.0, "page_pt_w": 595.0, "page_pt_h": 842.0, "crop_x0": 0.0, "crop_y0": 0.0},
    {"rendered_w": 780.0, "rendered_h": 1000.0, "page_top": 1000.0, "page_pt_w": 595.0, "page_pt_h": 842.0, "crop_x0": 0.0, "crop_y0": 0.0},
]

_CONTRACT = {
    "coordinate_space": "page_fraction",
    "suggestions": {
        "questions": [
            {"uid": "Q1", "label": "1", "order": 0, "max_marks": 5, "is_container": True, "page": 1},
            {"uid": "Q1a", "parent_uid": "Q1", "label": "1(a)", "order": 1, "max_marks": 3,
             "answer_type": "short", "is_container": False, "page": 1},
            {"uid": "Q1b", "parent_uid": "Q1", "label": "1(b)", "order": 2, "max_marks": 2,
             "answer_type": "mcq", "is_container": False, "page": 2},
        ],
        "response_areas": [
            {"uid": "RA1", "question_uid": "Q1a", "page": 1, "kind": "response", "response_form": "answer-box",
             "confidence": 0.9, "bounds": {"x": 0.1, "y": 0.2, "w": 0.5, "h": 0.05},
             "meta": {"unit": "m/s", "quantity": "v", "n_lines": 1}},
            {"uid": "RA2", "question_uid": "Q1b", "page": 2, "kind": "response", "response_form": "tick-boxes",
             "bounds": {"x": 0.2, "y": 0.3, "w": 0.4, "h": 0.2},
             "meta": {"select_n": 2, "n_options": 5, "boxes": [{"x0": 0.2, "y0": 0.3, "fill": 0.8}]}},
            {"uid": "CTX1", "question_uid": "Q1b", "page": 2, "kind": "context", "response_form": None,
             "bounds": {"x": 0.1, "y": 0.1, "w": 0.6, "h": 0.15}},
            {"uid": "ORPH", "question_uid": "GHOST", "page": 1, "kind": "response", "response_form": "lines",
             "bounds": {"x": 0.1, "y": 0.5, "w": 0.5, "h": 0.1}},   # orphan owner → must be dropped
        ],
        "boundaries": [
            {"question_uid": "Q1", "label": "1", "page_index": 0, "y": 0.18, "confidence": 0.85},
        ],
    },
    "meta": {"n_pages": 2},
}


def test_contract_maps_to_canvas_rows(monkeypatch):
    monkeypatch.setattr(T, "_pdf_page_geometry", lambda _b: _GEOM)
    rows = T._map_service_contract_to_rows(TID, _CONTRACT, b"%PDF-1.4")

    q = rows["questions"]
    assert len(q) == 3
    container = next(x for x in q if x["label"] == "1")
    a = next(x for x in q if x["label"] == "1(a)")
    assert container["is_container"] is True and container["parent_id"] is None
    assert a["parent_id"] == container["id"]          # parent remapped to the container's per-template id
    assert a["answer_type"] == "short" and a["max_marks"] == 3
    assert all(x["source"] == "ai" and x["confirmed"] is False for x in q)

    ra = rows["response_areas"]
    assert len(ra) == 3                                # orphan (owner GHOST) dropped
    ids = {r["id"] for r in ra}
    assert len(ids) == 3                               # ids unique
    box = next(r for r in ra if r["response_form"] == "answer-box")
    # page-fraction → 780-canvas: x=0.1*780=78, y=0.2*1000=200, w=0.5*780=390, h=0.05*1000=50
    assert box["bounds"] == {"x": 78.0, "y": 200.0, "w": 390.0, "h": 50.0}
    assert box["meta"]["unit"] == "m/s"               # rich meta preserved
    tick = next(r for r in ra if r["response_form"] == "tick-boxes")
    # page 2 y stacked: 0.3*1000 + page_top(1000) = 1300
    assert tick["bounds"]["y"] == 1300.0 and tick["meta"]["select_n"] == 2
    ctx = next(r for r in ra if r["kind"] == "context")
    assert ctx["response_form"] is None               # context carries no answer form
    assert all(r["question_id"] in {x["id"] for x in q} for r in ra)   # FK-safe

    b = rows["boundaries"]
    assert len(b) == 1 and b[0]["y"] == 180.0 and b[0]["question_id"] == container["id"]
