"""Tests for evals.importers.base — shared importer utilities.

Uses synthetic data only (no HuggingFace downloads).
"""

from __future__ import annotations

import json

from evals.importers.base import (
    count_patch_lines,
    generate_oracle_patch,
    validate_generated_case,
    write_case_files,
)

# ---------------------------------------------------------------------------
# generate_oracle_patch
# ---------------------------------------------------------------------------


class TestGenerateOraclePatch:
    def test_simple_change(self) -> None:
        buggy = "def f():\n    return 1\n"
        fixed = "def f():\n    return 2\n"
        patch = generate_oracle_patch(buggy, fixed)
        assert "diff --git" in patch
        assert "--- a/buggy.py" in patch
        assert "+++ b/buggy.py" in patch
        assert "-    return 1" in patch
        assert "+    return 2" in patch

    def test_custom_filename(self) -> None:
        buggy = "x = 1\n"
        fixed = "x = 2\n"
        patch = generate_oracle_patch(buggy, fixed, filename="module.py")
        assert "a/module.py" in patch
        assert "b/module.py" in patch

    def test_no_diff_returns_empty(self) -> None:
        code = "def f():\n    return 1\n"
        patch = generate_oracle_patch(code, code)
        assert patch == ""

    def test_multiline_change(self) -> None:
        buggy = "a = 1\nb = 2\nc = 3\n"
        fixed = "a = 1\nb = 20\nc = 3\n"
        patch = generate_oracle_patch(buggy, fixed)
        assert "-b = 2" in patch
        assert "+b = 20" in patch

    def test_addition_only(self) -> None:
        buggy = "a = 1\n"
        fixed = "a = 1\nb = 2\n"
        patch = generate_oracle_patch(buggy, fixed)
        assert "+b = 2" in patch


# ---------------------------------------------------------------------------
# write_case_files
# ---------------------------------------------------------------------------


class TestWriteCaseFiles:
    def test_creates_all_files(self, tmp_path) -> None:
        case_dir = tmp_path / "test_case"
        write_case_files(
            case_dir,
            buggy_code="def f():\n    return 1\n",
            test_code="def test_f():\n    assert True\n",
            oracle_patch="--- a/buggy.py\n+++ b/buggy.py\n",
            metadata={"category": "logic_error", "difficulty": "easy"},
        )

        assert (case_dir / "buggy.py").exists()
        assert (case_dir / "test_buggy.py").exists()
        assert (case_dir / "oracle.patch").exists()
        assert (case_dir / "case.json").exists()

        meta = json.loads((case_dir / "case.json").read_text())
        assert meta["category"] == "logic_error"
        assert meta["difficulty"] == "easy"

    def test_creates_nested_directories(self, tmp_path) -> None:
        case_dir = tmp_path / "nested" / "deep" / "case"
        write_case_files(
            case_dir,
            buggy_code="x = 1\n",
            test_code="assert True\n",
            oracle_patch="patch\n",
            metadata={},
        )
        assert case_dir.exists()

    def test_file_contents_match(self, tmp_path) -> None:
        case_dir = tmp_path / "case"
        write_case_files(
            case_dir,
            buggy_code="CODE",
            test_code="TEST",
            oracle_patch="PATCH",
            metadata={"key": "value"},
        )
        assert (case_dir / "buggy.py").read_text() == "CODE"
        assert (case_dir / "test_buggy.py").read_text() == "TEST"
        assert (case_dir / "oracle.patch").read_text() == "PATCH"


# ---------------------------------------------------------------------------
# validate_generated_case
# ---------------------------------------------------------------------------


class TestValidateGeneratedCase:
    def test_valid_case_passes(self, tmp_path) -> None:
        case_dir = tmp_path / "valid"
        write_case_files(
            case_dir,
            buggy_code="def f():\n    return 1\n",
            test_code="def test_f():\n    assert True\n",
            oracle_patch="diff --git a/buggy.py b/buggy.py\n-    return 1\n+    return 2\n",
            metadata={
                "category": "logic_error",
                "difficulty": "easy",
                "snapshot_dependency": "helpful",
                "expected_exception": "AssertionError",
            },
        )
        ok, msg = validate_generated_case(case_dir, schema_only=True)
        assert ok, msg

    def test_missing_file_fails(self, tmp_path) -> None:
        case_dir = tmp_path / "bad"
        case_dir.mkdir()
        (case_dir / "buggy.py").write_text("x = 1\n")
        (case_dir / "case.json").write_text("{}")
        # Missing test_buggy.py and oracle.patch
        ok, msg = validate_generated_case(case_dir, schema_only=True)
        assert not ok
        assert "missing required file" in msg

    def test_invalid_category_fails(self, tmp_path) -> None:
        case_dir = tmp_path / "bad_cat"
        write_case_files(
            case_dir,
            buggy_code="x = 1\n",
            test_code="assert True\n",
            oracle_patch="patch content\n",
            metadata={
                "category": "nonexistent_category",
                "difficulty": "easy",
                "snapshot_dependency": "helpful",
                "expected_exception": "AssertionError",
            },
        )
        ok, msg = validate_generated_case(case_dir, schema_only=True)
        assert not ok
        assert "invalid category" in msg

    def test_empty_patch_fails(self, tmp_path) -> None:
        case_dir = tmp_path / "empty_patch"
        write_case_files(
            case_dir,
            buggy_code="x = 1\n",
            test_code="assert True\n",
            oracle_patch="",
            metadata={
                "category": "logic_error",
                "difficulty": "easy",
                "snapshot_dependency": "helpful",
                "expected_exception": "AssertionError",
            },
        )
        ok, msg = validate_generated_case(case_dir, schema_only=True)
        assert not ok
        assert "empty" in msg


# ---------------------------------------------------------------------------
# count_patch_lines
# ---------------------------------------------------------------------------


class TestCountPatchLines:
    def test_simple_replacement(self) -> None:
        patch = (
            "--- a/buggy.py\n"
            "+++ b/buggy.py\n"
            "@@ -1,3 +1,3 @@\n"
            " def f():\n"
            "-    return 1\n"
            "+    return 2\n"
        )
        assert count_patch_lines(patch) == 2  # 1 addition + 1 deletion

    def test_addition_only(self) -> None:
        patch = "+new_line\n+another_line\n"
        assert count_patch_lines(patch) == 2

    def test_excludes_header_lines(self) -> None:
        patch = "--- a/file.py\n+++ b/file.py\n-old\n+new\n"
        assert count_patch_lines(patch) == 2  # Only -old and +new

    def test_empty_patch(self) -> None:
        assert count_patch_lines("") == 0
