"""Fonctions spécifiques aux analyses de vent (roses et composantes)."""

from __future__ import annotations

from typing import Sequence

import numpy as np
import pandas as pd

from .core import _ensure_datetime_index

__all__ = ['compute_wind_rose_distribution', 'compute_mean_wind_components']


def _format_speed_bin_labels(speed_bins: Sequence[float]) -> list[str]:
    labels: list[str] = []
    for i in range(len(speed_bins) - 1):
        low = speed_bins[i]
        high = speed_bins[i + 1]
        if np.isinf(high):
            labels.append(f"≥{low:g}")
        else:
            labels.append(f"{low:g}–{high:g}")
    return labels

def compute_wind_rose_distribution(
    df: pd.DataFrame,
    *,
    direction_sector_size: int = 30,
    speed_bins: Sequence[float] = (0, 10, 20, 30, 50, float("inf")),
) -> tuple[pd.DataFrame, list[str], float]:
    """
    Regroupe la distribution vent/direction en secteurs angulaires et classes de vitesse.
    Retourne un DataFrame indexé par le début du secteur (en degrés) et colonnes = classes de vitesse (%).
    """
    if direction_sector_size <= 0 or direction_sector_size > 180:
        raise ValueError("direction_sector_size doit être compris entre 1 et 180 degrés.")

    if "wind_speed" not in df.columns or "wind_direction" not in df.columns:
        raise KeyError("Le DataFrame doit contenir 'wind_speed' et 'wind_direction'.")

    data = df[["wind_speed", "wind_direction"]].dropna()
    if data.empty:
        return pd.DataFrame(), [], float(direction_sector_size)

    n_sectors = int(360 / direction_sector_size)
    direction = data["wind_direction"].to_numpy(dtype=float) % 360.0
    sector_indices = np.floor(direction / direction_sector_size).astype(int) % n_sectors

    bins = list(speed_bins)
    if not np.isinf(bins[-1]):
        bins.append(float("inf"))
    labels = _format_speed_bin_labels(bins)

    speed_categories = pd.cut(
        data["wind_speed"],
        bins=bins,
        right=False,
        include_lowest=True,
        labels=labels,
    )

    counts = (
        pd.crosstab(sector_indices, speed_categories)
        .reindex(range(n_sectors), fill_value=0)
        .reindex(columns=labels, fill_value=0)
    )

    total = counts.values.sum()
    frequencies = counts / total * 100.0 if total > 0 else counts.astype(float)
    frequencies.index = frequencies.index * direction_sector_size
    return frequencies, labels, float(direction_sector_size)

def compute_mean_wind_components(
    df: pd.DataFrame,
    *,
    freq: str = "1M",
) -> pd.DataFrame:
    """
    Calcule les composantes zonale (u) et méridienne (v) du vent pour une fréquence donnée.
    Retourne également la vitesse moyenne.
    """
    if "wind_speed" not in df.columns or "wind_direction" not in df.columns:
        raise KeyError("Les colonnes 'wind_speed' et 'wind_direction' sont requises.")

    _ensure_datetime_index(df)
    subset = df[["wind_speed", "wind_direction"]].dropna()
    if subset.empty:
        return pd.DataFrame(columns=["u", "v", "speed"])

    radians = np.deg2rad(subset["wind_direction"].to_numpy(dtype=float))
    speed = subset["wind_speed"].to_numpy(dtype=float)

    u = speed * np.sin(radians) * -1  # composante est-ouest (positive vers l'est)
    v = speed * np.cos(radians) * -1  # composante nord-sud (positive vers le nord)

    vector_df = pd.DataFrame(
        {
            "u": u,
            "v": v,
            "speed": speed,
        },
        index=subset.index,
    )

    actual_freq = "1ME" if freq == "1M" else freq
    grouped = vector_df.resample(actual_freq).mean()
    return grouped.dropna(how="all")
