from __future__ import annotations

import json

import pytest

from evals.analyze_results import (
    _derive_case_condition_rows,
    _load_and_group_records,
    _summarize_bucket,
)


def _record(
    *,
    run_id: str,
    case_id: str,
    attempt: int,
    success: bool,
    helper_invoked: bool,
    helper_reason: str,
) -> dict:
    helper_calls_after = 1 if helper_invoked else 0
    return {
        "schema_version": 3,
        "run_id": run_id,
        "case_id": case_id,
        "condition": "adaptive_gated",
        "attempt": attempt,
        "success": success,
        "category": "traceback_controls",
        "difficulty": "easy",
        "snapshot_dependency": "helpful",
        "run_metadata": {
            "run_id": run_id,
            "started_at_utc": "2026-02-10T00:00:00Z",
            "deterministic": False,
            "seed": None,
            "python_version": "3.12.0",
            "platform": "test",
            "llmdebug_version": "2.1.0",
            "patcher_name": "command",
            "patcher_config_fingerprint": "abc123",
            "run_settings_fingerprint": "runfp123",
        },
        "attempt_metadata": {
            "attempt": attempt,
            "max_attempts": 2,
            "attempt_ordinal_label": f"{attempt}/2",
            "llm_call_count": 1,
            "tokens_prompt": 0,
            "tokens_completion": 0,
            "tokens_total": 0,
            "retry_history_budget_chars": 12000,
            "retry_history_budget_applied": False,
            "prompt_sha256": "p",
            "prompt_path": None,
            "evidence_sha256": "e",
            "evidence_path": None,
            "adaptive_mode": "gated",
            "adaptive_policy": "balanced",
            "adaptive_helper_invoked": helper_invoked,
            "adaptive_helper_decision_reason": helper_reason,
            "adaptive_helper_need_more_context": True if helper_invoked else None,
            "adaptive_helper_calls_used_before_attempt": 0 if attempt == 1 else 1,
            "adaptive_helper_calls_used_after_attempt": helper_calls_after,
            "adaptive_helper_budget_max_calls": 0,
            "adaptive_helper_budget_unlimited": True,
            "adaptive_helper_budget_exhausted": False,
            "adaptive_helper_saturation_guarded": False,
        },
        "model_metadata": {"backend": "command", "name": "command"},
        "patch_meta": {
            "adaptive_mode": "gated",
            "adaptive_helper_invoked": helper_invoked,
            "adaptive_helper_decision_reason": helper_reason,
            "adaptive_helper_need_more_context": True if helper_invoked else None,
            "adaptive_helper_calls_used_after_attempt": helper_calls_after,
        },
        "cumulative_runtime_sec": float(attempt),
    }


def test_analyze_adaptive_helper_metrics(tmp_path) -> None:
    records = [
        _record(
            run_id="run_adapt",
            case_id="case_a",
            attempt=1,
            success=False,
            helper_invoked=False,
            helper_reason="gate_balanced_waiting",
        ),
        _record(
            run_id="run_adapt",
            case_id="case_a",
            attempt=2,
            success=True,
            helper_invoked=True,
            helper_reason="gate_balanced_repeated_signature",
        ),
        _record(
            run_id="run_adapt",
            case_id="case_b",
            attempt=1,
            success=False,
            helper_invoked=False,
            helper_reason="gate_balanced_waiting",
        ),
        _record(
            run_id="run_adapt",
            case_id="case_b",
            attempt=2,
            success=False,
            helper_invoked=True,
            helper_reason="gate_balanced_repeated_signature",
        ),
    ]
    jsonl = tmp_path / "adaptive_results.jsonl"
    jsonl.write_text("\n".join(json.dumps(record) for record in records) + "\n", encoding="utf-8")

    grouped, input_errors = _load_and_group_records([str(jsonl)])
    assert input_errors == {"files_unreadable": 0, "json_lines_invalid": 0, "records_skipped": 0}

    rows = _derive_case_condition_rows(grouped)
    assert len(rows) == 2
    row_map = {row["case_id"]: row for row in rows}
    assert row_map["case_a"]["helper_invoked_case"] is True
    assert row_map["case_a"]["helper_rescued"] is True
    assert row_map["case_b"]["helper_invoked_case"] is True
    assert row_map["case_b"]["helper_rescued"] is False

    overall = _summarize_bucket(rows)
    adaptive = overall["adaptive_gated"]
    assert adaptive["helper_invoked_cases"] == 2
    assert adaptive["helper_invocation_case_rate"] == pytest.approx(1.0)
    assert adaptive["helper_invocation_attempt_rate"] == pytest.approx(0.5)
    assert adaptive["helper_success_when_invoked"] == pytest.approx(0.5)
    assert adaptive["helper_rescue_rate"] == pytest.approx(0.5)
    assert adaptive["helper_need_more_true_rate"] == pytest.approx(1.0)


def test_analyze_adaptive_decision_attempts_ignore_attempt_zero(tmp_path) -> None:
    run_id = "run_adapt_zero"
    records = [
        {
            "schema_version": 3,
            "run_id": run_id,
            "case_id": "case_zero",
            "condition": "adaptive_gated",
            "attempt": 0,
            "success": True,
            "category": "traceback_controls",
            "difficulty": "easy",
            "snapshot_dependency": "helpful",
            "run_metadata": {
                "run_id": run_id,
                "started_at_utc": "2026-02-10T00:00:00Z",
                "deterministic": False,
                "seed": None,
                "python_version": "3.12.0",
                "platform": "test",
                "llmdebug_version": "2.1.0",
                "patcher_name": "command",
                "patcher_config_fingerprint": "abc123",
                "run_settings_fingerprint": "runfp123",
            },
            "attempt_metadata": {
                "attempt": 0,
                "max_attempts": 2,
                "attempt_ordinal_label": "0/2",
                "llm_call_count": 0,
                "tokens_prompt": 0,
                "tokens_completion": 0,
                "tokens_total": 0,
                "retry_history_budget_chars": 12000,
                "retry_history_budget_applied": False,
                "prompt_sha256": "p",
                "prompt_path": None,
                "evidence_sha256": "e",
                "evidence_path": None,
                "adaptive_mode": "gated",
                "adaptive_policy": "balanced",
                "adaptive_helper_invoked": False,
                "adaptive_helper_decision_reason": "",
                "adaptive_helper_need_more_context": None,
                "adaptive_helper_calls_used_before_attempt": 0,
                "adaptive_helper_calls_used_after_attempt": 0,
                "adaptive_helper_budget_max_calls": 0,
                "adaptive_helper_budget_unlimited": True,
                "adaptive_helper_budget_exhausted": False,
                "adaptive_helper_saturation_guarded": False,
            },
            "model_metadata": {"backend": "command", "name": "command"},
            "patch_meta": {"adaptive_mode": "gated"},
            "cumulative_runtime_sec": 0.01,
        }
    ]
    jsonl = tmp_path / "adaptive_zero.jsonl"
    jsonl.write_text("\n".join(json.dumps(record) for record in records) + "\n", encoding="utf-8")

    grouped, _ = _load_and_group_records([str(jsonl)])
    rows = _derive_case_condition_rows(grouped)
    assert len(rows) == 1
    assert rows[0]["helper_decision_attempts"] == 0

    overall = _summarize_bucket(rows)
    adaptive = overall["adaptive_gated"]
    assert adaptive["helper_decision_attempts"] == 0
