"""
Test quality and profiling modules.
"""

import pytest
import pandas as pd
import numpy as np

from app.quality.profiler import DataProfiler
from app.quality.scanner import QualityScanner
from app.utils.schema_detection import SchemaDetector


class TestDataProfiler:
    """Tests for DataProfiler."""

    def test_profile_numeric_column(self):
        """Test profiling numeric column."""
        profiler = DataProfiler()
        df = pd.DataFrame({
            "age": [25, 30, 35, 40, 45, None],
        })
        
        profile = profiler.profile_dataset(df)
        
        assert len(profile.column_profiles) == 1
        col_profile = profile.column_profiles[0]
        
        assert col_profile.name == "age"
        assert col_profile.min_value == 25
        assert col_profile.max_value == 45
        assert col_profile.mean == pytest.approx(35.0)
        assert col_profile.null_count == 1

    def test_profile_categorical_column(self):
        """Test profiling categorical column."""
        profiler = DataProfiler()
        df = pd.DataFrame({
            "color": ["red", "blue", "red", "green", "red"],
        })
        
        profile = profiler.profile_dataset(df)
        col_profile = profile.column_profiles[0]
        
        assert col_profile.unique_count == 3
        assert col_profile.most_common is not None
        assert col_profile.most_common[0][0] == "red"  # Most common value

    def test_detect_outliers(self):
        """Test outlier detection."""
        profiler = DataProfiler()
        df = pd.DataFrame({
            "value": [1, 2, 3, 4, 5, 100],  # 100 is outlier
        })
        
        profile = profiler.profile_dataset(df)
        col_profile = profile.column_profiles[0]
        
        assert col_profile.outlier_count > 0

    def test_detect_anomalies(self):
        """Test anomaly detection."""
        profiler = DataProfiler()
        
        # High missing rate
        df = pd.DataFrame({
            "mostly_null": [1, None, None, None, None, None],
        })
        
        profile = profiler.profile_dataset(df)
        col_profile = profile.column_profiles[0]
        
        assert len(col_profile.anomalies) > 0
        assert any("High missing rate" in a for a in col_profile.anomalies)

    def test_correlation_calculation(self):
        """Test correlation calculation."""
        profiler = DataProfiler()
        df = pd.DataFrame({
            "x": [1, 2, 3, 4, 5],
            "y": [2, 4, 6, 8, 10],  # Perfect correlation with x
        })
        
        profile = profiler.profile_dataset(df)
        
        assert "correlations" in profile.model_dump()
        assert len(profile.correlations) > 0


class TestQualityScanner:
    """Tests for QualityScanner."""

    def test_scan_basic_metrics(self, sample_dataframe):
        """Test basic quality metrics."""
        scanner = QualityScanner()
        metrics = scanner.scan(sample_dataframe)
        
        assert metrics.total_rows == 6
        assert metrics.total_columns == 5
        assert metrics.duplicate_rows > 0

    def test_missing_values_detection(self):
        """Test missing values detection."""
        scanner = QualityScanner()
        df = pd.DataFrame({
            "col1": [1, 2, None, 4],
            "col2": ["a", "b", "c", None],
        })
        
        metrics = scanner.scan(df)
        
        assert metrics.missing_values["col1"] == 1
        assert metrics.missing_values["col2"] == 1
        assert metrics.missing_percentage["col1"] == 25.0
        assert metrics.missing_percentage["col2"] == 25.0

    def test_unique_values_count(self):
        """Test unique values counting."""
        scanner = QualityScanner()
        df = pd.DataFrame({
            "id": [1, 2, 3, 4, 5],  # All unique
            "type": ["A", "A", "B", "B", "A"],  # 2 unique
        })
        
        metrics = scanner.scan(df)
        
        assert metrics.unique_values["id"] == 5
        assert metrics.unique_values["type"] == 2

    def test_memory_usage(self, sample_dataframe):
        """Test memory usage calculation."""
        scanner = QualityScanner()
        metrics = scanner.scan(sample_dataframe)
        
        assert metrics.memory_usage_mb > 0


class TestSchemaDetector:
    """Tests for SchemaDetector."""

    def test_detect_int_column(self):
        """Test detecting integer column."""
        detector = SchemaDetector()
        df = pd.DataFrame({
            "age": [25, 30, 35],
        })
        
        schema = detector.detect_schema(df)
        col_schema = schema.columns[0]
        
        assert col_schema.name == "age"
        assert col_schema.dtype == "int64"
        assert not col_schema.nullable

    def test_detect_float_column(self):
        """Test detecting float column."""
        detector = SchemaDetector()
        df = pd.DataFrame({
            "price": [19.99, 29.99, 39.99],
        })
        
        schema = detector.detect_schema(df)
        col_schema = schema.columns[0]
        
        assert col_schema.dtype == "float64"

    def test_detect_string_column(self):
        """Test detecting string column."""
        detector = SchemaDetector()
        df = pd.DataFrame({
            "name": ["Alice", "Bob", "Charlie"],
        })
        
        schema = detector.detect_schema(df)
        col_schema = schema.columns[0]
        
        assert col_schema.dtype == "string"

    def test_detect_nullable(self):
        """Test detecting nullable columns."""
        detector = SchemaDetector()
        df = pd.DataFrame({
            "value": [1, 2, None],
        })
        
        schema = detector.detect_schema(df)
        col_schema = schema.columns[0]
        
        assert col_schema.nullable is True

    def test_detect_unique(self):
        """Test detecting unique columns."""
        detector = SchemaDetector()
        df = pd.DataFrame({
            "id": [1, 2, 3],  # Unique
            "type": ["A", "A", "B"],  # Not unique
        })
        
        schema = detector.detect_schema(df)
        
        id_schema = next(c for c in schema.columns if c.name == "id")
        type_schema = next(c for c in schema.columns if c.name == "type")
        
        assert id_schema.unique is True
        assert type_schema.unique is False

    def test_detect_min_max(self):
        """Test detecting min/max for numeric columns."""
        detector = SchemaDetector()
        df = pd.DataFrame({
            "score": [10, 20, 30, 40, 50],
        })
        
        schema = detector.detect_schema(df)
        col_schema = schema.columns[0]
        
        assert col_schema.min_value == 10.0
        assert col_schema.max_value == 50.0
