# Released under the MIT License. See LICENSE for details.
#
"""Examples/tests for using DocUI to build UIs."""

import time
import copy
from typing import TYPE_CHECKING, override

from efro.error import CleanError
import bauiv1 as bui
from bauiv1 import builtinassets
from bauiv1 import classicassets

from bauiv1lib.docui import DocUIWindow, DocUIController

if TYPE_CHECKING:
    from bacommon.docui import DocUIRequest, DocUIResponse
    import bacommon.docui.v2
    from bacommon.langstr import LangStrSpec

    from bauiv1lib.docui import DocUILocalAction


def _btex(name: str) -> str:
    """Qualified ref for a texture in the builtin asset-package."""
    return f'{builtinassets.__asset_package__}:textures/{name}'


def _stex(name: str) -> str:
    """Qualified classicassets texture ref."""
    return f'{classicassets.__asset_package__}:textures/{name}'


def show_test_doc_ui_v2_window() -> None:
    """Bust out a doc-ui test window built locally on the client.

    Test pages authored as language-agnostic v2 documents — text as
    ``LangStrSpec`` values from the ``badocuiv2testassets`` package (decoded
    in the client's locale at render time), textures/meshes as typed
    refs, and multi-line labels via wrap-params instead of hand-baked
    newlines. The Cloud-Msg and Web buttons fetch equivalent v2 pages
    from bamaster, keeping the full cloud/web resolve -> decode ->
    render paths exercised.
    """
    import bacommon.docui.v2 as dui2

    bui.app.ui_v1.auxiliary_window_activate(
        win_type=DocUIWindow,
        win_create_call=bui.CallStrict(
            TestDocUIV2Controller().create_window, dui2.Request('/')
        ),
        win_extra_type_id=TestDocUIV2Controller.get_window_extra_type_id(),
    )


class TestDocUIV2Controller(DocUIController):
    """Tests/demonstrations of native (v2 / l-string) docui.

    Local pages are authored client-side; the ``/cloudmsgtest/*`` and
    ``/webtest/*`` paths fetch equivalent v2 pages from bamaster.
    """

    @override
    def fulfill_request(self, request: DocUIRequest) -> DocUIResponse:
        """Fulfill a v2 request (called in a background thread)."""
        # pylint: disable=too-many-return-statements
        import bacommon.docui.v2 as dui2

        if not isinstance(request, dui2.Request):
            raise CleanError('Invalid request version.')

        # Handle some pages purely locally.
        if request.path == '/':
            return _test_v2_page_root(request)
        if request.path == '/test2':
            return _test_v2_page_2(request)
        if request.path == '/slow':
            return _test_v2_page_long(request)
        if request.path == '/timedactions':
            return _test_v2_page_timed_actions(request)
        if request.path == '/displayitems':
            return _test_v2_page_display_items(request)
        if request.path == '/emptypage':
            return _test_v2_page_empty(request)
        if request.path == '/boundstests':
            return _test_v2_bounds(request)

        # Ship '/webtest/*' off to some webserver to handle.
        if request.path.startswith('/webtest/'):
            return self.fulfill_request_web(
                request, 'https://www.ballistica.net/docuitest'
            )

        # Ship '/cloudmsgtest/*' through our cloud connection to handle.
        if request.path.startswith('/cloudmsgtest/'):
            return self.fulfill_request_cloud(request, 'docuitestv2')

        raise CleanError('Invalid request path.')

    @override
    def local_action(self, action: DocUILocalAction) -> None:
        bui.screenmessage(
            f'Would do {action.name!r} with args {action.args!r}.'
        )


def _test_v2_page_root(
    request: bacommon.docui.v2.Request,
) -> bacommon.docui.v2.Response:
    """Author the v2 (l-string) test root page purely on the client.

    The full v1 test root page, with all text authored as
    language-agnostic ``LangStrSpec`` values from the ``badocuiv2testassets``
    package, textures/meshes as typed refs from
    ``builtinassets``/``classicassets``, and multi-line labels wrapped via
    definition-time :class:`~bacommon.langstr.WrapParams` on the
    package's string definitions (decision D-t) instead of v1's
    hand-baked newlines. The client resolves the referenced packages
    in its own locale, decodes, and wraps -- so this single response
    renders in any language.
    """
    import bacommon.clienteffect as clfx
    import bacommon.docui.v2 as dui2

    from bauiv1 import _docuiv2testassets

    strs = _docuiv2testassets.strings

    # Show some specific debug bits if they ask us to.
    debug = bool(request.args.get('debug', False))

    response = dui2.Response(
        page=dui2.Page(
            title=strs.nav.test_root_title.spec,
            rows=[
                dui2.ButtonRow(
                    debug=debug,
                    header_height=100,
                    header_decorations_left=[
                        dui2.Text(
                            text=strs.common.header_left.spec,
                            position=(0, 10 + 20),
                            color=(1, 1, 1, 0.3),
                            size=(150, 30),
                            h_align=dui2.HAlign.LEFT,
                            debug=debug,
                        ),
                    ],
                    header_decorations_center=[
                        dui2.Text(
                            text=strs.common.hello_from_docui.spec,
                            position=(0, 10 + 20),
                            size=(300, 30),
                            debug=debug,
                        ),
                        dui2.Text(
                            text=strs.common.docui_reference.spec,
                            scale=0.5,
                            position=(0, -18 + 20),
                            size=(600, 23),
                            debug=debug,
                        ),
                        dui2.Image(
                            texture=builtinassets.textures.nub,
                            position=(0, -58 + 20),
                            size=(60, 60),
                        ),
                    ],
                    header_decorations_right=[
                        dui2.Text(
                            text=strs.common.header_right.spec,
                            position=(0, 10 + 20),
                            color=(1, 1, 1, 0.3),
                            size=(150, 30),
                            h_align=dui2.HAlign.RIGHT,
                            debug=debug,
                        ),
                    ],
                    title=strs.nav.some_tests.spec,
                    buttons=[
                        dui2.Button(
                            label=strs.nav.browse.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/test2')),
                        ),
                        dui2.Button(
                            label=strs.nav.replace.spec,
                            size=(120, 80),
                            action=dui2.Replace(dui2.Request('/test2')),
                        ),
                        dui2.Button(
                            label=strs.nav.close.spec,
                            size=(120, 80),
                            action=dui2.Local(close_window=True),
                        ),
                        dui2.Button(
                            label=strs.common.invalid_request.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/invalidrequest')),
                        ),
                        dui2.Button(
                            label=strs.effects.immediate_client_effects.spec,
                            size=(120, 80),
                            action=dui2.Local(
                                # V2 effect forms: l-string text decoded
                                # in the client's locale + a typed sound
                                # ref from an asset package. The page
                                # resolve pre-warms the referenced
                                # packages, so press-time runs are
                                # cache hits.
                                immediate_client_effects=[
                                    clfx.ScreenMessageV2(
                                        message=(
                                            strs.effects.immediate_effects_hello
                                        ).spec,
                                        color=(0, 1, 0),
                                    ),
                                    clfx.PlaySoundV2(
                                        sound=(
                                            builtinassets.audio
                                        ).cash_register,
                                    ),
                                    clfx.Delay(1.0),
                                    clfx.ScreenMessageV2(
                                        message=(
                                            strs.effects.effect_success
                                        ).spec,
                                        color=(0, 1, 0),
                                    ),
                                    clfx.PlaySoundV2(
                                        sound=(
                                            builtinassets.audio
                                        ).cash_register,
                                    ),
                                ]
                            ),
                        ),
                        dui2.Button(
                            label=strs.effects.response_client_effects.spec,
                            size=(120, 80),
                            action=dui2.Browse(
                                dui2.Request('/', args={'test_effects': True})
                            ),
                        ),
                        dui2.Button(
                            label=strs.effects.immediate_local_action.spec,
                            size=(120, 80),
                            action=dui2.Local(
                                immediate_local_action='testaction',
                                immediate_local_action_args={'testparam': 123},
                            ),
                        ),
                        dui2.Button(
                            label=strs.effects.response_local_action.spec,
                            size=(120, 80),
                            action=dui2.Browse(
                                dui2.Request('/', args={'test_action': True})
                            ),
                        ),
                    ],
                ),
                dui2.ButtonRow(
                    title=strs.nav.few_more_tests.spec,
                    buttons=[
                        dui2.Button(
                            label=(
                                strs.common.hide_debug.spec
                                if debug
                                else strs.common.show_debug.spec
                            ),
                            size=(120, 80),
                            action=dui2.Replace(
                                dui2.Request('/', args={'debug': not debug})
                            ),
                        ),
                        dui2.Button(
                            label=strs.nav.slow_browse.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/slow')),
                        ),
                        dui2.Button(
                            label=strs.nav.slow_replace.spec,
                            size=(120, 80),
                            action=dui2.Replace(dui2.Request('/slow')),
                        ),
                        dui2.Button(
                            label=strs.common.timed_actions.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/timedactions')),
                        ),
                        dui2.Button(
                            label=strs.web.web_get.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/webtest/get')),
                        ),
                        dui2.Button(
                            label=strs.web.web_post.spec,
                            size=(120, 80),
                            action=dui2.Browse(
                                dui2.Request(
                                    '/webtest/post',
                                    method=dui2.RequestMethod.POST,
                                )
                            ),
                        ),
                        dui2.Button(
                            label=strs.items.display_items.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/displayitems')),
                        ),
                        dui2.Button(
                            label=strs.layout.empty_page.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/emptypage')),
                        ),
                    ],
                ),
                dui2.ButtonRow(
                    title=strs.nav.even_more_tests.spec,
                    buttons=[
                        dui2.Button(
                            label=strs.cloud.cloud_msg_get.spec,
                            size=(120, 80),
                            action=dui2.Browse(
                                dui2.Request('/cloudmsgtest/get')
                            ),
                        ),
                        dui2.Button(
                            label=strs.cloud.cloud_msg_post.spec,
                            size=(120, 80),
                            action=dui2.Browse(
                                dui2.Request(
                                    '/cloudmsgtest/post',
                                    method=dui2.RequestMethod.POST,
                                )
                            ),
                        ),
                        dui2.Button(
                            label=strs.layout.bounds_tests.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/boundstests')),
                        ),
                    ],
                ),
                dui2.ButtonRow(title=strs.layout.empty_row.spec, buttons=[]),
                dui2.ButtonRow(
                    title=strs.layout.layout_tests.spec,
                    debug=debug,
                    padding_left=5.0,
                    buttons=[
                        dui2.Button(
                            label=strs.nav.test.spec,
                            size=(180, 200),
                            decorations=_layout_test_decos(debug),
                        ),
                        dui2.Button(
                            label=strs.nav.test_two.spec,
                            size=(100, 100),
                            color=(1, 0, 0, 1),
                            label_color=(1, 1, 1, 1),
                            padding_right=4,
                        ),
                        # Should look like the first button but
                        # scaled down.
                        dui2.Button(
                            label=strs.nav.test.spec,
                            size=(180, 200),
                            scale=0.6,
                            padding_bottom=30,  # Should nudge us up.
                            debug=debug,  # Show bounds.
                            decorations=_layout_test_decos(debug),
                        ),
                        # Testing custom button images and opacity.
                        dui2.Button(
                            label=strs.nav.test_three.spec,
                            texture=builtinassets.textures.button_square_wide,
                            padding_left=10.0,
                            padding_right=10.0,
                            color=(1, 1, 1, 0.3),
                            size=(200, 100),
                        ),
                        # Testing image drawing vs bounds
                        dui2.Button(
                            label=strs.layout.bounds_test.spec,
                            texture=builtinassets.textures.white,
                            color=(1, 1, 1, 0.3),
                            size=(150, 100),
                            debug=debug,
                        ),
                    ],
                ),
                dui2.ButtonRow(
                    title=strs.layout.long_row_test.spec,
                    subtitle=strs.layout.look_a_subtitle.spec,
                    buttons=[
                        dui2.Button(
                            size=(150, 100),
                            decorations=[
                                dui2.Text(
                                    text=strs.layout.max_width_test.spec,
                                    position=(0, 25),
                                    size=(150 * 0.8, 32.0),
                                    flatness=1.0,
                                    shadow=0.0,
                                    debug=debug,
                                ),
                                # v1 bakes a newline into this one
                                # ('MaxHeightTest\nSecondLine'); we ask
                                # for two balanced lines instead.
                                dui2.Text(
                                    text=strs.layout.max_height_test.spec,
                                    position=(0, -20),
                                    size=(150 * 0.8, 40),
                                    flatness=1.0,
                                    shadow=0.0,
                                    debug=debug,
                                ),
                            ],
                        ),
                        dui2.Button(
                            size=(150, 100),
                            decorations=[
                                dui2.Image(
                                    texture=classicassets.textures.zoe_icon,
                                    position=(0, 0),
                                    size=(70, 70),
                                    tint_texture=(
                                        classicassets.textures
                                    ).zoe_icon_color_mask,
                                    tint_color=(1, 0, 0),
                                    tint2_color=(0, 1, 0),
                                    mask_texture=(
                                        builtinassets.textures
                                    ).character_icon_mask,
                                ),
                            ],
                        ),
                        dui2.Button(
                            size=(150, 100),
                            decorations=[
                                dui2.Image(
                                    texture=(
                                        classicassets.textures
                                    ).bridgit_preview,
                                    position=(0, 10),
                                    size=(120, 60),
                                    mask_texture=(
                                        classicassets.textures.map_preview_mask
                                    ),
                                    mesh_opaque=(
                                        classicassets.meshes
                                    ).level_select_button_opaque,
                                    mesh_transparent=(
                                        classicassets.meshes
                                    ).level_select_button_transparent,
                                ),
                            ],
                        ),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(
                            label=strs.common.foo.spec,
                            size=(150, 100),
                            scale=0.4,
                            padding_left=100,
                            padding_right=200,
                        ),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                        dui2.Button(size=(150, 100)),
                    ],
                ),
                dui2.ButtonRow(
                    spacing_top=-15,
                    subtitle=strs.layout.subtitle_only.spec,
                    buttons=[
                        dui2.Button(size=(200, 120)),
                    ],
                ),
                dui2.ButtonRow(
                    buttons=[
                        dui2.Button(
                            label=strs.layout.row_with_no_title.spec,
                            size=(300, 80),
                            style=dui2.ButtonStyle.MEDIUM,
                            color=(0.8, 0.8, 0.8, 1),
                            icon=classicassets.textures.button_punch,
                            icon_color=(0.5, 0.3, 1.0, 1.0),
                            icon_scale=1.2,
                        ),
                    ],
                ),
                dui2.ButtonRow(
                    title=strs.layout.centered_faded_title.spec,
                    title_color=(0.6, 0.6, 1.0, 0.3),
                    title_flatness=1.0,
                    title_shadow=1.0,
                    subtitle=strs.layout.testing_centered.spec,
                    subtitle_color=(1.0, 0.5, 1.0, 0.5),
                    subtitle_flatness=1.0,
                    subtitle_shadow=0.0,
                    center_content=True,
                    center_title=True,
                    buttons=[
                        dui2.Button(
                            label=strs.common.hello_there.spec,
                            size=(200, 120),
                            color=(0.7, 0.7, 0.9, 1),
                        ),
                    ],
                ),
            ],
        )
    )

    # Include some client effects if they ask (the 'Response
    # ClientEffects' button). V2 effect forms; see the immediate-effects
    # note above.
    if request.args.get('test_effects', False):
        response.client_effects = [
            clfx.ScreenMessageV2(
                message=strs.effects.response_effects_hello.spec,
                color=(0, 1, 0),
            ),
            clfx.PlaySoundV2(sound=builtinassets.audio.cash_register),
            clfx.Delay(1.0),
            clfx.ScreenMessageV2(
                message=strs.effects.effect_success.spec, color=(0, 1, 0)
            ),
            clfx.PlaySoundV2(sound=builtinassets.audio.cash_register),
        ]

    # Include a local-action if they ask (the 'Response LocalAction'
    # button).
    if request.args.get('test_action', False):
        response.local_action = 'testaction'
        response.local_action_args = {'testparam': 234}

    return response


def _layout_test_decos(debug: bool) -> list[bacommon.docui.v2.Decoration]:
    """Decorations for the layout-test buttons (used on two buttons)."""
    import bacommon.docui.v2 as dui2

    from bauiv1 import _docuiv2testassets

    strs = _docuiv2testassets.strings

    return [
        dui2.Image(
            texture=classicassets.textures.powerup_punch,
            position=(-70, 0),
            size=(40, 40),
            h_align=dui2.HAlign.LEFT,
        ),
        dui2.Image(
            texture=classicassets.textures.powerup_speed,
            position=(0, 75),
            size=(35, 35),
            v_align=dui2.VAlign.TOP,
        ),
        dui2.Text(
            text=strs.layout.corner_tl.spec,
            position=(-70, 75),
            size=(50, 50),
            h_align=dui2.HAlign.LEFT,
            v_align=dui2.VAlign.TOP,
            debug=debug,
        ),
        dui2.Text(
            text=strs.layout.corner_tr.spec,
            position=(70, 75),
            size=(50, 50),
            h_align=dui2.HAlign.RIGHT,
            v_align=dui2.VAlign.TOP,
            debug=debug,
        ),
        dui2.Text(
            text=strs.layout.corner_bl.spec,
            position=(-70, -75),
            size=(50, 50),
            h_align=dui2.HAlign.LEFT,
            v_align=dui2.VAlign.BOTTOM,
            debug=debug,
        ),
        dui2.Text(
            text=strs.layout.corner_br.spec,
            position=(70, -75),
            size=(50, 50),
            h_align=dui2.HAlign.RIGHT,
            v_align=dui2.VAlign.BOTTOM,
            debug=debug,
        ),
    ]


def _test_v2_page_2(
    request: bacommon.docui.v2.Request,
) -> bacommon.docui.v2.Response:
    """More testing (v2 mirror of the v1 '/test2' page)."""
    import bacommon.docui.v2 as dui2

    from bauiv1 import _docuiv2testassets

    del request  # Unused.

    strs = _docuiv2testassets.strings

    return dui2.Response(
        page=dui2.Page(
            title=strs.nav.test_two_title.spec,
            rows=[
                dui2.ButtonRow(
                    title=strs.nav.more_tests.spec,
                    buttons=[
                        dui2.Button(
                            label=strs.nav.browse.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/')),
                        ),
                        dui2.Button(
                            label=strs.nav.replace.spec,
                            size=(120, 80),
                            action=dui2.Replace(dui2.Request('/')),
                        ),
                        dui2.Button(
                            label=strs.nav.close.spec,
                            size=(120, 80),
                            action=dui2.Local(close_window=True),
                            selected=True,  # Testing this
                        ),
                    ],
                ),
            ],
        )
    )


def _test_v2_page_long(
    request: bacommon.docui.v2.Request,
) -> bacommon.docui.v2.Response:
    """Testing a page that takes a bit of time to load (v2 mirror)."""
    import bacommon.docui.v2 as dui2

    from bauiv1 import _docuiv2testassets

    del request  # Unused.

    strs = _docuiv2testassets.strings

    # Simulate a slow connection or whatnot.
    time.sleep(3.0)

    return dui2.Response(
        page=dui2.Page(
            title=strs.nav.test.spec,
            center_vertically=True,
            rows=[
                dui2.ButtonRow(
                    title=strs.common.that_took_a_while.spec,
                    center_title=True,
                    center_content=True,
                    buttons=[
                        dui2.Button(
                            label=strs.common.sure_did.spec,
                            size=(120, 80),
                            action=dui2.Browse(dui2.Request('/')),
                        ),
                    ],
                ),
            ],
        )
    )


def _test_v2_page_timed_actions(
    request: bacommon.docui.v2.Request,
) -> bacommon.docui.v2.Response:
    """Testing timed actions (v2 mirror of '/timedactions')."""
    import bacommon.docui.v2 as dui2

    from bauiv1 import _docuiv2testassets

    strs = _docuiv2testassets.strings

    val = request.args.get('val')
    if not isinstance(val, int):
        val = 5

    return dui2.Response(
        page=dui2.Page(
            title=strs.nav.test.spec,
            center_vertically=True,
            rows=[
                dui2.ButtonRow(
                    title=strs.common.hello_there_num(num=str(val)).spec,
                    subtitle=strs.common.each_change.spec,
                    center_title=True,
                    center_content=True,
                    buttons=[
                        dui2.Button(
                            label=strs.nav.done.spec,
                            size=(120, 80),
                            action=dui2.Local(close_window=True),
                            default=True,
                        ),
                    ],
                ),
            ],
        ),
        # Refresh this page with a countdown until we hit zero and then
        # close the window.
        timed_action=(
            dui2.Replace(dui2.Request('/timedactions', args={'val': val - 1}))
            if (val - 1) > 0
            else dui2.Local(close_window=True)
        ),
        timed_action_delay=1.0,
    )


def _test_v2_page_empty(
    request: bacommon.docui.v2.Request,
) -> bacommon.docui.v2.Response:
    """An empty page (v2 mirror of '/emptypage')."""
    import bacommon.docui.v2 as dui2

    from bauiv1 import _docuiv2testassets

    del request  # Unused.

    return dui2.Response(
        page=dui2.Page(
            title=_docuiv2testassets.strings.layout.empty_page_title.spec,
            rows=[],
        )
    )


def _test_v2_page_display_items(
    request: bacommon.docui.v2.Request,
) -> bacommon.docui.v2.Response:
    """Testing display-items (v2 mirror of '/displayitems')."""
    from bacommon.classic import ClassicChestAppearance, ClassicChestDisplayItem
    import bacommon.displayitem as ditm
    import bacommon.docui.v2 as dui2

    from bauiv1 import _docuiv2testassets

    strs = _docuiv2testassets.strings

    # Show some specific debug bits if they ask us to.
    debug = bool(request.args.get('debug', False))

    def _make_test_button(
        scale: float,
        wrapper: ditm.Wrapper,
    ) -> bacommon.docui.v2.Button:

        # See how this looks when unrecognized (relying on wrapper info
        # only).
        uwrapper = copy.deepcopy(wrapper)
        uwrapper.item = ditm.Unknown()

        return dui2.Button(
            size=(300, 400),
            scale=scale,
            decorations=[
                dui2.DisplayItem(
                    wrapper=wrapper,
                    style=dui2.DisplayItemStyle.FULL,
                    position=(-62, 100),
                    size=(120, 120),
                    debug=debug,
                ),
                dui2.DisplayItem(
                    wrapper=uwrapper,
                    style=dui2.DisplayItemStyle.FULL,
                    position=(62, 100),
                    size=(120, 120),
                    debug=debug,
                ),
                dui2.DisplayItem(
                    wrapper=wrapper,
                    style=dui2.DisplayItemStyle.COMPACT,
                    position=(-55, -20),
                    size=(80, 80),
                    debug=debug,
                ),
                dui2.DisplayItem(
                    wrapper=uwrapper,
                    style=dui2.DisplayItemStyle.COMPACT,
                    position=(55, -20),
                    size=(80, 80),
                    debug=debug,
                ),
                dui2.DisplayItem(
                    wrapper=wrapper,
                    style=dui2.DisplayItemStyle.ICON,
                    position=(-55, -120),
                    size=(100, 80),
                    debug=debug,
                ),
                dui2.DisplayItem(
                    wrapper=uwrapper,
                    style=dui2.DisplayItemStyle.ICON,
                    position=(55, -120),
                    size=(100, 80),
                    debug=debug,
                ),
            ],
        )

    return dui2.Response(
        page=dui2.Page(
            padding_left=20,
            padding_right=20,
            title=strs.items.display_items.spec,
            rows=[
                dui2.ButtonRow(
                    debug=debug,
                    padding_left=-10,
                    title=strs.items.display_item_tests.spec,
                    subtitle=strs.items.display_items_sub.spec,
                    buttons=[
                        _make_test_button(
                            1.0,
                            ditm.Wrapper.for_item(ditm.Tickets(count=213)),
                        ),
                        _make_test_button(
                            0.47,
                            ditm.Wrapper.for_item(ditm.Tickets(count=213)),
                        ),
                        _make_test_button(
                            1.0,
                            ditm.Wrapper.for_item(
                                ClassicChestDisplayItem(
                                    appearance=ClassicChestAppearance.L3
                                )
                            ),
                        ),
                        _make_test_button(
                            1.0,
                            ditm.Wrapper.for_item(ditm.Tokens(count=3)),
                        ),
                        _make_test_button(
                            1.0,
                            ditm.Wrapper.for_item(ditm.Tokens(count=1414287)),
                        ),
                        _make_test_button(
                            1.0,
                            ditm.Wrapper.for_item(ditm.Test()),
                        ),
                    ],
                ),
                dui2.ButtonRow(
                    buttons=[
                        dui2.Button(
                            label=(
                                strs.common.hide_debug.spec
                                if debug
                                else strs.common.show_debug.spec
                            ),
                            style=dui2.ButtonStyle.MEDIUM,
                            size=(240, 60),
                            color=(0.6, 0.4, 0.8, 1.0),
                            action=dui2.Replace(
                                dui2.Request(
                                    request.path, args={'debug': not debug}
                                )
                            ),
                        )
                    ],
                ),
            ],
        )
    )


def _test_v2_bounds(
    request: bacommon.docui.v2.Request,
) -> bacommon.docui.v2.Response:
    """Button-style bounds tests (v2 mirror of '/boundstests')."""
    import bacommon.docui.v2 as dui2

    from bauiv1 import _docuiv2testassets

    del request  # Unused.

    strs = _docuiv2testassets.strings

    def _nm(style: bacommon.docui.v2.ButtonStyle) -> LangStrSpec:
        # Code-literal pass-through: renders the raw enum name
        # verbatim in every locale.
        return strs.common.code_literal(
            text=f'{type(style).__name__}.{style.name}'
        ).spec

    def _hello_row(
        title: LangStrSpec,
        sizes: list[tuple[float, float]],
        style: bacommon.docui.v2.ButtonStyle,
        texture: bool = False,
    ) -> bacommon.docui.v2.ButtonRow:
        return dui2.ButtonRow(
            title=title,
            buttons=[
                dui2.Button(
                    label=strs.layout.hello.spec,
                    size=size,
                    style=style,
                    texture=(builtinassets.textures.white if texture else None),
                    color=(1, 0, 0, 0.3) if texture else None,
                    debug=True,
                )
                for size in sizes
            ],
        )

    styles = dui2.ButtonStyle

    return dui2.Response(
        page=dui2.Page(
            title=strs.layout.bounds_tests_title.spec,
            rows=[
                _hello_row(
                    _nm(styles.SQUARE),
                    [(300, 300), (200, 200), (100, 100)],
                    styles.SQUARE,
                ),
                _hello_row(
                    _nm(styles.SQUARE_WIDE),
                    [(400, 200), (200, 250), (60, 100)],
                    styles.SQUARE_WIDE,
                ),
                _hello_row(
                    strs.layout.background_texture.spec,
                    [(300, 300), (200, 200), (100, 100)],
                    styles.SQUARE,
                    texture=True,
                ),
                _hello_row(
                    _nm(styles.TAB),
                    [(400, 100), (200, 50), (100, 60)],
                    styles.TAB,
                ),
                _hello_row(
                    _nm(styles.LARGER),
                    [(500, 100), (200, 50), (100, 60)],
                    styles.LARGER,
                ),
                _hello_row(
                    _nm(styles.LARGE),
                    [(400, 100), (200, 50), (100, 60)],
                    styles.LARGE,
                ),
                _hello_row(
                    _nm(styles.MEDIUM),
                    [(300, 100), (200, 50), (100, 60)],
                    styles.MEDIUM,
                ),
                _hello_row(
                    _nm(styles.SMALL),
                    [(200, 100), (200, 50), (100, 60)],
                    styles.SMALL,
                ),
                _hello_row(
                    _nm(styles.BACK),
                    [(200, 100), (200, 50), (100, 60)],
                    styles.BACK,
                ),
                _hello_row(
                    _nm(styles.BACK_SMALL),
                    [(200, 100), (200, 50), (100, 60)],
                    styles.BACK_SMALL,
                ),
            ],
        )
    )
