"""
Solution: reading LLM calls like an engineer (Lab 01, scripts 1 and 5).
"""
import difflib
import itertools


def was_cut(response: dict) -> bool:
    """True when the answer stopped at the output limit instead of finishing."""
    return response["choices"][0]["finish_reason"] == "length"


def alike(runs: list[str]) -> float:
    """Average word-sequence similarity of every pair of runs, rounded to 2 places."""
    pairs = list(itertools.combinations(runs, 2))
    if not pairs:
        return 1.0
    r = [difflib.SequenceMatcher(None, a.split(), b.split()).ratio() for a, b in pairs]
    return round(sum(r) / len(r), 2)


def unfair_cases(cases: list[dict]) -> list[str]:
    """Inputs where exactly one prompt's answer was cut off: the comparison measured the limit there."""
    return [c["input"] for c in cases if c["a_cut"] != c["b_cut"]]


def verdict(a: list[int], b: list[int]) -> str:
    """One question judged twice: index 0 with A shown first, index 1 with B shown first.

    "A" or "B" if that prompt won (scored higher) in both orders, "flips" if the winner changed
    with the order, "tie" otherwise.
    """
    winners = ["A" if x > y else "B" if y > x else "tie" for x, y in zip(a, b)]
    if winners[0] == winners[1]:
        return winners[0]
    if "tie" in winners:
        return "tie"
    return "flips"


def compare(cases: list[dict]) -> dict:
    """Tally verdicts over every question: {"A": n, "B": n, "flips": n, "tie": n}."""
    out = {"A": 0, "B": 0, "flips": 0, "tie": 0}
    for c in cases:
        out[verdict(c["a"], c["b"])] += 1
    return out


def position_bias(cases: list[dict]) -> tuple[float, float]:
    """Average score of the answer shown first, and of the answer shown second, over every judgment."""
    first = [c["a"][0] for c in cases] + [c["b"][1] for c in cases]
    second = [c["a"][1] for c in cases] + [c["b"][0] for c in cases]
    return round(sum(first) / len(first), 2), round(sum(second) / len(second), 2)
