"""Tests para categorical_top_pie_figure (donut de categorías top, grupo eda).

Usa el backend Agg sin pyplot; no muestra ni guarda figuras. Cada test cierra
explícitamente la Figure construida (matplotlib.pyplot.close) para no acumular
estado entre tests.
"""

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt  # noqa: E402
from matplotlib.figure import Figure  # noqa: E402

from categorical_top_pie_figure import categorical_top_pie_figure


def _make_top(n):
    """n items {value, count, pct} ordenados desc por count."""
    return [
        {"value": f"cat_{i}", "count": n - i, "pct": (n - i) / sum(range(1, n + 1))}
        for i in range(n)
    ]


def _wedges(ax):
    """Devuelve los wedges (sectores) de un Axes con un pie."""
    from matplotlib.patches import Wedge

    return [p for p in ax.patches if isinstance(p, Wedge)]


def test_returns_figure():
    fig = categorical_top_pie_figure(_make_top(3), n_distinct=3, title="col")
    assert isinstance(fig, Figure)
    plt.close(fig)


def test_ten_items_topk_six_yields_seven_wedges():
    top = _make_top(10)
    fig = categorical_top_pie_figure(top, n_distinct=10, title="muchas", top_k=6)
    ax = fig.axes[0]
    wedges = _wedges(ax)
    # 6 categorías explícitas + 1 sector "Otros".
    assert len(wedges) == 7
    plt.close(fig)


def test_empty_top_does_not_raise_and_returns_figure():
    fig = categorical_top_pie_figure([], n_distinct=0, title="vacía")
    assert isinstance(fig, Figure)
    # Sin datos: no debe haber sectores de pie.
    assert len(_wedges(fig.axes[0])) == 0
    plt.close(fig)


def test_long_value_truncated_in_legend():
    long_value = "una_categoria_con_un_nombre_larguisimo_que_excede_el_limite"
    top = [
        {"value": long_value, "count": 10, "pct": 0.5},
        {"value": "corta", "count": 10, "pct": 0.5},
    ]
    fig = categorical_top_pie_figure(top, n_distinct=2, title="col", top_k=6)
    ax = fig.axes[0]
    legend = ax.get_legend()
    assert legend is not None
    texts = [t.get_text() for t in legend.get_texts()]
    # El valor largo aparece truncado con elipsis y NO en su forma completa.
    assert any("…" in t for t in texts)
    assert long_value not in " ".join(texts)
    plt.close(fig)


def test_none_value_and_none_count_are_handled():
    top = [
        {"value": None, "count": 5, "pct": 0.5},
        {"value": "b", "count": None, "pct": 0.0},  # count None -> se descarta
        {"value": "c", "count": 5, "pct": 0.5},
    ]
    fig = categorical_top_pie_figure(top, n_distinct=2, title="con nones", top_k=6)
    assert isinstance(fig, Figure)
    # Solo 2 items válidos, sin overflow -> 2 wedges, sin "Otros".
    assert len(_wedges(fig.axes[0])) == 2
    plt.close(fig)


def test_n_rows_adds_exact_others_slice():
    # 3 categorías mostradas suman 30, dataset real 100 -> "Otros" = 70.
    top = _make_top(3)  # counts 3,2,1 -> reescalamos abajo
    top = [
        {"value": "a", "count": 15, "pct": 0.15},
        {"value": "b", "count": 10, "pct": 0.10},
        {"value": "c", "count": 5, "pct": 0.05},
    ]
    fig = categorical_top_pie_figure(
        top, n_distinct=20, title="col", top_k=3, n_rows=100
    )
    ax = fig.axes[0]
    # 3 explícitas + Otros.
    assert len(_wedges(ax)) == 4
    legend_texts = [t.get_text() for t in ax.get_legend().get_texts()]
    # El sector Otros refleja n_distinct - top_k = 17 categorías y count 70.
    assert any("Otros (17 categorías)" in t and "70" in t for t in legend_texts)
    plt.close(fig)
