"""
Practice: the launch checklist as code, the way Lab 17's script 03 does it. Each item is a check
that returns its evidence (a file, or the line it matched), not a tick, so a person can read it.
You'll write a keyword check and the stricter CI-file check, count what 16 systems have, and
measure how far two raters agree on which items would have caught 12 real failures.

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


def find_line(files: dict[str, str], pattern: str) -> str | None:
    """files: {path: text}, searched in the order given. Return "path: the whole line" for the first
    line that matches the pattern (case-insensitive, ^ and $ at line ends), stripped, or None."""
    # TODO
    raise NotImplementedError


def ci_runs_on_change(workflows: dict[str, str], folder: str) -> str | None:
    """workflows: {path: YAML text} under .github/workflows. Evidence that a system is re-tested on
    every change: a workflow with a `pull_request:` or `push:` trigger line that names the system's
    folder as "labs/<folder>". Return "<path>: runs on every change" for the first, or None."""
    # TODO
    raise NotImplementedError


def evidence_counts(systems: dict[str, dict[str, str | None]]) -> dict[str, int]:
    """systems: {system: {item: evidence or None}}. How many systems have evidence for each item?"""
    # TODO
    raise NotImplementedError


def best_system(systems: dict[str, dict[str, str | None]], items: list[str]) -> tuple[str, int]:
    """The system with evidence for the most of these items, and how many (the first, on a tie)."""
    # TODO
    raise NotImplementedError


def agreement(a: list[bool], b: list[bool]) -> dict[str, float]:
    """Two raters' yes/no labels for the same judgments. Return
      raw     the share where they agree
      chance  the agreement two raters with these yes rates would reach by luck:
              pa*pb + (1-pa)*(1-pb), where pa, pb are each rater's share of yes
      kappa   (raw - chance) / (1 - chance); 1.0 if chance is 1"""
    # TODO
    raise NotImplementedError


def cases_with(cases: list[dict], items: list[str]) -> int:
    """cases: [{"caught_by": [item ids]}]. How many cases list at least one of these items?"""
    # TODO
    raise NotImplementedError
