# meteo/season.py
from __future__ import annotations

from typing import Iterable, Sequence

import numpy as np
import pandas as pd


SEASON_LABELS = np.array(["winter", "spring", "summer", "autumn"])
MONTH_TO_SEASON_INDEX = {
    12: 0,
    1: 0,
    2: 0,
    3: 1,
    4: 1,
    5: 1,
    6: 2,
    7: 2,
    8: 2,
    9: 3,
    10: 3,
    11: 3,
}


def _ensure_datetime_index(index: pd.Index) -> pd.DatetimeIndex:
    if not isinstance(index, pd.DatetimeIndex):
        raise TypeError("Cette fonction nécessite un DatetimeIndex.")
    return index


def _season_indices_for_month(months: np.ndarray, hemisphere: str) -> np.ndarray:
    base_indices = np.vectorize(MONTH_TO_SEASON_INDEX.get)(months)
    if hemisphere == "south":
        return (base_indices + 2) % len(SEASON_LABELS)
    return base_indices


def compute_season_series(
    index: pd.Index,
    *,
    hemisphere: str = "north",
    column_name: str = "season",
) -> pd.Series:
    """
    Retourne une série catégorielle indiquant la saison météorologique pour chaque timestamp.
    """
    hemisphere = hemisphere.lower()
    if hemisphere not in {"north", "south"}:
        raise ValueError("hemisphere doit valoir 'north' ou 'south'.")

    dt_index = _ensure_datetime_index(index)
    month_array = dt_index.month.to_numpy()
    season_indices = _season_indices_for_month(month_array, hemisphere)
    labels = SEASON_LABELS[season_indices]
    return pd.Series(labels, index=dt_index, name=column_name)


def add_season_column(
    df: pd.DataFrame,
    *,
    hemisphere: str = "north",
    column_name: str = "season",
) -> pd.DataFrame:
    """
    Ajoute une colonne 'season' (winter/spring/summer/autumn) au DataFrame.
    """
    series = compute_season_series(df.index, hemisphere=hemisphere, column_name=column_name)
    df[column_name] = series
    return df


def sort_season_labels(
    labels: Iterable[str],
    *,
    order: Sequence[str] | None = None,
) -> list[str]:
    """
    Trie la liste fournie en respectant l'ordre saisonnier par défaut.
    """
    reference = [str(season) for season in (order if order is not None else SEASON_LABELS)]
    label_set = {str(label) for label in labels if label}
    return [season for season in reference if season in label_set]
