"""REC-158: breakdown_extract robustness — span_quote substring + retry."""

from types import SimpleNamespace

import pytest

from recoil.pipeline._lib import breakdown_extract as bx


def _scene(span_text: str):
    return SimpleNamespace(
        scene_id="EP001_SC001", scene_hash="abc123", span_text=span_text
    )


def _mention(span_quote: str, **over):
    m = {
        "kind": "prop",
        "prop_id": "salvage_hook",
        "surface_text": "the salvage hook",
        "span_quote": span_quote,
        "scene_id": "EP001_SC001",
        "scene_hash": "abc123",
    }
    m.update(over)
    return m


def test_span_quote_substring_of_long_multisentence_line_is_valid():
    # A long, multi-sentence action line on a single line. The model quotes a
    # SUB-SPAN (one sentence), which is the realistic case the old full-line
    # check spuriously rejected.
    line = (
        "She throws it into the dark. Drops down against the bulkhead — right "
        "where she started. And now, finally, the face: JADE. Late twenties, "
        "all economy."
    )
    scene = _scene(line)
    # Sub-span quote — must NOT raise.
    bx._validate_mention(_mention("And now, finally, the face: JADE."), scene)


def test_span_quote_whitespace_normalized_match():
    scene = _scene("the salvage\n    hook bites a seam")
    # Quote spans the wrapped newline; whitespace-normalized substring still matches.
    bx._validate_mention(_mention("the salvage hook bites a seam"), scene)


def test_span_quote_not_in_scene_still_raises():
    scene = _scene("Empty corridor. Flickering lights.")
    with pytest.raises(bx.BreakdownExtractError):
        bx._validate_mention(_mention("a cryo-pod hanging over the abyss"), scene)


def test_empty_span_quote_raises():
    scene = _scene("Empty corridor.")
    with pytest.raises(bx.BreakdownExtractError):
        bx._validate_mention(_mention("   "), scene)


def test_extract_max_attempts_constant_is_at_least_one():
    assert bx.EXTRACT_MAX_ATTEMPTS >= 1
