"""Tests for the regression guard module."""

import pytest

from autodebug.detection.regression_guard import RegressionCheckResult, RegressionGuard


def _make_results(statuses, git_commit=None):
    """Helper to create mock run results."""
    results = []
    for i, status in enumerate(statuses):
        results.append({
            "id": f"run_{i}",
            "status": status,
            "git_commit": git_commit or f"commit_{i}",
            "failure_signature": "Error: test failed" if status == "FAILED" else None,
            "created_at": f"2026-01-{15-i:02d}T10:00:00Z",
        })
    return results


@pytest.fixture
def guard_same_commit_pass():
    """Guard that returns results where same commit passed before."""
    async def get_results(test_name, project_id):
        return _make_results(
            ["FAILED", "PASSED", "PASSED", "PASSED", "PASSED"],
            git_commit="abc123",
        )

    async def get_signatures(test_name, project_id):
        return ["Error: test failed"]

    return RegressionGuard(
        db_get_recent_results=get_results,
        db_get_failure_signatures=get_signatures,
    )


@pytest.fixture
def guard_pass_rate_drop():
    """Guard that returns results showing pass rate drop."""
    async def get_results(test_name, project_id):
        # Recent 5: all failed, Old 5: all passed
        return _make_results(
            ["FAILED", "FAILED", "FAILED", "FAILED", "FAILED",
             "PASSED", "PASSED", "PASSED", "PASSED", "PASSED"],
        )

    async def get_signatures(test_name, project_id):
        return []  # New signature

    return RegressionGuard(
        db_get_recent_results=get_results,
        db_get_failure_signatures=get_signatures,
    )


@pytest.fixture
def guard_new_signature():
    """Guard that returns results with a brand new failure signature."""
    async def get_results(test_name, project_id):
        return _make_results(
            ["FAILED", "PASSED", "PASSED", "PASSED", "PASSED",
             "PASSED", "PASSED", "PASSED", "PASSED", "PASSED"],
        )

    async def get_signatures(test_name, project_id):
        return ["Old error: something else"]  # Different from current failure

    return RegressionGuard(
        db_get_recent_results=get_results,
        db_get_failure_signatures=get_signatures,
    )


@pytest.fixture
def guard_mixed_history():
    """Guard that returns results with mixed pass/fail history."""
    async def get_results(test_name, project_id):
        return _make_results(
            ["FAILED", "PASSED", "FAILED", "PASSED", "FAILED",
             "PASSED", "FAILED", "PASSED", "FAILED", "PASSED"],
        )

    async def get_signatures(test_name, project_id):
        return ["Error: test failed"]

    return RegressionGuard(
        db_get_recent_results=get_results,
        db_get_failure_signatures=get_signatures,
    )


@pytest.fixture
def guard_no_history():
    """Guard that returns no historical results."""
    async def get_results(test_name, project_id):
        return []

    async def get_signatures(test_name, project_id):
        return []

    return RegressionGuard(
        db_get_recent_results=get_results,
        db_get_failure_signatures=get_signatures,
    )


@pytest.fixture
def guard_already_flaky():
    """Guard for a test that is already known flaky."""
    async def get_results(test_name, project_id):
        # Mix of pass/fail across different commits
        results = []
        for i in range(10):
            results.append({
                "id": f"run_{i}",
                "status": "PASSED" if i % 3 != 0 else "FAILED",
                "git_commit": f"commit_{i % 3}",
                "failure_signature": "Error: flaky stuff" if i % 3 == 0 else None,
                "created_at": f"2026-01-{15-i:02d}T10:00:00Z",
            })
        return results

    async def get_signatures(test_name, project_id):
        return ["Error: flaky stuff"]

    return RegressionGuard(
        db_get_recent_results=get_results,
        db_get_failure_signatures=get_signatures,
    )


class TestRegressionGuard:
    """Test regression guard classification."""

    @pytest.mark.asyncio
    async def test_same_commit_passed_is_flaky(self, guard_same_commit_pass):
        """If test passed with the same commit, it's flaky."""
        result = await guard_same_commit_pass.check(
            test_name="test_example",
            test_file="tests/test_example.py",
            branch="main",
            git_commit="abc123",
            project_id="proj_1",
        )

        assert result.is_regression is False
        assert result.confidence >= 0.90
        assert "same commit" in result.reason.lower()

    @pytest.mark.asyncio
    async def test_pass_rate_drop_is_regression(self, guard_pass_rate_drop):
        """Sharp pass rate drop indicates regression."""
        result = await guard_pass_rate_drop.check(
            test_name="test_example",
            test_file="tests/test_example.py",
            branch="main",
            git_commit="new_commit",
            project_id="proj_1",
            failure_signature="Error: new failure",
        )

        assert result.is_regression is True
        assert result.confidence >= 0.85
        assert "pass rate" in result.reason.lower() or "drop" in result.reason.lower()

    @pytest.mark.asyncio
    async def test_new_signature_leans_regression(self, guard_new_signature):
        """Brand new failure signature on stable test leans regression."""
        result = await guard_new_signature.check(
            test_name="test_example",
            test_file="tests/test_example.py",
            branch="main",
            git_commit="new_commit",
            project_id="proj_1",
            failure_signature="Error: completely new failure type",
        )

        assert result.is_regression is True
        assert result.confidence >= 0.60

    @pytest.mark.asyncio
    async def test_mixed_history_is_flaky(self, guard_mixed_history):
        """Mixed pass/fail history suggests flakiness."""
        result = await guard_mixed_history.check(
            test_name="test_example",
            test_file="tests/test_example.py",
            branch="main",
            git_commit="some_commit",
            project_id="proj_1",
            failure_signature="Error: test failed",
        )

        assert result.is_regression is False

    @pytest.mark.asyncio
    async def test_no_history_defaults_flaky(self, guard_no_history):
        """No history defaults to flaky."""
        result = await guard_no_history.check(
            test_name="test_brand_new",
            test_file="tests/test_new.py",
            branch="main",
            git_commit="abc123",
            project_id="proj_1",
        )

        assert result.is_regression is False
        assert result.confidence <= 0.60

    @pytest.mark.asyncio
    async def test_already_flaky_not_regression(self, guard_already_flaky):
        """Already-flaky test should not be flagged as regression."""
        result = await guard_already_flaky.check(
            test_name="test_flaky_one",
            test_file="tests/test_flaky.py",
            branch="main",
            git_commit="commit_0",
            project_id="proj_1",
            failure_signature="Error: flaky stuff",
        )

        # Same commit passed before, so flaky
        assert result.is_regression is False


class TestRegressionCheckResult:
    """Test the result dataclass."""

    def test_result_fields(self):
        result = RegressionCheckResult(
            is_regression=True,
            confidence=0.85,
            reason="Test reason",
            signals={"signal": "test"},
        )
        assert result.is_regression is True
        assert result.confidence == 0.85
        assert result.reason == "Test reason"
        assert result.signals["signal"] == "test"


class TestPassRate:
    """Test the pass rate helper."""

    def test_empty_results(self):
        assert RegressionGuard._pass_rate([]) == 0.0

    def test_all_passed(self):
        results = [{"status": "PASSED"} for _ in range(5)]
        assert RegressionGuard._pass_rate(results) == 1.0

    def test_all_failed(self):
        results = [{"status": "FAILED"} for _ in range(5)]
        assert RegressionGuard._pass_rate(results) == 0.0

    def test_mixed(self):
        results = [
            {"status": "PASSED"},
            {"status": "FAILED"},
            {"status": "PASSED"},
            {"status": "FAILED"},
        ]
        assert RegressionGuard._pass_rate(results) == 0.5
