"""
Data profiler for generating comprehensive dataset profiles.
"""

from pathlib import Path
from typing import Any

import pandas as pd
import polars as pl

from app.core.logging import get_logger
from app.domain.models.dataset import ColumnProfile, DatasetProfile

logger = get_logger(__name__)


class DataProfiler:
    """Generate comprehensive data profiles."""

    def __init__(self):
        """Initialize profiler."""
        self.logger = logger

    def profile_dataset(
        self, df: pd.DataFrame | pl.DataFrame
    ) -> DatasetProfile:
        """Generate complete dataset profile."""
        column_profiles = []

        # Convert to pandas for profiling if needed
        if isinstance(df, pl.DataFrame):
            df_pandas = df.to_pandas()
        else:
            df_pandas = df

        for column in df_pandas.columns:
            profile = self._profile_column(df_pandas, column)
            column_profiles.append(profile)

        # Calculate correlations for numeric columns
        numeric_cols = df_pandas.select_dtypes(include=["number"]).columns.tolist()
        correlations = {}
        if len(numeric_cols) > 1:
            corr_matrix = df_pandas[numeric_cols].corr()
            for col in numeric_cols:
                correlations[col] = corr_matrix[col].to_dict()

        return DatasetProfile(
            column_profiles=column_profiles,
            correlations=correlations,
        )

    def _profile_column(self, df: pd.DataFrame, column: str) -> ColumnProfile:
        """Profile a single column."""
        col_data = df[column]
        dtype = str(col_data.dtype)
        count = len(col_data)
        null_count = int(col_data.isna().sum())
        null_percentage = (null_count / count * 100) if count > 0 else 0
        unique_count = int(col_data.nunique())

        profile = ColumnProfile(
            name=column,
            dtype=dtype,
            count=count,
            null_count=null_count,
            null_percentage=round(null_percentage, 2),
            unique_count=unique_count,
        )

        # Numeric statistics
        if pd.api.types.is_numeric_dtype(col_data):
            profile.min_value = float(col_data.min()) if not col_data.isna().all() else None
            profile.max_value = float(col_data.max()) if not col_data.isna().all() else None
            profile.mean = float(col_data.mean()) if not col_data.isna().all() else None
            profile.median = float(col_data.median()) if not col_data.isna().all() else None
            profile.std = float(col_data.std()) if not col_data.isna().all() else None

            # Detect outliers using IQR
            if not col_data.isna().all():
                Q1 = col_data.quantile(0.25)
                Q3 = col_data.quantile(0.75)
                IQR = Q3 - Q1
                outliers = col_data[(col_data < Q1 - 1.5 * IQR) | (col_data > Q3 + 1.5 * IQR)]
                profile.outlier_count = len(outliers)

        # Categorical statistics
        else:
            value_counts = col_data.value_counts()
            if len(value_counts) > 0:
                profile.most_common = [
                    (str(val), int(count)) for val, count in value_counts.head(5).items()
                ]

        # Detect anomalies
        anomalies = []
        if null_percentage > 50:
            anomalies.append(f"High missing rate: {null_percentage:.1f}%")
        if unique_count == 1:
            anomalies.append("Constant column (only one unique value)")
        if unique_count == count and count > 10:
            anomalies.append("All values unique (potential identifier)")

        profile.anomalies = anomalies

        return profile
