"""Heatmap chart builders for HTML reports."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pandas as pd
import plotly.graph_objects as go

from .theme import DARK_THEME, get_winrate_colorscale, to_html

if TYPE_CHECKING:
    from ..loaders.elo import EloData
    from ..loaders.validation import ValidationData


def create_matchup_heatmap(data: ValidationData) -> str:
    """Create an interactive matchup heatmap.

    Args:
        data: ValidationData with matchup information

    Returns:
        HTML string with embedded Plotly chart
    """
    matrix = data.get_matchup_matrix()

    # Get commander names for labels
    deck_to_commander = {d.deck_id: d.commander_name for d in data.deck_stats}

    # Sort by faction then by commander name
    deck_to_faction = {d.deck_id: d.faction for d in data.deck_stats}
    faction_order = ["argentum", "symbiote", "obsidion", "neutral"]

    def sort_key(deck_id):
        faction = deck_to_faction.get(deck_id, "neutral")
        faction_idx = faction_order.index(faction) if faction in faction_order else 999
        return (faction_idx, deck_to_commander.get(deck_id, deck_id))

    sorted_decks = sorted(matrix.index, key=sort_key)
    matrix = matrix.loc[sorted_decks, sorted_decks]

    labels = [deck_to_commander.get(d, d) for d in sorted_decks]

    # Create hover text
    hover_text = []
    for i, d1 in enumerate(sorted_decks):
        row = []
        for j, d2 in enumerate(sorted_decks):
            val = matrix.loc[d1, d2]
            if pd.isna(val):
                row.append("")
            else:
                row.append(f"{labels[i]} vs {labels[j]}<br>Win Rate: {val*100:.1f}%")
        hover_text.append(row)

    # Convert NaN to None for proper display
    z_values = matrix.values.tolist()
    for i in range(len(z_values)):
        for j in range(len(z_values[i])):
            if pd.isna(z_values[i][j]):
                z_values[i][j] = None

    fig = go.Figure(
        data=go.Heatmap(
            z=z_values,
            x=labels,
            y=labels,
            colorscale=get_winrate_colorscale(),
            zmin=0,
            zmax=1,
            text=hover_text,
            hoverinfo="text",
            colorbar={
                "title": {"text": "Win Rate", "font": {"color": DARK_THEME["text_primary"]}},
                "tickvals": [0, 0.25, 0.5, 0.75, 1.0],
                "ticktext": ["0%", "25%", "50%", "75%", "100%"],
                "tickfont": {"color": DARK_THEME["text_primary"]},
            },
        )
    )

    fig.update_layout(
        title={"text": "Matchup Matrix", "font": {"color": DARK_THEME["text_primary"]}},
        paper_bgcolor=DARK_THEME["bg_primary"],
        plot_bgcolor=DARK_THEME["bg_secondary"],
        font={"color": DARK_THEME["text_primary"]},
        xaxis={"title": "Opponent", "tickangle": 45},
        yaxis={"title": "Deck", "autorange": "reversed"},
        height=max(400, len(labels) * 45),
        margin={"t": 50, "b": 120, "l": 150, "r": 30},
    )

    return to_html(fig)


def create_elo_prediction_heatmap(elo_data: EloData) -> str:
    """Create a heatmap of predicted win rates based on ELO.

    Args:
        elo_data: EloData object with ratings

    Returns:
        HTML string with embedded Plotly chart
    """
    predictions = elo_data.get_matchup_predictions()

    # Sort by rating
    ranked_decks = elo_data.get_ranked_decks()
    sorted_ids = [d.deck_id for d in ranked_decks]

    # Build labels and matrix
    labels = [
        elo_data.ratings[d].commander_name or d
        for d in sorted_ids
        if d in predictions
    ]

    z_values = []
    hover_text = []

    for d1 in sorted_ids:
        if d1 not in predictions:
            continue
        row = []
        hover_row = []
        for d2 in sorted_ids:
            if d2 not in predictions:
                continue
            val = predictions[d1].get(d2, 0.5)
            row.append(val)
            name1 = elo_data.ratings[d1].commander_name or d1
            name2 = elo_data.ratings[d2].commander_name or d2
            hover_row.append(f"{name1} vs {name2}<br>Expected: {val*100:.1f}%")
        z_values.append(row)
        hover_text.append(hover_row)

    fig = go.Figure(
        data=go.Heatmap(
            z=z_values,
            x=labels,
            y=labels,
            colorscale=get_winrate_colorscale(),
            zmin=0,
            zmax=1,
            text=hover_text,
            hoverinfo="text",
            colorbar={
                "title": {"text": "Win Prob", "font": {"color": DARK_THEME["text_primary"]}},
                "tickvals": [0, 0.25, 0.5, 0.75, 1.0],
                "ticktext": ["0%", "25%", "50%", "75%", "100%"],
                "tickfont": {"color": DARK_THEME["text_primary"]},
            },
        )
    )

    fig.update_layout(
        title={"text": "Predicted Win Rates (ELO-based)", "font": {"color": DARK_THEME["text_primary"]}},
        paper_bgcolor=DARK_THEME["bg_primary"],
        plot_bgcolor=DARK_THEME["bg_secondary"],
        font={"color": DARK_THEME["text_primary"]},
        xaxis={"title": "Opponent", "tickangle": 45},
        yaxis={"title": "Deck", "autorange": "reversed"},
        height=max(400, len(labels) * 40),
        margin={"t": 50, "b": 120, "l": 150, "r": 30},
    )

    return to_html(fig)


def create_faction_matchup_heatmap(faction_matrix: dict[str, dict[str, float]]) -> str:
    """Create a heatmap showing faction vs faction win rates.

    Args:
        faction_matrix: Nested dict of faction -> faction -> win_rate (0-1)

    Returns:
        HTML string with embedded Plotly chart
    """
    factions = ["argentum", "symbiote", "obsidion"]
    labels = ["Argentum", "Symbiote", "Obsidion"]

    # Build matrix values
    z_values = []
    hover_text = []

    for f1 in factions:
        row = []
        hover_row = []
        for f2 in factions:
            val = faction_matrix.get(f1, {}).get(f2, 0.5)
            row.append(val)
            hover_row.append(
                f"{f1.title()} vs {f2.title()}<br>Win Rate: {val * 100:.1f}%"
            )
        z_values.append(row)
        hover_text.append(hover_row)

    # Create annotations for cell values
    annotations = []
    for i, _f1 in enumerate(factions):
        for j, _f2 in enumerate(factions):
            val = z_values[i][j]
            # Use dark text for values near 50%, white for extreme values
            text_color = "#fff" if abs(val - 0.5) > 0.05 else "#333"
            annotations.append(
                {
                    "x": labels[j],
                    "y": labels[i],
                    "text": f"{val * 100:.1f}%",
                    "showarrow": False,
                    "font": {"color": text_color, "size": 18, "family": "Arial Black"},
                }
            )

    fig = go.Figure(
        data=go.Heatmap(
            z=z_values,
            x=labels,
            y=labels,
            colorscale=[
                [0.0, "#dc3545"],  # Red for 0%
                [0.4, "#ffc107"],  # Yellow for 40%
                [0.5, "#f8f9fa"],  # Near-white for 50%
                [0.6, "#28a745"],  # Green for 60%
                [1.0, "#00d26a"],  # Bright green for 100%
            ],
            zmin=0.3,
            zmax=0.7,
            text=hover_text,
            hoverinfo="text",
            showscale=True,
            colorbar={
                "title": {"text": "Win Rate", "font": {"color": DARK_THEME["text_primary"]}},
                "tickvals": [0.3, 0.4, 0.5, 0.6, 0.7],
                "ticktext": ["30%", "40%", "50%", "60%", "70%"],
                "tickfont": {"color": DARK_THEME["text_primary"]},
            },
        )
    )

    fig.update_layout(
        annotations=annotations,
        paper_bgcolor=DARK_THEME["bg_primary"],
        plot_bgcolor=DARK_THEME["bg_secondary"],
        font={"color": DARK_THEME["text_primary"]},
        xaxis={"title": "Opponent Faction", "tickfont": {"size": 14}},
        yaxis={"title": "Your Faction", "autorange": "reversed", "tickfont": {"size": 14}},
        height=400,
        margin={"t": 30, "b": 80, "l": 100, "r": 80},
    )

    return to_html(fig)
