"""High-value tests for benchmarks/efficiency/resource_monitor.py.

Covers ResourceMonitor init, start/stop lifecycle, stats computation,
export_data, context manager, and check_system_resources.
"""

from unittest.mock import MagicMock, patch

from local_deep_research.benchmarks.efficiency.resource_monitor import (
    ResourceMonitor,
    check_system_resources,
)


class TestResourceMonitorInit:
    """Test ResourceMonitor initialization."""

    def test_default_sampling_interval(self):
        monitor = ResourceMonitor()
        assert monitor.sampling_interval == 1.0

    def test_custom_sampling_interval(self):
        monitor = ResourceMonitor(sampling_interval=0.5)
        assert monitor.sampling_interval == 0.5

    def test_not_monitoring_initially(self):
        monitor = ResourceMonitor()
        assert monitor.monitoring is False
        assert monitor.monitor_thread is None

    def test_empty_data_initially(self):
        monitor = ResourceMonitor()
        assert monitor.process_data == []
        assert monitor.system_data == []

    def test_track_flags_stored(self):
        monitor = ResourceMonitor(track_process=False, track_system=True)
        assert monitor.track_process is False
        assert monitor.track_system is True


class TestResourceMonitorStats:
    """Test stats computation with pre-populated data."""

    def test_get_process_stats_empty_returns_empty_dict(self):
        """Empty process_data returns empty dict."""
        monitor = ResourceMonitor()
        assert monitor.get_process_stats() == {}

    def test_get_system_stats_empty_returns_empty_dict(self):
        """Empty system_data returns empty dict."""
        monitor = ResourceMonitor()
        assert monitor.get_system_stats() == {}

    def test_get_process_stats_with_data(self):
        """Process stats are computed correctly from pre-populated data."""
        monitor = ResourceMonitor()
        monitor.start_time = 100.0
        monitor.end_time = 110.0
        monitor.process_data = [
            {
                "timestamp": 101.0,
                "cpu_percent": 10.0,
                "memory_rss": 100 * 1024 * 1024,  # 100 MB
                "memory_vms": 200 * 1024 * 1024,
                "memory_shared": 0,
                "num_threads": 4,
                "open_files": 2,
                "status": "running",
            },
            {
                "timestamp": 102.0,
                "cpu_percent": 20.0,
                "memory_rss": 200 * 1024 * 1024,  # 200 MB
                "memory_vms": 300 * 1024 * 1024,
                "memory_shared": 0,
                "num_threads": 6,
                "open_files": 3,
                "status": "running",
            },
        ]
        stats = monitor.get_process_stats()
        assert stats["duration"] == 10.0
        assert stats["sample_count"] == 2
        assert stats["cpu_min"] == 10.0
        assert stats["cpu_max"] == 20.0
        assert stats["cpu_avg"] == 15.0
        assert stats["memory_min_mb"] == 100.0
        assert stats["memory_max_mb"] == 200.0
        assert stats["thread_max"] == 6

    def test_get_system_stats_with_data(self):
        """System stats are computed correctly."""
        monitor = ResourceMonitor()
        monitor.start_time = 100.0
        monitor.end_time = 110.0
        monitor.system_data = [
            {
                "timestamp": 101.0,
                "cpu_percent": 30.0,
                "memory_total": 16 * 1024**3,
                "memory_available": 8 * 1024**3,
                "memory_used": 8 * 1024**3,
                "memory_percent": 50.0,
                "disk_total": 500 * 1024**3,
                "disk_used": 250 * 1024**3,
                "disk_percent": 50.0,
            },
        ]
        stats = monitor.get_system_stats()
        assert stats["sample_count"] == 1
        assert stats["cpu_min"] == 30.0
        assert stats["memory_total_gb"] == 16.0
        assert stats["disk_total_gb"] == 500.0

    def test_get_combined_stats_merges_both(self):
        """Combined stats include prefixed process and system stats."""
        monitor = ResourceMonitor()
        monitor.start_time = 100.0
        monitor.end_time = 110.0
        monitor.process_data = [
            {
                "timestamp": 101.0,
                "cpu_percent": 10.0,
                "memory_rss": 100 * 1024 * 1024,
                "memory_vms": 200 * 1024 * 1024,
                "memory_shared": 0,
                "num_threads": 4,
                "open_files": 2,
                "status": "running",
            },
        ]
        monitor.system_data = [
            {
                "timestamp": 101.0,
                "cpu_percent": 30.0,
                "memory_total": 16 * 1024**3,
                "memory_available": 8 * 1024**3,
                "memory_used": 8 * 1024**3,
                "memory_percent": 50.0,
                "disk_total": 500 * 1024**3,
                "disk_used": 250 * 1024**3,
                "disk_percent": 50.0,
            },
        ]
        stats = monitor.get_combined_stats()
        assert "process_cpu_avg" in stats
        assert "system_cpu_avg" in stats
        assert stats["duration"] == 10.0


class TestResourceMonitorExport:
    """Test export_data."""

    def test_export_structure(self):
        """Export contains all expected keys."""
        monitor = ResourceMonitor(sampling_interval=2.0)
        monitor.start_time = 100.0
        monitor.end_time = 110.0
        data = monitor.export_data()
        assert data["start_time"] == 100.0
        assert data["end_time"] == 110.0
        assert data["sampling_interval"] == 2.0
        assert data["process_data"] == []
        assert data["system_data"] == []


class TestResourceMonitorLifecycle:
    """Test start/stop lifecycle."""

    def test_stop_when_not_monitoring_is_noop(self):
        """Stopping when not monitoring does nothing."""
        monitor = ResourceMonitor()
        monitor.stop()  # Should not raise
        assert monitor.monitoring is False

    @patch(
        "local_deep_research.benchmarks.efficiency.resource_monitor.PSUTIL_AVAILABLE",
        False,
    )
    def test_start_without_psutil_is_noop(self):
        """Start without psutil logs warning but doesn't crash."""
        monitor = ResourceMonitor()
        monitor.can_monitor = False
        monitor.start()
        assert monitor.monitoring is False


class TestCheckSystemResources:
    """Test the check_system_resources function."""

    @patch(
        "local_deep_research.benchmarks.efficiency.resource_monitor.PSUTIL_AVAILABLE",
        False,
    )
    def test_returns_error_without_psutil(self):
        """Returns error dict when psutil not available."""
        result = check_system_resources()
        assert result["available"] is False
        assert "error" in result

    @patch(
        "local_deep_research.benchmarks.efficiency.resource_monitor.PSUTIL_AVAILABLE",
        True,
    )
    @patch("local_deep_research.benchmarks.efficiency.resource_monitor.psutil")
    def test_returns_system_info_with_psutil(self, mock_psutil):
        """Returns system info when psutil is available."""
        mock_psutil.cpu_count.return_value = 8
        mock_psutil.cpu_percent.return_value = 25.0
        mock_psutil.virtual_memory.return_value = MagicMock(
            total=16 * 1024**3,
            available=8 * 1024**3,
            used=8 * 1024**3,
            percent=50.0,
        )
        mock_psutil.disk_usage.return_value = MagicMock(
            total=500 * 1024**3,
            free=250 * 1024**3,
            percent=50.0,
        )
        result = check_system_resources()
        assert result["available"] is True
        assert result["cpu_count"] == 8
        assert result["memory_percent"] == 50.0
