"""
Intuitiveness: Transform raw datasets into purpose-built data
==============================================================

A Python package implementing the descent-ascent methodology for dataset transformation.

Quick Start
-----------
>>> import pandas as pd
>>> from intuitiveness import assess_quality
>>>
>>> df = pd.read_csv("data.csv")
>>> report = assess_quality(df, target_column="label")
>>> print(f"Usability Score: {report.usability_score:.2f}")

Installation Options
--------------------
- Core: `pip install intuitiveness`
- With quality assessment: `pip install intuitiveness[quality]`
- With Neo4j graphs: `pip install intuitiveness[neo4j]`
- Full features: `pip install intuitiveness[all]`

For more information, see https://github.com/ArthurSrz/intuitiveness
"""

from intuitiveness._version import __version__

# ============================================================================
# CORE MODULES (always available)
# ============================================================================

# Complexity system
from intuitiveness.complexity import (
    ComplexityLevel,
    Dataset,
    Level4Dataset,
    Level3Dataset,
    Level2Dataset,
    Level1Dataset,
    Level0Dataset
)

# Redesigner (descent/ascent operations)
from intuitiveness.redesign import Redesigner

# Interactive workflow
from intuitiveness.interactive import (
    InteractiveRedesigner,
    TransitionQuestions,
    QuestionType,
    UserAnswer,
    DataModelGenerator,
    Neo4jDataModel,
    SemanticMatcher,
    run_interactive_descent
)

# Neo4j writer
from intuitiveness.neo4j_writer import (
    Neo4jMCPWriter,
    Neo4jWriteResult,
    generate_constraint_queries,
    generate_node_ingest_query,
    generate_relationship_ingest_query,
    generate_full_ingest_script,
    graph_to_neo4j_records
)

# Navigation system
from intuitiveness.navigation import (
    NavigationSession,
    NavigationState,
    NavigationStep,
    NavigationHistory,
    NavigationError,
    SessionNotFoundError,
    NavigationTree,
    NavigationTreeNode,
    NavigationAction
)

# Ascent operations
from intuitiveness.ascent import (
    EnrichmentFunction,
    EnrichmentRegistry,
    DimensionDefinition,
    DimensionRegistry,
    AscentOperation,
    suggest_dimensions,
    find_duplicates,
    create_dimension_groups,
    get_dimension_hierarchy
)

# UI Components
from intuitiveness import ui
from intuitiveness import export

# ============================================================================
# OPTIONAL MODULES (conditional imports with feature flags)
# ============================================================================

# Quality Assessment (requires: pip install intuitiveness[quality])
QUALITY_AVAILABLE = False
try:
    from intuitiveness.quality import (
        # Models
        QualityReport,
        FeatureProfile,
        FeatureSuggestion,
        AnomalyRecord,
        SyntheticDataMetrics,
        SyntheticBenchmarkReport,
        ModelBenchmarkResult,
        TransformationResult,
        TransformationLog,
        ReadinessIndicator,
        ExportPackage,
        # Assessment
        assess_dataset,
        apply_all_suggestions,
        get_readiness_indicator,
        quick_benchmark,
        # Feature engineering
        suggest_features,
        apply_suggestion,
        # Anomaly detection
        detect_anomalies,
        explain_anomaly,
        get_anomaly_summary,
        # Synthetic generation
        generate_synthetic,
        validate_synthetic,
        check_tabpfn_auth,
        get_synthetic_summary,
        benchmark_synthetic,
        generate_balanced_synthetic,
        generate_targeted_synthetic,
        # Export
        export_dataset,
        export_to_bytes,
        export_with_metadata,
        generate_python_snippet,
        get_mime_type,
        # Workflow
        ReadinessStatus,
        get_readiness_status,
        estimate_score_improvement,
        GREEN_THRESHOLD,
        YELLOW_THRESHOLD,
        WorkflowResult,
        run_60_second_workflow,
        quick_export,
        # Instant Export
        InstantExporter,
        instant_check_and_export,
        export_clean_csv,
        ExportResult,
        CleaningAction,
    )
    QUALITY_AVAILABLE = True
except ImportError:
    # Placeholder None values for unavailable quality components
    QualityReport = None
    FeatureProfile = None
    FeatureSuggestion = None
    AnomalyRecord = None
    SyntheticDataMetrics = None
    SyntheticBenchmarkReport = None
    ModelBenchmarkResult = None
    TransformationResult = None
    TransformationLog = None
    ReadinessIndicator = None
    ExportPackage = None
    assess_dataset = None
    apply_all_suggestions = None
    get_readiness_indicator = None
    quick_benchmark = None
    suggest_features = None
    apply_suggestion = None
    detect_anomalies = None
    explain_anomaly = None
    get_anomaly_summary = None
    generate_synthetic = None
    validate_synthetic = None
    check_tabpfn_auth = None
    get_synthetic_summary = None
    benchmark_synthetic = None
    generate_balanced_synthetic = None
    generate_targeted_synthetic = None
    export_dataset = None
    export_to_bytes = None
    export_with_metadata = None
    generate_python_snippet = None
    get_mime_type = None
    ReadinessStatus = None
    get_readiness_status = None
    estimate_score_improvement = None
    GREEN_THRESHOLD = None
    YELLOW_THRESHOLD = None
    WorkflowResult = None
    run_60_second_workflow = None
    quick_export = None
    InstantExporter = None
    instant_check_and_export = None
    export_clean_csv = None
    ExportResult = None
    CleaningAction = None

# Data Discovery (requires: pip install intuitiveness[discovery])
DISCOVERY_AVAILABLE = False
try:
    from intuitiveness.data_sources import (
        MCPClient,
        DataGouvClient,
        NLQueryEngine,
        NLQueryResult,
        parse_french_query,
        DatasetQualityScore,
        quick_assess_dataset,
        filter_by_quality,
        get_quality_cache_key,
        should_show_quality_indicator,
    )
    DISCOVERY_AVAILABLE = True
except ImportError:
    MCPClient = None
    DataGouvClient = None
    NLQueryEngine = None
    NLQueryResult = None
    parse_french_query = None
    DatasetQualityScore = None
    quick_assess_dataset = None
    filter_by_quality = None
    get_quality_cache_key = None
    should_show_quality_indicator = None

# Neo4j Integration (requires: pip install intuitiveness[neo4j])
NEO4J_AVAILABLE = False
try:
    from intuitiveness.neo4j_client import Neo4jClient, Neo4jResult
    from intuitiveness.agent import SmolLM2Agent, AgentResult, AgentStep, AgentAction, simple_chat
    NEO4J_AVAILABLE = True
except ImportError:
    Neo4jClient = None
    Neo4jResult = None
    SmolLM2Agent = None
    AgentResult = None
    AgentStep = None
    AgentAction = None
    simple_chat = None

# ============================================================================
# CONVENIENCE API FUNCTIONS
# ============================================================================

def from_csv(filepath: str, name: str = None) -> "Level4Dataset":
    """
    Load a CSV file and create a Level4Dataset.

    This is the simplest entry point for starting a descent-ascent cycle.

    Parameters
    ----------
    filepath : str
        Path to the CSV file
    name : str, optional
        Name for the data source. Defaults to filename without extension.

    Returns
    -------
    Level4Dataset
        Dataset at complexity level 4 (raw data)

    Examples
    --------
    >>> from intuitiveness import from_csv, quick_descent
    >>>
    >>> l4 = from_csv("schools.csv")
    >>> l0 = quick_descent(l4, column="score", aggregation="mean")
    >>> print(f"Average score: {l0.get_data()}")
    """
    import pandas as pd
    from pathlib import Path

    df = pd.read_csv(filepath)
    if name is None:
        name = Path(filepath).stem

    return Level4Dataset({name: df})


def quick_descent(
    data,
    column: str = None,
    aggregation: str = "mean",
    filter_query: str = None
) -> "Level0Dataset":
    """
    Quick descent from raw data to a single value (L0).

    This is the simplest way to extract a core datum from your data.
    Automatically handles the full L4 → L3 → L2 → L1 → L0 chain.

    Parameters
    ----------
    data : Level4Dataset, Level2Dataset, pd.DataFrame, or str (CSV path)
        Input data at any level. If string, loads as CSV.
    column : str, optional
        Column to focus on. If None with single numeric column, uses that.
    aggregation : str, default "mean"
        Aggregation method: "sum", "mean", "count", "min", "max", "median"
    filter_query : str, optional
        Pandas query string to filter rows (e.g., "score > 50")

    Returns
    -------
    Level0Dataset
        Single aggregated value with parent data for potential ascent

    Examples
    --------
    >>> from intuitiveness import quick_descent
    >>>
    >>> # From CSV file path
    >>> l0 = quick_descent("schools.csv", column="score", aggregation="mean")
    >>> print(f"Average: {l0.get_data()}")
    >>>
    >>> # From DataFrame
    >>> l0 = quick_descent(df, column="revenue", aggregation="sum")
    >>>
    >>> # With filtering
    >>> l0 = quick_descent(df, column="score", filter_query="region == 'urban'")
    """
    import pandas as pd

    # Handle different input types
    if isinstance(data, str):
        data = from_csv(data)

    if isinstance(data, pd.DataFrame):
        df = data
    elif isinstance(data, Level4Dataset):
        # Get first DataFrame from L4
        sources = data.get_data()
        if not sources:
            raise ValueError("Level4Dataset is empty")
        df = next(iter(sources.values()))
        if not isinstance(df, pd.DataFrame):
            raise ValueError("Level4Dataset must contain pandas DataFrames")
    elif isinstance(data, Level2Dataset):
        df = data.get_data()
    elif isinstance(data, Level1Dataset):
        # Already at L1, just aggregate
        series = data.get_data()
        return _aggregate_series(series, aggregation, data.name)
    elif isinstance(data, Level0Dataset):
        # Already at L0
        return data
    else:
        raise TypeError(f"Unsupported data type: {type(data)}")

    # Apply filter if specified
    if filter_query:
        df = df.query(filter_query)

    # Auto-detect column if not specified
    if column is None:
        numeric_cols = df.select_dtypes(include=['number']).columns.tolist()
        if len(numeric_cols) == 1:
            column = numeric_cols[0]
        elif len(numeric_cols) == 0:
            raise ValueError("No numeric columns found. Please specify 'column'.")
        else:
            raise ValueError(
                f"Multiple numeric columns found: {numeric_cols}. "
                f"Please specify 'column'."
            )

    # Extract column as Series (L1)
    if column not in df.columns:
        raise ValueError(f"Column '{column}' not found. Available: {list(df.columns)}")

    series = df[column]

    # Aggregate to L0
    return _aggregate_series(series, aggregation, column)


def _aggregate_series(series, aggregation: str, name: str) -> "Level0Dataset":
    """Internal helper to aggregate a Series to Level0Dataset."""
    import pandas as pd

    if aggregation == "sum":
        val = series.sum()
    elif aggregation == "mean":
        val = series.mean()
    elif aggregation == "count":
        val = series.count()
    elif aggregation == "min":
        val = series.min()
    elif aggregation == "max":
        val = series.max()
    elif aggregation == "median":
        val = series.median()
    elif aggregation == "std":
        val = series.std()
    else:
        raise ValueError(
            f"Unknown aggregation: {aggregation}. "
            f"Use: sum, mean, count, min, max, median, std"
        )

    return Level0Dataset(
        value=val,
        description=f"{aggregation} of {name}",
        parent_data=series.copy(),
        aggregation_method=aggregation
    )


def quick_ascent(
    l0: "Level0Dataset",
    dimensions: list = None,
    categorize_by: str = None,
    n_bins: int = 5
) -> "Level2Dataset":
    """
    Quick ascent from L0 datum back to a categorized table (L2).

    Uses the parent data stored during descent to rebuild with new dimensions.

    Parameters
    ----------
    l0 : Level0Dataset
        The datum to ascend from (must have parent_data from descent)
    dimensions : list, optional
        Column names from parent data to include as dimensions
    categorize_by : str, optional
        Create categorical bins based on this parent column
        (e.g., "high"/"low" based on median)
    n_bins : int, default 5
        Number of bins if categorize_by is specified

    Returns
    -------
    Level2Dataset
        Table with value column and dimension columns

    Examples
    --------
    >>> from intuitiveness import quick_descent, quick_ascent
    >>>
    >>> # Descent then ascent
    >>> l0 = quick_descent("schools.csv", column="score", aggregation="mean")
    >>> l2 = quick_ascent(l0, categorize_by="score")
    >>> print(l2.get_data())  # DataFrame with 'score' and 'score_category'
    """
    import pandas as pd
    import numpy as np

    if not isinstance(l0, Level0Dataset):
        raise TypeError(f"Expected Level0Dataset, got {type(l0)}")

    if not l0.has_parent:
        raise ValueError(
            "L0 dataset has no parent data for ascent. "
            "Use quick_descent() to create L0 with parent data preserved."
        )

    parent_series = l0.get_parent_data()
    parent_name = parent_series.name or "value"

    # Start building DataFrame
    df = pd.DataFrame({parent_name: parent_series})

    # Add categorical dimension if requested
    if categorize_by:
        if categorize_by == parent_name or categorize_by == "value":
            # Categorize the main value column
            median = parent_series.median()
            df[f"{parent_name}_category"] = pd.cut(
                parent_series,
                bins=n_bins,
                labels=[f"Q{i+1}" for i in range(n_bins)]
            )
            # Also add simple high/low classification
            df[f"{parent_name}_level"] = np.where(
                parent_series >= median, "high", "low"
            )
        else:
            raise ValueError(
                f"categorize_by='{categorize_by}' not found. "
                f"Available: {parent_name}"
            )

    # Add any additional dimensions from parent index if named
    if parent_series.index.name:
        df[parent_series.index.name] = parent_series.index

    return Level2Dataset(df, name=f"ascent_from_{l0.description}")


def descend(dataset, target_level, **kwargs):
    """
    Simplify a dataset by descending to a lower complexity level.

    Parameters
    ----------
    dataset : Dataset
        The input dataset at any complexity level (L4, L3, L2, L1)
    target_level : ComplexityLevel or int
        Target complexity level (0-4)
    **kwargs : dict
        Additional parameters passed to Redesigner.reduce_complexity

    Returns
    -------
    Dataset
        Dataset at the target complexity level

    Examples
    --------
    >>> from intuitiveness import Level4Dataset, ComplexityLevel, descend
    >>> import pandas as pd
    >>>
    >>> df = pd.read_csv("raw_data.csv")
    >>> l4 = Level4Dataset({"raw": df})
    >>> l0 = descend(l4, ComplexityLevel.LEVEL_0)
    >>> print(f"Core datum: {l0.data}")
    """
    return Redesigner.reduce_complexity(dataset, target_level, **kwargs)


def ascend(dataset, target_level, **kwargs):
    """
    Enrich a dataset by ascending to a higher complexity level.

    Parameters
    ----------
    dataset : Dataset
        The input dataset at any complexity level (L0, L1, L2, L3)
    target_level : ComplexityLevel or int
        Target complexity level (1-4)
    **kwargs : dict
        Additional parameters passed to Redesigner.increase_complexity
        Typically includes `enrichments` for specifying dimensions to add

    Returns
    -------
    Dataset
        Dataset at the target complexity level

    Examples
    --------
    >>> from intuitiveness import Level0Dataset, ComplexityLevel, ascend
    >>>
    >>> l0 = Level0Dataset({"average_score": 85.5})
    >>> l3 = ascend(l0, ComplexityLevel.LEVEL_3, enrichments=["region", "school_type"])
    >>> print(l3.data)  # Graph with enriched dimensions
    """
    return Redesigner.increase_complexity(dataset, target_level, **kwargs)


def assess_quality(df, target_column=None):
    """
    Perform quick TabPFN-based quality assessment on a DataFrame.

    Requires: pip install intuitiveness[quality]

    Parameters
    ----------
    df : pd.DataFrame
        The dataset to assess
    target_column : str, optional
        Target column for supervised learning assessment
        If None, performs unsupervised assessment

    Returns
    -------
    QualityReport
        Comprehensive quality report with usability score, feature profiles,
        and improvement suggestions

    Raises
    ------
    ImportError
        If quality module is not installed

    Examples
    --------
    >>> import pandas as pd
    >>> from intuitiveness import assess_quality
    >>>
    >>> df = pd.read_csv("data.csv")
    >>> report = assess_quality(df, target_column="label")
    >>> print(f"Usability: {report.usability_score:.2f}")
    >>> print(f"Suggestions: {len(report.suggestions)}")
    """
    if not QUALITY_AVAILABLE:
        raise ImportError(
            "Quality assessment requires TabPFN dependencies.\n"
            "Install with: pip install intuitiveness[quality]"
        )
    return assess_dataset(df, target_column)


# ============================================================================
# PACKAGE METADATA
# ============================================================================

__all__ = [
    # Version
    "__version__",

    # Feature flags
    "QUALITY_AVAILABLE",
    "DISCOVERY_AVAILABLE",
    "NEO4J_AVAILABLE",

    # Convenience functions
    "from_csv",
    "quick_descent",
    "quick_ascent",
    "descend",
    "ascend",
    "assess_quality",

    # Core complexity classes
    "ComplexityLevel",
    "Dataset",
    "Level4Dataset",
    "Level3Dataset",
    "Level2Dataset",
    "Level1Dataset",
    "Level0Dataset",

    # Redesigner
    "Redesigner",

    # Interactive workflow
    "InteractiveRedesigner",
    "TransitionQuestions",
    "QuestionType",
    "UserAnswer",
    "DataModelGenerator",
    "Neo4jDataModel",
    "SemanticMatcher",
    "run_interactive_descent",

    # Neo4j writer
    "Neo4jMCPWriter",
    "Neo4jWriteResult",
    "generate_constraint_queries",
    "generate_node_ingest_query",
    "generate_relationship_ingest_query",
    "generate_full_ingest_script",
    "graph_to_neo4j_records",

    # Navigation
    "NavigationSession",
    "NavigationState",
    "NavigationStep",
    "NavigationHistory",
    "NavigationError",
    "SessionNotFoundError",
    "NavigationTree",
    "NavigationTreeNode",
    "NavigationAction",

    # Ascent operations
    "EnrichmentFunction",
    "EnrichmentRegistry",
    "DimensionDefinition",
    "DimensionRegistry",
    "AscentOperation",
    "suggest_dimensions",
    "find_duplicates",
    "create_dimension_groups",
    "get_dimension_hierarchy",

    # UI and Export subpackages
    "ui",
    "export",

    # Quality module (conditional)
    "QualityReport",
    "FeatureProfile",
    "FeatureSuggestion",
    "AnomalyRecord",
    "SyntheticDataMetrics",
    "SyntheticBenchmarkReport",
    "ModelBenchmarkResult",
    "TransformationResult",
    "TransformationLog",
    "ReadinessIndicator",
    "ExportPackage",
    "assess_dataset",
    "apply_all_suggestions",
    "get_readiness_indicator",
    "quick_benchmark",
    "suggest_features",
    "apply_suggestion",
    "detect_anomalies",
    "explain_anomaly",
    "get_anomaly_summary",
    "generate_synthetic",
    "validate_synthetic",
    "check_tabpfn_auth",
    "get_synthetic_summary",
    "benchmark_synthetic",
    "generate_balanced_synthetic",
    "generate_targeted_synthetic",
    "export_dataset",
    "export_to_bytes",
    "export_with_metadata",
    "generate_python_snippet",
    "get_mime_type",
    "ReadinessStatus",
    "get_readiness_status",
    "estimate_score_improvement",
    "GREEN_THRESHOLD",
    "YELLOW_THRESHOLD",
    "WorkflowResult",
    "run_60_second_workflow",
    "quick_export",
    "InstantExporter",
    "instant_check_and_export",
    "export_clean_csv",
    "ExportResult",
    "CleaningAction",

    # Discovery module (conditional)
    "MCPClient",
    "DataGouvClient",
    "NLQueryEngine",
    "NLQueryResult",
    "parse_french_query",
    "DatasetQualityScore",
    "quick_assess_dataset",
    "filter_by_quality",
    "get_quality_cache_key",
    "should_show_quality_indicator",

    # Neo4j Agent (conditional)
    "Neo4jClient",
    "Neo4jResult",
    "SmolLM2Agent",
    "AgentResult",
    "AgentStep",
    "AgentAction",
    "simple_chat",
]
