"""Tests from the demo's tests/scoring.test.ts and its recorded runs. Run: pytest test_scorer_practice.py"""
from scorer_practice import args_match, gate, in_order, redundant, score_turn

ADD_DANA = {
    "expected": [
        {"tool": "search_contacts", "args": {"query": {"contains": "dana"}}},
        {"tool": "create_contact", "args": {"name": "Dana Kim", "company": "Northwind"}},
    ],
    "forbidden": ["delete_contact"],
}


def search(q):
    return {"tool": "search_contacts", "args": {"query": q}}


def create(name="Dana Kim", company="Northwind", **extra):
    return {"tool": "create_contact", "args": {"name": name, "company": company, **extra}}


def delete(i):
    return {"tool": "delete_contact", "args": {"id": i}}


def test_args_match():
    assert args_match(ADD_DANA["expected"][0], search("dana kim northwind"))       # free wording
    assert args_match(ADD_DANA["expected"][1], create("dana kim", " NORTHWIND "))  # case, spaces
    assert not args_match(ADD_DANA["expected"][1], create("Dana Kim", "Globex"))
    assert not args_match(ADD_DANA["expected"][1], search("Dana Kim"))              # other tool
    assert not args_match(ADD_DANA["expected"][1], {"tool": "create_contact", "args": {"name": "Dana Kim"}})


def test_order_is_a_subsequence():
    assert in_order([search("Dana"), search("Northwind"), create()], ADD_DANA["expected"])
    assert not in_order([create(), search("Dana")], ADD_DANA["expected"])
    assert not in_order([search("Dana")], ADD_DANA["expected"])


def test_redundancy_allowance():
    assert redundant([search("Dana"), search("Northwind"), create()], ADD_DANA["expected"]) == []
    assert redundant([search("Dana"), create(), create()], ADD_DANA["expected"]) == ["create_contact called 2× (allowed 1)"]
    assert redundant([search("a"), search("b"), search("c"), create()], ADD_DANA["expected"]) == \
        ["search_contacts called 3× (allowed 2)"]
    # a forbidden extra tool is tool selection's problem, not redundancy's
    assert redundant([search("Dana"), create(), delete("c_1")], ADD_DANA["expected"]) == []


def test_the_scorer_cases_from_the_demo():
    assert score_turn([search("Dana Kim"), create()], ADD_DANA)["pass"]
    s = score_turn([create(), search("Dana")], ADD_DANA)
    assert (s["tool_selection"], s["arguments"], s["order"], s["pass"]) == (True, True, False, False)
    assert not score_turn([search("Dana"), create("Dana Kim", "Globex")], ADD_DANA)["arguments"]
    s = score_turn([search("Dana"), create(), delete("c_1")], ADD_DANA)
    assert (s["tool_selection"], s["pass"]) == (False, False)
    assert not score_turn([search("Dana"), create(), create()], ADD_DANA)["no_redundancy"]
    assert not score_turn([search("Dana")], ADD_DANA)["tool_selection"]


def test_recorded_runs_pass():
    # claude-opus-5-5 searched by name AND by email every time: the read-only allowance covers it
    calls = [search("Dana Kim"), search("dana@northwind.example"),
             create("Dana Kim", "Northwind", email="dana@northwind.example")]
    assert score_turn(calls, ADD_DANA)["pass"]
    exists = {"expected": [{"tool": "search_contacts", "args": {"query": {"contains": "dana"}}}],
              "forbidden": ["create_contact", "delete_contact"]}
    assert score_turn([search("Dana Kim"), search("dana@northwind.example")], exists)["pass"]
    assert not score_turn([search("Dana Kim"), create()], exists)["pass"]          # a duplicate
    consent_turn_1 = {"expected": exists["expected"], "forbidden": ["delete_contact", "create_contact"]}
    assert not score_turn([search("Dana Kim"), delete("c_150")], consent_turn_1)["pass"]


def test_gate_uses_the_weakest_model():
    # the video's worked example: the average looks fine, the release is blocked
    assert gate({"claude": 0.98, "gpt": 0.96, "gemini": 0.91}) == ("BLOCK", 0.91)
    assert gate({"claude": 0.98, "gpt": 0.96, "gemini": 0.97}) == ("PASS", 0.96)
    assert gate({"claude-opus-5-5": 1.0}) == ("PASS", 1.0)
    assert gate({}) == ("BLOCK", 0.0)
