"""Tests for inline-bold rendering (**bold**) in the AutomaticEDA engine.

Covers the pure helpers (parse_inline_bold / wrap_rich) and an end-to-end PPTX
check that a ``**bold**`` span is rendered with NATIVE PowerPoint bold
(``run.font.bold is True``) while no line overflows the wrap width (no-cut).
"""

import os
import sys

import pytest

# Make the engine importable as a package (datascience.automatic_eda).
_HERE = os.path.dirname(os.path.abspath(__file__))
_FUNCTIONS = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))  # python/functions
if _FUNCTIONS not in sys.path:
    sys.path.insert(0, _FUNCTIONS)

from datascience.automatic_eda import model  # noqa: E402
from datascience.automatic_eda import text_layout as tl  # noqa: E402
from datascience.automatic_eda import render_pptx  # noqa: E402


# --------------------------------------------------------------------------- #
# Pure helpers.
# --------------------------------------------------------------------------- #
def test_parse_inline_bold_marks_spans_and_preserves_visible_text():
    src = "**Estacionariedad:** serie no estacionaria con `code` y normal."
    segs = tl.parse_inline_bold(src)
    # Visible text equals strip_inline_md (no characters lost, markers removed).
    visible = "".join(s for s, _ in segs)
    assert visible == tl.strip_inline_md(src)
    # The span "Estacionariedad:" is flagged bold; the rest is not.
    bold_text = "".join(s for s, b in segs if b)
    assert "Estacionariedad:" in bold_text
    assert "serie no estacionaria" not in bold_text


def test_parse_inline_bold_handles_unbalanced_markers():
    # An unbalanced ** must not crash and must be stripped (matches strip_inline_md).
    segs = tl.parse_inline_bold("texto **sin cierre aqui")
    visible = "".join(s for s, _ in segs)
    assert visible == "texto sin cierre aqui"
    assert not any(b for _, b in segs)  # nothing rendered bold.


def test_wrap_rich_never_overflows_and_keeps_bold():
    text = ("**Segmento premium.** Clientes de alto gasto y baja frecuencia con "
            "ticket medio elevado y recurrencia anual estable a lo largo del año.")
    max_chars = 30
    lines = tl.wrap_rich(text, max_chars)
    # No visible line exceeds max_chars (no-cut: the renderer measures these).
    for ln in lines:
        visible = "".join(s for s, _ in ln)
        assert len(visible) <= max_chars, f"línea desborda: {visible!r}"
    # At least one segment is bold and it is the span content.
    bold_segs = [s for ln in lines for s, b in ln if b]
    assert any("Segmento premium." in s for s in bold_segs)


def test_wrap_rich_hard_splits_long_token():
    long = "x" * 50
    lines = tl.wrap_rich(f"**{long}**", 20)
    for ln in lines:
        assert len("".join(s for s, _ in ln)) <= 20
    # The whole long token is preserved across the split lines.
    joined = "".join(s for ln in lines for s, _ in ln)
    assert joined == long


# --------------------------------------------------------------------------- #
# End-to-end: PPTX renders **bold** as a real bold run.
# --------------------------------------------------------------------------- #
def _has_pptx():
    try:
        import pptx  # noqa: F401
        return True
    except Exception:  # noqa: BLE001
        return False


@pytest.mark.skipif(not _has_pptx(), reason="python-pptx no instalado")
def test_pptx_renders_bold_span_as_native_bold_run(tmp_path):
    from pptx import Presentation

    doc = [model.Chapter(
        id="t", title="Negrita", version="1.0.0",
        blocks=[model.Markdown(
            text="Frase con **PALABRACLAVE** resaltada y texto normal después.")],
    )]
    out = str(tmp_path / "bold.pptx")
    res = render_pptx(doc, out, {"title": "T"})
    assert res.get("path") == out
    assert os.path.exists(out)

    prs = Presentation(out)
    bold_texts = []
    all_text = []
    for slide in prs.slides:
        for shape in slide.shapes:
            if not shape.has_text_frame:
                continue
            for para in shape.text_frame.paragraphs:
                for run in para.runs:
                    all_text.append(run.text)
                    if run.font.bold:
                        bold_texts.append(run.text)
    # The bold span text appears in a run with font.bold True (native bold).
    assert any("PALABRACLAVE" in t for t in bold_texts), \
        f"no se encontró run bold con el span; bold={bold_texts}"
    # And the surrounding plain text is NOT bold (markers did not bleed).
    assert any("resaltada" in t for t in all_text)
    assert not any("resaltada" in t for t in bold_texts)
