from __future__ import annotations

import pandas as pd

from storai.metrics.engine import pick_best_time_column

from .outliers import detect_series_outliers
from .patterns import detect_series_patterns


def detect_ml_findings(df, schema, max_outliers_per_metric: int = 3):
    if df is None or df.empty:
        return {"outliers": [], "patterns": [], "summary": {"metrics_analyzed": 0}}

    time_col = pick_best_time_column(df, schema.time)
    metric_cols = _resolve_metrics(
        df, schema.metrics, schema.time, schema.identifiers, schema.flags
    )
    if not metric_cols:
        return {"outliers": [], "patterns": [], "summary": {"metrics_analyzed": 0}}

    outliers = []
    patterns = []

    for metric_col in metric_cols:
        if metric_col not in df.columns:
            continue

        series = _build_series(df, metric_col, time_col)
        if series.empty:
            continue

        metric_outliers = detect_series_outliers(series)
        metric_patterns = detect_series_patterns(series)

        for item in metric_outliers[:max_outliers_per_metric]:
            outliers.append({"metric": metric_col, **item})

        for item in metric_patterns:
            patterns.append({"metric": metric_col, **item})

    outliers.sort(key=lambda item: item["score"], reverse=True)
    patterns.sort(key=lambda item: item["strength"], reverse=True)

    return {
        "outliers": outliers,
        "patterns": patterns,
        "summary": {
            "time_column": time_col or "_row_order",
            "metrics_analyzed": len(metric_cols),
            "outlier_count": len(outliers),
            "pattern_count": len(patterns),
        },
    }


def _resolve_metrics(df, schema_metrics, time_cols, identifiers, flags):
    if schema_metrics:
        return [col for col in schema_metrics if col in df.columns]

    excluded = set(time_cols or []) | set(identifiers or []) | set(flags or [])
    fallback = []
    for col in df.columns:
        if col in excluded:
            continue
        if pd.api.types.is_numeric_dtype(df[col]):
            fallback.append(col)
    return fallback


def _build_series(df, metric_col, time_col):
    if time_col and time_col in df.columns:
        series = df.groupby(time_col)[metric_col].sum().sort_index()
        return series.dropna()

    ordered = df[metric_col].reset_index(drop=True)
    ordered.index = pd.RangeIndex(start=1, stop=len(ordered) + 1, step=1)
    ordered.index.name = "_row_order"
    return ordered.dropna()
