"""
Logistic Regression Playground — interactive parameter exploration.
"""

import numpy as np

META = {
    "title": "Logistic Regression",
    "icon": "📊",
    "description": (
        "See how logistic regression draws a decision boundary between two classes. "
        "Adjust the regularization strength C and decision threshold to understand "
        "how the model balances precision and recall."
    ),
}

PARAMS = [
    {
        "name": "C",
        "label": "C (Inverse Regularization)",
        "type": "slider",
        "min": 0.01,
        "max": 100.0,
        "step": 0.5,
        "default": 1.0,
    },
    {
        "name": "threshold",
        "label": "Decision Threshold",
        "type": "slider",
        "min": 0.1,
        "max": 0.9,
        "step": 0.05,
        "default": 0.5,
    },
    {
        "name": "noise",
        "label": "Noise / Spread",
        "type": "slider",
        "min": 0.3,
        "max": 2.5,
        "step": 0.1,
        "default": 1.0,
    },
    {
        "name": "n_points",
        "label": "Points per Class",
        "type": "slider",
        "min": 30,
        "max": 150,
        "step": 10,
        "default": 60,
    },
]


def compute(params: dict) -> dict:
    """Fit logistic regression and show decision boundary + probability heatmap."""
    C = float(params.get("C", 1.0))
    threshold = float(params.get("threshold", 0.5))
    noise = float(params.get("noise", 1.0))
    n = int(params.get("n_points", 60))

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

    from sklearn.linear_model import LogisticRegression
    clf = LogisticRegression(C=C, random_state=42, max_iter=500)
    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))
    probs = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1].reshape(xx.shape)
    preds = (probs >= threshold).astype(float)

    # Accuracy at this threshold
    y_pred = (clf.predict_proba(X)[:, 1] >= threshold).astype(int)
    accuracy = np.mean(y_pred == y)

    data = [
        {
            "z": probs.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"}},
            "hoverinfo": "z",
        },
        {
            "x": c0[:, 0].tolist(),
            "y": c0[:, 1].tolist(),
            "mode": "markers",
            "type": "scatter",
            "name": "Class 0",
            "marker": {"size": 8, "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": 8, "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"Logistic Regression — C={C}, threshold={threshold}, 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}
