#!/usr/bin/env python3
"""
Test twin, inset, and panel axes.
"""

import numpy as np
import pytest
import matplotlib.patheffects as mpatheffects
import matplotlib.text as mtext

import ultraplot as uplt
from ultraplot.internals.warnings import UltraPlotWarning
from ultraplot.text import CurvedText


@pytest.mark.parametrize(
    "side,row_sel,col_sel,expected_len,fmt_kwargs",
    [
        ("right", slice(None), -1, 2, {"yticklabelloc": "l"}),
        ("left", slice(None), -1, 2, {"yticklabelloc": "l"}),
        ("top", -1, slice(None), 2, {"xticklabelloc": "b"}),
        ("bottom", -1, slice(None), 2, {"xticklabelloc": "b"}),
    ],
)
@pytest.mark.mpl_image_compare
def test_panel_only_gridspec_indexing_panels(
    side, row_sel, col_sel, expected_len, fmt_kwargs
):
    """
    Ensure indexing works for grids that consist only of panel axes across sides.
    For left/right panels, we index the last panel column with pax[:, -1].
    For top/bottom panels, we index the last panel row with pax[-1, :].
    """
    fig, ax = uplt.subplots(nrows=2, ncols=2)
    pax = ax.panel(side)

    # Should be able to index the desired panel slice without raising
    sub = pax[row_sel, col_sel]

    # It should return the expected number of panel axes
    try:
        n = len(sub)
    except TypeError:
        pytest.fail("Expected a SubplotGrid selection, got a single Axes.")
    else:
        assert n == expected_len

    # And formatting should work on the selection
    sub.format(**fmt_kwargs)

    # Draw to finalize layout and return figure for image comparison
    fig.canvas.draw()
    return fig


@pytest.mark.parametrize(
    "value",
    [
        5,  # int
        5.0,  # float
        "1em",  # string with unit
        "10pt",  # string with unit
        "2px",  # string with unit
    ],
)
@pytest.mark.parametrize(
    "kw",
    [
        "xticklen",
        "yticklen",
        "xticklabelpad",
        "yticklabelpad",
        "xlabelpad",
        "ylabelpad",
        "xtickwidth",
        "ytickwidth",
        "xlabelsize",
        "ylabelsize",
        "xticklabelsize",
        "yticklabelsize",
    ],
)
def test_cartesian_format_units_accepts_various_types(kw, value):
    """
    Test that CartesianAxes.format() accepts int, float, and string with units
    for all relevant padding/size/width/len arguments.
    """
    fig, ax = uplt.subplots(proj="cart")
    kwargs = {kw: value}
    ax.format(**kwargs)


@pytest.mark.parametrize(
    "kw",
    [
        "xticklen",
        "yticklen",
        "xticklabelpad",
        "yticklabelpad",
        "xlabelpad",
        "ylabelpad",
        "xtickwidth",
        "ytickwidth",
        "xlabelsize",
        "ylabelsize",
        "xticklabelsize",
        "yticklabelsize",
    ],
)
def test_cartesian_format_units_invalid_type_raises(kw):
    fig, ax = uplt.subplots(proj="cart")
    with pytest.raises((TypeError, ValueError)):
        ax.format(**{kw: object()})


def test_cartesian_format_all_units_types():
    """
    Test that all relevant unit/padding/size/width/len arguments accept int, float, and string.
    """
    fig, ax = uplt.subplots(proj="cart")
    kwargs = {
        "xticklen": "1em",
        "yticklen": 5,
        "xticklabelpad": 2.5,
        "yticklabelpad": "2px",
        "xlabelpad": 3,
        "ylabelpad": "10pt",
        "xtickwidth": 1.5,
        "ytickwidth": "2px",
        "xlabelsize": "12pt",
        "ylabelsize": 14,
        "xticklabelsize": "1em",
        "yticklabelsize": 10.0,
    }
    ax.format(**kwargs)


@pytest.mark.mpl_image_compare
def test_curvedtext_basic():
    fig, ax = uplt.subplots()
    x = np.linspace(0, 2 * np.pi, 200)
    y = np.sin(x)
    ax.plot(x, y, color="C0")
    ax.curvedtext(
        x,
        y,
        "curved text",
        ha="center",
        va="bottom",
        color="C1",
        size=16,
    )
    ax.format(xlim=(0, 2 * np.pi), ylim=(-1.2, 1.2))
    return fig


def test_text_scalar_returns_text():
    fig, ax = uplt.subplots()
    obj = ax.text(0.5, 0.5, "scalar")
    assert isinstance(obj, mtext.Text)
    assert not isinstance(obj, CurvedText)


def test_text_curve_xy_returns_curvedtext():
    fig, ax = uplt.subplots()
    x = np.linspace(0, 1, 20)
    y = x**2
    obj = ax.text(x, y, "curve")
    assert isinstance(obj, CurvedText)


def test_annotate_scalar_returns_annotation():
    fig, ax = uplt.subplots()
    obj = ax.annotate("point", xy=(0.5, 0.5))
    assert isinstance(obj, mtext.Annotation)
    assert not isinstance(obj, CurvedText)


def test_annotate_curve_xy_returns_curvedtext():
    fig, ax = uplt.subplots()
    x = np.linspace(0, 1, 20)
    y = np.sin(2 * np.pi * x)
    obj = ax.annotate("curve", xy=(x, y))
    assert isinstance(obj, CurvedText)
    assert not hasattr(obj, "_annotation")


def test_annotate_curve_xy_with_arrow_uses_curve_center():
    fig, ax = uplt.subplots()
    ax = ax[0]
    x = np.linspace(0, 1, 31)
    y = x**2
    obj = ax.annotate(
        "curve",
        xy=(x, y),
        xytext=(0.2, 0.8),
        arrowprops={"arrowstyle": "->"},
    )
    assert isinstance(obj, CurvedText)
    assert isinstance(getattr(obj, "_annotation", None), mtext.Annotation)

    xmid, ymid = ax._curve_center(x, y, ax.transData)
    ax_x, ax_y = obj._annotation.xy
    assert np.isclose(ax_x, xmid)
    assert np.isclose(ax_y, ymid)


def test_curvedtext_uses_rc_defaults():
    fig, ax = uplt.subplots()
    x = np.linspace(0, 1, 20)
    y = x**2
    with uplt.rc.context(
        {
            "text.curved.upright": False,
            "text.curved.ellipsis": True,
            "text.curved.avoid_overlap": False,
            "text.curved.overlap_tol": 0.25,
            "text.curved.curvature_pad": 3.5,
            "text.curved.min_advance": 2.5,
        }
    ):
        obj = ax.curvedtext(x, y, "curve")
    assert obj._upright is False
    assert obj._ellipsis is True
    assert obj._avoid_overlap is False
    assert np.isclose(obj._overlap_tol, 0.25)
    assert np.isclose(obj._curvature_pad, 3.5)
    assert np.isclose(obj._min_advance, 2.5)


def test_annotate_curve_xy_uses_rc_defaults():
    fig, ax = uplt.subplots()
    x = np.linspace(0, 1, 20)
    y = np.sin(2 * np.pi * x)
    with uplt.rc.context(
        {
            "text.curved.upright": False,
            "text.curved.ellipsis": True,
            "text.curved.avoid_overlap": False,
            "text.curved.overlap_tol": 0.2,
            "text.curved.curvature_pad": 4.0,
            "text.curved.min_advance": 1.5,
        }
    ):
        obj = ax.annotate("curve", xy=(x, y))
    assert isinstance(obj, CurvedText)
    assert obj._upright is False
    assert obj._ellipsis is True
    assert obj._avoid_overlap is False
    assert np.isclose(obj._overlap_tol, 0.2)
    assert np.isclose(obj._curvature_pad, 4.0)
    assert np.isclose(obj._min_advance, 1.5)


def _get_text_stroke_joinstyle(text):
    for effect in text.get_path_effects():
        if isinstance(effect, mpatheffects.Stroke):
            for attr in ("joinstyle", "_joinstyle"):
                if hasattr(effect, attr):
                    return getattr(effect, attr)
            if hasattr(effect, "_gc"):
                return effect._gc.get("joinstyle")
    return None


def test_text_borderstyle_rc_default():
    fig, ax = uplt.subplots()
    with uplt.rc.context({"text.borderstyle": "round"}):
        txt = ax.text(0.5, 0.5, "A", border=True)
    assert _get_text_stroke_joinstyle(txt) == "round"


def test_text_borderstyle_overrides_rc():
    fig, ax = uplt.subplots()
    with uplt.rc.context({"text.borderstyle": "round"}):
        txt = ax.text(0.5, 0.5, "A", border=True, borderstyle="bevel")
    assert _get_text_stroke_joinstyle(txt) == "bevel"


def test_dualx_log_transform_is_finite():
    """
    Ensure dualx transforms remain finite on log axes.
    """
    fig, ax = uplt.subplots()
    ax.set_xscale("log")
    ax.set_xlim(0.1, 10)
    sec = ax.dualx(lambda x: 1 / x)
    fig.canvas.draw()

    ticks = sec.get_xticks()
    assert ticks.size > 0
    xy = np.column_stack([ticks, np.zeros_like(ticks)])
    transformed = sec.transData.transform(xy)
    assert np.isfinite(transformed).all()


def test_title_manual_size_ignores_auto_shrink():
    """
    Ensure explicit title sizes bypass auto-scaling.
    """
    fig, axs = uplt.subplots(figsize=(2, 2))
    axs.format(
        abc=True,
        title="X" * 200,
        titleloc="left",
        abcloc="left",
        title_kw={"size": 20},
    )
    title_obj = axs[0]._title_dict["left"]
    fig.canvas.draw()
    assert title_obj.get_fontsize() == 20


def test_title_shrinks_when_abc_overlaps_different_loc():
    """
    Ensure long titles shrink when overlapping abc at a different location.
    """
    fig, axs = uplt.subplots(figsize=(3, 2))
    axs.format(abc=True, title="X" * 200, titleloc="center", abcloc="left")
    title_obj = axs[0]._title_dict["center"]
    original_size = title_obj.get_fontsize()
    fig.canvas.draw()
    assert title_obj.get_fontsize() < original_size


def test_title_shrinks_right_aligned_same_location():
    """
    Test that right-aligned titles shrink when they would overflow with abc label.
    """
    fig, axs = uplt.subplots(figsize=(2, 2))
    axs.format(abc=True, title="X" * 100, titleloc="right", abcloc="right")
    title_obj = axs[0]._title_dict["right"]
    original_size = title_obj.get_fontsize()
    fig.canvas.draw()
    assert title_obj.get_fontsize() < original_size


def test_title_shrinks_centered_same_location():
    """
    Test that centered titles shrink when they would overflow with abc label.
    """
    fig, axs = uplt.subplots(figsize=(2, 2))
    axs.format(abc=True, title="X" * 150, titleloc="center", abcloc="center")
    title_obj = axs[0]._title_dict["center"]
    original_size = title_obj.get_fontsize()
    fig.canvas.draw()
    assert title_obj.get_fontsize() < original_size


def test_title_shrinks_right_aligned_different_location():
    """
    Test that right-aligned titles shrink when overlapping abc at different location.
    """
    fig, axs = uplt.subplots(figsize=(3, 2))
    axs.format(abc=True, title="X" * 100, titleloc="right", abcloc="left")
    title_obj = axs[0]._title_dict["right"]
    original_size = title_obj.get_fontsize()
    fig.canvas.draw()
    assert title_obj.get_fontsize() < original_size


def test_title_shrinks_left_aligned_different_location():
    """
    Test that left-aligned titles shrink when overlapping abc at different location.
    """
    fig, axs = uplt.subplots(figsize=(3, 2))
    axs.format(abc=True, title="X" * 100, titleloc="left", abcloc="right")
    title_obj = axs[0]._title_dict["left"]
    original_size = title_obj.get_fontsize()
    fig.canvas.draw()
    assert title_obj.get_fontsize() < original_size


def test_title_no_shrink_when_no_overlap():
    """
    Test that titles don't shrink when there's no overlap with abc label.
    """
    fig, axs = uplt.subplots(figsize=(4, 2))
    axs.format(abc=True, title="Short Title", titleloc="left", abcloc="right")
    title_obj = axs[0]._title_dict["left"]
    original_size = title_obj.get_fontsize()
    fig, ax = uplt.subplots()
    ax.set_xscale("log")
    ax.set_xlim(0.1, 10)
    sec = ax.dualx(lambda x: 1 / x)
    fig.canvas.draw()
    assert title_obj.get_fontsize() == original_size


def test_title_shrinks_centered_left_of_abc():
    """
    Test that centered titles shrink when they are to the left of abc label.
    This covers the specific case where base_x <= ax0 - pad for centered titles.
    """
    fig, axs = uplt.subplots(figsize=(3, 2))
    axs.format(abc=True, title="X" * 100, titleloc="center", abcloc="right")
    title_obj = axs[0]._title_dict["center"]
    original_size = title_obj.get_fontsize()
    fig.canvas.draw()
    assert title_obj.get_fontsize() < original_size
    ticks = axs[0].get_xticks()
    assert ticks.size > 0
    xy = np.column_stack([ticks, np.zeros_like(ticks)])
    transformed = axs[0].transData.transform(xy)
    assert np.isfinite(transformed).all()


def test_axis_access():
    # attempt to access the ax object 2d and linearly
    fig, ax = uplt.subplots(ncols=2, nrows=2)
    ax[0, 0]
    ax[1, 0]
    with pytest.raises(IndexError):
        ax[0, 3]
    ax[3]


@pytest.mark.mpl_image_compare
def test_inset_colors_1():
    """
    Test color application for zoom boxes.
    """
    fig, ax = uplt.subplots()
    ax.format(xlim=(0, 100), ylim=(0, 100))
    ix = ax.inset_axes((0.5, 0.5, 0.3, 0.3), zoom=True, zoom_kw={"fc": "r", "ec": "b"})
    ix.format(xlim=(10, 20), ylim=(10, 20), grid=False)
    return fig


@pytest.mark.mpl_image_compare
def test_inset_colors_2():
    fig, ax = uplt.subplots()
    ax.format(xlim=(0, 100), ylim=(0, 100))
    ix = ax.inset_axes(
        (0.3, 0.5, 0.5, 0.3),
        zoom=True,
        zoom_kw={"lw": 3, "ec": "red9", "a": 1, "fc": uplt.set_alpha("red4", 0.5)},
    )
    ix.format(xlim=(10, 20), ylim=(10, 20))
    return fig


@pytest.mark.mpl_image_compare
def test_inset_zoom_update():
    """
    Test automatic limit adjusment with successive changes. Without the extra
    lines in `draw()` and `get_tight_bbox()` this fails.
    """
    fig, ax = uplt.subplots()
    ax.format(xlim=(0, 100), ylim=(0, 100))
    ix = ax.inset_axes((40, 40, 20, 20), zoom=True, transform="data")
    ix.format(xlim=(10, 20), ylim=(10, 20), grid=False)
    ix.format(xlim=(10, 20), ylim=(10, 30))
    ax.format(ylim=(0, 300))
    return fig


@pytest.mark.mpl_image_compare
def test_panels_with_sharing():
    """
    Previously the below text would hide the second y label.
    """
    fig, axs = uplt.subplots(ncols=2, share=False, refwidth=1.5)
    axs.panel("left")
    fig.format(ylabel="ylabel", xlabel="xlabel")
    return fig


@pytest.mark.mpl_image_compare
def test_panels_without_sharing_1():
    """
    What should happen if `share=False` but figure-wide sharing enabled?
    Strange use case but behavior appears "correct."
    """
    fig, axs = uplt.subplots(ncols=2, share=True, refwidth=1.5, includepanels=False)
    axs.panel("left", share=False)
    fig.format(ylabel="ylabel", xlabel="xlabel")
    return fig


@pytest.mark.mpl_image_compare
def test_panels_without_sharing_2():
    fig, axs = uplt.subplots(ncols=2, refwidth=1.5, includepanels=True)
    for _ in range(3):
        p = axs[0].panel("l", space=0)
        p.format(xlabel="label")
    fig.format(xlabel="xlabel")
    return fig


@pytest.mark.mpl_image_compare
def test_panels_suplabels_three_hor_panels():
    """
    Test label sharing for `includepanels=True`.
    Test for 1 subplot with 3 left panels
    Include here centers the x label to include the panels
    The xlabel should be centered along the main plot with the included side panels
    """
    fig = uplt.figure()
    ax = fig.subplots(refwidth=1.5, includepanels=True)
    for _ in range(3):
        ax[0].panel("l")
    ax.format(xlabel="xlabel", ylabel="ylabel\nylabel\nylabel", suptitle="sup")
    return fig


@pytest.mark.mpl_image_compare
def test_panels_suplabels_three_hor_panels_donotinlcude():
    """
    Test label sharing for `includepanels=True`.
    Test for 1 subplot with 3 left panels
    The xlabel should be centered on the main plot
    """
    fig = uplt.figure()
    ax = fig.subplots(refwidth=1.5, includepanels=False)
    for _ in range(3):
        ax[0].panel("l")
    ax.format(
        xlabel="xlabel",
        ylabel="ylabel\nylabel\nylabel",
        suptitle="sup",
    )
    return fig


@pytest.mark.mpl_image_compare
def test_twin_axes_1():
    """
    Adjust twin axis positions. Should allow easily switching the location.
    """
    # Test basic twin creation and tick, spine, label location changes
    fig = uplt.figure()
    ax = fig.subplot()
    ax.format(
        ycolor="blue",
        ylabel="orig",
        ylabelcolor="blue9",
        yspineloc="l",
        labelweight="bold",
        xlabel="xlabel",
        xtickloc="t",
        xlabelloc="b",
    )
    ax.alty(loc="r", color="r", labelcolor="red9", label="other", labelweight="bold")
    return fig


@pytest.mark.mpl_image_compare
def test_twin_axes_2():
    # Simple example but doesn't quite work. Figure out how to specify left vs. right
    # spines for 'offset' locations... maybe needs another keyword.
    fig, ax = uplt.subplots()
    ax.format(ymax=10, ylabel="Reference")
    ax.alty(color="green", label="Green", max=8)
    ax.alty(color="red", label="Red", max=15, loc=("axes", -0.2))
    ax.alty(color="blue", label="Blue", max=5, loc=("axes", 1.2), ticklabeldir="out")
    return fig


@pytest.mark.mpl_image_compare
def test_twin_axes_3(rng):
    # A worked example from Riley Brady
    # Uses auto-adjusting limits
    fig, ax = uplt.subplots()
    axs = [ax, ax.twinx(), ax.twinx()]
    axs[-1].spines["right"].set_position(("axes", 1.2))
    colors = ("Green", "Red", "Blue")
    for ax, color in zip(axs, colors):
        data = rng.random(1) * rng.random(10)
        ax.plot(data, marker="o", linestyle="none", color=color)
        ax.format(ylabel="%s Thing" % color, ycolor=color)
    axs[0].format(xlabel="xlabel")
    return fig


def test_subset_format():
    fig, axs = uplt.subplots(nrows=1, ncols=3)
    axs[1:].format(title=["a", "b"])  # allowed
    # Subset formatting
    axs[1:].format(title=["c", "d", "e"])  # allowed but does not use e
    assert axs[-1].get_title() == "d"
    assert axs[0].get_title() == ""
    # Shorter than number of axs
    with pytest.raises(ValueError):
        axs.format(title=["a"])


def test_unsharing():
    """
    Test some basic properties of unsharing axes.
    """
    fig, ax = uplt.subplots(ncols=2)
    # Does nothing since key is not an axis or a view
    with pytest.warns(uplt.internals.warnings.UltraPlotWarning):
        ax[0]._unshare(which="key does not exist")
    # 1 shares with 0 but not vice versa
    assert ax[1]._sharey == ax[0]
    assert ax[0]._sharey is None

    ax[0]._unshare(which="y")
    # Nothing should be sharing now
    assert ax[0]._sharey == None
    assert ax[0]._sharex == None
    assert ax[1]._sharey == None
    assert ax[1]._sharex == None


def test_toggling_spines():
    """Test private function to toggle spines"""
    fig, ax = uplt.subplots()
    # Need to get the actual ax not the SubplotGridspec
    # Turn all spines on
    ax[0]._toggle_spines(True)
    assert ax.spines["bottom"].get_visible()
    assert ax.spines["top"].get_visible()
    assert ax.spines["left"].get_visible()
    assert ax.spines["right"].get_visible()
    # Turn all spines off
    ax[0]._toggle_spines(False)
    assert not ax.spines["bottom"].get_visible()
    assert not ax.spines["top"].get_visible()
    assert not ax.spines["left"].get_visible()
    assert not ax.spines["right"].get_visible()
    # Test toggling specific spines
    ax[0]._toggle_spines(spines=["left"])
    assert ax.spines["left"].get_visible()

    # If we toggle right only right is on
    # So left should be off again
    ax[0]._toggle_spines(spines="right")
    assert ax.spines["right"].get_visible()
    assert not ax.spines["left"].get_visible()
    with pytest.raises(ValueError):
        ax[0]._toggle_spines(spines=1)


def test_sharing_labels_top_right():
    fig, ax = uplt.subplots(ncols=3, nrows=3, share="all")
    # On the first format sharexy is modified
    ax.format(
        xticklabelloc="t",
        yticklabelloc="r",
    )
    # If we format again, we expect all the limits to be changed
    # Plot on one shared axis a non-trivial point
    # and check whether the limits are correctly adjusted
    # for all other plots
    ax[0].scatter([30, 40], [30, 40])
    xlim = ax[0].get_xlim()
    ylim = ax[0].get_ylim()

    for axi in ax:
        for i, j in zip(axi.get_xlim(), xlim):
            assert i == j
        for i, j in zip(axi.get_ylim(), ylim):
            assert i == j


@pytest.mark.parametrize(
    "layout, share, tick_loc, y_visible_indices, x_visible_indices",
    [
        # Test case 1: Irregular layout with share=3 (default)
        (
            [
                [1, 2, 0],
                [1, 2, 5],
                [3, 4, 5],
                [3, 4, 0],
            ],
            True,  # default sharing level
            {"xticklabelloc": "t", "yticklabelloc": "r"},
            [1, 3, 4],  # y-axis labels visible indices
            [0, 1, 4],  # x-axis labels visible indices
        ),
        # Test case 2: Irregular layout with share=1
        (
            [
                [1, 0, 2],
                [0, 3, 0],
                [4, 0, 5],
            ],
            1,  # share only labels, not tick labels
            {"xticklabelloc": "t", "yticklabelloc": "r"},
            [0, 1, 2, 3, 4],  # all y-axis labels visible
            [0, 1, 2, 3, 4],  # all x-axis labels visible
        ),
    ],
)
def test_sharing_labels_with_layout(
    layout, share, tick_loc, y_visible_indices, x_visible_indices
):
    """
    Test if tick labels are correctly visible or hidden based on layout and sharing.

    Parameters
    ----------
    layout : list of list of int
        The layout configuration for the subplots
    share : int
        The sharing level (0-4)
    tick_loc : dict
        Tick label location settings
    y_visible_indices : list
        Indices in the axes array where y-tick labels should be visible
    x_visible_indices : list
        Indices in the axes array where x-tick labels should be visible
    """

    # Helper function to check if the labels on an axis direction are visible
    def check_state(ax, numbers, state, which):
        for number in numbers:
            for label in getattr(ax[number], f"get_{which}ticklabels")():
                assert label.get_visible() == state, (
                    f"Expected {which}-tick label visibility to be {state} "
                    f"for axis {number}, but got {not state}"
                )

    # Create figure with the specified layout and sharing level
    fig, ax = uplt.subplots(layout, share=share)

    # Format axes with the specified tick label locations
    ax.format(**tick_loc)
    fig.canvas.draw()  # needed for sharing labels

    # Calculate the indices where labels should be hidden
    all_indices = list(range(len(ax)))
    y_hidden_indices = [i for i in all_indices if i not in y_visible_indices]
    x_hidden_indices = [i for i in all_indices if i not in x_visible_indices]

    # Check that labels are visible or hidden as expected
    check_state(ax, y_visible_indices, True, which="y")
    check_state(ax, y_hidden_indices, False, which="y")
    check_state(ax, x_visible_indices, True, which="x")
    check_state(ax, x_hidden_indices, False, which="x")

    uplt.close(fig)


@pytest.mark.mpl_image_compare
def test_alt_axes_y_shared():
    layout = [[1, 2], [3, 4]]
    fig, ax = uplt.subplots(ncols=2, nrows=2)

    for axi in ax:
        alt = axi.alty()
        alt.set_ylabel("Alt Y")
        assert alt.get_ylabel() == "Alt Y"
        assert alt.get_xlabel() == ""
        axi.set_ylabel("Y")
    return fig


@pytest.mark.mpl_image_compare
def test_alt_axes_x_shared():
    layout = [[1, 2], [3, 4]]
    fig, ax = uplt.subplots(ncols=2, nrows=2)

    for axi in ax:
        alt = axi.altx()
        alt.set_xlabel("Alt X")
        assert alt.get_xlabel() == "Alt X"
        assert alt.get_ylabel() == ""
        axi.set_xlabel("X")
    return fig


def test_inset_format_scope():
    """
    Test that calling format() on an inset axes does not affect the
    parent figure's properties like suptitle or super labels.
    """
    fig, axs = uplt.subplots()
    ax = axs[0]
    # Inset axes are instances of the same class as the parent
    ix = ax.inset_axes([0.5, 0.5, 0.4, 0.4])
    assert ix._inset_parent is ax, "Inset parent should be the main axes"

    # Test that suptitle is not set
    ix.format(suptitle="This should not appear")
    assert (
        fig._suptitle is None or fig._suptitle.get_text() == ""
    ), "Inset format should not set the figure's suptitle."

    # Test that leftlabels are not set
    # Create a copy to ensure we're not comparing against a modified list
    original_left_labels = list(fig._suplabel_dict["left"])
    ix.format(leftlabels=["a", "b"])
    assert (
        list(fig._suplabel_dict["left"]) == original_left_labels
    ), "Inset format should not set the figure's leftlabels."


def test_panel_format_scope():
    """
    Test that calling format() on a panel axes does not affect the
    parent figure's properties like suptitle.
    """
    fig, axs = uplt.subplots()
    ax = axs[0]
    pax = ax.panel_axes("right")
    assert pax._panel_parent is ax, "Panel parent should be the main axes"

    # Test that suptitle is not set
    pax.format(suptitle="This should not appear")
    assert (
        fig._suptitle is None or fig._suptitle.get_text() == ""
    ), "Panel format should not set the figure's suptitle."
