"""
Cross-Validation — comprehensive topic content with interactive graphs.
"""

import json
import numpy as np


def _kfold_visualization():
    """Show K-Fold cross-validation splits."""
    k = 5
    n = 5  # blocks per fold
    data = []
    colors_train = "rgba(108,99,255,0.5)"
    colors_val = "rgba(255,101,132,0.8)"

    z = []
    for fold in range(k):
        row = []
        for block in range(k):
            row.append(1 if block == fold else 0)
        z.append(row)

    x_labels = [f"Block {i+1}" for i in range(k)]
    y_labels = [f"Fold {i+1}" for i in range(k)]
    text = [["Validation" if z[i][j] == 1 else "Training" for j in range(k)] for i in range(k)]

    data = [{
        "z": z, "x": x_labels, "y": y_labels,
        "type": "heatmap",
        "colorscale": [[0, "#6C63FF"], [1, "#FF6584"]],
        "text": text, "texttemplate": "%{text}",
        "textfont": {"size": 14, "color": "#fff"},
        "showscale": False,
    }]
    layout = {
        "template": "plotly_dark", "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": "5-Fold Cross-Validation Splits", "font": {"size": 18}},
        "yaxis": {"autorange": "reversed"},
        "margin": {"l": 80, "r": 30, "t": 50, "b": 50},
    }
    return json.dumps({"data": data, "layout": layout})


def _cv_scores():
    """Show cross-validation scores across folds."""
    folds = ["Fold 1", "Fold 2", "Fold 3", "Fold 4", "Fold 5"]
    scores = [0.92, 0.88, 0.95, 0.91, 0.89]
    mean_score = np.mean(scores)
    colors = ["#34d399" if s >= mean_score else "#FF6584" for s in scores]

    data = [
        {"x": folds, "y": scores, "type": "bar",
         "marker": {"color": colors, "line": {"width": 1.5, "color": "#fff"}},
         "text": [f"{s:.2f}" for s in scores], "textposition": "outside",
         "textfont": {"color": "#e0e0e0", "size": 14}},
        {"x": folds, "y": [mean_score] * 5, "mode": "lines", "type": "scatter",
         "name": f"Mean = {mean_score:.3f}",
         "line": {"color": "#fbbf24", "width": 2.5, "dash": "dash"}},
    ]
    layout = {
        "template": "plotly_dark", "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": "Accuracy per Fold", "font": {"size": 18}},
        "yaxis": {"title": "Accuracy", "gridcolor": "rgba(255,255,255,0.08)", "range": [0.82, 1.0]},
        "margin": {"l": 60, "r": 30, "t": 50, "b": 50},
    }
    return json.dumps({"data": data, "layout": layout})


def _cv_vs_holdout():
    """Compare hold-out vs cross-validation variance."""
    np.random.seed(42)
    holdout_scores = np.random.normal(0.90, 0.04, 20).tolist()
    cv_scores = np.random.normal(0.91, 0.015, 20).tolist()

    data = [
        {"y": holdout_scores, "type": "box", "name": "Hold-Out (random splits)",
         "marker": {"color": "#FF6584"}, "boxmean": True},
        {"y": cv_scores, "type": "box", "name": "5-Fold CV",
         "marker": {"color": "#34d399"}, "boxmean": True},
    ]
    layout = {
        "template": "plotly_dark", "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": "Variance: Hold-Out vs Cross-Validation", "font": {"size": 18}},
        "yaxis": {"title": "Accuracy", "gridcolor": "rgba(255,255,255,0.08)"},
        "margin": {"l": 60, "r": 30, "t": 50, "b": 50},
    }
    return json.dumps({"data": data, "layout": layout})


def get_content() -> dict:
    return {
        "title": "Cross-Validation",
        "icon": "🔄",
        "subtitle": "Reliable model evaluation via systematic data splitting",
        "sections": [
            {"id": "introduction", "heading": "What is Cross-Validation?", "type": "text",
             "content": ("Cross-validation is a technique for <strong>reliably evaluating</strong> model performance "
                         "by using different portions of the data for training and testing across multiple rounds.\n\n"
                         "Instead of a single train/test split (which can be lucky or unlucky), CV gives you a "
                         "<strong>robust estimate</strong> of how your model will perform on unseen data.")},
            {"id": "kfold", "heading": "K-Fold Cross-Validation", "type": "graph",
             "description": "Data is split into K equal folds. In each round, one fold is held out for validation while the rest train the model. Every data point gets to be in the validation set exactly once.",
             "graph_json": _kfold_visualization()},
            {"id": "scores", "heading": "Scores Across Folds", "type": "graph",
             "description": "Each fold produces a score. The final metric is the mean ± std across all folds. If scores vary a lot, your model may be unstable or the data split matters.",
             "graph_json": _cv_scores()},
            {"id": "variance", "heading": "CV vs Hold-Out", "type": "graph",
             "description": "Cross-validation produces much more stable estimates than random hold-out splits. The box plot shows that CV (green) has much less variance than repeated hold-out (red).",
             "graph_json": _cv_vs_holdout()},
            {"id": "variants", "heading": "CV Strategies", "type": "list",
             "entries": [
                 {"title": "K-Fold (K=5 or 10)", "detail": "The standard. Good balance between bias and variance. K=5 is faster, K=10 is more thorough."},
                 {"title": "Stratified K-Fold", "detail": "Preserves class distribution in each fold. Essential for imbalanced datasets."},
                 {"title": "Leave-One-Out (LOO)", "detail": "K = n (each sample is a fold). Very low bias but high variance and slow. Use for very small datasets."},
                 {"title": "Time Series Split", "detail": "For temporal data. Training always comes before validation. Never leak future data into training."},
                 {"title": "Repeated K-Fold", "detail": "Runs K-Fold multiple times with different random splits. Reduces variance of the estimate. Use for final model selection."},
                 {"title": "Group K-Fold", "detail": "Ensures that samples from the same group (e.g., same patient) aren't in both train and test. Prevents data leakage."},
             ]},
            {"id": "implementation", "heading": "Python Implementation", "type": "code", "language": "python",
             "content": ("from sklearn.model_selection import (\n    cross_val_score, StratifiedKFold, GridSearchCV\n)\n"
                         "from sklearn.ensemble import RandomForestClassifier\nfrom sklearn.datasets import load_iris\n"
                         "import numpy as np\n\n# Load data\niris = load_iris()\nX, y = iris.data, iris.target\n\n"
                         "# Simple cross-validation\nmodel = RandomForestClassifier(n_estimators=100, random_state=42)\n"
                         "scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')\n"
                         "print(f'CV Accuracy: {scores.mean():.4f} ± {scores.std():.4f}')\n"
                         "print(f'Per fold: {scores}')\n\n"
                         "# Stratified K-Fold (manual control)\n"
                         "skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\n"
                         "for i, (train_idx, val_idx) in enumerate(skf.split(X, y)):\n"
                         "    model.fit(X[train_idx], y[train_idx])\n"
                         "    score = model.score(X[val_idx], y[val_idx])\n"
                         "    print(f'Fold {i+1}: {score:.4f}')\n\n"
                         "# GridSearchCV (hyperparameter tuning + CV)\nparam_grid = {'n_estimators': [50, 100, 200], 'max_depth': [3, 5, None]}\n"
                         "grid = GridSearchCV(model, param_grid, cv=5, scoring='accuracy')\n"
                         "grid.fit(X, y)\nprint(f'\\nBest params: {grid.best_params_}')\n"
                         "print(f'Best CV score: {grid.best_score_:.4f}')\n")},
            {"id": "tips", "heading": "Key Takeaways", "type": "list",
             "entries": [
                 {"title": "Always Use CV for Model Selection", "detail": "Never compare models on a single train/test split. Use cross-validation to get reliable comparisons."},
                 {"title": "Use Stratified for Classification", "detail": "Ensures each fold has the same class distribution. Default in sklearn's cross_val_score for classifiers."},
                 {"title": "Don't Fit on Test Data", "detail": "Feature scaling, encoding, and selection must be done INSIDE the CV loop. Use Pipeline to prevent leakage."},
                 {"title": "Report Mean ± Std", "detail": "Always report both the mean score and standard deviation. A model with 0.95 ± 0.08 may be worse than 0.92 ± 0.01."},
             ]},
        ],
    }
