"""
Tests for benchmarks/efficiency/resource_monitor.py

Tests cover:
- ResourceMonitor initialization
- Start/stop monitoring
- Resource data collection
- Summary generation
- Context manager usage
"""

import time


class TestResourceMonitorInit:
    """Tests for ResourceMonitor initialization."""

    def test_init_default_params(self):
        """Test initialization with default parameters."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()

        assert monitor.sampling_interval == 1.0
        assert monitor.track_process is True
        assert monitor.track_system is True
        assert monitor.monitoring is False
        assert monitor.process_data == []
        assert monitor.system_data == []

    def test_init_custom_params(self):
        """Test initialization with custom parameters."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor(
            sampling_interval=0.5,
            track_process=False,
            track_system=True,
        )

        assert monitor.sampling_interval == 0.5
        assert monitor.track_process is False
        assert monitor.track_system is True


class TestResourceMonitorStartStop:
    """Tests for start/stop monitoring."""

    def test_start_without_psutil(self):
        """Test starting without psutil available."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.can_monitor = False

        # Should not raise, just log warning
        monitor.start()

        assert monitor.monitoring is False

    def test_start_already_monitoring(self):
        """Test starting when already monitoring."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.monitoring = True

        # Should not start another thread
        monitor.start()

        # Should remain monitoring
        assert monitor.monitoring is True

    def test_stop_not_monitoring(self):
        """Test stopping when not monitoring."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()

        # Should not raise
        monitor.stop()

        assert monitor.monitoring is False

    def test_stop_clears_monitoring_flag(self):
        """Test that stop clears the monitoring flag."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.monitoring = True
        monitor.monitor_thread = None

        monitor.stop()

        assert monitor.monitoring is False
        assert monitor.end_time is not None


class TestResourceMonitorStats:
    """Tests for resource stats generation."""

    def test_get_process_stats_no_data(self):
        """Test get_process_stats with no data."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()

        stats = monitor.get_process_stats()

        assert stats == {}

    def test_get_system_stats_no_data(self):
        """Test get_system_stats with no data."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()

        stats = monitor.get_system_stats()

        assert stats == {}

    def test_get_process_stats_with_data(self):
        """Test get_process_stats with process data."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "memory_vms": 200_000_000,
                "num_threads": 4,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 60.0,
                "memory_rss": 110_000_000,
                "memory_vms": 210_000_000,
                "num_threads": 5,
            },
        ]

        stats = monitor.get_process_stats()

        assert stats["sample_count"] == 2
        assert stats["cpu_avg"] == 55.0
        assert stats["cpu_max"] == 60.0
        assert stats["cpu_min"] == 50.0
        assert stats["thread_max"] == 5

    def test_get_system_stats_with_data(self):
        """Test get_system_stats with system data."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.system_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 30.0,
                "memory_percent": 50.0,
                "disk_percent": 60.0,
                "memory_total": 16_000_000_000,
                "disk_total": 500_000_000_000,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 40.0,
                "memory_percent": 55.0,
                "disk_percent": 61.0,
                "memory_total": 16_000_000_000,
                "disk_total": 500_000_000_000,
            },
        ]

        stats = monitor.get_system_stats()

        assert stats["sample_count"] == 2
        assert stats["cpu_avg"] == 35.0
        assert stats["cpu_max"] == 40.0
        assert stats["memory_avg_percent"] == 52.5

    def test_get_combined_stats(self):
        """Test get_combined_stats returns combined data."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
        ]
        monitor.system_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 30.0,
                "memory_percent": 50.0,
                "disk_percent": 60.0,
                "memory_total": 16_000_000_000,
                "disk_total": 500_000_000_000,
            },
        ]

        stats = monitor.get_combined_stats()

        assert "duration" in stats
        assert "process_sample_count" in stats
        assert "system_sample_count" in stats

    def test_print_summary(self, capsys):
        """Test print_summary outputs correctly."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
        ]

        monitor.print_summary()

        captured = capsys.readouterr()
        assert "RESOURCE USAGE SUMMARY" in captured.out

    def test_export_data(self):
        """Test export_data returns all collected data."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.process_data = [{"test": "data"}]
        monitor.system_data = [{"system": "data"}]

        data = monitor.export_data()

        assert data["start_time"] == monitor.start_time
        assert data["end_time"] == monitor.end_time
        assert data["sampling_interval"] == monitor.sampling_interval
        assert data["process_data"] == monitor.process_data
        assert data["system_data"] == monitor.system_data


class TestResourceMonitorContextManager:
    """Tests for context manager functionality."""

    def test_context_manager(self):
        """Test using ResourceMonitor as context manager."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.can_monitor = False  # Disable actual monitoring

        with monitor.monitor():
            pass

        # Should have stopped monitoring
        assert monitor.monitoring is False


class TestResourceMonitorThreadSafety:
    """Tests for thread safety."""

    def test_multiple_start_calls(self):
        """Test that multiple start calls don't create multiple threads."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.can_monitor = False

        # Should be safe to call multiple times
        monitor.start()
        monitor.start()
        monitor.start()

        # Should not be monitoring (psutil not available)
        assert monitor.monitoring is False


class TestPsutilAvailability:
    """Tests for psutil availability handling."""

    def test_psutil_available_flag(self):
        """Test that PSUTIL_AVAILABLE flag is set correctly."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            PSUTIL_AVAILABLE,
        )

        # Should be a boolean
        assert isinstance(PSUTIL_AVAILABLE, bool)

    def test_monitor_with_psutil_unavailable(self):
        """Test monitoring when psutil is not available."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()

        # Simulate psutil not being available
        original_can_monitor = monitor.can_monitor
        monitor.can_monitor = False

        monitor.start()

        assert monitor.monitoring is False

        # Restore
        monitor.can_monitor = original_can_monitor


class TestResourceMonitorDataCollection:
    """Tests for data collection methods."""

    def test_process_data_structure(self):
        """Test that process data has expected structure."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 25.0,
                "memory_rss": 50_000_000,
                "memory_vms": 100_000_000,
                "memory_shared": 10_000_000,
                "num_threads": 4,
                "open_files": 10,
                "status": "running",
            }
        ]

        # Check structure
        data = monitor.process_data[0]
        assert "timestamp" in data
        assert "cpu_percent" in data
        assert "memory_rss" in data

    def test_system_data_structure(self):
        """Test that system data has expected structure."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.system_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 30.0,
                "cpu_per_core": [25.0, 35.0],
                "memory_total": 16_000_000_000,
                "memory_used": 8_000_000_000,
                "memory_percent": 50.0,
            }
        ]

        data = monitor.system_data[0]
        assert "timestamp" in data
        assert "cpu_percent" in data


class TestResourceMonitorDuration:
    """Tests for duration tracking."""

    def test_duration_calculation(self):
        """Test that duration is calculated correctly."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        start = time.time() - 5.0
        end = time.time()
        monitor.start_time = start
        monitor.end_time = end
        monitor.process_data = [
            {
                "timestamp": start,
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
        ]

        stats = monitor.get_process_stats()

        assert 4.9 < stats["duration"] < 5.1  # Allow for small variance


class TestResourceMonitorSamplingInterval:
    """Tests for sampling interval configuration."""

    def test_default_sampling_interval(self):
        """Test default sampling interval is 1.0 seconds."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()

        assert monitor.sampling_interval == 1.0

    def test_custom_sampling_interval(self):
        """Test custom sampling interval is stored correctly."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor(sampling_interval=0.25)

        assert monitor.sampling_interval == 0.25

    def test_very_small_sampling_interval(self):
        """Test very small sampling interval is accepted."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor(sampling_interval=0.01)

        assert monitor.sampling_interval == 0.01


class TestResourceMonitorTrackingOptions:
    """Tests for process/system tracking options."""

    def test_track_process_only(self):
        """Test tracking only process resources."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor(track_process=True, track_system=False)

        assert monitor.track_process is True
        assert monitor.track_system is False

    def test_track_system_only(self):
        """Test tracking only system resources."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor(track_process=False, track_system=True)

        assert monitor.track_process is False
        assert monitor.track_system is True

    def test_track_both(self):
        """Test tracking both process and system resources."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor(track_process=True, track_system=True)

        assert monitor.track_process is True
        assert monitor.track_system is True

    def test_track_neither(self):
        """Test tracking neither process nor system resources."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor(track_process=False, track_system=False)

        assert monitor.track_process is False
        assert monitor.track_system is False


class TestResourceMonitorMemoryCalculations:
    """Tests for memory calculation logic."""

    def test_memory_rss_conversion_to_mb(self):
        """Test that memory RSS is correctly converted to MB."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        # 100 MB in bytes
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 104_857_600,  # 100 MB in bytes
                "num_threads": 4,
            },
        ]

        stats = monitor.get_process_stats()

        # Should be converted to MB
        assert 99 < stats["memory_max_mb"] < 101

    def test_memory_stats_min_max_avg(self):
        """Test memory min/max/avg calculations."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 50_000_000,  # ~47.68 MB
                "num_threads": 4,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,  # ~95.37 MB
                "num_threads": 4,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 150_000_000,  # ~143.05 MB
                "num_threads": 4,
            },
        ]

        stats = monitor.get_process_stats()

        assert (
            stats["memory_min_mb"]
            < stats["memory_avg_mb"]
            < stats["memory_max_mb"]
        )


class TestResourceMonitorCPUCalculations:
    """Tests for CPU calculation logic."""

    def test_cpu_stats_with_varying_values(self):
        """Test CPU stats with varying values."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 10.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 90.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
        ]

        stats = monitor.get_process_stats()

        assert stats["cpu_min"] == 10.0
        assert stats["cpu_max"] == 90.0
        assert stats["cpu_avg"] == 50.0


class TestResourceMonitorSystemStats:
    """Tests for system stats calculations."""

    def test_system_disk_stats(self):
        """Test system disk stats calculation."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.system_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 30.0,
                "memory_percent": 50.0,
                "disk_percent": 40.0,
                "memory_total": 16_000_000_000,
                "disk_total": 500_000_000_000,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 40.0,
                "memory_percent": 55.0,
                "disk_percent": 60.0,
                "memory_total": 16_000_000_000,
                "disk_total": 500_000_000_000,
            },
        ]

        stats = monitor.get_system_stats()

        assert stats["disk_min_percent"] == 40.0
        assert stats["disk_max_percent"] == 60.0
        assert stats["disk_avg_percent"] == 50.0

    def test_system_memory_total_conversion(self):
        """Test that system memory total is converted to GB."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        # 16 GB in bytes
        monitor.system_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 30.0,
                "memory_percent": 50.0,
                "disk_percent": 40.0,
                "memory_total": 17_179_869_184,  # 16 GB
                "disk_total": 500_000_000_000,
            },
        ]

        stats = monitor.get_system_stats()

        assert 15.9 < stats["memory_total_gb"] < 16.1


class TestResourceMonitorCombinedStats:
    """Additional tests for combined stats."""

    def test_combined_stats_process_memory_percent(self):
        """Test that combined stats calculates process memory percent."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        # Process using 1GB of 16GB system memory
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 1_073_741_824,  # 1 GB
                "num_threads": 4,
            },
        ]
        monitor.system_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 30.0,
                "memory_percent": 50.0,
                "disk_percent": 40.0,
                "memory_total": 17_179_869_184,  # 16 GB
                "disk_total": 500_000_000_000,
            },
        ]

        stats = monitor.get_combined_stats()

        # 1 GB / 16 GB = 6.25%
        assert "process_memory_percent" in stats
        assert 6.0 < stats["process_memory_percent"] < 6.5

    def test_combined_stats_includes_duration(self):
        """Test that combined stats includes duration."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 5.0
        monitor.end_time = time.time()

        stats = monitor.get_combined_stats()

        assert "duration" in stats
        assert 4.9 < stats["duration"] < 5.1


class TestResourceMonitorExport:
    """Tests for data export functionality."""

    def test_export_data_includes_timestamps(self):
        """Test that exported data includes timestamps."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()

        data = monitor.export_data()

        assert "start_time" in data
        assert "end_time" in data
        assert data["start_time"] is not None
        assert data["end_time"] is not None

    def test_export_data_includes_sampling_interval(self):
        """Test that exported data includes sampling interval."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor(sampling_interval=0.5)

        data = monitor.export_data()

        assert data["sampling_interval"] == 0.5

    def test_export_data_includes_empty_lists_when_no_data(self):
        """Test that export returns empty lists when no data collected."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()

        data = monitor.export_data()

        assert data["process_data"] == []
        assert data["system_data"] == []


class TestResourceMonitorEdgeCases:
    """Edge case tests for ResourceMonitor."""

    def test_stats_with_single_sample(self):
        """Test stats calculation with single sample."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 1
        monitor.end_time = time.time()
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
        ]

        stats = monitor.get_process_stats()

        # With single sample, min == max == avg
        assert stats["cpu_min"] == stats["cpu_max"] == stats["cpu_avg"]

    def test_duration_none_when_end_time_not_set(self):
        """Test that duration is None when end_time not set."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time()
        monitor.end_time = None
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
        ]

        stats = monitor.get_process_stats()

        assert stats["duration"] is None

    def test_thread_max_tracking(self):
        """Test that max thread count is tracked correctly."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.start_time = time.time() - 10
        monitor.end_time = time.time()
        monitor.process_data = [
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 4,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 8,
            },
            {
                "timestamp": time.time(),
                "cpu_percent": 50.0,
                "memory_rss": 100_000_000,
                "num_threads": 6,
            },
        ]

        stats = monitor.get_process_stats()

        assert stats["thread_max"] == 8

    def test_print_summary_with_empty_data(self, capsys):
        """Test print_summary handles empty data gracefully."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()

        # Should not raise
        monitor.print_summary()

        captured = capsys.readouterr()
        assert "RESOURCE USAGE SUMMARY" in captured.out


class TestResourceMonitorCanMonitorFlag:
    """Tests for can_monitor flag behavior."""

    def test_can_monitor_matches_psutil_available(self):
        """Test that can_monitor matches PSUTIL_AVAILABLE."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
            PSUTIL_AVAILABLE,
        )

        monitor = ResourceMonitor()

        assert monitor.can_monitor == PSUTIL_AVAILABLE

    def test_start_does_nothing_when_cannot_monitor(self):
        """Test that start does nothing when can_monitor is False."""
        from local_deep_research.benchmarks.efficiency.resource_monitor import (
            ResourceMonitor,
        )

        monitor = ResourceMonitor()
        monitor.can_monitor = False

        monitor.start()

        assert monitor.monitoring is False
        assert monitor.start_time is None
