"""
Polars-Based Detector - High-performance issue detection.

18 detectors using Polars expressions for 10x speed.
"""

import polars as pl
from typing import List, Dict, Any
from dataclasses import dataclass
from pathlib import Path


@dataclass
class Issue:
    """Detected issue."""
    issue_type: str
    severity: str  # 'low', 'medium', 'high', 'critical'
    description: str
    affected_items: List[str]
    fix_available: bool
    metadata: Dict[str, Any]


class PolarsDetector:
    """High-performance detector using Polars."""
    
    def __init__(self):
        self.issues = []
    
    # Basic Detectors (1-8)
    
    def detect_missing_headers(self, df: pl.DataFrame) -> List[Issue]:
        """1. Detect missing or malformed headers."""
        issues = []
        
        # Check if columns are generic (Column1, Column2, etc.)
        generic_pattern = all(col.startswith('column') for col in df.columns)
        
        if generic_pattern:
            issues.append(Issue(
                issue_type='MISSING_HEADERS',
                severity='high',
                description='DataFrame has generic column names (column1, column2, ...)',
                affected_items=list(df.columns),
                fix_available=True,
                metadata={'column_count': len(df.columns)}
            ))
        
        return issues
    
    def detect_duplicates(self, df: pl.DataFrame) -> List[Issue]:
        """2. Detect duplicate rows."""
        dup_count = df.filter(df.is_duplicated()).height
        
        if dup_count > 0:
            return [Issue(
                issue_type='DUPLICATES',
                severity='medium',
                description=f'Found {dup_count} duplicate rows',
                affected_items=[],
                fix_available=True,
                metadata={'duplicate_count': dup_count}
            )]
        return []
    
    def detect_mixed_dates(self, df: pl.DataFrame) -> List[Issue]:
        """3. Detect mixed date formats."""
        issues = []
        
        for col in df.columns:
            if df[col].dtype == pl.Utf8:
                # Check if column might be dates
                sample = df[col].drop_nulls().limit(100)
                
                # Simple heuristic: contains slashes or dashes
                has_slashes = sample.str.contains('/').sum()
                has_dashes = sample.str.contains('-').sum()
                
                if has_slashes > 50 and has_dashes > 50:
                    issues.append(Issue(
                        issue_type='MIXED_DATES',
                        severity='high',
                        description=f'Column {col} has mixed date formats (/ and -)',
                        affected_items=[col],
                        fix_available=True,
                        metadata={'slash_count': has_slashes, 'dash_count': has_dashes}
                    ))
        
        return issues
    
    def detect_mixed_types(self, df: pl.DataFrame) -> List[Issue]:
        """4. Detect mixed data types in columns."""
        issues = []
        
        for col in df.columns:
            if df[col].dtype == pl.Utf8:
                # Try numeric conversion
                try:
                    numeric_count = df[col].cast(pl.Float64, strict=False).is_not_null().sum()
                    total_count = df[col].is_not_null().sum()
                    
                    if 0.3 < (numeric_count / total_count) < 0.7:
                        issues.append(Issue(
                            issue_type='MIXED_TYPES',
                            severity='high',
                            description=f'Column {col} has mixed numeric/string types',
                            affected_items=[col],
                            fix_available=True,
                            metadata={'numeric_ratio': numeric_count / total_count}
                        ))
                except:
                    pass
        
        return issues
    
    def detect_inconsistent_nulls(self, df: pl.DataFrame) -> List[Issue]:
        """5. Detect inconsistent null representations."""
        null_patterns = ['null', 'NULL', 'N/A', 'n/a', 'NA', 'None', '', 'NaN', 'nan']
        issues = []
        
        for col in df.columns:
            if df[col].dtype == pl.Utf8:
                found_patterns = []
                
                for pattern in null_patterns:
                    count = df.filter(pl.col(col) == pattern).height
                    if count > 0:
                        found_patterns.append(pattern)
                
                if len(found_patterns) > 1:
                    issues.append(Issue(
                        issue_type='INCONSISTENT_NULLS',
                        severity='medium',
                        description=f'Column {col} has {len(found_patterns)} different null patterns',
                        affected_items=[col],
                        fix_available=True,
                        metadata={'patterns': found_patterns}
                    ))
        
        return issues
    
    def detect_whitespace_anomalies(self, df: pl.DataFrame) -> List[Issue]:
        """6. Detect whitespace issues."""
        issues = []
        
        for col in df.columns:
            if df[col].dtype == pl.Utf8:
                # Check for leading/trailing whitespace
                with_whitespace = df.filter(
                    (pl.col(col).str.starts_with(' ')) |
                    (pl.col(col).str.ends_with(' '))
                ).height
                
                if with_whitespace > 0:
                    issues.append(Issue(
                        issue_type='WHITESPACE_ANOMALIES',
                        severity='low',
                        description=f'Column {col} has {with_whitespace} values with leading/trailing whitespace',
                        affected_items=[col],
                        fix_available=True,
                        metadata={'count': with_whitespace}
                    ))
        
        return issues
    
    def detect_encoding_anomalies(self, df: pl.DataFrame) -> List[Issue]:
        """7. Detect encoding issues."""
        issues = []
        
        for col in df.columns:
            if df[col].dtype == pl.Utf8:
                # Check for common encoding issues (�, mojibake)
                has_replacement_char = df[col].str.contains('�').sum()
                
                if has_replacement_char > 0:
                    issues.append(Issue(
                        issue_type='ENCODING_ANOMALIES',
                        severity='high',
                        description=f'Column {col} has {has_replacement_char} values with encoding issues',
                        affected_items=[col],
                        fix_available=True,
                        metadata={'count': has_replacement_char}
                    ))
        
        return issues
    
    def detect_empty_file(self, df: pl.DataFrame) -> List[Issue]:
        """8. Detect empty or malformed files."""
        if df.height == 0:
            return [Issue(
                issue_type='EMPTY_FILE',
                severity='critical',
                description='File is empty',
                affected_items=[],
                fix_available=False,
                metadata={'rows': 0}
            )]
        return []
    
    # Structural Detectors (9-18)
    
    def detect_schema_drift(self, dfs: Dict[str, pl.DataFrame]) -> List[Issue]:
        """9. Detect schema drift across files."""
        if len(dfs) < 2:
            return []
        
        issues = []
        all_columns = [set(df.columns) for df in dfs.values()]
        common = set.intersection(*all_columns)
        union = set.union(*all_columns)
        
        drift_count = len(union) - len(common)
        if drift_count > 0:
            issues.append(Issue(
                issue_type='SCHEMA_DRIFT',
                severity='high',
                description=f'{drift_count} columns differ across files',
                affected_items=list(union - common),
                fix_available=True,
                metadata={'common_count': len(common), 'total_count': len(union)}
            ))
        
        return issues
    
    def detect_type_drift(self, dfs: Dict[str, pl.DataFrame]) -> List[Issue]:
        """10. Detect type drift for same columns across files."""
        if len(dfs) < 2:
            return []
        
        issues = []
        common_cols = set.intersection(*[set(df.columns) for df in dfs.values()])
        
        for col in common_cols:
            types = {str(df[col].dtype) for df in dfs.values() if col in df.columns}
            
            if len(types) > 1:
                issues.append(Issue(
                    issue_type='TYPE_DRIFT',
                    severity='high',
                    description=f'Column {col} has {len(types)} different types across files',
                    affected_items=[col],
                    fix_available=True,
                    metadata={'types': list(types)}
                ))
        
        return issues
    
    def detect_column_explosion(self, df: pl.DataFrame, max_columns: int = 100) -> List[Issue]:
        """11. Detect unexpected number of columns."""
        if len(df.columns) > max_columns:
            return [Issue(
                issue_type='COLUMN_EXPLOSION',
                severity='medium',
                description=f'File has {len(df.columns)} columns (>{max_columns})',
                affected_items=[],
                fix_available=False,
                metadata={'column_count': len(df.columns)}
            )]
        return []
    
    def detect_category_explosion(self, df: pl.DataFrame, cardinality_threshold: float = 0.9) -> List[Issue]:
        """12. Detect columns with too many unique values."""
        issues = []
        
        for col in df.columns:
            if df[col].dtype == pl.Utf8:
                unique_ratio = df[col].n_unique() / df.height
                
                if unique_ratio > cardinality_threshold:
                    issues.append(Issue(
                        issue_type='CATEGORY_EXPLOSION',
                        severity='low',
                        description=f'Column {col} has {df[col].n_unique()} unique values',
                        affected_items=[col],
                        fix_available=False,
                        metadata={'unique_count': df[col].n_unique(), 'ratio': unique_ratio}
                    ))
        
        return issues
    
    def detect_all(self, df: pl.DataFrame, dfs: Dict[str, pl.DataFrame] = None) -> List[Issue]:
        """Run all detectors."""
        all_issues = []
        
        # Single-file detectors
        all_issues.extend(self.detect_missing_headers(df))
        all_issues.extend(self.detect_duplicates(df))
        all_issues.extend(self.detect_mixed_dates(df))
        all_issues.extend(self.detect_mixed_types(df))
        all_issues.extend(self.detect_inconsistent_nulls(df))
        all_issues.extend(self.detect_whitespace_anomalies(df))
        all_issues.extend(self.detect_encoding_anomalies(df))
        all_issues.extend(self.detect_empty_file(df))
        all_issues.extend(self.detect_column_explosion(df))
        all_issues.extend(self.detect_category_explosion(df))
        
        # Multi-file detectors
        if dfs and len(dfs) > 1:
            all_issues.extend(self.detect_schema_drift(dfs))
            all_issues.extend(self.detect_type_drift(dfs))
        
        return all_issues
