"""Coverage-driven extraction retry ladder — classic path (pipeline/extractor.py)."""

from backend import config
from backend.pipeline import extractor

PAGE_TEXT = ("1. \nALL SAWN LUMBER IN CONTACT WITH SOIL TO BE SOUTHERN PINE, "
             "PRESSURE TREATED.\n2. \nROOF SHEATHING: 5/8\" PLYWOOD, C-D GRADE, "
             "STRUCTURAL I.")


def _page(n=8, text=PAGE_TEXT):
    return {"page_number": n, "base64": "AAAA", "text_layer": text}


def test_classic_fallback_when_vision_returns_nothing(monkeypatch):
    """Vision pass returns an unusable bare list; text retry disabled ->
    deterministic fallback stubs make a dark sheet impossible."""
    monkeypatch.setattr(extractor, "call_json",
                        lambda **kw: [{"name": "general notes", "value": "notes"}])
    monkeypatch.setattr(config, "EXTRACT_TEXT_RETRY_ENABLED", False)
    sheet = extractor._extract_one(_page())
    assert sheet["assertions"], "dark sheet must be impossible with fallback enabled"
    assert all(a.get("grounding") == "text_layer_fallback"
               for a in sheet["assertions"])
    assert sheet["coverage"]["ratio"] >= 0.6


def test_classic_merge_preserves_graphical_objects(monkeypatch):
    """Rung-2 merge must never drop vision-only graphical objects."""
    def fake(**kw):
        if kw.get("images_b64"):
            return {"sheet": {}, "objects": [
                {"object_id": "g1", "object_type": "lighting_fixture",
                 "name": "pendant at grid C-4", "source_text": None,
                 "graphical_basis": "16in pendant symbol at grid C-4"}]}
        return {"sheet": {}, "objects": [
            {"object_id": "t1", "object_type": "general_note",
             "source_text": "ALL SAWN LUMBER IN CONTACT WITH SOIL TO BE "
                            "SOUTHERN PINE, PRESSURE TREATED.",
             "name": "lumber note"}]}

    monkeypatch.setattr(extractor, "call_json", fake)
    sheet = extractor._extract_one(_page())
    assert any(a.get("graphical_basis") for a in sheet["assertions"])
    assert any("SAWN LUMBER" in (a.get("source_text") or "")
               for a in sheet["assertions"])


def test_classic_recovers_sheet_number(monkeypatch):
    text = ("REFLECTED CEILING PLAN\n"
            "GYP. BD. CEILING 8'-11 3/8\" A.F.F. TYP. FOR ALL STOREFRONT\n"
            "LED TAPE LIGHT. SEE ELEC. SCONCE 8'-0\" A.F.F., SEE ELEC.\n"
            "A102")

    def fake(**kw):
        if kw.get("images_b64"):
            return {"sheet": {}, "objects": [
                {"object_id": "o1", "name": "RCP ceiling note",
                 "source_text": "GYP. BD. CEILING 8'-11 3/8\" A.F.F. TYP. "
                                "FOR ALL STOREFRONT",
                 "attributes": {"height": "8'-11 3/8\""}}]}
        return {"sheet": {}, "objects": []}

    monkeypatch.setattr(extractor, "call_json", fake)
    sheet = extractor._extract_one(_page(18, text))
    assert sheet["sheet_number"] == "A102"


def test_classic_skips_retry_when_coverage_healthy(monkeypatch):
    calls = []

    def fake(**kw):
        calls.append(kw)
        return {"sheet": {"sheet_number": "S202"}, "objects": [
            {"object_id": "o1", "name": "lumber note",
             "source_text": "ALL SAWN LUMBER IN CONTACT WITH SOIL TO BE "
                            "SOUTHERN PINE, PRESSURE TREATED.",
             "attributes": {"species": "southern pine"}},
            {"object_id": "o2", "name": "sheathing note",
             "source_text": "ROOF SHEATHING: 5/8\" PLYWOOD, C-D GRADE, "
                            "STRUCTURAL I.",
             "attributes": {"sheathing": "5/8 plywood"}}]}

    monkeypatch.setattr(extractor, "call_json", fake)
    sheet = extractor._extract_one(_page())
    assert len(calls) == 1, "healthy coverage must not trigger the text-only rung"
    assert sheet["coverage"]["ratio"] == 1.0
    assert sheet["sheet_number"] == "S202"


def test_classic_scanned_page_keeps_failed_sheet_shape(monkeypatch):
    """No text layer (scanned page): total parse failure keeps the existing
    'extraction failed' empty-sheet return — ladder is text-layer-only."""
    monkeypatch.setattr(extractor, "call_json", lambda **kw: None)
    sheet = extractor._extract_one({"page_number": 4, "base64": "AAAA",
                                    "text_layer": None})
    assert sheet["assertions"] == []
    assert "extraction failed" in (sheet.get("sheet_title") or "")
