"""Grounding-guard rescue tier, text-layer prompt block, and render hygiene."""

from backend import config
from backend.pipeline._stage import render
from backend.pipeline.extractor import (
    _is_grounded,
    _normalize_sheet,
    _text_layer_block,
)
from backend.prompts import EXTRACTOR_USER_INSTRUCTION, VERIFY_USER_INSTRUCTION

PAGE_TEXT = "NOTES: (5) 2X6 STUD PACK AT BEARING. HSS16X4 BEAM. 7'-0\" AFF."


def _parsed(value, source_text):
    return {
        "sheet": {"sheet_number": "S401"},
        "objects": [{
            "object_id": "o1",
            "object_type": "framing",
            "name": "stud pack",
            "attributes": {"count": value},
            "source_text": source_text,
        }],
    }


def test_rescue_tier_keeps_and_stamps():
    """Digits absent from source_text but present in the page text layer:
    kept, stamped grounding=text_layer (vision quoted imperfectly)."""
    sheet = _normalize_sheet(_parsed("(2)", "(2) 2x6 STUD PACK"), 1,
                             page_text=PAGE_TEXT)
    # "(2)" is not grounded by its own source_text alone? it is - use a value
    # whose digits differ from the quote to exercise the rescue path.
    sheet = _normalize_sheet(_parsed("5", "(2) 2x6 STUD PACK"), 1,
                             page_text=PAGE_TEXT)
    assert len(sheet["assertions"]) == 1
    assert sheet["assertions"][0]["grounding"] == "text_layer"


def test_no_rescue_without_page_text():
    sheet = _normalize_sheet(_parsed("5", "(2) 2x6 STUD PACK"), 1)
    assert sheet["assertions"] == []


def test_still_dropped_when_digits_nowhere():
    sheet = _normalize_sheet(_parsed("99", "(2) 2x6 STUD PACK"), 1,
                             page_text=PAGE_TEXT)
    assert sheet["assertions"] == []


def test_is_grounded_backward_compatible():
    assert _is_grounded("(5)", "(5) 2x6 STUD PACK") is True
    # Digit-run guard is a set check: "(3)" has no support anywhere.
    assert _is_grounded("(3)", "(5) 2x6 STUD PACK") is False
    assert _is_grounded("(3)", "(5) 2x6 STUD PACK",
                        page_text="(3) 2x6 STUD PACK") is True


def test_text_layer_block_empty_without_layer():
    assert _text_layer_block({"page_number": 1}) == ""
    assert _text_layer_block({"page_number": 1, "text_layer": None}) == ""


def test_text_layer_block_appends_and_caps(monkeypatch):
    block = _text_layer_block({"page_number": 1, "text_layer": PAGE_TEXT})
    assert "TEXT LAYER" in block and "STUD PACK" in block
    monkeypatch.setattr(config, "TEXT_LAYER_MAX_CHARS", 50)
    block = _text_layer_block({"page_number": 1, "text_layer": "x" * 500})
    assert len(block.split(":\n", 1)[1]) == 50


def test_verify_instruction_fully_rendered():
    """render() silently leaves missing keys as literals - both placeholders
    must be substituted at the (single) verify render site."""
    out = render(VERIFY_USER_INSTRUCTION,
                 {"finding": "FINDING_JSON", "text_layer": "PAGE_TEXT"})
    assert "{finding}" not in out and "{text_layer}" not in out
    assert "FINDING_JSON" in out and "PAGE_TEXT" in out


def test_extractor_instruction_fully_substituted():
    page = {"page_number": 1, "text_layer": PAGE_TEXT}
    out = (EXTRACTOR_USER_INSTRUCTION.replace("{sheet_hint}", "")
           + _text_layer_block(page))
    assert "{sheet_hint}" not in out


def test_vision_unverified_stamp_when_source_text_not_in_text_layer():
    """Digits ground the object against the page text, but its quoted
    source_text is not actually present in the text layer: kept, stamped
    vision_unverified (the wave-5b verifier consumes grounding stamps)."""
    page_text = "WALL: 2X6 WD STUD @ 16\" O.C. WITH R-13 BATT INSULATION"
    parsed = {"sheet": {}, "objects": [
        {"object_id": "x1", "name": "stud pack",
         "source_text": "(5) 2X6 STUD PACK AT JAMB",  # NOT in page text
         "attributes": {"count": "5"}}]}
    sheet = _normalize_sheet(parsed, 1, page_text=page_text)
    assert len(sheet["assertions"]) == 1
    assert sheet["assertions"][0]["grounding"] == "vision_unverified"


def test_no_unverified_stamp_when_source_text_in_text_layer():
    page_text = "WALL: 2X6 WD STUD @ 16\" O.C. WITH R-13 BATT INSULATION"
    parsed = {"sheet": {}, "objects": [
        {"object_id": "x1", "name": "stud note",
         "source_text": "2X6 WD STUD @ 16\" O.C.",
         "attributes": {"size": "2x6"}}]}
    sheet = _normalize_sheet(parsed, 1, page_text=page_text)
    assert len(sheet["assertions"]) == 1
    assert "grounding" not in sheet["assertions"][0]


def test_preset_grounding_stamp_survives_normalization():
    """Fallback/merge rungs stamp grounding upstream; normalization must
    preserve a pre-set stamp instead of recomputing it away."""
    parsed = {"sheet": {}, "objects": [
        {"object_id": "f1", "name": "lumber note",
         "source_text": "ALL LUMBER SOUTHERN PINE",
         "grounding": "text_layer_fallback"}]}
    sheet = _normalize_sheet(parsed, 1, page_text="ALL LUMBER SOUTHERN PINE")
    assert sheet["assertions"][0]["grounding"] == "text_layer_fallback"
