"""
PCA Playground — interactive parameter exploration.
"""

import numpy as np

META = {
    "title": "PCA",
    "icon": "🔀",
    "description": (
        "See how Principal Component Analysis projects high-dimensional data onto "
        "fewer dimensions. Adjust the number of original features and components "
        "to see how much variance is retained."
    ),
}

PARAMS = [
    {
        "name": "n_features",
        "label": "Original Features",
        "type": "slider",
        "min": 3,
        "max": 15,
        "step": 1,
        "default": 6,
    },
    {
        "name": "n_components",
        "label": "PCA Components",
        "type": "slider",
        "min": 2,
        "max": 10,
        "step": 1,
        "default": 2,
    },
    {
        "name": "n_points",
        "label": "Data Points",
        "type": "slider",
        "min": 50,
        "max": 300,
        "step": 25,
        "default": 150,
    },
    {
        "name": "noise",
        "label": "Noise Level",
        "type": "slider",
        "min": 0.1,
        "max": 3.0,
        "step": 0.1,
        "default": 0.5,
    },
]


def compute(params: dict) -> dict:
    n_features = int(params.get("n_features", 6))
    n_comp = int(params.get("n_components", 2))
    n_points = int(params.get("n_points", 150))
    noise = float(params.get("noise", 0.5))

    # Clamp n_components to n_features
    n_comp = min(n_comp, n_features)

    np.random.seed(42)
    # Generate correlated data with 3 clusters
    n_per = n_points // 3
    centers = np.random.randn(3, n_features) * 3
    X = np.vstack([
        np.random.randn(n_per, n_features) * noise + centers[i]
        for i in range(3)
    ])
    labels = np.repeat([0, 1, 2], n_per)

    # Manual PCA
    X_centered = X - X.mean(axis=0)
    cov = np.cov(X_centered, rowvar=False)
    eigenvalues, eigenvectors = np.linalg.eigh(cov)
    # Sort by eigenvalue descending
    idx = np.argsort(eigenvalues)[::-1]
    eigenvalues = eigenvalues[idx]
    eigenvectors = eigenvectors[:, idx]

    # Project
    X_proj = X_centered @ eigenvectors[:, :n_comp]

    # Explained variance
    total_var = eigenvalues.sum()
    explained = eigenvalues[:n_comp].sum() / total_var * 100 if total_var > 0 else 0
    individual_explained = (eigenvalues / total_var * 100)[:min(n_features, 10)]

    colors = ["#6C63FF", "#FF6584", "#34d399"]

    # Subplot 1: projected scatter (use first 2 components for viz)
    data = []
    for i in range(3):
        mask = labels == i
        data.append({
            "x": X_proj[mask, 0].tolist(),
            "y": X_proj[mask, 1].tolist() if n_comp >= 2 else [0.0] * mask.sum(),
            "mode": "markers", "type": "scatter", "name": f"Cluster {i}",
            "marker": {"size": 7, "color": colors[i], "opacity": 0.8, "line": {"width": 1, "color": "#fff"}},
        })

    # Subplot 2: scree plot (explained variance per component)
    comp_labels = [f"PC{i+1}" for i in range(len(individual_explained))]
    cum_var = np.cumsum(individual_explained).tolist()
    data.append({
        "x": comp_labels, "y": individual_explained.tolist(),
        "type": "bar", "name": "Individual",
        "marker": {"color": ["#6C63FF" if i < n_comp else "rgba(108,99,255,0.2)"
                              for i in range(len(individual_explained))],
                    "line": {"width": 1, "color": "#fff"}},
        "xaxis": "x2", "yaxis": "y2",
    })
    data.append({
        "x": comp_labels, "y": cum_var,
        "mode": "lines+markers", "type": "scatter", "name": "Cumulative",
        "line": {"color": "#FF6584", "width": 2},
        "marker": {"size": 6, "color": "#FF6584"},
        "xaxis": "x2", "yaxis": "y2",
    })

    layout = {
        "template": "plotly_dark",
        "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": f"PCA — {n_features}D → {n_comp}D ({explained:.1f}% variance retained)", "font": {"size": 20}},
        "grid": {"rows": 1, "columns": 2, "pattern": "independent"},
        "xaxis": {"title": "PC 1", "gridcolor": "rgba(255,255,255,0.08)", "domain": [0, 0.45]},
        "yaxis": {"title": "PC 2", "gridcolor": "rgba(255,255,255,0.08)"},
        "xaxis2": {"title": "Component", "gridcolor": "rgba(255,255,255,0.08)", "domain": [0.55, 1]},
        "yaxis2": {"title": "Variance Explained (%)", "gridcolor": "rgba(255,255,255,0.08)", "range": [0, 105], "anchor": "x2"},
        "legend": {"x": 0.02, "y": 0.98},
        "margin": {"l": 60, "r": 30, "t": 60, "b": 50},
    }
    return {"data": data, "layout": layout}
