"""Shared mpltern helpers for ternary phase-diagram figures."""

from pathlib import Path

import matplotlib.pyplot as plt
import mpltern  # pylint: disable=unused-import  # noqa: F401
import numpy as np


STYLE_PATH = Path(__file__).with_name("plotting_style.mplstyle")


def ternary_axes(
    *,
    ternary_sum=100.0,
    labels=("A", "B", "Polymer"),
    style=None,
    fig=None,
):
    """Create a ternary axis with shared formatting."""
    if style is None:
        style = STYLE_PATH

    plt.style.use(style)

    if fig is None:
        fig = plt.figure()

    ax = plt.subplot(
        projection="ternary",
        ternary_sum=ternary_sum,
    )

    ax.set_tlabel(labels[0])
    ax.set_llabel(labels[1])
    ax.set_rlabel(labels[2])

    for axis in (ax.taxis, ax.laxis, ax.raxis):
        axis.set_label_position("corner")
        axis.set_label_rotation_mode("horizontal")

    ax.tick_params(labelrotation="horizontal")

    ax.annotate(
        "mass fractions [%]",
        xy=(0.5, -0.1),
        xycoords="axes fraction",
        ha="center",
        va="top",
    )

    return fig, ax


def ternary_coords(weights, order=(0, 1, 2), scale=100.0):
    """Return ternary coordinates in the requested component order."""
    idx_top, idx_left, idx_right = order
    return scale * np.asarray(
        [[v[idx_top], v[idx_left], v[idx_right]] for v in weights]
    )


def plot_ternary(ax, xyz, **kwargs):
    """Plot a ternary line from an (N, 3) array."""
    return ax.plot(xyz[:, 0], xyz[:, 1], xyz[:, 2], **kwargs)


def scatter_ternary(ax, xyz, *, idx=0, **kwargs):
    """Scatter one or many ternary points."""
    pts = xyz if xyz.ndim == 2 else xyz[None, :]
    if idx is not None:
        pts = pts[[idx]]
    return ax.scatter(pts[:, 0], pts[:, 1], pts[:, 2], **kwargs)


def close_envelope(
    left,
    right,
    *,
    reverse="left",
    close=False,
):
    """
    Build a closed or open ternary envelope from two curves.

    Parameters
    ----------
    left, right : (N, 3) array_like
        Envelope branches.
    reverse : {"left", "right", None}, optional
        Which branch to reverse before concatenation.
    close : {"start", "end", None}, optional
        If "start", append the first point of the first branch.
        If "end", append the last point of the last branch.
        If None, return an open envelope.
    """
    if reverse == "left":
        left = left[::-1]
    elif reverse == "right":
        right = right[::-1]
    elif reverse is not None:
        raise ValueError("reverse must be 'left', 'right', or None")

    parts = [left, right]

    if close:
        parts.append(parts[0][0][None, :])
    else:
        return np.vstack(parts)

    return np.vstack(parts)
