"""
df_utils.py — Utilitaires partagés pour DataFrames de résultat Facture.

Colonnes standard pour les DF de section :
    Ligne | Formule | Entrée(s) | Coefficient | Résultat | Annuel

Colonnes pour le DF de comparaison (Relevé vs Calculé) :
    Composante | Relevé (facture) | Calculé (Python) | Écart
"""

import pandas as pd

# ---------------------------------------------------------------------------
# Constantes
# ---------------------------------------------------------------------------

STANDARD_COLUMNS = [
    "Ligne",
    "Formule",
    "Entrée(s)",
    "Coefficient",
    "Résultat",
    "Annuel",
]

COMPARISON_COLUMNS = [
    "Composante",
    "Relevé (facture)",
    "Calculé (Python)",
    "Écart",
]

# ---------------------------------------------------------------------------
# Formatage
# ---------------------------------------------------------------------------


def smart_round(val):
    """Arrondi adaptatif : 6 décimales si |val| < 10, sinon 2 décimales.

    Les chaînes et None sont retournées telles quelles.
    """
    if val is None or val == "":
        return ""
    if isinstance(val, str):
        return val
    if not isinstance(val, (int, float)):
        return val
    if abs(val) < 10:
        return round(val, 6)
    return round(val, 2)


def format_number(val, decimals=2, unit=""):
    """Formater un nombre pour les colonnes texte (Entrée, Coefficient).

    >>> format_number(150000, 0, "kWh")
    '150,000 kWh'
    >>> format_number(0.00827, 5, "EUR/kWh")
    '0.00827 EUR/kWh'
    >>> format_number(None)
    ''
    """
    if val is None or val == "":
        return ""
    if isinstance(val, str):
        return val
    formatted = "{:,.{prec}f}".format(val, prec=decimals)
    if unit:
        formatted += " " + unit
    return formatted


# ---------------------------------------------------------------------------
# Lignes standard
# ---------------------------------------------------------------------------


def add_row(rows, ligne, formule="", entrees="", coefficient="", resultat="", annuel=""):
    """Ajouter une ligne au tableau de résultats (colonnes standard).

    Parameters
    ----------
    rows : list[dict]
        Liste mutable dans laquelle la ligne est ajoutée.
    ligne : str
        Description du poste de calcul.
    formule : str
        Équation utilisée (ex: ``"kWh × prix"``).
    entrees : str
        Valeurs d'entrée formatées (ex: ``"150 000 kWh"``).
    coefficient : str
        Taux / coefficient + source (ex: ``"0.00827 EUR/kWh (ATRD7)"``).
    resultat : float | str
        Montant calculé — sera arrondi via :func:`smart_round`.
    annuel : float | str
        Projection annuelle — sera arrondi via :func:`smart_round`.
    """
    rows.append({
        "Ligne": ligne,
        "Formule": formule,
        "Entrée(s)": entrees,
        "Coefficient": coefficient,
        "Résultat": smart_round(resultat),
        "Annuel": smart_round(annuel),
    })


def add_separator(rows):
    """Ajouter une ligne vide pour séparer visuellement des blocs."""
    rows.append({col: "" for col in STANDARD_COLUMNS})


def build_section_df(rows):
    """Construire un DataFrame pandas à partir des lignes accumulées.

    Returns
    -------
    pandas.DataFrame
        DataFrame avec les colonnes :data:`STANDARD_COLUMNS`.
    """
    return pd.DataFrame(rows, columns=STANDARD_COLUMNS)


# ---------------------------------------------------------------------------
# Comparaison (Relevé vs Calculé)
# ---------------------------------------------------------------------------


def fmt(val, decimals=2):
    """Formater un nombre ou retourner ``'—'`` si *None*.

    >>> fmt(1234.5)
    '1,234.50'
    >>> fmt(None)
    '—'
    """
    if val is None:
        return "\u2014"  # em-dash
    return "{:,.{prec}f}".format(val, prec=decimals)


def ecart(releve, calcule):
    """Calculer l'écart entre une valeur relevée et une valeur calculée.

    Returns
    -------
    str
        ``'—'`` si *releve* est ``None``, ``'OK'`` si |écart| < 0.01,
        sinon ``'+X (±Y%)'``.
    """
    if releve is None:
        return "\u2014"
    diff = releve - calcule
    pct = (diff / releve * 100) if releve != 0 else 0
    if abs(diff) < 0.01:
        return "OK"
    return "{:+,.2f} ({:+.2f}%)".format(diff, pct)


def add_comparison_row(rows, composante, releve, calcule):
    """Ajouter une ligne au tableau de comparaison (Relevé vs Calculé).

    Parameters
    ----------
    rows : list[dict]
        Liste mutable.
    composante : str
        Nom du poste comparé.
    releve : float | None
        Valeur lue sur la facture.
    calcule : float
        Valeur calculée par le modèle.
    """
    rows.append({
        "Composante": composante,
        "Relevé (facture)": fmt(releve),
        "Calculé (Python)": fmt(calcule),
        "Écart": ecart(releve, calcule),
    })


def add_comparison_separator(rows):
    """Ajouter une ligne vide au tableau de comparaison."""
    rows.append({col: "" for col in COMPARISON_COLUMNS})


def build_comparison_df(rows):
    """Construire un DataFrame de comparaison (Relevé vs Calculé).

    Returns
    -------
    pandas.DataFrame
        DataFrame avec les colonnes :data:`COMPARISON_COLUMNS`.
    """
    return pd.DataFrame(rows, columns=COMPARISON_COLUMNS)


# ---------------------------------------------------------------------------
# Affichage pandas
# ---------------------------------------------------------------------------


def set_display_options():
    """Configurer pandas pour un affichage optimal des DF Facture."""
    pd.set_option("display.max_rows", None)
    pd.set_option("display.max_colwidth", 55)
    pd.set_option("display.width", 160)
