"""Tests for pytest plugin."""

import os
import subprocess
import sys

from llmdebug import get_latest_snapshot


def test_plugin_captures_on_failure(tmp_path):
    """Test that the pytest plugin captures snapshots on test failures."""
    # Create a test file
    test_file = tmp_path / "test_example.py"
    test_file.write_text(
        """
def test_failing():
    x = [1, 2, 3]
    assert False, "intentional failure"
"""
    )

    # Run pytest in subprocess
    result = subprocess.run(
        [sys.executable, "-m", "pytest", str(test_file), "-v"],
        cwd=tmp_path,
        capture_output=True,
        text=True,
    )

    # Test should fail
    assert result.returncode != 0

    # Check that snapshot was created
    llmdebug_dir = tmp_path / ".llmdebug"
    assert llmdebug_dir.exists(), f"No .llmdebug dir. stderr: {result.stderr}"

    snapshots = list(llmdebug_dir.glob("*.json"))
    assert len(snapshots) >= 1  # At least the snapshot (and maybe latest.json)

    # Check that repro command is present (use get_latest_snapshot to auto-expand keys)
    snapshot = get_latest_snapshot(str(llmdebug_dir))
    assert snapshot is not None, "Failed to read snapshot"
    repro = snapshot.get("pytest", {}).get("repro")
    assert isinstance(repro, list)
    assert "-m" in repro
    assert "pytest" in repro
    assert "test_example.py::test_failing" in repro


def test_no_snapshot_marker(tmp_path):
    """Test that no_snapshot marker prevents capture."""
    test_file = tmp_path / "test_no_capture.py"
    test_file.write_text(
        """
import pytest

@pytest.mark.no_snapshot
def test_failing_no_capture():
    assert False, "should not capture"
"""
    )

    result = subprocess.run(
        [sys.executable, "-m", "pytest", str(test_file), "-v"],
        cwd=tmp_path,
        capture_output=True,
        text=True,
    )

    # Test should fail
    assert result.returncode != 0

    # Check that no snapshot was created for this specific test
    llmdebug_dir = tmp_path / ".llmdebug"
    if llmdebug_dir.exists():
        snapshots = list(llmdebug_dir.glob("*test_failing_no_capture*.json"))
        assert len(snapshots) == 0, "Snapshot should not be created for no_snapshot marker"


def test_passing_test_no_snapshot(tmp_path):
    """Test that passing tests don't create snapshots."""
    test_file = tmp_path / "test_passing.py"
    test_file.write_text(
        """
def test_passing():
    assert True
"""
    )

    result = subprocess.run(
        [sys.executable, "-m", "pytest", str(test_file), "-v"],
        cwd=tmp_path,
        capture_output=True,
        text=True,
    )

    # Test should pass
    assert result.returncode == 0

    # Check that no snapshot was created
    llmdebug_dir = tmp_path / ".llmdebug"
    assert not llmdebug_dir.exists() or len(list(llmdebug_dir.glob("*.json"))) == 0


def _run_pytest_and_get_snapshot(tmp_path, test_code, env_overrides=None):
    """Helper: write test file, run pytest subprocess, return snapshot."""
    test_file = tmp_path / "test_subject.py"
    test_file.write_text(test_code)

    env = os.environ.copy()
    if env_overrides:
        env.update(env_overrides)

    result = subprocess.run(
        [sys.executable, "-m", "pytest", str(test_file), "-v"],
        cwd=tmp_path,
        capture_output=True,
        text=True,
        env=env,
    )
    assert result.returncode != 0, f"Test was expected to fail.\nstdout:\n{result.stdout}"

    llmdebug_dir = tmp_path / ".llmdebug"
    assert llmdebug_dir.exists(), f"No .llmdebug dir. stderr: {result.stderr}"

    snapshot = get_latest_snapshot(str(llmdebug_dir))
    assert snapshot is not None, "Failed to read snapshot"
    return snapshot


def test_fixture_values_in_snapshot(tmp_path):
    """Test that custom fixture values appear in the snapshot and internal fixtures are excluded."""
    snapshot = _run_pytest_and_get_snapshot(tmp_path, """
import pytest

@pytest.fixture
def data():
    return [1, 2, 3]

@pytest.fixture
def label():
    return "hello"

def test_with_fixtures(data, label, request):
    assert False, "intentional failure"
""")
    pytest_ctx = snapshot["pytest"]
    fixtures = pytest_ctx.get("fixtures")
    assert fixtures is not None, f"Expected 'fixtures' key. pytest keys: {list(pytest_ctx.keys())}"
    assert "data" in fixtures
    assert "label" in fixtures
    # Internal fixture 'request' should be excluded
    assert "request" not in fixtures


def test_markers_in_snapshot(tmp_path):
    """Test that custom markers and timeout marker appear in the snapshot."""
    snapshot = _run_pytest_and_get_snapshot(tmp_path, """
import pytest

@pytest.mark.slow
@pytest.mark.timeout(30)
def test_marked():
    assert False, "intentional failure"
""")
    pytest_ctx = snapshot["pytest"]
    markers = pytest_ctx.get("markers")
    assert markers is not None, f"Expected 'markers' key. pytest keys: {list(pytest_ctx.keys())}"
    assert "slow" in markers
    # timeout marker is captured separately in timeout_config, but also appears in markers
    assert "timeout" in markers

    # Timeout config should also be set
    toc = pytest_ctx.get("timeout_config")
    assert toc is not None, f"Expected 'timeout_config' key. pytest keys: {list(pytest_ctx.keys())}"
    assert toc["seconds"] == 30


def test_param_indices_in_snapshot(tmp_path):
    """Test that parametrize indices are captured."""
    snapshot = _run_pytest_and_get_snapshot(tmp_path, """
import pytest

@pytest.mark.parametrize("x", [10, 20, 30])
def test_param(x):
    if x == 20:
        assert False, "fails on 20"
""")
    pytest_ctx = snapshot["pytest"]
    param_indices = pytest_ctx.get("param_indices")
    assert param_indices is not None, (
        f"Expected 'param_indices'. pytest keys: {list(pytest_ctx.keys())}"
    )
    assert "x" in param_indices
    # x=20 is index 1 in the parametrize list [10, 20, 30]
    assert param_indices["x"] == 1


def test_internal_fixtures_excluded(tmp_path):
    """Test that internal/noisy fixtures like capsys and tmp_path don't appear."""
    snapshot = _run_pytest_and_get_snapshot(tmp_path, """
def test_with_capsys(capsys, tmp_path):
    print("hello")
    assert False, "intentional failure"
""")
    pytest_ctx = snapshot["pytest"]
    fixtures = pytest_ctx.get("fixtures", {})
    assert "capsys" not in fixtures
    # tmp_path is NOT in the exclude list and should appear
    assert "tmp_path" in fixtures


def test_fixtures_disabled_by_env(tmp_path):
    """Test that LLMDEBUG_INCLUDE_FIXTURES=false suppresses fixtures."""
    snapshot = _run_pytest_and_get_snapshot(tmp_path, """
import pytest

@pytest.fixture
def data():
    return [1, 2, 3]

def test_with_fixture(data):
    assert False, "intentional failure"
""", env_overrides={"LLMDEBUG_INCLUDE_FIXTURES": "false"})
    pytest_ctx = snapshot["pytest"]
    assert "fixtures" not in pytest_ctx


def test_redaction_profile_from_env(tmp_path):
    """Test that redaction profile env applies to pytest snapshot capture."""
    snapshot = _run_pytest_and_get_snapshot(
        tmp_path,
        """
def test_secret_failure():
    assert False, "password=secret123"
""",
        env_overrides={"LLMDEBUG_REDACTION_PROFILE": "prod"},
    )
    message = snapshot["exception"]["message"]
    assert "[REDACTED]" in message
    assert "password=secret123" not in message
