# Copyright 2026 Marimo. All rights reserved.
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Final

from marimo._output.formatting import as_html
from marimo._output.hypertext import Html
from marimo._output.rich_help import mddoc
from marimo._plugins.stateless import lazy
from marimo._plugins.ui._core.ui_element import UIElement

if TYPE_CHECKING:
    from collections.abc import Callable, Coroutine


@mddoc
class routes(UIElement[str, str]):
    """Renders a list of routes that are switched based on the URL path.

    Routes currently don't support nested routes, or dynamic routes (e.g. `#/user/:id`).
    If you'd like to see these features, please let us know on GitHub:
    https://github.com/marimo-team/marimo/issues

    For a simple-page-application (SPA) experience, you should use hash-based routing.
    For example, prefix your routes with `#/`.

    If you are using a multi-page-application (MPA) with `marimo.create_asgi_app`,
    you should use path-based routing. For example, prefix your routes with `/`.

    Examples:
        ```python
        mo.routes(
            {
                "#/": render_home,
                "#/about": render_about,
                "#/contact": render_contact,
                mo.routes.CATCH_ALL: render_home,
            }
        )
        ```

    Args:
        routes (dict[str, Union[Callable[[], object], Callable[[], Coroutine[None, None, object]], object]]):
            A dictionary of routes, where the key is the URL path and the value is a function
            that returns the content to display.

    Returns:
        Html (marimo.Html): An Html object.
    """

    _name: Final[str] = "marimo-routes"

    CATCH_ALL = "{/*path}"
    DEFAULT = "/"

    def __init__(
        self,
        routes: dict[
            str,
            Callable[[], object]
            | Callable[[], Coroutine[None, None, object]]
            | object,
        ],
    ) -> None:
        # For functions, wrap in lazy
        children: list[Html] = []
        for content in routes.values():
            if callable(content):
                children.append(lazy.lazy(content))
            else:
                children.append(as_html(content))

        self._children = children
        text = "".join([child.text for child in children])

        super().__init__(
            component_name=self._name,
            initial_value="",
            label=None,
            args={
                "routes": list(routes.keys()),
            },
            on_change=None,
            slotted_html=text,
        )

    def _convert_value(self, value: str) -> str:
        return value

    # Not supported
    def batch(self, *args: Any, **kwargs: Any) -> Any:
        del args, kwargs
        raise TypeError(".batch() is not supported on mo.sidebar")

    def center(self, *args: Any, **kwargs: Any) -> Html:
        del args, kwargs
        raise TypeError(".center() is not supported on mo.sidebar")

    def right(self, *args: Any, **kwargs: Any) -> Html:
        del args, kwargs
        raise TypeError(".right() is not supported on mo.sidebar")

    def left(self, *args: Any, **kwargs: Any) -> Html:
        del args, kwargs
        raise TypeError(".left() is not supported on mo.sidebar")

    def callout(self, *args: Any, **kwargs: Any) -> Html:
        del args, kwargs
        raise TypeError(".callout() is not supported on mo.sidebar")

    def style(self, *args: Any, **kwargs: Any) -> Html:
        del args, kwargs
        raise TypeError(".style() is not supported on mo.sidebar")
