"""Deterministic alt-text scaffold for matplotlib and plotly figures."""

from typing import Any


def alt_template(fig: Any) -> str:
    """Generate an alt-text template for a matplotlib or plotly figure.

    Returns a sentence scaffold with chart type, axis labels, and group
    counts pre-filled. A bracketed placeholder marks where the
    substantive trend description belongs. Pass the edited string to
    :func:`a11yviz.alt_text` to attach it.
    """
    if _is_matplotlib(fig):
        return _alt_template_matplotlib(fig)
    if _is_plotly(fig):
        return _alt_template_plotly(fig)
    raise TypeError(
        f"alt_template() supports matplotlib and plotly figures, got {type(fig).__name__}"
    )


def _is_matplotlib(fig):
    return type(fig).__module__.startswith("matplotlib.")


def _is_plotly(fig):
    return type(fig).__module__.startswith("plotly.")


_mpl_phrases = {
    "scatter":   "Scatter plot",
    "line":      "Line plot",
    "bar":       "Bar chart",
    "image":     "Heatmap",
    "boxplot":   "Boxplot",
    "histogram": "Histogram",
    "violin":    "Violin plot",
}

_plotly_phrases = {
    "scatter:markers":       "Scatter plot",
    "scatter:lines":         "Line plot",
    "scatter:lines+markers": "Line plot with markers",
    "scatter:none":          "Scatter plot",
    "scatter":               "Scatter plot",
    "bar":                   "Bar chart",
    "heatmap":               "Heatmap",
    "contour":               "Contour plot",
    "box":                   "Boxplot",
    "violin":                "Violin plot",
    "histogram":             "Histogram",
    "surface":               "3-D surface plot",
}


def _alt_template_matplotlib(fig):
    ax = fig if hasattr(fig, "get_xlabel") else (fig.axes[0] if fig.axes else None)
    if ax is None:
        return "Plot. [REPLACE - describe the substantive trend.]"

    chart_type = _detect_mpl_chart_type(ax)
    x_lab = ax.get_xlabel() or "x"
    y_lab = ax.get_ylabel() or "y"
    n     = _count_mpl_points(ax)

    parts = [f"{chart_type} of {y_lab} versus {x_lab}"]
    if n is not None:
        parts[0] += f", {n} data points"
    parts[0] += "."

    x_rng = _ax_numeric_range(ax, "x")
    y_rng = _ax_numeric_range(ax, "y")
    if x_rng is not None:
        parts.append(f"X axis: {x_lab}, range {x_rng[0]} to {x_rng[1]}.")
    if y_rng is not None:
        parts.append(f"Y axis: {y_lab}, range {y_rng[0]} to {y_rng[1]}.")

    legend = ax.get_legend()
    if legend is not None and legend.texts:
        labels = [t.get_text() for t in legend.texts]
        parts.append(f"Groups ({len(labels)}): {', '.join(labels[:5])}.")

    parts.append("[REPLACE - describe the substantive trend, e.g., direction, clusters, outliers.]")
    return " ".join(parts)


def _detect_mpl_chart_type(ax):
    if ax.images:     return _mpl_phrases["image"]
    if ax.collections:return _mpl_phrases["scatter"]
    if ax.patches:    return _mpl_phrases["bar"]
    if ax.lines:      return _mpl_phrases["line"]
    return "Plot"


def _count_mpl_points(ax):
    if ax.collections: return sum(int(c.get_offsets().shape[0]) for c in ax.collections)
    if ax.lines:       return sum(len(line.get_xdata()) for line in ax.lines)
    if ax.patches:     return len(ax.patches)
    return None


def _ax_numeric_range(ax, axis):
    try:
        lo, hi = ax.get_xlim() if axis == "x" else ax.get_ylim()
    except Exception:
        return None
    return (f"{lo:.3g}", f"{hi:.3g}")


def _alt_template_plotly(fig):
    chart_type = "Plot"
    if fig.data:
        first = fig.data[0]
        t     = getattr(first, "type", "scatter") or "scatter"
        mode  = getattr(first, "mode", None)
        key   = f"{t}:{mode}" if (t == "scatter" and mode) else t
        chart_type = _plotly_phrases.get(key, _plotly_phrases.get(t, "Plot"))

    x_lab = _plotly_axis_title(fig, "xaxis") or "x"
    y_lab = _plotly_axis_title(fig, "yaxis") or "y"

    parts = [f"{chart_type} of {y_lab} versus {x_lab}."]
    if len(fig.data) > 1:
        parts.append(f"{len(fig.data)} traces.")
    parts.append("[REPLACE - describe the substantive trend.]")
    return " ".join(parts)


def _plotly_axis_title(fig, axis_name):
    ax = getattr(fig.layout, axis_name, None)
    if ax is None: return None
    title = getattr(ax, "title", None)
    if title is None: return None
    return getattr(title, "text", None)
