"""Tests for scripts/verify_transcript_clean.py, the deterministic transcript-cleanup verifier.

Fixtures reproduce the real regressions found in a 49-transcript cleanup batch:
a deleted case citation, unformatted currency, paragraph-count drift, and
proper-noun spelling disagreement across editors.
"""

import json
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
SCRIPTS_DIR = ROOT / "scripts"
sys.path.insert(0, str(SCRIPTS_DIR))

import verify_transcript_clean as vtc  # noqa: E402


def _write_pair(tmp_path, name, src_text, out_text):
    src_dir = tmp_path / "src"
    out_dir = tmp_path / "out"
    src_dir.mkdir(exist_ok=True)
    out_dir.mkdir(exist_ok=True)
    (src_dir / name).write_text(src_text, encoding="utf-8")
    (out_dir / name).write_text(out_text, encoding="utf-8")
    return src_dir, out_dir


# --- clean pair --------------------------------------------------------------

def test_clean_pair_passes(tmp_path):
    text = (
        "Today we discuss Thompson v. Libbey and the rule against perpetuities.\n\n"
        "The plaintiff paid $1,200 for the goods.\n"
    )
    src_dir, out_dir = _write_pair(tmp_path, "clean.txt", text, text)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.88, 1.12)
    assert report["error_count"] == 0
    assert vtc.main(["--src", str(src_dir), "--out", str(out_dir)]) == 0


# --- dropped citation ---------------------------------------------------------

def test_dropped_citation_is_error(tmp_path):
    src = "Today we discuss a different case called Boving v. Vandover, that the court held was distinguishable.\n"
    out = "Today we discuss a different case, that the court held was distinguishable.\n"
    src_dir, out_dir = _write_pair(tmp_path, "dropped.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    assert any("Boving v. Vandover" in e for e in errors)


def test_legitimate_respelling_not_flagged_as_dropped(tmp_path):
    src = "In Thompson v. Libbey the court held for the defendant.\n"
    out = "In Thompson v. Libby the court held for the defendant.\n"
    src_dir, out_dir = _write_pair(tmp_path, "respell.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    assert not any("dropped citation" in e for e in errors)


# --- paragraph-scoped citation check ------------------------------------------------

def test_citation_dropped_from_one_paragraph_is_error_even_if_kept_elsewhere(tmp_path):
    # Section 373 appears in paragraphs 1 and 3 of src, but out only keeps paragraph 3's
    # mention. File-wide membership would pass this; paragraph scoping must catch it.
    src = (
        "The rule appears in Section 373 of the Restatement.\n\n"
        "This paragraph has no citation at all.\n\n"
        "We return to Section 373 again here.\n"
    )
    out = (
        "The rule appears in the Restatement.\n\n"
        "This paragraph has no citation at all.\n\n"
        "We return to Section 373 again here.\n"
    )
    src_dir, out_dir = _write_pair(tmp_path, "partial.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    assert any("Section 373" in e and "paragraph 1" in e for e in errors)
    assert vtc.main(["--src", str(src_dir), "--out", str(out_dir), "--min-ratio", "0.5", "--max-ratio", "1.5"]) == 1


def test_citation_present_in_same_paragraph_both_sides_no_error(tmp_path):
    text = "The rule appears in Section 373 of the Restatement here.\n"
    src_dir, out_dir = _write_pair(tmp_path, "same.txt", text, text)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    assert report["files"][0]["errors"] == []


def test_citation_dropped_from_every_paragraph_one_error_per_paragraph_no_duplicates(tmp_path):
    src = (
        "First mention of Section 373 here.\n\n"
        "Second mention of Section 373 here.\n"
    )
    out = (
        "First paragraph with nothing relevant.\n\n"
        "Second paragraph with nothing relevant.\n"
    )
    src_dir, out_dir = _write_pair(tmp_path, "fulldrop.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    citation_errors = [e for e in errors if "Section 373" in e]
    assert len(citation_errors) == 2
    assert any("paragraph 1" in e for e in citation_errors)
    assert any("paragraph 2" in e for e in citation_errors)
    assert len(set(citation_errors)) == 2  # no duplicate error for the same paragraph


def test_respelling_within_same_paragraph_still_saved_by_difflib(tmp_path):
    text_src = "In this paragraph, Thompson v. Libbey the court held for the defendant.\n"
    text_out = "In this paragraph, Thompson v. Libby the court held for the defendant.\n"
    src_dir, out_dir = _write_pair(tmp_path, "respell_para.txt", text_src, text_out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    assert report["files"][0]["errors"] == []


def test_paragraph_count_mismatch_falls_back_to_whole_file_membership(tmp_path):
    # Paragraph counts differ (3 vs 2), so alignment is impossible and the check
    # falls back to check_citations' whole-file membership semantics: a citation
    # dropped from one paragraph but present elsewhere in the file is NOT an error,
    # while a citation dropped from the entire file still is. The paragraph-count
    # mismatch itself is still reported.
    src = (
        "First mention of Section 373 here.\n\n"
        "Second mention of Boving v. Vandover here.\n\n"
        "Third paragraph with nothing relevant.\n"
    )
    out = (
        "First paragraph now missing the section cite.\n\n"
        "Second mention of Boving v. Vandover here, combined.\n"
    )
    src_dir, out_dir = _write_pair(tmp_path, "mismatch.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    assert any("paragraph count mismatch" in e for e in errors)
    assert not any("Boving v. Vandover" in e for e in errors)
    assert any("Section 373" in e for e in errors)
    assert not any("paragraph" in e and "Section 373" in e for e in errors)


# --- paragraph parity ----------------------------------------------------------

def test_paragraph_count_mismatch_is_error(tmp_path):
    src = "Paragraph one.\n\nParagraph two.\n\nParagraph three.\n"
    out = "Paragraph one.\n\nParagraph two combined with three.\n"
    src_dir, out_dir = _write_pair(tmp_path, "paras.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    assert any("paragraph count mismatch" in e for e in errors)


# --- word-count ratio -----------------------------------------------------------

def test_word_ratio_085_is_error_and_095_passes(tmp_path):
    src_text = " ".join(f"word{i}" for i in range(100)) + "\n"
    out_085 = " ".join(f"word{i}" for i in range(85)) + "\n"
    src_dir, out_dir = _write_pair(tmp_path, "ratio.txt", src_text, out_085)

    report = vtc.run(str(src_dir), str(out_dir), None, 0.88, 1.12)
    errors = report["files"][0]["errors"]
    assert any("word count ratio" in e for e in errors)

    (out_dir / "ratio.txt").write_text(" ".join(f"word{i}" for i in range(95)) + "\n", encoding="utf-8")
    report = vtc.run(str(src_dir), str(out_dir), None, 0.88, 1.12)
    assert report["files"][0]["errors"] == []


# --- number formatting -----------------------------------------------------------

def test_unformatted_currency_is_warning_not_error(tmp_path):
    src = "The plaintiff paid $1,200 for the goods.\n"
    out = "The plaintiff paid 1200 dollars for the goods.\n"
    src_dir, out_dir = _write_pair(tmp_path, "money.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    result = report["files"][0]
    assert result["errors"] == []
    assert any("1200" in w and "dollars" in w for w in result["warnings"])
    exit_code = vtc.main([
        "--src", str(src_dir), "--out", str(out_dir),
        "--min-ratio", "0.5", "--max-ratio", "1.5",
    ])
    assert exit_code == 0


# --- em dash / en dash -----------------------------------------------------------

def test_em_dash_in_output_is_error(tmp_path):
    src = "The rule is clear and the outcome follows.\n"
    out = "The rule is clear" + "\u2014" + "the outcome follows.\n"
    src_dir, out_dir = _write_pair(tmp_path, "dash.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    assert any("dash" in e.lower() for e in errors)


# --- glossary canonical spellings -------------------------------------------------

def test_glossary_variant_yields_warning_naming_canonical(tmp_path):
    glossary_path = ROOT / "standards" / "glossaries" / "law-contracts.md"
    src = "The court cited Tyra v. Cheney in its opinion.\n"
    out = "The court cited Tira v. Cheney in its opinion.\n"
    src_dir, out_dir = _write_pair(tmp_path, "glossary.txt", src, out)
    report = vtc.run(str(src_dir), str(out_dir), str(glossary_path), 0.5, 1.5)
    result = report["files"][0]
    assert any("Tira v. Cheney" in w and "Tyra v. Cheney" in w for w in result["warnings"])
    # the respelling is close enough that it must not also raise a dropped-citation error
    assert result["errors"] == []


# --- --json CLI output -------------------------------------------------------------

def test_json_output_parses_and_reports_counts(tmp_path, capsys):
    src = "The plaintiff paid $1,200 for the goods.\n"
    out = "The plaintiff paid 1200 dollars for the goods.\n"
    src_dir, out_dir = _write_pair(tmp_path, "j.txt", src, out)
    exit_code = vtc.main([
        "--src", str(src_dir), "--out", str(out_dir), "--json",
        "--min-ratio", "0.5", "--max-ratio", "1.5",
    ])
    assert exit_code == 0
    report = json.loads(capsys.readouterr().out)
    assert report["error_count"] == 0
    assert report["warning_count"] >= 1
    assert report["files"][0]["name"] == "j.txt"


# --- single-file mode with mismatched basenames ------------------------------------

def test_two_files_different_basenames_identical_content(tmp_path):
    text = "Alpha beta.\n\nGamma delta.\n"
    src = tmp_path / "src.txt"
    out = tmp_path / "cleaned.txt"
    src.write_text(text, encoding="utf-8")
    out.write_text(text, encoding="utf-8")
    report = vtc.run(str(src), str(out), None, 0.88, 1.12)
    assert len(report["files"]) == 1
    assert report["error_count"] == 0
    assert vtc.main(["--src", str(src), "--out", str(out)]) == 0


def test_two_files_different_basenames_dropped_citation(tmp_path):
    src = tmp_path / "src.txt"
    out = tmp_path / "cleaned.txt"
    src.write_text("The court in Boving v. Vandover held for the plaintiff.\n", encoding="utf-8")
    out.write_text("The court held for the plaintiff.\n", encoding="utf-8")
    report = vtc.run(str(src), str(out), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    assert any("Boving v. Vandover" in e for e in errors)
    exit_code = vtc.main([
        "--src", str(src), "--out", str(out),
        "--min-ratio", "0.5", "--max-ratio", "1.5",
    ])
    assert exit_code == 1


# --- "Section N" refs (not just the § glyph) ---------------------------------------

def test_section_word_reference_present_in_both_no_error(tmp_path):
    text = "The rule appears in Section 373 of the Restatement.\n"
    src = tmp_path / "src.txt"
    out = tmp_path / "cleaned.txt"
    src.write_text(text, encoding="utf-8")
    out.write_text(text, encoding="utf-8")
    report = vtc.run(str(src), str(out), None, 0.5, 1.5)
    assert report["files"][0]["errors"] == []


def test_dropped_section_word_reference_is_error(tmp_path):
    src = tmp_path / "src.txt"
    out = tmp_path / "cleaned.txt"
    src.write_text("The rule appears in Section 373 of the Restatement here.\n", encoding="utf-8")
    out.write_text("The rule appears in the Restatement here.\n", encoding="utf-8")
    report = vtc.run(str(src), str(out), None, 0.5, 1.5)
    errors = report["files"][0]["errors"]
    assert any("Section 373" in e for e in errors)
