"""Tests on Lab 17's script 02, from its recorded run (October 2026): 40 made-up use cases labeled by
hand, gpt-5.4-mini's level when asked for it, and its answers to the seven factual questions; then the
company's 16 systems and the inputs to its own tier. Run: pytest test_risk_practice.py"""
from risk_practice import decide, internal_tier, mcnemar, score

KEYS = ['prohibited_practice', 'annex_iii_area', 'exception', 'profiles_people', 'interacts_with_people', 'generates_synthetic_content', 'emotion_or_biometric_categorisation']

# Each case: [our label, gpt-5.4-mini's level when asked for it, its answers to the seven questions
# (in KEYS order), the start of the use case].
CASES = [["prohibited", "prohibited", ["c", "essential_services", "none", True, False, False, False], "A city scores residents on their social behavior and uses th"],
 ["prohibited", "prohibited", ["a", "none", "none", False, True, False, False], "A betting app plays sounds below the threshold of awareness "],
 ["prohibited", "prohibited", ["b", "none", "none", False, True, False, False], "A talking toy encourages young children to try risky physica"],
 ["prohibited", "prohibited", ["e", "biometrics", "none", False, False, False, False], "A startup scrapes faces from social media and CCTV footage t"],
 ["prohibited", "prohibited", ["f", "employment", "none", False, False, False, True], "An employer uses webcams to infer employees' emotions during"],
 ["prohibited", "prohibited", ["f", "biometrics", "none", False, False, False, True], "An app infers people's sexual orientation and religion from "],
 ["prohibited", "prohibited", ["d", "law_enforcement", "none", True, False, False, False], "Police use a system that predicts whether a person will comm"],
 ["prohibited", "prohibited", ["g", "law_enforcement", "none", False, False, False, True], "Police run real-time facial recognition on all passers-by in"],
 ["prohibited", "prohibited", ["g", "none", "none", False, False, True, False], "A website generates sexually explicit images of a named real"],
 ["high", "high", ["none", "employment", "none", True, False, False, False], "A tool reads job applications and ranks candidates, and recr"],
 ["high", "high", ["none", "employment", "none", True, False, False, False], "A system recommends which employees to promote and which to "],
 ["high", "high", ["none", "essential_services", "none", True, False, False, False], "A bank scores consumers' creditworthiness to decide on perso"],
 ["high", "high", ["none", "essential_services", "none", True, False, False, False], "An insurer prices individual life insurance policies from a "],
 ["high", "high", ["none", "essential_services", "none", False, False, False, False], "A government agency decides who is eligible for unemployment"],
 ["high", "high", ["none", "essential_services", "none", False, False, False, False], "A system ranks incoming emergency calls to decide which get "],
 ["high", "high", ["none", "education", "none", False, False, False, False], "An online proctoring tool watches students during exams and "],
 ["high", "high", ["none", "education", "none", False, False, False, False], "A university uses a model to decide which applicants are adm"],
 ["high", "high", ["none", "education", "none", False, False, False, False], "A model grades students' final exams, and the grade goes on "],
 ["high", "high", ["none", "employment", "none", True, False, False, False], "A warehouse tracks each worker's pace with a model that rate"],
 ["high", "high", ["none", "none", "none", False, False, False, True], "A call center analyzes customers' voices to detect their emo"],
 ["high", "high", ["none", "critical_infrastructure", "none", False, False, False, False], "A grid operator's AI controls the balancing of electricity s"],
 ["high", "high", ["none", "migration", "none", True, False, False, False], "Border authorities use a model to assess the risk posed by v"],
 ["high", "high", ["none", "justice_democracy", "none", False, False, False, False], "A court system suggests how judges should apply the law to t"],
 ["limited", "minimal", ["none", "none", "none", False, True, False, False], "An online outdoor-gear shop runs a chatbot that answers cust"],
 ["limited", "limited", ["none", "none", "none", False, False, True, False], "A marketing team generates product images with an image mode"],
 ["limited", "limited", ["none", "none", "none", False, False, True, False], "An agency makes a realistic video of a celebrity endorsing a"],
 ["limited", "limited", ["none", "none", "none", False, False, True, False], "A news site publishes AI-written articles on public affairs "],
 ["limited", "minimal", ["none", "none", "none", False, True, False, False], "A phone voice assistant books restaurant tables by calling t"],
 ["limited", "minimal", ["none", "none", "none", False, True, False, False], "An internal assistant answers employees' questions about the"],
 ["limited", "limited", ["none", "none", "none", False, False, True, False], "A writing app generates whole blog posts that users publish "],
 ["minimal", "minimal", ["none", "none", "none", False, False, False, False], "An email service filters spam."],
 ["minimal", "minimal", ["none", "none", "none", True, False, False, False], "An online shop recommends products based on browsing history"],
 ["minimal", "minimal", ["none", "none", "none", False, False, False, False], "A retailer forecasts demand to plan warehouse stock."],
 ["minimal", "minimal", ["none", "none", "none", False, False, False, False], "A factory camera spots scratches on parts coming off the lin"],
 ["minimal", "minimal", ["none", "none", "preparatory_task", False, False, False, False], "A delivery company optimizes van routes."],
 ["minimal", "minimal", ["none", "essential_services", "detects_patterns_without_replacing_human_review", False, False, False, False], "A card issuer flags possibly fraudulent payments for review."],
 ["minimal", "minimal", ["none", "none", "none", False, False, False, False], "A video game's characters adapt their behavior to the player"],
 ["minimal", "high", ["none", "employment", "preparatory_task", False, False, False, False], "A tool converts job applicants' CVs into a structured table "],
 ["minimal", "minimal", ["none", "essential_services", "detects_patterns_without_replacing_human_review", False, False, False, False], "After a loan officer has decided, a model checks whether the"],
 ["minimal", "minimal", ["none", "education", "improves_completed_human_activity", False, False, False, False], "A tool improves the wording of feedback a teacher has alread"]]

LABELS = [c[0] for c in CASES]
ASKED = [c[1] for c in CASES]
ANSWERS = [dict(zip(KEYS, c[2])) for c in CASES]

# The company's systems: (name, (severity, likelihood, reversible, affects specific people), tier).
SYSTEMS = [('Support message tagger', (1, 2, True, False), 'low'),
 ('Internal Q&A service', (1, 2, True, False), 'low'),
 ('FAQ search', (1, 2, True, False), 'low'),
 ('Help assistant and SQL tool', (2, 2, True, False), 'low'),
 ('Release judge', (1, 2, True, False), 'low'),
 ('Customer chat with memory', (2, 2, True, False), 'low'),
 ('Employee assistant', (1, 2, True, False), 'low'),
 ('Phone voice assistant', (2, 2, True, False), 'low'),
 ('Refund agent', (3, 2, False, True), 'high'),
 ('Refund agent, multi-agent', (3, 2, False, True), 'high'),
 ('Ticket triage model', (2, 2, True, True), 'medium'),
 ('Self-hosted models', (1, 2, True, False), 'low'),
 ('Refund agent, with fallbacks', (3, 2, False, True), 'high'),
 ('Docs assistant', (1, 2, True, False), 'low'),
 ('Email assistant', (3, 2, False, False), 'high'),
 ('HR documents assistant', (3, 1, False, False), 'medium')]


def facts(**kw):
    a = dict(prohibited_practice="none", annex_iii_area="none", exception="none", profiles_people=False,
             interacts_with_people=False, generates_synthetic_content=False,
             emotion_or_biometric_categorisation=False)
    a.update(kw)
    return a


def test_the_rules_in_order():
    assert decide(facts(prohibited_practice="c")) == ("prohibited", "Art. 5(1)(c) social scoring")
    assert decide(facts(annex_iii_area="employment")) == ("high", "Annex III, employment")
    assert decide(facts(interacts_with_people=True)) == ("limited", "Art. 50")
    assert decide(facts()) == ("minimal", "none")


def test_strictest_level_wins():
    # A chatbot that screens job applicants is high-risk, not limited.
    assert decide(facts(annex_iii_area="employment", interacts_with_people=True))[0] == "high"
    assert decide(facts(prohibited_practice="f", annex_iii_area="employment"))[0] == "prohibited"


def test_helper_task_exception_unless_it_profiles_people():
    cv = facts(annex_iii_area="employment", exception="narrow_procedural_task")
    assert decide(cv) == ("minimal", "Art. 6(3) narrow_procedural_task, Annex III employment")
    assert decide({**cv, "profiles_people": True})[0] == "high"
    assert decide({**cv, "interacts_with_people": True})[0] == "limited"


def test_facts_then_rules_39_of_40():
    levels = [decide(a)[0] for a in ANSWERS]
    assert score(levels, LABELS) == {"right": 39, "too_low": 1}
    miss = [i for i, (l, t) in enumerate(zip(levels, LABELS)) if l != t]
    assert miss == [19] and CASES[19][3].startswith("A call center")


def test_asked_for_the_level_36_of_40():
    assert score(ASKED, LABELS) == {"right": 36, "too_low": 3}


def test_the_one_miss_is_one_wrong_fact():
    # It said the call center isn't in a high-risk area. Correct that one fact and the level is right.
    a = dict(ANSWERS[19])
    assert a["annex_iii_area"] == "none"
    a["annex_iii_area"] = "biometrics"
    assert decide(a)[0] == LABELS[19] == "high"


def test_five_cases_differ_and_that_could_be_chance():
    a_right = [x == t for x, t in zip(ASKED, LABELS)]
    b_right = [decide(x)[0] == t for x, t in zip(ANSWERS, LABELS)]
    r = mcnemar(a_right, b_right)
    assert (r["b_only"], r["a_only"]) == (4, 1)
    assert abs(r["p"] - 0.375) < 1e-9


def test_mcnemar_edges():
    assert mcnemar([True] * 5, [True] * 5)["p"] == 1.0
    assert abs(mcnemar([False] * 6, [True] * 6)["p"] - 2 / 64) < 1e-9


def test_internal_tiers():
    for name, inputs, tier in SYSTEMS:
        assert internal_tier(*inputs) == tier, name
    assert sorted(n for n, _, t in SYSTEMS if t == "high") == [
        "Email assistant", "Refund agent", "Refund agent, multi-agent", "Refund agent, with fallbacks"]
    assert internal_tier(4, 4, False, True) == "critical"
