"""Solution for the Lab 07 practice: quality gates in CI."""
import re


def relative_verdict(change: float, lo: float, hi: float, tolerance: float) -> str:
    if change > tolerance and lo > 0:
        return "fail"
    if lo > 0:
        return "within"
    if hi < 0:
        return "better"
    return "unclear"


def no_new_failures(current: dict[str, str], baseline_runs: list[dict[str, str]],
                    max_new: int = 0) -> tuple[str, list[str], list[str]]:
    before = {clip for run in baseline_runs for clip, text in run.items() if text.strip()}
    now = {clip for clip, text in current.items() if text.strip()}
    new, fixed = sorted(now - before), sorted(before - now)
    return ("fail" if len(new) > max_new else "pass"), new, fixed


def normalize(text: str) -> str:
    text = re.sub(r"(?<!\d)\.|\.(?!\d)", " ", text.lower())
    text = re.sub(r"[^\w%.\s]", " ", text)
    return " ".join(text.split())


def must_pass(hypothesis: str, phrase: str) -> bool:
    return f" {normalize(phrase)} " in f" {normalize(hypothesis)} "


def exit_code(results: list[tuple[str, str]], problems: list[str]) -> int:
    if problems:
        return 2
    if any(status == "fail" and severity == "block" for status, severity in results):
        return 1
    return 0
