import logging
from unittest.mock import Mock, patch

import pytest


@pytest.fixture(autouse=True, scope="session")
def mock_keyring_session():
    """Mock keyring for entire test session to prevent any keychain access."""
    # Mock keyring at the module level
    mock_storage = {}

    def mock_get_password(service, username):
        key = f"{service}:{username}"
        return mock_storage.get(key)

    def mock_set_password(service, username, password):
        key = f"{service}:{username}"
        mock_storage[key] = password

    def mock_delete_password(service, username):
        key = f"{service}:{username}"
        mock_storage.pop(key, None)

    # Patch keyring module functions
    with (
        patch("keyring.get_password", mock_get_password),
        patch("keyring.set_password", mock_set_password),
        patch("keyring.delete_password", mock_delete_password),
    ):
        yield


@pytest.fixture(autouse=True)
def reset_credential_singleton():
    """Reset credential manager singleton before each test for isolation."""
    # Import here to ensure keyring is already mocked
    from adversary_mcp_server.credentials import reset_credential_manager

    reset_credential_manager()
    yield
    # Reset again after test to clean up
    reset_credential_manager()


@pytest.fixture(autouse=True)
def reset_config_manager_singleton():
    """Reset config manager singleton before each test for isolation."""
    # Import here to avoid circular imports
    from adversary_mcp_server.config_manager import reset_config_manager

    reset_config_manager()
    yield
    # Reset again after test to clean up
    reset_config_manager()


@pytest.fixture(autouse=True)
def mute_logs():
    logging.getLogger().setLevel(logging.WARNING)


@pytest.fixture(autouse=True)
def prevent_network_calls():
    """Global fixture to prevent accidental network calls in tests."""
    # Mock subprocess calls that could make network requests
    mock_subprocess = Mock()

    def smart_subprocess_run(args, **kwargs):
        """Smart subprocess.run mock that allows semgrep version checks."""
        # Allow semgrep version checks
        if isinstance(args, list) and len(args) >= 2 and args[1] == "--version":
            # Mock a successful semgrep version check
            mock_result = Mock()
            mock_result.returncode = 0
            mock_result.stdout = b"semgrep 1.0.0"
            mock_result.stderr = b""
            return mock_result

        # Block other subprocess calls
        raise RuntimeError(
            "Real subprocess.run() calls are not allowed in tests! "
            "Please mock this call to prevent accidental network requests."
        )

    mock_subprocess.run = smart_subprocess_run
    mock_subprocess.call = Mock(
        side_effect=RuntimeError(
            "Real subprocess.call() calls are not allowed in tests! "
            "Please mock this call to prevent accidental network requests."
        )
    )
    mock_subprocess.check_call = Mock(
        side_effect=RuntimeError(
            "Real subprocess.check_call() calls are not allowed in tests! "
            "Please mock this call to prevent accidental network requests."
        )
    )
    mock_subprocess.check_output = Mock(
        side_effect=RuntimeError(
            "Real subprocess.check_output() calls are not allowed in tests! "
            "Please mock this call to prevent accidental network requests."
        )
    )

    # Create a special mock for Popen that supports type subscripting
    class MockPopenMeta(type):
        def __getitem__(cls, item):
            # Support type subscripting like Popen[bytes]
            return cls

    class MockPopen(metaclass=MockPopenMeta):
        def __init__(self, *args, **kwargs):
            raise RuntimeError(
                "Real subprocess.Popen() calls are not allowed in tests! "
                "Please mock this call to prevent accidental network requests."
            )

        def __getitem__(self, item):
            # Support type subscripting like Popen[bytes]
            return self

    mock_subprocess.Popen = MockPopen

    with (
        patch("subprocess.run", mock_subprocess.run),
        patch("subprocess.call", mock_subprocess.call),
        patch("subprocess.check_call", mock_subprocess.check_call),
        patch("subprocess.check_output", mock_subprocess.check_output),
        patch("subprocess.Popen", mock_subprocess.Popen),
    ):
        yield
