"""
automl_lib.preprocessing
~~~~~~~~~~~~~~~~~~~~~~~~~
Per-modality preprocessing pipelines.
"""

from __future__ import annotations

from typing import Any

import numpy as np
import pandas as pd

from .utils import get_logger

logger = get_logger(__name__)


def preprocess_tabular(
    df: pd.DataFrame,
) -> tuple[np.ndarray, dict[str, Any]]:
    """Auto-preprocess a tabular DataFrame.

    Steps:
    1. Drop columns that are >50 % missing.
    2. Impute remaining missing values (median for numeric, mode for
       categorical).
    3. One-hot encode categorical columns.
    4. Standard-scale numeric columns.

    Returns ``(X_array, encoders_dict)`` where *encoders_dict* stores the
    fitted transformers for later inference use.
    """
    encoders: dict[str, Any] = {}
    df = df.copy()

    # 1. Drop high-missing columns
    drop_thresh = 0.5
    missing_frac = df.isnull().mean()
    drop_cols = missing_frac[missing_frac > drop_thresh].index.tolist()
    if drop_cols:
        logger.info(f"Dropping {len(drop_cols)} columns with >{drop_thresh*100:.0f}% missing values")
        df.drop(columns=drop_cols, inplace=True)

    # Separate numeric and categorical
    num_cols = df.select_dtypes(include=["number"]).columns.tolist()
    cat_cols = df.select_dtypes(include=["object", "category", "bool"]).columns.tolist()

    # 2. Impute
    if num_cols:
        medians = df[num_cols].median()
        df[num_cols] = df[num_cols].fillna(medians)
        encoders["num_medians"] = medians.to_dict()

    if cat_cols:
        modes = df[cat_cols].mode().iloc[0] if len(df) > 0 else pd.Series(dtype=object)
        df[cat_cols] = df[cat_cols].fillna(modes)
        encoders["cat_modes"] = modes.to_dict()

    # 3. One-hot encode categoricals
    if cat_cols:
        df = pd.get_dummies(df, columns=cat_cols, drop_first=True, dtype=float)
        encoders["ohe_columns"] = df.columns.tolist()

    # 4. Standard scaling
    if num_cols:
        means = df[num_cols].mean()
        stds = df[num_cols].std().replace(0, 1)
        df[num_cols] = (df[num_cols] - means) / stds
        encoders["num_means"] = means.to_dict()
        encoders["num_stds"] = stds.to_dict()

    X = df.values.astype(np.float32)
    logger.info(f"Preprocessed tabular data: shape={X.shape}")
    return X, encoders


def preprocess_text(texts: list[str]) -> list[str]:
    """Basic text cleaning."""
    cleaned = []
    for t in texts:
        t = t.strip().lower()
        cleaned.append(t)
    return cleaned
