import re
from collections import Counter

from copilot.evaluate import load_golden, ranked_lessons
from copilot.ingest import MAX_WORDS, discover_courses, site_root


def test_every_course_is_ingested(chunks):
    courses = Counter(c.course for c in chunks)
    assert set(discover_courses(site_root())) <= set(courses)     # every course with lessons has chunks
    assert all(n > 50 for n in courses.values())


def test_chunk_ids_are_unique_and_sizes_are_capped(chunks):
    assert len({c.id for c in chunks}) == len(chunks)
    # a chunk may exceed the cap only by the one paragraph of overlap it carries, or be one huge sentence
    sizes = sorted(len(c.text.split()) for c in chunks)
    assert sizes[int(0.99 * len(sizes))] <= 2 * MAX_WORDS


def test_urls_point_at_real_anchors(chunks):
    root = site_root()
    sample = [c for c in chunks if "#" in c.url][::50]
    for c in sample:
        page, anchor = c.url.split("#")
        html = (root / page).read_text(encoding="utf-8")
        assert re.search(rf'id="{re.escape(anchor)}"', html), c.url


def test_quiz_options_and_example_output_are_not_indexed(chunks):
    text = "\n".join(c.text for c in chunks)
    # a wrong answer option from the AI Engineering lesson 01 quiz, and a line of example output
    assert "The model remembered them from yesterday's training update." not in text
    assert "trained on 26,993 words" not in text


def test_hybrid_retrieval_meets_the_golden_bar(copilot):
    golden = load_golden()
    hits = sum(bool(set(g["lessons"]) & set(ranked_lessons(copilot, g["question"], k=5))) for g in golden)
    assert hits / len(golden) >= 0.80                                # measured 0.90; a regression gate


def test_exact_identifiers_are_found_by_keyword_search(index):
    top = index.search("innodb_buffer_pool_size", k=3, mode="bm25")
    assert any(h.chunk.course == "mysql" for h in top)


def test_course_filter_restricts_results(index):
    hits = index.search("window functions running total", k=5, course="sql")
    assert hits and all(h.chunk.course == "sql" for h in hits)
