"""
Practice: scoring prompt versions the way Lab 01's 06_prompt_techniques.py does.

Fill in the TODOs, then run the tests:
    pytest test_triage_practice.py        (locally)
or press "Run tests" on the chapter page.
"""
import json
import re

CATEGORIES = {"billing", "bug", "account", "feature_request", "security"}
PRIORITIES = {"P1", "P2", "P3"}


def parse_reply(text: str) -> dict | None:
    """Read a reply the way the routing code would.

    Accept either the whole reply as JSON, or JSON inside <answer>…</answer> (the thinking
    versions write their reasoning first). Return {"category": ..., "priority": ...} only if
    it parses, is a JSON object, the category is one of CATEGORIES and the priority one of
    PRIORITIES. Anything else, like "**Category:** Security", returns None.

    TODO 1
    """
    return None


def is_right(reply: str, label: dict) -> bool:
    """Right means it parsed AND both the category and the priority match the label."""
    got = parse_reply(reply)
    return got is not None and got == label


def score(replies: dict, labels: dict) -> tuple[int, int]:
    """(number right, number of tickets), over the tickets in `labels`.

    `replies` and `labels` are keyed by ticket id. An unreadable reply counts as wrong.

    TODO 2
    """
    return 0, len(labels)


def changed(before: dict, after: dict, labels: dict) -> dict:
    """Follow which tickets changed, not just the total.

    Return {"fixed": [ids right in `after` but not in `before`],
            "broke": [ids right in `before` but not in `after`]}, in the order of `labels`.

    TODO 3
    """
    return {"fixed": [], "broke": []}


def pick(versions: list[dict]) -> str:
    """Each version is {"name", "holdout", "tokens"}. Return the name of the version with the
    best held-back score; among ties, the one that uses the fewest tokens.

    TODO 4
    """
    return versions[-1]["name"]
