"""
Loss Functions — comprehensive topic content with interactive graphs.
"""

import json
import numpy as np


def _regression_losses():
    """Compare regression loss functions."""
    y_pred = np.linspace(-3, 3, 200)
    y_true = 0  # error = y_pred - y_true = y_pred
    mse = y_pred ** 2
    mae = np.abs(y_pred)
    huber = np.where(np.abs(y_pred) <= 1, 0.5 * y_pred ** 2, np.abs(y_pred) - 0.5)

    data = [
        {"x": y_pred.tolist(), "y": mse.tolist(), "mode": "lines", "type": "scatter", "name": "MSE (L2)",
         "line": {"color": "#6C63FF", "width": 3}},
        {"x": y_pred.tolist(), "y": mae.tolist(), "mode": "lines", "type": "scatter", "name": "MAE (L1)",
         "line": {"color": "#FF6584", "width": 3}},
        {"x": y_pred.tolist(), "y": huber.tolist(), "mode": "lines", "type": "scatter", "name": "Huber (δ=1)",
         "line": {"color": "#34d399", "width": 3}},
    ]
    layout = {
        "template": "plotly_dark", "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": "Regression Loss Functions", "font": {"size": 18}},
        "xaxis": {"title": "Error (y_pred - y_true)", "gridcolor": "rgba(255,255,255,0.08)"},
        "yaxis": {"title": "Loss", "gridcolor": "rgba(255,255,255,0.08)", "range": [0, 5]},
        "margin": {"l": 60, "r": 30, "t": 50, "b": 50},
    }
    return json.dumps({"data": data, "layout": layout})


def _classification_losses():
    """Compare classification loss functions."""
    p = np.linspace(0.01, 0.99, 200)
    # For true label y=1
    ce = -np.log(p)
    hinge = np.maximum(0, 1 - (2 * p - 1))  # mapped to [-1,1]
    focal = -((1 - p) ** 2) * np.log(p)

    data = [
        {"x": p.tolist(), "y": ce.tolist(), "mode": "lines", "type": "scatter", "name": "Cross-Entropy",
         "line": {"color": "#6C63FF", "width": 3}},
        {"x": p.tolist(), "y": focal.tolist(), "mode": "lines", "type": "scatter", "name": "Focal Loss (γ=2)",
         "line": {"color": "#FF6584", "width": 3}},
        {"x": p.tolist(), "y": hinge.tolist(), "mode": "lines", "type": "scatter", "name": "Hinge Loss",
         "line": {"color": "#34d399", "width": 3}},
    ]
    layout = {
        "template": "plotly_dark", "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)",
        "title": {"text": "Classification Loss Functions (y=1)", "font": {"size": 18}},
        "xaxis": {"title": "Predicted Probability for True Class", "gridcolor": "rgba(255,255,255,0.08)"},
        "yaxis": {"title": "Loss", "gridcolor": "rgba(255,255,255,0.08)", "range": [0, 5]},
        "margin": {"l": 60, "r": 30, "t": 50, "b": 50},
    }
    return json.dumps({"data": data, "layout": layout})


def get_content() -> dict:
    return {
        "title": "Loss Functions",
        "icon": "📉",
        "subtitle": "Measuring how wrong your model is",
        "sections": [
            {"id": "introduction", "heading": "What are Loss Functions?", "type": "text",
             "content": ("A loss function quantifies the <strong>difference between predictions and reality</strong>. "
                         "It's the objective that the model minimizes during training via gradient descent.\n\n"
                         "Choosing the right loss function is critical — it determines <strong>what</strong> the model "
                         "optimizes for. Different problems require different loss functions.")},
            {"id": "regression", "heading": "Regression Losses", "type": "graph",
             "description": "MSE penalizes large errors quadratically (sensitive to outliers). MAE penalizes linearly (robust). Huber combines both — quadratic for small errors, linear for large.",
             "graph_json": _regression_losses()},
            {"id": "classification", "heading": "Classification Losses", "type": "graph",
             "description": "Cross-entropy is the standard. Focal loss down-weights easy examples (great for imbalanced data). Hinge loss is used in SVMs.",
             "graph_json": _classification_losses()},
            {"id": "math", "heading": "The Mathematics", "type": "math",
             "content": [
                 {"label": "MSE (Mean Squared Error)", "formula": "\\mathcal{L} = \\frac{1}{n} \\sum_{i=1}^{n} (y_i - \\hat{y}_i)^2",
                  "explanation": "Standard for regression. Differentiable, convex, but sensitive to outliers."},
                 {"label": "Binary Cross-Entropy", "formula": "\\mathcal{L} = -\\frac{1}{n} \\sum [y \\log(\\hat{y}) + (1-y)\\log(1-\\hat{y})]",
                  "explanation": "Standard for binary classification. Heavily penalizes confident wrong predictions."},
                 {"label": "Categorical Cross-Entropy", "formula": "\\mathcal{L} = -\\sum_{k=1}^{K} y_k \\log(\\hat{y}_k)",
                  "explanation": "For multi-class classification with softmax output. Only the true class contributes to loss."},
                 {"label": "Focal Loss", "formula": "FL(p_t) = -(1-p_t)^\\gamma \\log(p_t)",
                  "explanation": "Down-weights well-classified examples. γ=2 is common. Essential for object detection with class imbalance."},
             ]},
            {"id": "guide", "heading": "Which Loss to Use", "type": "list",
             "entries": [
                 {"title": "Regression → MSE", "detail": "Default. If outliers matter, use MAE or Huber. If you want relative error, use MAPE."},
                 {"title": "Binary Classification → BCE", "detail": "Binary Cross-Entropy with sigmoid output. Use BCEWithLogitsLoss in PyTorch for numerical stability."},
                 {"title": "Multi-class → CE + Softmax", "detail": "CrossEntropyLoss in PyTorch (includes softmax). NLLLoss if you apply log_softmax yourself."},
                 {"title": "Imbalanced Classes → Focal Loss", "detail": "Or weighted cross-entropy. Focal loss is preferred for severe imbalance (e.g., 1:100 ratio)."},
                 {"title": "Ranking/Similarity → Triplet Loss", "detail": "Learn embeddings where similar items are close and dissimilar items are far. Used in face recognition."},
                 {"title": "Image Generation → Perceptual Loss", "detail": "Compare feature maps from a pretrained CNN instead of pixel values. Produces sharper images."},
             ]},
            {"id": "implementation", "heading": "Python Implementation", "type": "code", "language": "python",
             "content": ("import torch\nimport torch.nn as nn\n\n"
                         "# Regression losses\nmse = nn.MSELoss()\nl1 = nn.L1Loss()        # MAE\n"
                         "huber = nn.HuberLoss(delta=1.0)\n\n"
                         "# Classification losses\n"
                         "ce = nn.CrossEntropyLoss()       # multi-class (includes softmax)\n"
                         "bce = nn.BCEWithLogitsLoss()      # binary (includes sigmoid)\n\n"
                         "# Weighted cross-entropy for imbalanced data\n"
                         "weights = torch.tensor([1.0, 5.0, 1.0])  # upweight minority class\n"
                         "weighted_ce = nn.CrossEntropyLoss(weight=weights)\n\n"
                         "# Example usage\n"
                         "pred = torch.randn(8, 3)    # batch=8, classes=3\n"
                         "target = torch.randint(0, 3, (8,))\n"
                         "loss = ce(pred, target)\n"
                         "print(f'Loss: {loss.item():.4f}')\n")},
        ],
    }
