"""
Gradient Boosting Playground — interactive parameter exploration.
"""

import numpy as np

META = {
    "title": "Gradient Boosting",
    "icon": "🚀",
    "description": (
        "Watch how gradient boosting builds an ensemble one tree at a time. "
        "A lower learning rate requires more trees but generalizes better. "
        "Increase depth for more expressive stumps."
    ),
}

PARAMS = [
    {
        "name": "n_estimators",
        "label": "Number of Trees",
        "type": "slider",
        "min": 1,
        "max": 200,
        "step": 5,
        "default": 50,
    },
    {
        "name": "learning_rate",
        "label": "Learning Rate",
        "type": "slider",
        "min": 0.01,
        "max": 1.0,
        "step": 0.01,
        "default": 0.1,
    },
    {
        "name": "max_depth",
        "label": "Max Depth",
        "type": "slider",
        "min": 1,
        "max": 8,
        "step": 1,
        "default": 3,
    },
    {
        "name": "noise",
        "label": "Noise / Spread",
        "type": "slider",
        "min": 0.3,
        "max": 2.0,
        "step": 0.1,
        "default": 0.9,
    },
]


def compute(params: dict) -> dict:
    n_est = int(params.get("n_estimators", 50))
    lr = float(params.get("learning_rate", 0.1))
    max_depth = int(params.get("max_depth", 3))
    noise = float(params.get("noise", 0.9))

    np.random.seed(42)
    n = 80
    c0 = np.random.randn(n, 2) * noise + [1, 1]
    c1 = np.random.randn(n, 2) * noise + [3, 3]
    X = np.vstack([c0, c1])
    y = np.array([0] * n + [1] * n)

    from sklearn.ensemble import GradientBoostingClassifier
    clf = GradientBoostingClassifier(
        n_estimators=n_est, learning_rate=lr, max_depth=max_depth, random_state=42
    )
    clf.fit(X, y)

    margin = 2
    x_min, x_max = X[:, 0].min() - margin, X[:, 0].max() + margin
    y_min, y_max = X[:, 1].min() - margin, X[:, 1].max() + margin
    res = 100
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, res), np.linspace(y_min, y_max, res))
    Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1].reshape(xx.shape)
    accuracy = clf.score(X, y)

    data = [
        {
            "z": Z.tolist(),
            "x": np.linspace(x_min, x_max, res).tolist(),
            "y": np.linspace(y_min, y_max, res).tolist(),
            "type": "heatmap",
            "colorscale": [[0, "rgba(108,99,255,0.35)"], [0.5, "rgba(100,100,100,0.1)"], [1, "rgba(255,101,132,0.35)"]],
            "showscale": True,
            "colorbar": {"title": "P(class=1)", "tickfont": {"color": "#aaa"}, "titlefont": {"color": "#aaa"}},
        },
        {
            "x": c0[:, 0].tolist(), "y": c0[:, 1].tolist(),
            "mode": "markers", "type": "scatter", "name": "Class 0",
            "marker": {"size": 7, "color": "#6C63FF", "opacity": 0.85, "line": {"width": 1, "color": "#fff"}},
        },
        {
            "x": c1[:, 0].tolist(), "y": c1[:, 1].tolist(),
            "mode": "markers", "type": "scatter", "name": "Class 1",
            "marker": {"size": 7, "color": "#FF6584", "opacity": 0.85, "line": {"width": 1, "color": "#fff"}},
        },
    ]

    layout = {
        "template": "plotly_dark",
        "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": f"Gradient Boosting — {n_est} trees, lr={lr}, depth={max_depth}, acc={accuracy:.1%}", "font": {"size": 18}},
        "xaxis": {"title": "Feature 1", "gridcolor": "rgba(255,255,255,0.08)"},
        "yaxis": {"title": "Feature 2", "gridcolor": "rgba(255,255,255,0.08)", "scaleanchor": "x"},
        "legend": {"x": 0.02, "y": 0.98},
        "margin": {"l": 60, "r": 30, "t": 60, "b": 50},
    }
    return {"data": data, "layout": layout}
