import marimo

__generated_with = "0.15.2"
app = marimo.App(width="medium")


@app.cell
def _():
    import marimo as mo
    return (mo,)


@app.cell
def _():
    # import marimo as mo
    import sys
    import subprocess
    import importlib
    import pandas as pd
    import altair as alt

    # Instalación perezosa de drawdata si no está disponible
    def _ensure(pkg: str):
        try:
            importlib.import_module(pkg)
        except ImportError:
            subprocess.check_call([sys.executable, "-m", "pip", "install", pkg, "--quiet"])

    _ensure("drawdata")

    from drawdata import ScatterWidget, BarWidget  # API principal
    return BarWidget, ScatterWidget


@app.cell
def _(mo):
    mode_selector = mo.ui.radio(
        options=["Scatter", "Barras"],
        value="Scatter",
        label="Modo de dibujo"
    )
    mode_selector
    return (mode_selector,)


@app.cell
def _(BarWidget, ScatterWidget, mo):
    # Creamos ambos widgets una sola vez y los reusamos reactivamente
    scatter_widget = ScatterWidget()
    bar_widget = BarWidget(
        collection_names=["Serie A", "Serie B"],  # nombres de colecciones (series)
        n_bins=20                                  # nº de bins para eje X
    )

    mo.md("Widgets inicializados. Usa el selector para mostrar uno u otro.")
    return bar_widget, scatter_widget


@app.cell
def _(bar_widget, mode_selector, scatter_widget):
    # Importante en marimo: leer .value en celda separada
    current_widget = scatter_widget if mode_selector.value == "Scatter" else bar_widget
    current_widget
    return


@app.cell
def _(bar_widget, mo, mode_selector, scatter_widget):
    # Para ScatterWidget y BarWidget existe .data_as_pandas
    # En scatter: columnas típicas x, y, color
    # En barras: columnas por bin/serie/valor (según widget)
    df_drawn = (scatter_widget.data_as_pandas
                if mode_selector.value == "Scatter"
                else bar_widget.data_as_pandas)

    # Validación básica
    is_empty = (df_drawn is None) or (len(df_drawn) == 0)
    mo.stop(is_empty, mo.md("Dibuja puntos (Scatter) o columnas (Barras) para ver datos."))

    # Vista tabular interactiva
    mo.ui.dataframe(df_drawn)
    return (df_drawn,)


@app.cell
def _(df_drawn, mode_selector):
    # Nota: todo está encapsulado en funciones locales para evitar redefinir variables globales.

    def _build_chart_c6(df, mode):
        import altair as alt
        import marimo as mo
        # Si no hay datos, mostramos aviso
        if df is None or len(df) == 0:
            return mo.md("Dibuja datos para visualizar el gráfico.")

        # Mapeo robusto (lower -> original) DENTRO de la función
        orig_cols = list(df.columns)
        lower_map = {str(c).lower(): c for c in orig_cols}

        if mode == "Scatter":
            x_col = lower_map.get("x", orig_cols[0])
            y_col = lower_map.get("y", orig_cols[1 if len(orig_cols) > 1 else 0])
            color_col = lower_map.get("color", None)

            chart_scatter = (
                alt.Chart(df)
                .mark_point(filled=True, opacity=0.8, size=80)
                .encode(
                    x=alt.X(x_col, title=str(x_col)),
                    y=alt.Y(y_col, title=str(y_col)),
                    color=alt.Color(color_col, title=str(color_col)) if color_col else alt.value("steelblue"),
                    tooltip=orig_cols
                )
                .properties(title="Dispersión dibujada (reactivo)")
                .interactive()
            )
            return chart_scatter
        else:
            # Barras: detectar bin/x, value/y y collection/serie si existe
            bin_col = lower_map.get("bin") or lower_map.get("x") or orig_cols[0]
            val_col = lower_map.get("value") or lower_map.get("y") or (orig_cols[1] if len(orig_cols) > 1 else orig_cols[0])
            serie_col = (
                lower_map.get("collection")
                or lower_map.get("serie")
                or lower_map.get("series")
                or lower_map.get("group")
                or None
            )

            base = alt.Chart(df).encode(
                x=alt.X(bin_col, title=str(bin_col)),
                y=alt.Y(val_col, title=str(val_col)),
                tooltip=orig_cols
            )

            chart_bars = (
                base.mark_bar()
                .encode(color=alt.Color(serie_col, title=str(serie_col)) if serie_col else alt.value("steelblue"))
                .properties(title="Barras dibujadas (reactivo)")
                .interactive()
            )
            return chart_bars

    # Construimos el gráfico en una variable y la devolvemos como última expresión
    _chart_c6_output = _build_chart_c6(df_drawn, mode_selector.value)
    _chart_c6_output
    return


@app.cell
def _(df_drawn, mode_selector, scatter_widget):
    # También encapsulado para no chocar con nombres de otras celdas.

    def _prepare_ml_or_matrix_c7(df, mode):
        import pandas as pd
        import marimo as mo

        if df is None or len(df) == 0:
            return mo.md("No hay datos para preparar. Dibuja primero en el lienzo.")

        if mode == "Scatter":
            xy = getattr(scatter_widget, "data_as_X_y", None)
            if (xy is None) or (xy[0] is None) or (xy[1] is None):
                return mo.md("Añade puntos en el scatter para obtener X, y.")
            X, y = xy
            n_feats = X.shape[1] if hasattr(X, "shape") and len(X.shape) > 1 else 1
            if n_feats == 1:
                x_cols = ["x"]
            elif n_feats == 2:
                x_cols = ["x", "y"]
            else:
                x_cols = [f"x{i+1}" for i in range(n_feats)]
            preview = pd.DataFrame(X, columns=x_cols)
            preview["target"] = y
            return mo.vstack([
                mo.md(f"Listo para ML — **X**: {getattr(X, 'shape', '')}, **y**: {getattr(y, 'shape', '')}"),
                mo.ui.dataframe(preview.head(20))
            ])
        else:
            # Barras → matriz (filas=bins, columnas=series) o columna única si no hay series
            orig_cols = list(df.columns)
            lower_map = {str(c).lower(): c for c in orig_cols}
            bin_col = lower_map.get("bin") or lower_map.get("x") or orig_cols[0]
            val_col = lower_map.get("value") or lower_map.get("y") or (orig_cols[-1] if len(orig_cols) > 1 else orig_cols[0])
            serie_col = (
                lower_map.get("collection")
                or lower_map.get("serie")
                or lower_map.get("series")
                or lower_map.get("group")
                or None
            )

            if serie_col:
                mat_df = df.pivot_table(index=bin_col, columns=serie_col, values=val_col, fill_value=0, aggfunc="sum").sort_index()
            else:
                mat_df = df[[bin_col, val_col]].set_index(bin_col).sort_index()

            return mo.vstack([
                mo.md(f"Matriz lista para modelar/featurear — forma: **{mat_df.shape}**"),
                mo.ui.dataframe(mat_df.reset_index().head(50))
            ])

    _ml_matrix_c7_output = _prepare_ml_or_matrix_c7(df_drawn, mode_selector.value)
    _ml_matrix_c7_output
    return


@app.cell
def _():
    return


if __name__ == "__main__":
    app.run()
