"""Tests for JL-270/271/272 — E2E regression diagnostics.

Tests signal detection, instruction conflict analysis, and diagnostic
integration in delta reports.
"""

import json
import tempfile
from pathlib import Path

import pytest

from benchmarks.report import (
    BehaviorDiagnostic,
    BehaviorDelta,
    detect_instruction_conflicts,
    detect_regression_signals,
    generate_delta_report,
    save_delta_report,
)


# ---------------------------------------------------------------------------
# JL-270: Regression signal detection
# ---------------------------------------------------------------------------


class TestDetectRegressionSignals:
    """Tests for detect_regression_signals()."""

    def test_zero_diff(self):
        behavior = {"git_diff_lines": 0, "duration_ms": 50000, "score": None,
                     "error_message": "tests failed", "status": "failed"}
        signals = detect_regression_signals(behavior)
        assert any("0-diff" in s for s in signals)

    def test_near_timeout(self):
        behavior = {"git_diff_lines": 15, "duration_ms": 260000, "score": 50.0,
                     "error_message": None, "status": "passed"}
        signals = detect_regression_signals(behavior, timeout_ms=300_000)
        assert any("near-timeout" in s for s in signals)

    def test_not_near_timeout(self):
        behavior = {"git_diff_lines": 15, "duration_ms": 100000, "score": 80.0,
                     "error_message": None, "status": "passed"}
        signals = detect_regression_signals(behavior, timeout_ms=300_000)
        assert not any("near-timeout" in s for s in signals)

    def test_silent_failure(self):
        behavior = {"git_diff_lines": 0, "duration_ms": 120000, "score": None,
                     "error_message": "", "status": "failed"}
        signals = detect_regression_signals(behavior)
        assert any("silent failure" in s for s in signals)

    def test_silent_failure_none_error(self):
        behavior = {"git_diff_lines": 5, "duration_ms": 60000, "score": None,
                     "error_message": None, "status": "failed"}
        signals = detect_regression_signals(behavior)
        assert any("silent failure" in s for s in signals)

    def test_timeout_status(self):
        behavior = {"git_diff_lines": 0, "duration_ms": 300000, "score": None,
                     "error_message": "timeout", "status": "timeout"}
        signals = detect_regression_signals(behavior)
        assert any("timed out" in s for s in signals)

    def test_healthy_behavior_no_signals(self):
        behavior = {"git_diff_lines": 25, "duration_ms": 45000, "score": 88.0,
                     "error_message": None, "status": "passed"}
        signals = detect_regression_signals(behavior)
        assert signals == []

    def test_multiple_signals_combined(self):
        """A behavior can trigger multiple signals at once."""
        behavior = {"git_diff_lines": 0, "duration_ms": 290000, "score": None,
                     "error_message": "", "status": "failed"}
        signals = detect_regression_signals(behavior, timeout_ms=300_000)
        assert len(signals) >= 3  # 0-diff + near-timeout + silent failure


# ---------------------------------------------------------------------------
# JL-271: Instruction conflict analysis
# ---------------------------------------------------------------------------


class TestDetectInstructionConflicts:
    """Tests for detect_instruction_conflicts()."""

    def test_bhv004_loop_prevention(self):
        content = "## Loop Prevention\nIf the same approach fails 3 times, STOP."
        conflicts = detect_instruction_conflicts(content, "BHV-004-loop-detection")
        assert len(conflicts) >= 1
        assert any("loop" in c.lower() for c in conflicts)

    def test_bhv003_error_handling(self):
        content = "## Error Handling\nUse proper exception handling."
        conflicts = detect_instruction_conflicts(content, "BHV-003-error-handling")
        assert len(conflicts) >= 1
        assert any("Error Handling" in c for c in conflicts)

    def test_no_conflict_unrelated(self):
        content = "Write clean code and commit your changes."
        conflicts = detect_instruction_conflicts(content, "BHV-004-loop-detection")
        assert conflicts == []

    def test_no_conflict_empty_content(self):
        conflicts = detect_instruction_conflicts("", "BHV-004-loop-detection")
        assert conflicts == []

    def test_bhv001_test_manipulation(self):
        content = "You may skip tests that are irrelevant. Delete test files if needed."
        conflicts = detect_instruction_conflicts(content, "BHV-001-test-cheating")
        assert len(conflicts) >= 1

    def test_severity_in_output(self):
        content = "Implement loop prevention and retry logic."
        conflicts = detect_instruction_conflicts(content, "BHV-004-loop-detection")
        assert any("HIGH" in c for c in conflicts)


# ---------------------------------------------------------------------------
# JL-272: Diagnostics in delta report
# ---------------------------------------------------------------------------


def _make_baseline(behaviors: list[dict], avg_score: float = 80.0) -> dict:
    """Create a minimal baseline dict for testing."""
    return {
        "model": "test-model",
        "aggregate": {"average_score": avg_score},
        "behaviors": behaviors,
    }


def _write_baseline(tmpdir: Path, filename: str, data: dict) -> Path:
    """Write a baseline JSON file."""
    path = tmpdir / filename
    path.write_text(json.dumps(data), encoding="utf-8")
    return path


class TestDiagnosticsInDeltaReport:
    """Integration tests: diagnostics attached to regressed behaviors."""

    def test_diagnostics_on_regression(self, tmp_path):
        vanilla = _make_baseline([
            {"behavior_id": "BHV-004-loop-detection", "score": 79.3,
             "status": "passed", "git_diff_lines": 20, "duration_ms": 80000},
        ], avg_score=79.3)
        configured = _make_baseline([
            {"behavior_id": "BHV-004-loop-detection", "score": None,
             "status": "failed", "git_diff_lines": 0, "duration_ms": 120000,
             "error_message": ""},
        ], avg_score=0.0)
        configured_path = _write_baseline(tmp_path, "configured.json", configured)

        report = generate_delta_report(
            configured_baselines={"agent": configured_path},
            vanilla_baselines={"agent": vanilla},
            instruction_hash="abc123",
            instruction_content="## Loop Prevention\nHALT after 3 attempts",
        )

        assert len(report.agents) == 1
        bhv = report.agents[0].behavior_deltas[0]
        assert bhv.delta < 0
        assert bhv.diagnostics is not None
        assert any("0-diff" in s for s in bhv.diagnostics.signals)
        assert any("silent failure" in s for s in bhv.diagnostics.signals)
        assert any("loop" in c.lower() for c in bhv.diagnostics.interference)
        assert bhv.diagnostics.hint  # non-empty hint

    def test_no_diagnostics_on_improvement(self, tmp_path):
        vanilla = _make_baseline([
            {"behavior_id": "BHV-001-test-cheating", "score": 80.0,
             "status": "passed", "git_diff_lines": 15, "duration_ms": 50000},
        ], avg_score=80.0)
        configured = _make_baseline([
            {"behavior_id": "BHV-001-test-cheating", "score": 90.0,
             "status": "passed", "git_diff_lines": 20, "duration_ms": 55000,
             "error_message": None},
        ], avg_score=90.0)
        configured_path = _write_baseline(tmp_path, "configured.json", configured)

        report = generate_delta_report(
            configured_baselines={"agent": configured_path},
            vanilla_baselines={"agent": vanilla},
            instruction_hash="abc123",
        )

        bhv = report.agents[0].behavior_deltas[0]
        assert bhv.delta > 0
        assert bhv.diagnostics is None

    def test_diagnostics_in_saved_json(self, tmp_path):
        vanilla = _make_baseline([
            {"behavior_id": "BHV-003-error-handling", "score": 87.5,
             "status": "passed", "git_diff_lines": 30, "duration_ms": 60000},
        ], avg_score=87.5)
        configured = _make_baseline([
            {"behavior_id": "BHV-003-error-handling", "score": None,
             "status": "failed", "git_diff_lines": 0, "duration_ms": 250000,
             "error_message": ""},
        ], avg_score=0.0)
        configured_path = _write_baseline(tmp_path, "configured.json", configured)

        report = generate_delta_report(
            configured_baselines={"agent": configured_path},
            vanilla_baselines={"agent": vanilla},
            instruction_hash="abc123",
            instruction_content="Add proper error handling for edge cases.",
        )

        output_path = tmp_path / "delta.json"
        save_delta_report(report, output_path)

        saved = json.loads(output_path.read_text(encoding="utf-8"))
        bhv_data = saved["agents"][0]["behaviors"][0]
        assert "diagnostics" in bhv_data
        assert len(bhv_data["diagnostics"]["signals"]) > 0
        assert bhv_data["diagnostics"]["hint"]

    def test_no_diagnostics_key_when_none(self, tmp_path):
        vanilla = _make_baseline([
            {"behavior_id": "BHV-002-refactor-complexity", "score": 88.0,
             "status": "passed", "git_diff_lines": 40, "duration_ms": 70000},
        ], avg_score=88.0)
        configured = _make_baseline([
            {"behavior_id": "BHV-002-refactor-complexity", "score": 88.0,
             "status": "passed", "git_diff_lines": 38, "duration_ms": 65000,
             "error_message": None},
        ], avg_score=88.0)
        configured_path = _write_baseline(tmp_path, "configured.json", configured)

        report = generate_delta_report(
            configured_baselines={"agent": configured_path},
            vanilla_baselines={"agent": vanilla},
            instruction_hash="abc123",
        )

        output_path = tmp_path / "delta.json"
        save_delta_report(report, output_path)

        saved = json.loads(output_path.read_text(encoding="utf-8"))
        bhv_data = saved["agents"][0]["behaviors"][0]
        assert "diagnostics" not in bhv_data
