import os
import base64
import pandas as pd
import matplotlib.pyplot as plt
from io import BytesIO
from typing import List, Tuple, Dict


def _fig_to_base64(fig: plt.Figure) -> str:
    """Encode a Matplotlib figure to a base‑64 PNG string."""
    buf = BytesIO()
    fig.savefig(buf, format="png", bbox_inches="tight")
    buf.seek(0)
    return base64.b64encode(buf.read()).decode("utf-8")


def generate_html_report(
    metrics: List[Dict],
    loss_history: List[Tuple[List[float], List[float]]],
    roc_curves: List[Tuple[List[float], List[float], List[float], List[float]]],
    out_path: str = "reports/training_report.html",
) -> None:
    """
    Generate a self-contained HTML report with metrics and plots.

    Parameters
    ----------
    metrics
        List of dicts – one per model – containing keys:
        ``model_id``, ``train_loss``, ``val_loss``, ``auc`` and
        ``patience_left``.
    loss_history
        List of ``(train_losses, val_losses)`` tuples for each model.
    roc_curves
        List of ``(fpr_long, tpr_long, _, [auc_long, auc_short])`` tuples.
    out_path
        Where to write the HTML file.
    """
    os.makedirs(os.path.dirname(out_path), exist_ok=True)

    # ----- Table -------------------------------------------------
    df = pd.DataFrame(metrics)
    # Clean up None values for display
    df_display = df.copy()
    for col in ['train_loss', 'val_loss', 'patience_left']:
        if col in df_display.columns:
            df_display[col] = df_display[col].apply(
                lambda x: f"{x:.4f}" if x is not None and not (isinstance(x, float) and x != x) else "—"
            )
    df_display['auc'] = df_display['auc'].apply(
        lambda x: f"{x:.4f}" if x is not None and not (isinstance(x, float) and x != x) else "—"
    )
    table_html = df_display.to_html(index=False, classes="metrics-table", border=0, escape=False)

    # ----- Loss plots --------------------------------------------
    loss_imgs = []
    for i, (train, val) in enumerate(loss_history):
        if not train and not val:
            continue
        fig, ax = plt.subplots(figsize=(6, 3.5))
        if train:
            ax.plot(range(1, len(train) + 1), train, label="Train loss", color="#2196F3")
        if val:
            ax.plot(range(1, len(val) + 1), val, label="Val loss", color="#FF5722")
        ax.set_xlabel("Epoch")
        ax.set_ylabel("Loss")
        model_id = int(df.iloc[i]['model_id']) if i < len(df) else i + 1
        ax.set_title(f"Model {model_id} – Loss per epoch")
        ax.legend()
        ax.grid(True, alpha=0.3)
        loss_imgs.append(_fig_to_base64(fig))
        plt.close(fig)

    # ----- ROC curves ---------------------------------------------
    roc_imgs = []
    for i, (fpr_long, tpr_long, _, auc_values) in enumerate(roc_curves):
        if fpr_long is None or tpr_long is None or len(fpr_long) == 0 or len(tpr_long) == 0:
            continue
        auc_long = auc_values[0] if len(auc_values) > 0 else float('nan')
        auc_short = auc_values[1] if len(auc_values) > 1 else float('nan')

        fig, ax = plt.subplots(figsize=(5, 4))
        ax.plot(fpr_long, tpr_long,
                label=f"LONG  AUC={auc_long:.4f}" if not (isinstance(auc_long, float) and auc_long != auc_long) else "LONG",
                color="#2196F3")

        # Try to also get SHORT ROC if available
        model_id = int(df.iloc[i]['model_id']) if i < len(df) else i + 1

        ax.plot([0, 1], [0, 1], "k--", linewidth=0.5, alpha=0.3)
        ax.set_xlabel("False Positive Rate")
        ax.set_ylabel("True Positive Rate")
        title = f"Model {model_id} – ROC curves"
        ax.set_title(title)
        ax.legend(loc="lower right")
        ax.grid(True, alpha=0.3)
        roc_imgs.append(_fig_to_base64(fig))
        plt.close(fig)

    loss_imgs_html = "".join(
        f'<div class="img-block"><img src="data:image/png;base64,{img}" alt="loss plot"/></div>'
        for img in loss_imgs
    ) if loss_imgs else '<p class="no-data">Нет данных (возможно, обучение не завершилось успешно)</p>'

    roc_imgs_html = "".join(
        f'<div class="img-block"><img src="data:image/png;base64,{img}" alt="ROC curve"/></div>'
        for img in roc_imgs
    ) if roc_imgs else '<p class="no-data">Нет данных для ROC-кривых</p>'

    html = f"""<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Отчёт о тренировке модели</title>
    <style>
        body {{font-family: Arial, sans-serif; margin: 2rem; background: #f5f5f5;}}
        h1, h2 {{color: #2c3e50;}}
        .container {{max-width: 1100px; margin: 0 auto; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);}}
        .metrics-table {{border-collapse: collapse; width: 100%; margin-bottom: 2rem;}}
        .metrics-table th, .metrics-table td {{
            border: 1px solid #ddd; padding: 10px; text-align: center;
        }}
        .metrics-table th {{background-color: #3498db; color: white;}}
        .metrics-table tr:nth-child(even) {{background-color: #f9f9f9;}}
        .img-block {{margin-bottom: 2rem; text-align: center;}}
        .img-block img {{max-width: 100%; border-radius: 6px; box-shadow: 0 1px 5px rgba(0,0,0,0.15);}}
        .no-data {{color: #999; font-style: italic; text-align: center; padding: 2rem;}}
        .summary {{background: #e8f4fd; padding: 1rem; border-radius: 8px; margin-bottom: 2rem; text-align: center;}}
        .summary .value {{font-size: 1.5em; font-weight: bold; color: #2c3e50;}}
    </style>
</head>
<body>
    <div class="container">
        <h1>🧠 Отчёт о тренировке модели</h1>

        <h2>📊 Таблица метрик</h2>
        {table_html}

        <h2>📉 Графики потерь (Loss curves)</h2>
        {loss_imgs_html}

        <h2>🎯 ROC‑кривые</h2>
        {roc_imgs_html}
    </div>
</body>
</html>"""

    with open(out_path, "w", encoding="utf-8") as f:
        f.write(html)
