from typing import Any, Dict, List, Optional, Tuple

import numpy as np
import plotly.graph_objects as go
from plotly.basedatatypes import BaseTraceType
from skfem import Mesh


# 将网格与标量场转为 plotly 三角形 traces + 布局，供 RL 环境实时渲染
def get_plotly_mesh_traces_and_layout(
    mesh: Mesh,
    scalars: np.ndarray,
    title: str = "Mesh",
    mesh_dimension: int = 2,
    boundary: Optional[np.ndarray] = None,
) -> Tuple[List[BaseTraceType], Dict[str, Any]]:
    vertices = mesh.p
    triangles = mesh.t
    n_elements = triangles.shape[1]
    s = np.asarray(scalars, dtype=np.float64).flatten()

    x_tri = vertices[0, triangles].T
    y_tri = vertices[1, triangles].T
    intensity_tri = s[triangles].T

    vmin, vmax = s.min(), s.max()

    traces = []
    for elem_idx in range(n_elements):
        x_e, y_e, s_e = x_tri[elem_idx], y_tri[elem_idx], intensity_tri[elem_idx]
        traces.append(go.Scatter(
            x=x_e.tolist() + [x_e[0]],
            y=y_e.tolist() + [y_e[0]],
            mode="lines",
            fill="toself",
            fillcolor=_get_color(float(np.mean(s_e)), vmin, vmax),
            line=dict(color="black", width=0.5),
            showlegend=False,
            hoverinfo="skip",
        ))

    if traces:
        traces[0].marker = dict(
            color=s.min(), colorscale="RdBu_r", showscale=True,
            colorbar=dict(title="Solution"),
        )

    layout = {
        "title": title,
        "xaxis": {"title": "x", "scaleanchor": "y"},
        "yaxis": {"title": "y"},
        "showlegend": False,
    }
    if boundary is not None:
        layout["xaxis"]["range"] = [boundary[0], boundary[2]]
        layout["yaxis"]["range"] = [boundary[1], boundary[3]]

    return traces, layout


# 标量值 → matplotlib RdBu_r 色表映射的 RGBA 字符串
def _get_color(value: float, vmin: float, vmax: float) -> str:
    import matplotlib.cm as cm
    import matplotlib.colors as mcolors

    norm = mcolors.Normalize(vmin=vmin, vmax=vmax)
    rgba = cm.RdBu_r(norm(value))
    r, g, b, a = rgba
    return f"rgba({int(r * 255)},{int(g * 255)},{int(b * 255)},{a:.2f})"
