"""Tests for SWE-bench I/O utilities (paths, predictions, paired validation)."""

from __future__ import annotations

from pathlib import Path

import pytest

from evals.swebench.io import (
    copy_instructions_path,
    parse_paired_conditions,
    prediction_path,
    run_index_path,
    validate_paired_predictions,
    validate_prediction_file,
    write_copy_instructions,
    write_instance_ids,
    write_predictions,
    write_run_index,
)
from evals.swebench.types import PredictionRecord
from llmdebug import _json as json


def _write_records(path: Path, ids: list[str], *, model_tag: str) -> None:
    records = [
        PredictionRecord(
            instance_id=instance_id,
            model_patch=f"diff --git a/f.py b/f.py\n+{instance_id}\n",
            model_name_or_path=model_tag,
        )
        for instance_id in ids
    ]
    write_predictions(path, records)


def test_validate_prediction_file_reports_duplicate_instance_ids(tmp_path) -> None:
    path = tmp_path / "predictions.jsonl"
    write_predictions(
        path,
        [
            PredictionRecord("inst-1", "diff --git a/a.py b/a.py\n", "m"),
            PredictionRecord("inst-1", "diff --git a/a.py b/a.py\n", "m"),
        ],
    )

    rows, duplicates = validate_prediction_file(path)

    assert rows == 2
    assert duplicates == 1


def test_validate_paired_predictions_requires_all_condition_files(tmp_path) -> None:
    run_dir = tmp_path / "run"
    (run_dir / "baseline").mkdir(parents=True, exist_ok=True)
    _write_records(
        prediction_path(run_dir, "baseline"),
        ["inst-1", "inst-2"],
        model_tag="model::baseline",
    )

    with pytest.raises(ValueError, match="missing predictions file"):
        validate_paired_predictions(
            run_dir,
            conditions=["baseline", "llmdebug"],
            require_files=True,
        )


def test_validate_paired_predictions_rejects_condition_skew(tmp_path) -> None:
    run_dir = tmp_path / "run"
    (run_dir / "baseline").mkdir(parents=True, exist_ok=True)
    (run_dir / "llmdebug").mkdir(parents=True, exist_ok=True)
    _write_records(
        prediction_path(run_dir, "baseline"),
        ["inst-1", "inst-2"],
        model_tag="model::baseline",
    )
    _write_records(
        prediction_path(run_dir, "llmdebug"),
        ["inst-1", "inst-3"],
        model_tag="model::llmdebug",
    )

    with pytest.raises(ValueError, match="paired prediction mismatch"):
        validate_paired_predictions(run_dir, conditions=["baseline", "llmdebug"])


def test_parse_paired_conditions_rejects_single_condition() -> None:
    with pytest.raises(ValueError, match="conditions must include exactly baseline,llmdebug"):
        parse_paired_conditions("baseline")


def test_validate_paired_predictions_accepts_matching_dual_condition_sets(tmp_path) -> None:
    run_dir = tmp_path / "run"
    (run_dir / "baseline").mkdir(parents=True, exist_ok=True)
    (run_dir / "llmdebug").mkdir(parents=True, exist_ok=True)
    ids = ["inst-1", "inst-2", "inst-3"]
    _write_records(
        prediction_path(run_dir, "baseline"),
        ids,
        model_tag="model::baseline",
    )
    _write_records(
        prediction_path(run_dir, "llmdebug"),
        ids,
        model_tag="model::llmdebug",
    )

    validate_paired_predictions(
        run_dir,
        conditions=["baseline", "llmdebug"],
        expected_ids=ids,
        require_files=True,
    )


def test_write_handoff_index_and_copy_instructions(tmp_path) -> None:
    run_dir = tmp_path / "run"
    (run_dir / "baseline").mkdir(parents=True, exist_ok=True)
    (run_dir / "llmdebug").mkdir(parents=True, exist_ok=True)
    _write_records(prediction_path(run_dir, "baseline"), ["inst-1"], model_tag="baseline-model")
    _write_records(prediction_path(run_dir, "llmdebug"), ["inst-1"], model_tag="llmdebug-model")
    write_instance_ids(run_dir / "instance_ids.txt", ["inst-1"])
    (run_dir / "run_metadata.json").write_text('{"run_id":"demo"}\n', encoding="utf-8")

    index = write_run_index(run_dir, run_id="demo", conditions=["baseline", "llmdebug"])
    instructions = write_copy_instructions(run_dir, conditions=["baseline", "llmdebug"])

    assert index == run_index_path(run_dir)
    assert instructions == copy_instructions_path(run_dir)

    payload = json.loads(index.read_text(encoding="utf-8"))
    assert payload["run_id"] == "demo"
    assert "baseline" in payload["predictions"]
    assert "llmdebug" in payload["predictions"]
    assert "<copied_run_dir>" in instructions.read_text(encoding="utf-8")
