"""Calibration: run judge on human-graded traces, measure agreement.

Delegates to backend POST /calibrations/{project}/run.
This module handles payload prep and terminal formatting.
"""

from __future__ import annotations

from typing import Any


def run_calibration(
    project: str,
    traces_with_grades: list[tuple[dict[str, Any], int]],
    client: Any,
) -> dict:
    """Run calibration via the backend. Returns the raw response dict."""
    traces = [
        {
            "trace_id": trace.get("trace_id", "?"),
            "input": str(trace.get("input", "")),
            "output": str(trace.get("output", "")),
            "human_score": human_score,
        }
        for trace, human_score in traces_with_grades
    ]
    return client.run_calibration(project, traces)


def format_calibration_report(
    data: dict,
    traces_with_grades: list[tuple[dict[str, Any], int]] | None = None,
) -> str:
    """Format calibration results for terminal output.

    Args:
        data: CalibrationRunResponse dict from the backend.
        traces_with_grades: Original traces (for input/output in disagreements).
    """
    tpr = data["tpr"]
    tnr = data["tnr"]

    lines = [
        "",
        "  Calibration Results",
        f"  {'─' * 50}",
        "",
        f"  Traces evaluated: {data['total']}",
    ]
    if data.get("parse_errors"):
        lines.append(f"  Parse errors (skipped): {data['parse_errors']}")
    lines.append("")

    tpr_mark = "✓" if tpr >= 0.9 else "✗"
    tnr_mark = "✓" if tnr >= 0.9 else "✗"
    lines.extend([
        f"  {tpr_mark} TPR (recall):     {tpr:.0%}    (target: ≥90%)",
        f"  {tnr_mark} TNR (specificity): {tnr:.0%}    (target: ≥90%)",
        f"    Agreement:       {data['agreement']:.0%}",
        f"    Cohen's kappa:   {data['kappa']:.2f}",
    ])

    lines.extend([
        "",
        "  Confusion Matrix",
        f"  {'─' * 30}",
        f"                  Judge=1  Judge=0",
        f"    Human=1        {data['tp']:>4}     {data['fn']:>4}",
        f"    Human=0        {data['fp']:>4}     {data['tn']:>4}",
    ])

    lines.append("")
    if tpr >= 0.9 and tnr >= 0.9:
        lines.append("  Judge is well-calibrated.")
    else:
        if tpr < 0.9 and tnr < 0.9:
            lines.append("  Judge needs work on both dimensions.")
        elif tpr < 0.9:
            lines.append("  Judge is too strict — missing good responses (low TPR).")
        else:
            lines.append("  Judge is too lenient — passing bad responses (low TNR).")
        lines.append("  Review disagreements below, then refine your rubric.")

    # Disagreements from trace_results
    disagreements = [
        tr for tr in data.get("trace_results", [])
        if not tr.get("agreed", True)
    ]
    if disagreements:
        # Build lookup for original trace input/output
        orig_map: dict[str, dict] = {}
        if traces_with_grades:
            for trace, _ in traces_with_grades:
                orig_map[trace.get("trace_id", "")] = trace

        fp_list = [d for d in disagreements if d.get("kind") == "fp"]
        fn_list = [d for d in disagreements if d.get("kind") == "fn"]

        if fp_list:
            lines.extend([
                "",
                f"  Judge too lenient ({len(fp_list)} false positives — judge said good, you said bad):",
                f"  {'─' * 50}",
            ])
            for d in fp_list:
                lines.extend(_format_disagreement(d, orig_map))

        if fn_list:
            lines.extend([
                "",
                f"  Judge too strict ({len(fn_list)} false negatives — judge said bad, you said good):",
                f"  {'─' * 50}",
            ])
            for d in fn_list:
                lines.extend(_format_disagreement(d, orig_map))

    lines.append("")
    return "\n".join(lines)


def _format_disagreement(d: dict, orig_map: dict[str, dict]) -> list[str]:
    trace_id = d.get("trace_id", "?")
    orig = orig_map.get(trace_id, {})
    lines = [
        f"    {trace_id}",
        f"    {'·' * 46}",
    ]
    lines.extend(_wrap_field("Input", str(orig.get("input", ""))[:500]))
    lines.append("")
    lines.extend(_wrap_field("Output", str(orig.get("output", ""))[:500]))
    lines.append("")
    lines.extend(_wrap_field("Judge critique", d.get("judge_critique", "")))
    lines.append("")
    return lines


def _wrap_field(label: str, text: str, width: int = 60, indent: int = 6) -> list[str]:
    prefix = " " * indent
    lines = [f"{prefix}{label}:"]
    for paragraph in text.splitlines():
        paragraph = paragraph.strip()
        if not paragraph:
            continue
        while len(paragraph) > width:
            break_at = paragraph.rfind(" ", 0, width)
            if break_at <= 0:
                break_at = width
            lines.append(f"{prefix}  {paragraph[:break_at]}")
            paragraph = paragraph[break_at:].lstrip()
        if paragraph:
            lines.append(f"{prefix}  {paragraph}")
    return lines
