"""Case-verdict read seam — the single place the read path derives pass/connectivity.

The engine is the authority: it runs ``judgement.combine_case_outcome`` once and
writes each case's verdict into ``summary.case_outcomes``. Every read surface
(report generation, the run-logs endpoint) must present *that* verdict, never
recompute it — otherwise WEIGHTED/ANY logic and connectivity cases diverge from
what was judged.

This module is that single seam. It reads the authoritative ``case_outcomes``
when present, and only for older runs that predate it falls back to a documented
approximation from persisted turns/results. Pure — no I/O; callers build the
per-case ``CaseEvidence`` from whatever they already have in hand.
"""

from dataclasses import dataclass

from agenteval.models import CaseOutcomeSummary


@dataclass(frozen=True)
class CaseEvidence:
    """What the legacy approximation needs about one case's persisted record.

    ``result_passes`` is the per-rule pass flags (empty means no judged rule
    result exists for the case — the connectivity-vs-fault fork).
    """

    has_turns: bool
    all_replied: bool
    result_passes: tuple[bool, ...] = ()


def build_case_evidence(turns: list, results: list) -> dict[str, CaseEvidence]:
    """Group turns + results by case_id into CaseEvidence per case.

    Pure — no I/O. Callers supply whatever turn/result objects expose
    ``.case_id`` and (for turns) ``.get_reply()`` / (for results) ``.passed``.
    """
    evidence: dict[str, dict] = {}
    for t in turns:
        ev = evidence.get(t.case_id)
        if ev is None:
            ev = {"has_turns": True, "all_replied": True, "passes": []}
            evidence[t.case_id] = ev
        else:
            ev["has_turns"] = True
        if t.get_reply() is None:
            ev["all_replied"] = False
    for r in results:
        ev = evidence.setdefault(r.case_id, {"has_turns": False, "all_replied": True, "passes": []})
        ev["passes"].append(r.passed)
    return {
        cid: CaseEvidence(
            has_turns=ev["has_turns"],
            all_replied=ev["all_replied"],
            result_passes=tuple(ev["passes"]),
        )
        for cid, ev in evidence.items()
    }


def resolve_case_verdicts(
    *,
    case_outcomes: dict[str, CaseOutcomeSummary],
    evidence: dict[str, CaseEvidence],
    errored_case_ids: set[str],
) -> dict[str, CaseOutcomeSummary]:
    """Resolve every case in ``evidence`` to its authoritative-or-approximated verdict.

    Authoritative ``case_outcomes`` win verbatim. For a case missing from it (an
    older run), approximate per CONTEXT.md / ADR-0002: a case with no judged
    results but turns that all replied and no case-level error is a *connectivity*
    case (counts as passed); a case with results passes iff every rule passed;
    anything else (a fault) fails.
    """
    verdicts: dict[str, CaseOutcomeSummary] = {}
    for case_id, ev in evidence.items():
        authoritative = case_outcomes.get(case_id)
        if authoritative is not None:
            verdicts[case_id] = authoritative
            continue

        connectivity = (
            not ev.result_passes
            and ev.has_turns
            and ev.all_replied
            and case_id not in errored_case_ids
        )
        if connectivity:
            passed = True
        elif not ev.result_passes:
            passed = False
        else:
            passed = all(ev.result_passes)
        verdicts[case_id] = CaseOutcomeSummary(passed=passed, connectivity=connectivity)
    return verdicts
