"""Tests para render_automatic_eda_folder — EDA de una carpeta / base multi-tabla.

Golden: una carpeta con 3 CSV relacionados (customers/orders/products) produce el
documento-base en PDF + PPTX + MD, con las 3 tablas en el resumen y la FK
orders.customer_id -> customers.id en el capítulo de relaciones. Edges: carpeta
vacía (documento mínimo, sin lanzar), 1 sola tabla (sin relaciones) y la rama
"ya es una DuckDB" sobre un archivo .duckdb existente.
"""

import os
import sys

import duckdb

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))

from pipelines.render_automatic_eda_folder import (
    _relaciones_chapter,
    render_automatic_eda_folder,
)


def _write_demo_folder(folder: str) -> None:
    """3 CSV relacionados: orders.customer_id -> customers.id (FK detectable)."""
    with open(os.path.join(folder, "customers.csv"), "w", encoding="utf-8") as fh:
        fh.write("id,name,city\n")
        fh.write("1,Alice,Madrid\n2,Bob,Barcelona\n3,Carol,Valencia\n"
                 "4,Dave,Sevilla\n5,Eve,Madrid\n")
    with open(os.path.join(folder, "orders.csv"), "w", encoding="utf-8") as fh:
        fh.write("order_id,customer_id,product_id,total\n")
        fh.write("100,1,10,49.90\n101,1,11,12.50\n102,2,10,49.90\n"
                 "103,3,12,8.00\n104,3,11,12.50\n105,5,10,49.90\n"
                 "106,2,12,8.00\n")
    with open(os.path.join(folder, "products.csv"), "w", encoding="utf-8") as fh:
        fh.write("product_id,product_name,price\n")
        fh.write("10,Widget,49.90\n11,Gadget,12.50\n12,Gizmo,8.00\n")


def _has_fk(db_profile: dict, from_t: str, from_c: str, to_t: str) -> bool:
    for fk in db_profile.get("fk_candidates", []) or []:
        if (fk.get("from_table") == from_t and fk.get("from_col") == from_c
                and fk.get("to_table") == to_t):
            return True
    return False


def test_golden_folder_three_csv(tmp_path):
    """Carpeta con 3 CSV relacionados -> PDF+PPTX+MD, 3 tablas, FK detectada."""
    folder = tmp_path / "demo"
    folder.mkdir()
    _write_demo_folder(str(folder))
    out = tmp_path / "out"

    r = render_automatic_eda_folder(str(folder), out_dir=str(out))

    assert r["status"] == "ok", r
    assert r["n_tables"] == 3
    # Los tres formatos se emitieron y existen en disco.
    assert r["pdf_path"] and os.path.exists(r["pdf_path"])
    assert r["pptx_path"] and os.path.exists(r["pptx_path"])
    assert r["md_path"] and os.path.exists(r["md_path"])
    assert (r["n_pages"] or 0) >= 1
    assert (r["n_slides"] or 0) >= 1
    # La FK orders.customer_id -> customers.id se detecta por containment.
    assert _has_fk(r["db_profile"], "orders", "customer_id", "customers"), \
        r["db_profile"].get("fk_candidates")
    # El Markdown menciona las 3 tablas y la relación.
    md = open(r["md_path"], encoding="utf-8").read()
    for t in ("customers", "orders", "products"):
        assert t in md
    assert "customer_id" in md


def test_edge_empty_folder(tmp_path):
    """Carpeta vacía -> status ok con documento mínimo, sin lanzar."""
    folder = tmp_path / "empty"
    folder.mkdir()
    out = tmp_path / "out"

    r = render_automatic_eda_folder(str(folder), out_dir=str(out))

    assert r["status"] == "ok", r
    assert r["n_tables"] == 0
    # Aun sin tablas, emite el documento-base mínimo (portada + resumen vacío +
    # relaciones "sin FK").
    assert r["pdf_path"] and os.path.exists(r["pdf_path"])
    assert r["md_path"] and os.path.exists(r["md_path"])


def test_edge_single_table_no_relations(tmp_path):
    """Carpeta con 1 sola tabla -> funciona sin relaciones (capítulo 'sin FK')."""
    folder = tmp_path / "single"
    folder.mkdir()
    with open(folder / "lonely.csv", "w", encoding="utf-8") as fh:
        fh.write("a,b\n1,x\n2,y\n3,z\n")
    out = tmp_path / "out"

    r = render_automatic_eda_folder(str(folder), out_dir=str(out))

    assert r["status"] == "ok", r
    assert r["n_tables"] == 1
    assert not (r["db_profile"].get("fk_candidates") or [])
    md = open(r["md_path"], encoding="utf-8").read()
    assert "Sin relaciones FK" in md or "sin FK" in md.lower()


def test_accepts_existing_duckdb(tmp_path):
    """Rama 'ya es una DuckDB': un archivo .duckdb existente se perfila directo."""
    db = tmp_path / "base.duckdb"
    conn = duckdb.connect(str(db))
    try:
        conn.execute("CREATE TABLE customers (id INTEGER, name VARCHAR)")
        conn.execute("INSERT INTO customers VALUES (1,'Ana'),(2,'Luis'),(3,'Eva')")
        conn.execute("CREATE TABLE orders (oid INTEGER, customer_id INTEGER)")
        conn.execute("INSERT INTO orders VALUES (10,1),(11,2),(12,1),(13,3)")
    finally:
        conn.close()
    out = tmp_path / "out"

    r = render_automatic_eda_folder(str(db), out_dir=str(out))

    assert r["status"] == "ok", r
    assert r["n_tables"] == 2
    assert r["db_path"] == str(db)
    assert r["pdf_path"] and os.path.exists(r["pdf_path"])


def test_emit_flags_select_formats(tmp_path):
    """emit_pdf/pptx/md controlan qué formatos se emiten."""
    folder = tmp_path / "demo"
    folder.mkdir()
    _write_demo_folder(str(folder))
    out = tmp_path / "out"

    r = render_automatic_eda_folder(
        str(folder), out_dir=str(out),
        emit_pdf=True, emit_pptx=False, emit_md=False,
    )
    assert r["status"] == "ok", r
    assert r["pdf_path"] and os.path.exists(r["pdf_path"])
    assert r["pptx_path"] is None
    assert r["md_path"] is None


def test_path_does_not_exist(tmp_path):
    """Path inexistente -> status error, sin lanzar."""
    r = render_automatic_eda_folder(str(tmp_path / "nope"))
    assert r["status"] == "error"
    assert "no existe" in r["error"].lower()


def test_relaciones_chapter_has_real_figure_when_edges():
    """Con edges, el capítulo de relaciones incluye un bloque Figure matplotlib
    REAL (no solo el texto Mermaid): su make() devuelve una Figure."""
    db_profile = {
        "join_graph": {
            "nodes": [
                {"table": "orders", "out_degree": 1, "in_degree": 0, "role": "fact"},
                {"table": "customers", "out_degree": 0, "in_degree": 1, "role": "dim"},
            ],
            "edges": [{"from_table": "orders", "from_col": "customer_id",
                       "to_table": "customers", "to_col": "id",
                       "cardinality": "N:1"}],
            "mermaid": "graph LR orders --> customers",
            "hubs": ["orders"],
        },
        "fk_candidates": [{"from_table": "orders", "from_col": "customer_id",
                           "to_table": "customers", "to_col": "id",
                           "inclusion": 1.0, "cardinality": "N:1"}],
    }
    ch = _relaciones_chapter(db_profile)
    figs = [b for b in ch["blocks"] if b.get("kind") == "figure"]
    assert len(figs) == 1, ch["blocks"]
    # El make() perezoso produce una matplotlib Figure real.
    import matplotlib
    matplotlib.use("Agg")
    fig = figs[0]["make"]()
    from matplotlib.figure import Figure
    assert isinstance(fig, Figure)
    assert fig.get_axes(), "la Figure del join graph debe tener al menos un eje"


def test_relaciones_chapter_no_figure_when_no_edges():
    """Sin edges, no se añade bloque Figure (capítulo dice 'sin FK')."""
    db_profile = {"join_graph": {"nodes": [], "edges": [], "mermaid": "",
                                 "hubs": []}, "fk_candidates": []}
    ch = _relaciones_chapter(db_profile)
    assert not [b for b in ch["blocks"] if b.get("kind") == "figure"]
