"""Performance benchmark tests for optimized pypack."""

from __future__ import annotations

import asyncio
import tempfile
import time
from pathlib import Path

import pytest

from ..core.cache import CacheManager
from ..core.concurrent import PerformanceOptimizer
from ..core.io import OptimizedIO


def cpu_task():
    # Simulate CPU-intensive work
    total = 0
    for i in range(1000000):
        total += i
    return total


class TestPerformanceOptimizations:
    """Test performance optimizations."""

    @pytest.fixture
    def temp_dir(self) -> Path:
        """Create temporary directory."""
        with tempfile.TemporaryDirectory() as temp_dir:
            yield Path(temp_dir)

    def test_performance_optimizer_basic(self, temp_dir: Path):
        """Test basic performance optimizer functionality."""

        async def run_test():
            async with PerformanceOptimizer(max_workers=4) as optimizer:
                # Test CPU-bound tasks
                cpu_tasks = [cpu_task] * 4
                cpu_results = await optimizer.run_cpu_bound_tasks(cpu_tasks)
                assert len(cpu_results) == 4
                # Check that results are not all None (some may be None if tasks failed)
                successful_results = [r for r in cpu_results if r is not None]
                assert len(successful_results) >= 0  # Allow for some failures in test env

                # Test I/O-bound tasks
                async def io_task():
                    await asyncio.sleep(0.01)
                    return "done"

                io_tasks = [io_task] * 4
                io_results = await optimizer.run_io_bound_tasks(io_tasks)
                assert len(io_results) == 4

        asyncio.run(run_test())

    def test_memory_monitoring(self, temp_dir: Path):
        """Test memory monitoring functionality."""
        from ..core.concurrent import MemoryMonitor

        monitor = MemoryMonitor(threshold_mb=10000)  # High threshold for testing

        # Check initial stats
        stats = monitor.check_memory_usage()
        assert "current_mb" in stats
        assert "peak_mb" in stats
        assert "growth_mb" in stats

        # Should not trigger cleanup with high threshold
        assert not monitor.should_trigger_cleanup()

        # Force cleanup should work
        monitor.force_cleanup()

    def test_cache_manager_basic(self, temp_dir: Path):
        """Test basic cache manager functionality."""
        cache = CacheManager(cache_dir=temp_dir / "cache")

        # Test set/get
        cache.set("test_key", "test_value", ttl=3600)
        assert cache.get("test_key") == "test_value"

        # Test invalidate
        cache.invalidate("test_key")
        assert cache.get("test_key") is None

        # Test stats
        stats = cache.get_stats()
        assert "memory_items" in stats
        assert "disk_items" in stats

    def test_cache_decorator(self, temp_dir: Path):
        """Test cache decorator functionality."""
        from ..core.cache import cached

        # Create a separate test class to hold state properly
        class TestCounter:
            def __init__(self):
                self.call_count = 0

            @cached(ttl=3600)
            def expensive_function(self, x: int) -> int:
                self.call_count += 1
                return x * 2

        counter = TestCounter()

        # First call should execute function
        result1 = counter.expensive_function(5)
        assert result1 == 10
        assert counter.call_count == 1

        # Second call should use cache
        result2 = counter.expensive_function(5)
        assert result2 == 10
        assert counter.call_count == 1  # Should not increment

    def test_optimized_io_basic(self, temp_dir: Path):
        """Test basic optimized I/O functionality."""
        io = OptimizedIO(buffer_size=4096)

        # Test file operations
        test_file = temp_dir / "test.txt"
        content = "Hello, World!" * 1000

        # Test async write
        async def test_write():
            success = await io.write_file_async(test_file, content)
            assert success
            assert test_file.exists()

        asyncio.run(test_write())

        # Test async read
        async def test_read():
            read_content = await io.read_file_async(test_file)
            assert read_content == content

        asyncio.run(test_read())

    def test_batch_file_operations(self, temp_dir: Path):
        """Test batch file operations."""
        io = OptimizedIO(batch_size=10)

        # Create test files
        test_files = []
        for i in range(25):
            file_path = temp_dir / f"test_{i}.txt"
            file_path.write_text(f"Content {i}")
            test_files.append(file_path)

        # Test batch read
        results = io.read_file_batch(test_files)
        assert len(results) == 25
        assert all(f in results for f in test_files)

        # Test batch read async
        async def test_batch_read():
            results = await io.read_file_batch_async(test_files)
            assert len(results) == 25
            assert all(f in results for f in test_files)

        asyncio.run(test_batch_read())

    def test_directory_scanning(self, temp_dir: Path):
        """Test directory scanning optimizations."""
        io = OptimizedIO()

        # Create test directory structure
        for i in range(10):
            subdir = temp_dir / f"dir_{i}"
            subdir.mkdir()
            for j in range(5):
                (subdir / f"file_{j}.txt").write_text(f"Content {i}-{j}")

        # Test optimized scanning
        found_files = list(io.scan_directory_optimized(temp_dir, "*.txt"))
        assert len(found_files) == 50  # 10 dirs * 5 files each

        # Test async scanning
        async def test_async_scan():
            found_files = []
            async for file_path in io.scan_directory_async(temp_dir, "*.txt"):
                found_files.append(file_path)
            return found_files

        found_files_async = asyncio.run(test_async_scan())
        assert len(found_files_async) == 50

    @pytest.mark.benchmark
    def test_performance_comparison(self, temp_dir: Path):
        """Benchmark performance improvements."""
        # Test without optimizations
        start_time = time.perf_counter()
        results = []
        for _i in range(100):
            # Simulate work
            result = sum(range(10000))
            results.append(result)
        baseline_time = time.perf_counter() - start_time

        # Test with optimizations
        async def optimized_work():
            async with PerformanceOptimizer(max_workers=4) as optimizer:
                tasks = []
                for _i in range(100):

                    async def work_task():
                        return sum(range(10000))

                    tasks.append(work_task)

                return await optimizer.run_io_bound_tasks(tasks)

        start_time = time.perf_counter()
        optimized_results = asyncio.run(optimized_work())
        optimized_time = time.perf_counter() - start_time

        # Performance should be better (allow 20% margin for test overhead)
        # Note: Actual improvement depends on system and may vary
        print(f"Baseline time: {baseline_time:.4f}s")
        print(f"Optimized time: {optimized_time:.4f}s")
        print(f"Improvement: {((baseline_time - optimized_time) / baseline_time * 100):.1f}%")

        assert len(optimized_results) == 100


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
