"""
Principal Component Analysis (PCA) — comprehensive topic content with interactive graphs.
"""

import json
import numpy as np


def _pca_2d_demo():
    """Show PCA projecting 2D correlated data onto principal components."""
    np.random.seed(42)
    cov = [[2, 1.5], [1.5, 1.5]]
    X = np.random.multivariate_normal([3, 3], cov, 120)

    mean = X.mean(axis=0)
    eigvals, eigvecs = np.linalg.eigh(np.cov(X.T))
    idx = np.argsort(eigvals)[::-1]
    eigvals, eigvecs = eigvals[idx], eigvecs[:, idx]

    pc1_end = mean + eigvecs[:, 0] * eigvals[0] * 1.5
    pc2_end = mean + eigvecs[:, 1] * eigvals[1] * 1.5

    data = [
        {"x": X[:, 0].tolist(), "y": X[:, 1].tolist(), "mode": "markers", "type": "scatter", "name": "Data",
         "marker": {"size": 7, "color": "#6C63FF", "opacity": 0.6, "line": {"width": 1, "color": "#fff"}}},
        {"x": [mean[0], pc1_end[0]], "y": [mean[1], pc1_end[1]], "mode": "lines", "type": "scatter", "name": "PC1 (max variance)",
         "line": {"color": "#FF6584", "width": 4}},
        {"x": [mean[0], pc2_end[0]], "y": [mean[1], pc2_end[1]], "mode": "lines", "type": "scatter", "name": "PC2",
         "line": {"color": "#34d399", "width": 4}},
        {"x": [mean[0]], "y": [mean[1]], "mode": "markers", "type": "scatter", "name": "Mean",
         "marker": {"size": 14, "color": "#fbbf24", "symbol": "diamond"}},
    ]
    layout = {
        "template": "plotly_dark", "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": "Principal Components on 2D Data", "font": {"size": 18}},
        "xaxis": {"title": "Feature 1", "gridcolor": "rgba(255,255,255,0.08)", "scaleanchor": "y"},
        "yaxis": {"title": "Feature 2", "gridcolor": "rgba(255,255,255,0.08)"},
        "margin": {"l": 60, "r": 30, "t": 50, "b": 50},
    }
    return json.dumps({"data": data, "layout": layout})


def _variance_explained():
    """Show cumulative variance explained by each component."""
    components = list(range(1, 11))
    individual = [0.42, 0.22, 0.12, 0.08, 0.05, 0.04, 0.03, 0.02, 0.01, 0.01]
    cumulative = np.cumsum(individual).tolist()

    data = [
        {"x": components, "y": individual, "name": "Individual", "type": "bar",
         "marker": {"color": "#6C63FF", "line": {"width": 1, "color": "#fff"}}},
        {"x": components, "y": cumulative, "name": "Cumulative", "type": "scatter", "mode": "lines+markers",
         "line": {"color": "#FF6584", "width": 3}, "marker": {"size": 7}},
        {"x": [1, 10], "y": [0.95, 0.95], "mode": "lines", "type": "scatter", "name": "95% Threshold",
         "line": {"color": "#fbbf24", "width": 1.5, "dash": "dash"}},
    ]
    layout = {
        "template": "plotly_dark", "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": "Variance Explained by Components", "font": {"size": 18}},
        "xaxis": {"title": "Principal Component", "dtick": 1, "gridcolor": "rgba(255,255,255,0.08)"},
        "yaxis": {"title": "Variance Explained Ratio", "gridcolor": "rgba(255,255,255,0.08)", "range": [0, 1.05]},
        "margin": {"l": 60, "r": 30, "t": 50, "b": 50},
    }
    return json.dumps({"data": data, "layout": layout})


def _iris_3d_pca():
    """Project Iris dataset to 3D using PCA."""
    np.random.seed(42)
    # Simulate Iris PCA results
    n = 50
    X0 = np.random.randn(n, 3) * 0.3 + np.array([-2, 0.3, 0])
    X1 = np.random.randn(n, 3) * 0.5 + np.array([0.5, -0.8, 0.5])
    X2 = np.random.randn(n, 3) * 0.5 + np.array([1.5, 0.5, -0.3])

    data = [
        {"x": X0[:, 0].tolist(), "y": X0[:, 1].tolist(), "z": X0[:, 2].tolist(),
         "mode": "markers", "type": "scatter3d", "name": "Setosa",
         "marker": {"size": 5, "color": "#6C63FF", "opacity": 0.8}},
        {"x": X1[:, 0].tolist(), "y": X1[:, 1].tolist(), "z": X1[:, 2].tolist(),
         "mode": "markers", "type": "scatter3d", "name": "Versicolor",
         "marker": {"size": 5, "color": "#FF6584", "opacity": 0.8}},
        {"x": X2[:, 0].tolist(), "y": X2[:, 1].tolist(), "z": X2[:, 2].tolist(),
         "mode": "markers", "type": "scatter3d", "name": "Virginica",
         "marker": {"size": 5, "color": "#34d399", "opacity": 0.8}},
    ]
    layout = {
        "template": "plotly_dark", "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": "Iris Dataset — First 3 Principal Components", "font": {"size": 18}},
        "scene": {
            "xaxis": {"title": "PC1", "gridcolor": "rgba(255,255,255,0.08)"},
            "yaxis": {"title": "PC2", "gridcolor": "rgba(255,255,255,0.08)"},
            "zaxis": {"title": "PC3", "gridcolor": "rgba(255,255,255,0.08)"},
            "bgcolor": "rgba(0,0,0,0)",
        },
        "margin": {"l": 0, "r": 0, "t": 50, "b": 0},
    }
    return json.dumps({"data": data, "layout": layout})


def get_content() -> dict:
    return {
        "title": "Principal Component Analysis",
        "icon": "🔬",
        "subtitle": "Dimensionality reduction by finding directions of maximum variance",
        "sections": [
            {"id": "introduction", "heading": "What is PCA?", "type": "text",
             "content": ("PCA is the most widely-used <strong>dimensionality reduction</strong> technique. It transforms "
                         "high-dimensional data into a lower-dimensional space by finding the axes (<strong>principal components</strong>) "
                         "along which the data varies the most.\n\n"
                         "It's used for visualization, noise reduction, speeding up ML algorithms, and feature extraction.")},
            {"id": "visual", "heading": "Principal Components Visualized", "type": "graph",
             "description": "The red arrow (PC1) points in the direction of maximum variance. The green arrow (PC2) is perpendicular. Projecting data onto PC1 alone captures most of the information.",
             "graph_json": _pca_2d_demo()},
            {"id": "math", "heading": "The Mathematics", "type": "math",
             "content": [
                 {"label": "Covariance Matrix", "formula": "\\Sigma = \\frac{1}{n-1} X^T X",
                  "explanation": "After centering the data, the covariance matrix captures how features relate to each other."},
                 {"label": "Eigen Decomposition", "formula": "\\Sigma v = \\lambda v",
                  "explanation": "Eigenvectors (v) are the principal component directions. Eigenvalues (λ) represent the variance along each direction."},
                 {"label": "Projection", "formula": "Z = X W_k",
                  "explanation": "Data is projected onto the top-k eigenvectors (W_k) to reduce from d dimensions to k dimensions."},
                 {"label": "Variance Explained", "formula": "\\text{Ratio}_k = \\frac{\\lambda_k}{\\sum_{i=1}^{d} \\lambda_i}",
                  "explanation": "Each component explains a fraction of the total variance. Choose enough components to reach 95%."},
             ]},
            {"id": "variance", "heading": "Variance Explained (Scree Plot)", "type": "graph",
             "description": "The bar chart shows individual variance per component. The red line shows cumulative variance. Choose the number of components where cumulative reaches ~95%.",
             "graph_json": _variance_explained()},
            {"id": "3d-iris", "heading": "3D PCA on Iris Dataset", "type": "graph",
             "description": "The 4-dimensional Iris dataset projected into 3D using PCA. Classes that were overlapping in the original space become nicely separated. Drag to rotate!",
             "graph_json": _iris_3d_pca()},
            {"id": "implementation", "heading": "Python Implementation", "type": "code", "language": "python",
             "content": ("from sklearn.decomposition import PCA\nfrom sklearn.preprocessing import StandardScaler\n"
                         "from sklearn.datasets import load_iris\nimport numpy as np\n\n"
                         "# Load and scale data\niris = load_iris()\nX = StandardScaler().fit_transform(iris.data)\n\n"
                         "# Fit PCA\npca = PCA()\npca.fit(X)\n\n"
                         "# Variance explained\nprint('Variance explained per component:')\n"
                         "for i, v in enumerate(pca.explained_variance_ratio_):\n"
                         "    print(f'  PC{i+1}: {v:.3f} ({sum(pca.explained_variance_ratio_[:i+1]):.1%} cumulative)')\n\n"
                         "# Reduce to 2 components\npca_2d = PCA(n_components=2)\nX_reduced = pca_2d.fit_transform(X)\n"
                         "print(f'\\nOriginal shape: {X.shape}')\nprint(f'Reduced shape:  {X_reduced.shape}')\n"
                         "print(f'Information retained: {sum(pca_2d.explained_variance_ratio_):.1%}')\n")},
            {"id": "tips", "heading": "Key Takeaways", "type": "list",
             "entries": [
                 {"title": "Always Standardize First", "detail": "PCA is sensitive to scale. Without standardization, features with larger ranges dominate the components."},
                 {"title": "Not for Categorical Data", "detail": "PCA works with continuous features. For categorical, use MCA (Multiple Correspondence Analysis)."},
                 {"title": "Loses Interpretability", "detail": "Principal components are linear combinations of original features — harder to explain than raw features."},
                 {"title": "Use for Preprocessing", "detail": "Apply PCA before KNN, SVM, or neural networks to reduce computation and avoid the curse of dimensionality."},
             ]},
        ],
    }
