"""
Practice: checking that a failure was SAFE, not just that it failed, against the acme-crm-mcp demo.
A response can look right while the database tells another story, so every check here looks at
the response, the rows, the audit log and the counters together.

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


def outcome(status: int, body: str) -> str:
    """The metrics label for one response, as src/observability.ts computes it:
      - a status >= 400 -> that status as a string ("401", "403", "429", ...)
      - a 200 whose body contains "isError": true (any spaces after the colon) -> "tool_error"
      - otherwise -> "ok"

    TODO 1
    """
    return "ok"


def diff_rows(before: dict[str, dict], after: dict[str, dict]) -> dict[str, list[str]]:
    """Compare two snapshots of the contacts table, each {id: row}.
    Return {"added": [...], "removed": [...], "changed": [...]}, each list of ids sorted.

    TODO 2
    """
    return {"added": [], "removed": [], "changed": []}


def safety_violations(caller_tenant: str, expected_outcome: str, got_outcome: str,
                      before: dict[str, dict], after: dict[str, dict],
                      new_audit_rows: list[dict], write_tool: bool) -> list[str]:
    """Everything wrong with a call that was supposed to FAIL. In this order:
      - "outcome <got>, expected <expected>"      if the outcome differs
      - "data changed: <id>"                       for every added, removed or changed id (sorted)
      - "another tenant touched: <id>"             for every changed/removed id whose row belonged
                                                   to a tenant other than the caller (sorted)
      - "write attempt not audited"                if write_tool and no new audit row has
                                                   tenant == caller_tenant
    An empty list means it failed safely.

    TODO 3
    """
    return []


def tally(responses: list[tuple[str, int, str]]) -> dict[tuple[str, str], int]:
    """Count responses per (tenant, outcome), like mcp_responses_total.
    Each response is (tenant, status, body).

    TODO 4
    """
    return {}
