from __future__ import annotations

import subprocess
from dataclasses import dataclass

from evals import run_eval
from evals.patchers.base import PatchResult


def test_run_pytest_timeout_returns_structured_result(tmp_path, monkeypatch) -> None:
    def _timeout_run(*args, **kwargs):
        raise subprocess.TimeoutExpired(
            cmd=["python", "-m", "pytest"],
            timeout=1,
            output="partial stdout",
            stderr="partial stderr",
        )

    monkeypatch.setattr(run_eval.subprocess, "run", _timeout_run)

    result = run_eval.run_pytest(
        cwd=tmp_path,
        python_args=["-m", "pytest", "-q"],
        condition=run_eval.Condition.TRACEBACK_ONLY,
        timeout_sec=1,
        output_format="json_compact",
    )

    assert result.returncode == run_eval.PYTEST_TIMEOUT_RETURNCODE
    assert result.timed_out is True
    assert result.timeout_sec == 1
    assert "partial stdout" in result.stdout
    assert "pytest timed out after 1s" in result.stderr


@dataclass(frozen=True)
class _NoDiffPatcher:
    name: str = "no_diff"

    def propose_patch(self, *, prompt: str, context_dir, cwd) -> PatchResult:
        return PatchResult(diff=None, meta={"reason": "no_diff"})


def test_run_one_condition_records_timeout_metadata(tmp_path, monkeypatch) -> None:
    case_dir = tmp_path / "case_src"
    case_dir.mkdir(parents=True, exist_ok=True)
    (case_dir / "buggy.py").write_text("def f() -> int:\n    return 1\n", encoding="utf-8")
    (case_dir / "test_buggy.py").write_text(
        "from buggy import f\n\n\ndef test_f() -> None:\n    assert f() == 2\n",
        encoding="utf-8",
    )
    case = run_eval.CaseSpec(
        case_id="timeout_case",
        path=case_dir,
        category="traceback_controls",
        difficulty="easy",
        snapshot_dependency="none",
        expected_exception="AssertionError",
        tags=[],
        python_args=["-m", "pytest", "-q"],
        timeout_sec=1,
        description=None,
    )

    def _fake_run_pytest(cwd, python_args, condition, timeout_sec, output_format):
        return run_eval.RunResult(
            returncode=run_eval.PYTEST_TIMEOUT_RETURNCODE,
            duration_sec=0.01,
            stdout="",
            stderr="pytest timed out after 1s",
            timed_out=True,
            timeout_sec=1,
        )

    monkeypatch.setattr(run_eval, "run_pytest", _fake_run_pytest)

    records = run_eval.run_one_condition(
        case=case,
        condition=run_eval.Condition.TRACEBACK_ONLY,
        workdir=tmp_path / "workdir",
        context_dir_base=tmp_path / "context",
        patcher=_NoDiffPatcher(),
        k=1,
        output_format="json_compact",
        run_id="run-timeout",
        include_files_context=False,
        force_patch_attempt=False,
    )

    assert len(records) == 1
    record = records[0]
    assert record["note"] == "no_patch_proposed"
    assert record["timed_out"] is True
    assert record["timeout_sec"] == 1
