"""
DataShaper Python API - Programmatic access for MLOps pipelines.

Usage:
    from datashaper import clean, merge, detect, report, diff
    
    # Clean data
    result = clean("data/", workers=8)
    
    # Merge with reconciliation
    merged = merge(["file1.csv", "file2.csv"], reconcile=True)
    
    # Detect issues only
    issues = detect("file.csv")
"""

import sys
from pathlib import Path
from typing import List, Dict, Union, Optional
import polars as pl

# Add backend to path
sys.path.insert(0, str(Path(__file__).parent / "backend"))

from app.engines.polars_detector import PolarsDetector, Issue
from app.engines.polars_fixer import PolarsFixer
from app.engines.polars_schema_reconciler import PolarsSchemaReconciler
from app.schema.advanced_schema_engine import AdvancedSchemaEngine, DataSchema
from app.reporting.enterprise_report import EnterpriseReportGenerator


class CleanResult:
    """Result from clean operation."""
    
    def __init__(
        self,
        files_processed: int,
        issues_found: int,
        fixes_applied: int,
        quality_score: float,
        processing_time: float,
        issues: List[Issue],
        cleaned_dfs: Dict[str, pl.DataFrame]
    ):
        self.files_processed = files_processed
        self.issues_found = issues_found
        self.fixes_applied = fixes_applied
        self.quality_score = quality_score
        self.processing_time = processing_time
        self.issues = issues
        self.cleaned_dfs = cleaned_dfs
    
    def get_cleaned_data(self, filename: Optional[str] = None) -> Union[pl.DataFrame, Dict[str, pl.DataFrame]]:
        """Get cleaned DataFrame(s)."""
        if filename:
            return self.cleaned_dfs.get(filename)
        return self.cleaned_dfs
    
    def save_all(self, output_folder: Union[str, Path]):
        """Save all cleaned files."""
        output_path = Path(output_folder)
        output_path.mkdir(parents=True, exist_ok=True)
        
        for filename, df in self.cleaned_dfs.items():
            df.write_csv(output_path / filename)


def clean(
    source: Union[str, Path, List[str]],
    workers: int = 4,
    output: Optional[Union[str, Path]] = None,
    export_schema: bool = False,
    flag_only: bool = False,
    engine: str = "polars"
) -> CleanResult:
    """
    Clean CSV files.
    
    Args:
        source: Folder path or list of files
        workers: Number of parallel workers
        output: Output folder for cleaned files
        export_schema: Export schema JSON
        flag_only: Only detect issues, don't fix
        engine: 'polars' (fast) or 'pandas' (compatible)
    
    Returns:
        CleanResult object
    
    Example:
        >>> from datashaper import clean
        >>> result = clean("data/", workers=8, export_schema=True)
        >>> print(f"Quality: {result.quality_score}%")
        >>> result.save_all("cleaned_data/")
    """
    import time
    start_time = time.time()
    
    # Get file list
    if isinstance(source, (str, Path)):
        source_path = Path(source)
        if source_path.is_dir():
            files = list(source_path.glob("*.csv"))
        else:
            files = [source_path]
    else:
        files = [Path(f) for f in source]
    
    detector = PolarsDetector()
    fixer = PolarsFixer()
    
    all_issues = []
    cleaned_dfs = {}
    
    for file_path in files:
        # Load
        df = pl.read_csv(file_path)
        
        # Detect
        issues = detector.detect_all(df)
        all_issues.extend(issues)
        
        # Fix
        if not flag_only:
            df = fixer.apply_all_safe_fixes(df)
        
        cleaned_dfs[file_path.name] = df
    
    processing_time = time.time() - start_time
    quality_score = max(0, 100 - len(all_issues) * 2)
    
    result = CleanResult(
        files_processed=len(files),
        issues_found=len(all_issues),
        fixes_applied=len(all_issues) if not flag_only else 0,
        quality_score=quality_score,
        processing_time=processing_time,
        issues=all_issues,
        cleaned_dfs=cleaned_dfs
    )
    
    # Save if output specified
    if output:
        result.save_all(output)
    
    return result


def merge(
    files: List[Union[str, Path]],
    reconcile: bool = True,
    output: Optional[Union[str, Path]] = None,
    drift_threshold: float = 0.85
) -> pl.DataFrame:
    """
    Merge CSV files with optional schema reconciliation.
    
    Args:
        files: List of CSV file paths
        reconcile: Use fuzzy schema reconciliation
        output: Output file path
        drift_threshold: Fuzzy matching threshold (0.85 = 85%)
    
    Returns:
        Merged Polars DataFrame
    
    Example:
        >>> from datashaper import merge
        >>> df = merge(["q1.csv", "q2.csv", "q3.csv"], reconcile=True)
        >>> print(f"Total rows: {df.height}")
    """
    # Load all files
    dfs = {Path(f).name: pl.read_csv(f) for f in files}
    
    if reconcile and len(dfs) > 1:
        # Reconcile schemas
        reconciler = PolarsSchemaReconciler(fuzzy_threshold=drift_threshold)
        drift, unified = reconciler.analyze_schemas(dfs)
        
        # Align
        aligned_dfs = reconciler.reconcile_dataframes(dfs, unified)
        merged = pl.concat(list(aligned_dfs.values()), how='vertical')
    else:
        # Simple concat
        merged = pl.concat(list(dfs.values()), how='vertical')
    
    # Save if output specified
    if output:
        merged.write_csv(output)
    
    return merged


def detect(file: Union[str, Path]) -> List[Issue]:
    """
    Detect issues in a CSV file.
    
    Args:
        file: CSV file path
    
    Returns:
        List of detected issues
    
    Example:
        >>> from datashaper import detect
        >>> issues = detect("messy_data.csv")
        >>> for issue in issues:
        ...     print(f"{issue.severity}: {issue.description}")
    """
    df = pl.read_csv(file)
    detector = PolarsDetector()
    return detector.detect_all(df)


def report(
    result: CleanResult,
    format: str = 'html',
    tier: str = 'pro',
    output: Optional[Union[str, Path]] = None
) -> str:
    """
    Generate report from clean result.
    
    Args:
        result: CleanResult from clean()
        format: 'html' or 'json'
        tier: 'free' or 'pro' (enterprise reports)
        output: Output file path
    
    Returns:
        Report content (HTML or JSON string)
    
    Example:
        >>> from datashaper import clean, report
        >>> result = clean("data/")
        >>> html = report(result, format='html', tier='pro')
    """
    if format == 'html' and tier == 'pro':
        generator = EnterpriseReportGenerator()
        
        result_data = {
            'files_processed': result.files_processed,
            'issues_found': result.issues_found,
            'fixes_applied': result.fixes_applied,
            'quality_score': result.quality_score,
            'processing_time_seconds': result.processing_time,
            'issues': result.issues
        }
        
        html = generator.generate_report(result_data=result_data)
        
        if output:
            Path(output).write_text(html, encoding='utf-8')
        
        return html
    
    elif format == 'json':
        import json
        data = {
            'files_processed': result.files_processed,
            'issues_found': result.issues_found,
            'fixes_applied': result.fixes_applied,
            'quality_score': result.quality_score,
            'processing_time': result.processing_time,
            'issues': [
                {
                    'type': i.issue_type,
                    'severity': i.severity,
                    'description': i.description
                }
                for i in result.issues
            ]
        }
        
        json_str = json.dumps(data, indent=2)
        
        if output:
            Path(output).write_text(json_str)
        
        return json_str


def diff(schema1: Dict, schema2: Dict) -> Dict:
    """
    Compare two schemas.
    
    Args:
        schema1: First schema dict
        schema2: Second schema dict
    
    Returns:
        Diff results
    
    Example:
        >>> from datashaper import diff
        >>> result = diff(old_schema, new_schema)
        >>> print(result['columns_added'])
    """
    from app.schema.advanced_schema_engine import AdvancedSchemaEngine, DataSchema
    
    s1 = DataSchema(**schema1)
    s2 = DataSchema(**schema2)
    
    engine = AdvancedSchemaEngine()
    return engine.diff_schemas(s1, s2)


# MLOps Integration Examples
def example_airflow():
    """Example Airflow DAG integration."""
    example_code = '''
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
from datashaper import clean

def clean_daily_data():
    result = clean("/data/raw/", output="/data/clean/")
    if result.quality_score < 90:
        raise ValueError(f"Quality too low: {result.quality_score}%")

dag = DAG('datashaper_cleaning', start_date=datetime(2024, 1, 1))

task = PythonOperator(
    task_id='clean_data',
    python_callable=clean_daily_data,
    dag=dag
)
'''
    return example_code


def example_prefect():
    """Example Prefect flow integration."""
    example_code = '''
from prefect import flow, task
from datashaper import clean, merge

@task
def clean_task(folder):
    return clean(folder, workers=8)

@task
def merge_task(files):
    return merge(files, reconcile=True)

@flow
def etl_flow():
    result = clean_task("/data/raw/")
    merged = merge_task(result.get_cleaned_data().keys())
    return merged

if __name__ == "__main__":
    etl_flow()
'''
    return example_code


# Export all public APIs
__all__ = [
    'clean',
    'merge',
    'detect',
    'report',
    'diff',
    'CleanResult',
    'example_airflow',
    'example_prefect'
]
