"""Entity-preserving MT — #000056 §9 (the validated lever).

Deterministic detection + mask/restore round-trip + the
MaskedTranslator protocol wrapper. No model, no network.
"""
from __future__ import annotations

from arborist.qa.mt import StubTranslator
from arborist.qa.mt.entity_mask import MaskedTranslator, mask, protect_spans, restore


def _spans(text):
    return [text[s:e] for s, e in protect_spans(text)]


def test_detects_capitalised_runs_and_quotes_not_function_words():
    assert _spans("who is Paul of Tarsus?") == ["Paul of Tarsus"]
    assert _spans("when was the Eiffel Tower built?") == ["Eiffel Tower"]
    assert _spans("a bridge between New London & Groton?") == ["New London & Groton"]
    # quoted span keeps the quote chars (restored verbatim — harmless)
    assert _spans('what does "winter is coming" mean?') == ['"winter is coming"']
    # wh / function lead word is NOT an entity; lowercase noun isn't either.
    assert _spans("what is anarchocapitalism?") == []
    assert _spans("how does intel compare to amd?") == []


def test_mask_restore_round_trips_exactly():
    q = "who wrote the play Hamlet?"
    m, mp = mask(q)
    assert "Hamlet" not in m and "ZQX0XQZ" in m
    assert restore(m, mp) == q
    # Restore tolerates MT mangling the sentinel's case/spacing.
    assert restore("Quien escribio la obra zqx 0 xqz?", mp) == \
        "Quien escribio la obra Hamlet?"


def test_masked_translator_preserves_entity_through_a_mangling_engine():
    # Engine that lowercases everything it actually sees — would destroy
    # "Hamlet" if it reached MT. Masking keeps it verbatim.
    mangle = StubTranslator(fn=lambda t, s, g: t.lower())
    mt = MaskedTranslator(mangle)
    out = mt.translate("who wrote the play Hamlet?", "en", "es")
    assert "Hamlet" in out          # entity survived the mangler
    assert out.startswith("who")     # non-entity text still translated


def test_graceful_degrade_propagates():
    dead = StubTranslator(fn=lambda *a: "x", available=False)
    mt = MaskedTranslator(dead)
    assert mt.available is False
    # Inner unavailable → original text back (sandwich → Phase-0).
    assert mt.translate("who is Paul of Tarsus?", "es", "en") == \
        "who is Paul of Tarsus?"


def test_engine_identity_carries_mask_policy():
    mt = MaskedTranslator(StubTranslator(engine_id="opus-mt-v1"))
    assert "entmask-v1" in mt.engine_id
    assert mt.manifest_hash.endswith(":entmask-v1")
