"""
Performance benchmarks.
"""

import pytest
import pandas as pd
import numpy as np
import time
from pathlib import Path

from app.transformations.cleaning import drop_duplicates_pandas, fill_missing_pandas
from app.transformations.normalization import normalize_pandas
from app.quality.profiler import DataProfiler
from app.quality.scanner import QualityScanner


class TestPerformanceBenchmarks:
    """Performance benchmarks for key operations."""

    def test_large_dataset_profiling(self, benchmark):
        """Benchmark profiling large dataset."""
        # Create large dataset
        df = pd.DataFrame({
            "col1": np.random.randint(0, 1000, 10000),
            "col2": np.random.rand(10000),
            "col3": np.random.choice(["A", "B", "C"], 10000),
        })
        
        profiler = DataProfiler()
        
        result = benchmark(profiler.profile_dataset, df)
        assert result is not None

    def test_drop_duplicates_performance(self, benchmark):
        """Benchmark duplicate removal."""
        # Create dataset with many duplicates
        df = pd.DataFrame({
            "col1": np.random.choice([1, 2, 3], 10000),
            "col2": np.random.choice(["A", "B"], 10000),
        })
        
        result = benchmark(drop_duplicates_pandas, df)
        assert len(result) < len(df)

    def test_fill_missing_performance(self, benchmark):
        """Benchmark missing value filling."""
        # Create dataset with missing values
        df = pd.DataFrame({
            "value": [i if i % 5 != 0 else None for i in range(10000)],
        })
        
        result = benchmark(fill_missing_pandas, df, "value", "mean")
        assert result["value"].isna().sum() == 0

    def test_normalization_performance(self, benchmark):
        """Benchmark normalization."""
        df = pd.DataFrame({
            "value": np.random.rand(10000) * 1000,
        })
        
        result = benchmark(normalize_pandas, df, "value", "minmax")
        assert result["value"].min() >= 0
        assert result["value"].max() <= 1

    def test_quality_scan_performance(self, benchmark):
        """Benchmark quality scanning."""
        df = pd.DataFrame({
            f"col{i}": np.random.rand(5000) for i in range(20)
        })
        
        scanner = QualityScanner()
        result = benchmark(scanner.scan, df)
        assert result.total_rows == 5000


class TestMemoryEfficiency:
    """Test memory efficiency."""

    def test_memory_usage_small_data(self):
        """Test memory usage for small datasets."""
        import tracemalloc
        
        tracemalloc.start()
        
        df = pd.DataFrame({
            "col": range(1000),
        })
        
        from app.transformations.cleaning import drop_duplicates_pandas
        result = drop_duplicates_pandas(df)
        
        current, peak = tracemalloc.get_traced_memory()
        tracemalloc.stop()
        
        # Peak memory should be reasonable (< 10MB for small data)
        assert peak < 10 * 1024 * 1024

    def test_memory_usage_large_data(self):
        """Test memory usage for larger datasets."""
        import tracemalloc
        
        tracemalloc.start()
        
        # 100K rows
        df = pd.DataFrame({
            "col1": range(100000),
            "col2": np.random.rand(100000),
        })
        
        profiler = DataProfiler()
        result = profiler.profile_dataset(df)
        
        current, peak = tracemalloc.get_traced_memory()
        tracemalloc.stop()
        
        # Peak memory should be reasonable (< 100MB)
        assert peak < 100 * 1024 * 1024


class TestScalability:
    """Test scalability with varying data sizes."""

    @pytest.mark.parametrize("num_rows", [100, 1000, 10000])
    def test_profiling_scales_linearly(self, num_rows):
        """Test that profiling time scales linearly."""
        df = pd.DataFrame({
            "col1": range(num_rows),
            "col2": np.random.rand(num_rows),
        })
        
        profiler = DataProfiler()
        
        start = time.time()
        profiler.profile_dataset(df)
        elapsed = time.time() - start
        
        # Should complete in reasonable time
        # Rough estimate: < 1 second for 10K rows
        assert elapsed < (num_rows / 1000)

    @pytest.mark.parametrize("num_cols", [5, 10, 20])
    def test_wide_dataset_performance(self, num_cols):
        """Test performance with many columns."""
        df = pd.DataFrame({
            f"col{i}": np.random.rand(1000) for i in range(num_cols)
        })
        
        scanner = QualityScanner()
        
        start = time.time()
        metrics = scanner.scan(df)
        elapsed = time.time() - start
        
        # Should complete quickly even with many columns
        assert elapsed < 5.0
        assert metrics.total_columns == num_cols
