"""Provide classes related to widgets."""

from __future__ import annotations

from json import JSONEncoder, dumps
from pathlib import Path
from typing import TYPE_CHECKING, Any, TypeVar

from praw.const import API_PATH
from praw.models.base import PRAWBase
from praw.models.list.base import BaseList
from praw.util.cache import cachedproperty

if TYPE_CHECKING:
    import praw.models

WidgetType: TypeVar = TypeVar("WidgetType", bound="Widget")


class Button(PRAWBase):
    """Class to represent a single button inside a :class:`.ButtonWidget`.

    .. include:: ../../typical_attributes.rst

    ============== =====================================================================
    Attribute      Description
    ============== =====================================================================
    ``color``      The hex color used to outline the button.
    ``fillColor``  The hex color for the background of the button.
    ``height``     Image height. Only present on image buttons.
    ``hoverState`` A ``dict`` describing the state of the button when hovered over.
                   Optional.
    ``kind``       Either ``"text"`` or ``"image"``.
    ``linkUrl``    A link that can be visited by clicking the button. Only present on
                   image buttons.
    ``text``       The text displayed on the button.
    ``textColor``  The hex color for the text of the button.
    ``url``        - If the button is a text button, a link that can be visited by
                     clicking the button.
                   - If the button is an image button, the URL of a Reddit-hosted image.
    ``width``      Image width. Only present on image buttons.
    ============== =====================================================================

    """


class CalendarConfiguration(PRAWBase):
    """Class to represent the configuration of a :class:`.Calendar`.

    .. include:: ../../typical_attributes.rst

    =================== ================================================
    Attribute           Description
    =================== ================================================
    ``numEvents``       The number of events to display on the calendar.
    ``showDate``        Whether to show the dates of events.
    ``showDescription`` Whether to show the descriptions of events.
    ``showLocation``    Whether to show the locations of events.
    ``showTime``        Whether to show the times of events.
    ``showTitle``       Whether to show the titles of events.
    =================== ================================================

    """


class Hover(PRAWBase):
    """Class to represent the hover data for a :class:`.ButtonWidget`.

    These values will take effect when the button is hovered over (the user moves their
    cursor so it's on top of the button).

    .. include:: ../../typical_attributes.rst

    ============= =====================================================================
    Attribute     Description
    ============= =====================================================================
    ``color``     The hex color used to outline the button.
    ``fillColor`` The hex color for the background of the button.
    ``textColor`` The hex color for the text of the button.
    ``height``    Image height. Only present on image buttons.
    ``kind``      Either ``text`` or ``image``.
    ``text``      The text displayed on the button.
    ``url``       - If the button is a text button, a link that can be visited by
                    clicking the button.
                  - If the button is an image button, the URL of a Reddit-hosted image.
    ``width``     Image width. Only present on image buttons.
    ============= =====================================================================

    """


class Image(PRAWBase):
    """Class to represent an image that's part of a :class:`.ImageWidget`.

    .. include:: ../../typical_attributes.rst

    =========== =================================================
    Attribute   Description
    =========== =================================================
    ``height``  Image height.
    ``linkUrl`` A link that can be visited by clicking the image.
    ``url``     The URL of the (Reddit-hosted) image.
    ``width``   Image width.
    =========== =================================================

    """


class ImageData(PRAWBase):
    """Class for image data that's part of a :class:`.CustomWidget`.

    .. include:: ../../typical_attributes.rst

    ========== =========================================
    Attribute  Description
    ========== =========================================
    ``height`` The image height.
    ``name``   The image name.
    ``url``    The URL of the image on Reddit's servers.
    ``width``  The image width.
    ========== =========================================

    """


class MenuLink(PRAWBase):
    """Class to represent a single link inside a :class:`.Menu` or :class:`.Submenu`.

    .. include:: ../../typical_attributes.rst

    ========= ====================================
    Attribute Description
    ========= ====================================
    ``text``  The text of the menu link.
    ``url``   The URL that the menu item links to.
    ========= ====================================

    """


class Styles(PRAWBase):
    """Class to represent the style information of a widget.

    .. include:: ../../typical_attributes.rst

    =================== ========================================================
    Attribute           Description
    =================== ========================================================
    ``backgroundColor`` The background color of a widget, given as a hexadecimal
                        (``0x######``).
    ``headerColor``     The header color of a widget, given as a hexadecimal
                        (``0x######``).
    =================== ========================================================

    """


class Submenu(BaseList):
    r"""Class to represent a submenu of links inside a :class:`.Menu`.

    .. include:: ../../typical_attributes.rst

    ============ ======================================================================
    Attribute    Description
    ============ ======================================================================
    ``children`` A list of the :class:`.MenuLink`\ s in this submenu. Can be iterated
                 over by iterating over the :class:`.Submenu` (e.g., ``for menu_link in
                 submenu``).
    ``text``     The name of the submenu.
    ============ ======================================================================

    """

    CHILD_ATTRIBUTE = "children"


class SubredditWidgets(PRAWBase):
    """Class to represent a :class:`.Subreddit`'s widgets.

    Create an instance like so:

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets

    Data will be lazy-loaded. By default, PRAW will not request progressively loading
    images from Reddit. To enable this, instantiate a :class:`.SubredditWidgets` object
    via :meth:`~.Subreddit.widgets`, then set the attribute ``progressive_images`` to
    ``True`` before performing any action that would result in a network request.

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        widgets.progressive_images = True
        for widget in widgets.sidebar:
            # do something
            ...

    Access a :class:`.Subreddit`'s widgets with the following attributes:

    .. code-block:: python

        print(widgets.id_card)
        print(widgets.moderators_widget)
        print(widgets.sidebar)
        print(widgets.topbar)

    The attribute :attr:`.id_card` contains the :class:`.Subreddit`'s ID card, which
    displays information like the number of subscribers.

    The attribute :attr:`.moderators_widget` contains the :class:`.Subreddit`'s
    moderators widget, which lists the moderators of the subreddit.

    The attribute :attr:`.sidebar` contains a list of widgets which make up the sidebar
    of the subreddit.

    The attribute :attr:`.topbar` contains a list of widgets which make up the top bar
    of the subreddit.

    To edit a :class:`.Subreddit`'s widgets, use :attr:`~.SubredditWidgets.mod`. For
    example:

    .. code-block:: python

        widgets.mod.add_text_area(
            short_name="My title",
            text="**bold text**",
            styles={"backgroundColor": "#FFFF66", "headerColor": "#3333EE"},
        )

    For more information, see :class:`.SubredditWidgetsModeration`.

    To edit a particular widget, use ``.mod`` on the widget. For example:

    .. code-block:: python

        for widget in widgets.sidebar:
            widget.mod.update(shortName="Exciting new name")

    For more information, see :class:`.WidgetModeration`.

    **Currently available widgets**:

    - :class:`.ButtonWidget`
    - :class:`.Calendar`
    - :class:`.CommunityList`
    - :class:`.CustomWidget`
    - :class:`.IDCard`
    - :class:`.ImageWidget`
    - :class:`.Menu`
    - :class:`.ModeratorsWidget`
    - :class:`.PostFlairWidget`
    - :class:`.RulesWidget`
    - :class:`.TextArea`

    """

    @cachedproperty
    def id_card(self) -> praw.models.IDCard:
        """Get this :class:`.Subreddit`'s :class:`.IDCard` widget."""
        return self.items[self.layout["idCardWidget"]]

    @cachedproperty
    def items(self) -> dict[str, praw.models.Widget]:
        """Get this :class:`.Subreddit`'s widgets as a dict from ID to widget."""
        items = {}
        for item_name, data in self._raw_items.items():
            data["subreddit"] = self.subreddit
            items[item_name] = self._reddit._objector.objectify(data=data)
        return items

    @cachedproperty
    def mod(self) -> praw.models.SubredditWidgetsModeration:
        """Get an instance of :class:`.SubredditWidgetsModeration`.

        .. note::

            Using any of the methods of :class:`.SubredditWidgetsModeration` will likely
            result in the data of this :class:`.SubredditWidgets` being outdated. To
            re-sync, call :meth:`.refresh`.

        """
        return SubredditWidgetsModeration(self.subreddit, self._reddit)

    @cachedproperty
    def moderators_widget(self) -> praw.models.ModeratorsWidget:
        """Get this :class:`.Subreddit`'s :class:`.ModeratorsWidget`."""
        return self.items[self.layout["moderatorWidget"]]

    @cachedproperty
    def sidebar(self) -> list[praw.models.Widget]:
        r"""Get a list of :class:`.Widget`\ s that make up the sidebar."""
        return [self.items[widget_name] for widget_name in self.layout["sidebar"]["order"]]

    @cachedproperty
    def topbar(self) -> list[praw.models.Menu]:
        r"""Get a list of :class:`.Widget`\ s that make up the top bar."""
        return [self.items[widget_name] for widget_name in self.layout["topbar"]["order"]]

    def __getattr__(self, attr: str) -> Any:
        """Return the value of ``attr``."""
        if not attr.startswith("_") and not self._fetched:
            self._fetch()
            return getattr(self, attr)
        msg = f"{self.__class__.__name__!r} object has no attribute {attr!r}"
        raise AttributeError(msg)

    def __init__(self, subreddit: praw.models.Subreddit) -> None:
        """Initialize a :class:`.SubredditWidgets` instance.

        :param subreddit: The :class:`.Subreddit` the widgets belong to.

        """
        self._raw_items = None
        self._fetched = False
        self.subreddit = subreddit
        self.progressive_images = False

        super().__init__(subreddit._reddit, {})

    def __repr__(self) -> str:
        """Return an object initialization representation of the instance."""
        return f"SubredditWidgets(subreddit={self.subreddit!r})"

    def _fetch(self) -> None:
        data = self._reddit.get(
            API_PATH["widgets"].format(subreddit=self.subreddit),
            params={"progressive_images": self.progressive_images},
        )

        self._raw_items = data.pop("items")
        super().__init__(self.subreddit._reddit, data)

        cached_property_names = [
            "id_card",
            "moderators_widget",
            "sidebar",
            "topbar",
            "items",
        ]
        inst_dict_pop = self.__dict__.pop
        for name in cached_property_names:
            inst_dict_pop(name, None)

        self._fetched = True

    def refresh(self) -> None:
        """Refresh the :class:`.Subreddit`'s widgets.

        By default, PRAW will not request progressively loading images from Reddit. To
        enable this, set the attribute ``progressive_images`` to ``True`` prior to
        calling ``refresh()``.

        .. code-block:: python

            widgets = reddit.subreddit("test").widgets
            widgets.progressive_images = True
            widgets.refresh()

        """
        self._fetch()


class Widget(PRAWBase):
    """Base class to represent a :class:`.Widget`."""

    @cachedproperty
    def mod(self) -> praw.models.WidgetModeration:
        """Get an instance of :class:`.WidgetModeration` for this widget.

        .. note::

            Using any of the methods of :class:`.WidgetModeration` will likely make the
            data in the :class:`.SubredditWidgets` that this widget belongs to outdated.
            To remedy this, call :meth:`~.SubredditWidgets.refresh`.

        """
        return WidgetModeration(self, self.subreddit, self._reddit)

    def __eq__(self, other: object) -> bool:
        """Check equality against another object."""
        if isinstance(other, Widget):
            return self.id.lower() == other.id.lower()
        return str(other).lower() == self.id.lower()

    def __hash__(self) -> int:
        """Return the hash of the current instance."""
        return hash(self.__class__.__name__) ^ hash(self.id.lower())

    def __init__(self, reddit: praw.Reddit, _data: dict[str, Any]) -> None:
        """Initialize a :class:`.Widget` instance."""
        self.subreddit = ""  # in case it isn't in _data
        self.id = ""  # in case it isn't in _data
        super().__init__(reddit, _data=_data)
        self._mod = None


class WidgetEncoder(JSONEncoder):
    """Class to encode widget-related objects."""

    def default(self, o: Any) -> Any:
        """Serialize ``PRAWBase`` objects."""
        if isinstance(o, self._subreddit_class):
            return str(o)
        if isinstance(o, PRAWBase):
            return {key: val for key, val in vars(o).items() if not key.startswith("_")}
        return JSONEncoder.default(self, o)


class ButtonWidget(Widget, BaseList):
    r"""Class to represent a widget containing one or more buttons.

    Find an existing one:

    .. code-block:: python

        button_widget = None
        widgets = reddit.subreddit("test").widgets
        for widget in widgets.sidebar:
            if isinstance(widget, praw.models.ButtonWidget):
                button_widget = widget
                break

        for button in button_widget:
            print(button.text, button.url)

    Create one:

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        buttons = [
            {
                "kind": "text",
                "text": "View source",
                "url": "https://github.com/praw-dev/praw",
                "color": "#FF0000",
                "textColor": "#00FF00",
                "fillColor": "#0000FF",
                "hoverState": {
                    "kind": "text",
                    "text": "ecruos weiV",
                    "color": "#000000",
                    "textColor": "#FFFFFF",
                    "fillColor": "#0000FF",
                },
            },
            {
                "kind": "text",
                "text": "View documentation",
                "url": "https://praw.readthedocs.io",
                "color": "#FFFFFF",
                "textColor": "#FFFF00",
                "fillColor": "#0000FF",
            },
        ]
        styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
        button_widget = widgets.mod.add_button_widget(
            "Things to click", "Click some of these *cool* links!", buttons, styles
        )

    For more information on creation, see :meth:`.add_button_widget`.

    Update one:

    .. code-block:: python

        new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
        button_widget = button_widget.mod.update(shortName="My fav buttons", styles=new_styles)

    Delete one:

    .. code-block:: python

        button_widget.mod.delete()

    .. include:: ../../typical_attributes.rst

    ==================== ==============================================================
    Attribute            Description
    ==================== ==============================================================
    ``buttons``          A list of :class:`.Button`\ s. These can also be accessed just
                         by iterating over the :class:`.ButtonWidget` (e.g., ``for
                         button in button_widget``).
    ``description``      The description, in Markdown.
    ``description_html`` The description, in HTML.
    ``id``               The widget ID.
    ``kind``             The widget kind (always ``"button"``).
    ``shortName``        The short name of the widget.
    ``styles``           A ``dict`` with the keys ``"backgroundColor"`` and
                         ``"headerColor"``.
    ``subreddit``        The :class:`.Subreddit` the button widget belongs to.
    ==================== ==============================================================

    """

    CHILD_ATTRIBUTE = "buttons"


class Calendar(Widget):
    """Class to represent a calendar widget.

    Find an existing one:

    .. code-block:: python

        calendar = None
        widgets = reddit.subreddit("test").widgets
        for widget in widgets.sidebar:
            if isinstance(widget, praw.models.Calendar):
                calendar = widget
                break

        print(calendar.googleCalendarId)

    Create one:

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
        config = {
            "numEvents": 10,
            "showDate": True,
            "showDescription": False,
            "showLocation": False,
            "showTime": True,
            "showTitle": True,
        }
        cal_id = "y6nm89jy427drk8l71w75w9wjn@group.calendar.google.com"
        calendar = widgets.mod.add_calendar(
            short_name="Upcoming Events",
            google_calendar_id=cal_id,
            requires_sync=True,
            configuration=config,
            styles=styles,
        )

    For more information on creation, see :meth:`.add_calendar`.

    Update one:

    .. code-block:: python

        new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
        calendar = calendar.mod.update(shortName="My fav events", styles=new_styles)

    Delete one:

    .. code-block:: python

        calendar.mod.delete()

    .. include:: ../../typical_attributes.rst

    ================= =====================================================
    Attribute         Description
    ================= =====================================================
    ``configuration`` A ``dict`` describing the calendar configuration.
    ``data``          A list of dictionaries that represent events.
    ``id``            The widget ID.
    ``kind``          The widget kind (always ``"calendar"``).
    ``requiresSync``  A ``bool`` representing whether the calendar requires
                      synchronization.
    ``shortName``     The short name of the widget.
    ``styles``        A ``dict`` with the keys ``"backgroundColor"`` and
                      ``"headerColor"``.
    ``subreddit``     The :class:`.Subreddit` the button widget belongs to.
    ================= =====================================================

    """


class CommunityList(Widget, BaseList):
    r"""Class to represent a Related Communities widget.

    Find an existing one:

    .. code-block:: python

        community_list = None
        widgets = reddit.subreddit("test").widgets
        for widget in widgets.sidebar:
            if isinstance(widget, praw.models.CommunityList):
                community_list = widget
                break

        print(community_list)

    Create one:

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
        subreddits = ["learnpython", reddit.subreddit("test")]
        community_list = widgets.mod.add_community_list(
            short_name="Related subreddits",
            data=subreddits,
            styles=styles,
            description="description",
        )

    For more information on creation, see :meth:`.add_community_list`.

    Update one:

    .. code-block:: python

        new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
        community_list = community_list.mod.update(shortName="My fav subs", styles=new_styles)

    Delete one:

    .. code-block:: python

        community_list.mod.delete()

    .. include:: ../../typical_attributes.rst

    ============= =====================================================================
    Attribute     Description
    ============= =====================================================================
    ``data``      A list of :class:`.Subreddit`\ s. These can also be iterated over by
                  iterating over the :class:`.CommunityList` (e.g., ``for sub in
                  community_list``).
    ``id``        The widget ID.
    ``kind``      The widget kind (always ``"community-list"``).
    ``shortName`` The short name of the widget.
    ``styles``    A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
    ``subreddit`` The :class:`.Subreddit` the button widget belongs to.
    ============= =====================================================================

    """

    CHILD_ATTRIBUTE = "data"


class CustomWidget(Widget):
    """Class to represent a custom widget.

    Find an existing one:

    .. code-block:: python

        custom = None
        widgets = reddit.subreddit("test").widgets
        for widget in widgets.sidebar:
            if isinstance(widget, praw.models.CustomWidget):
                custom = widget
                break

        print(custom.text)
        print(custom.css)

    Create one:

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
        custom = widgets.mod.add_custom_widget(
            short_name="My custom widget",
            text="# Hello world!",
            css="/**/",
            height=200,
            image_data=[],
            styles=styles,
        )

    For more information on creation, see :meth:`.add_custom_widget`.

    Update one:

    .. code-block:: python

        new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
        custom = custom.mod.update(shortName="My fav customization", styles=new_styles)

    Delete one:

    .. code-block:: python

        custom.mod.delete()

    .. include:: ../../typical_attributes.rst

    ================= ============================================================
    Attribute         Description
    ================= ============================================================
    ``css``           The CSS of the widget, as a ``str``.
    ``height``        The height of the widget, as an ``int``.
    ``id``            The widget ID.
    ``imageData``     A ``list`` of :class:`.ImageData` that belong to the widget.
    ``kind``          The widget kind (always ``"custom"``).
    ``shortName``     The short name of the widget.
    ``styles``        A ``dict`` with the keys ``"backgroundColor"`` and
                      ``"headerColor"``.
    ``stylesheetUrl`` A link to the widget's stylesheet.
    ``subreddit``     The :class:`.Subreddit` the button widget belongs to.
    ``text``          The text contents, as Markdown.
    ``textHtml``      The text contents, as HTML.
    ================= ============================================================

    """

    def __init__(self, reddit: praw.Reddit, _data: dict[str, Any]) -> None:
        """Initialize a :class:`.CustomWidget` instance."""
        _data["imageData"] = [ImageData(reddit, data) for data in _data.pop("imageData")]
        super().__init__(reddit, _data=_data)


class IDCard(Widget):
    """Class to represent an ID card widget.

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        id_card = widgets.id_card
        print(id_card.subscribersText)

    Update one:

    .. code-block:: python

        widgets.id_card.mod.update(currentlyViewingText="Bots")

    .. include:: ../../typical_attributes.rst

    ========================= =======================================================
    Attribute                 Description
    ========================= =======================================================
    ``currentlyViewingCount`` The number of redditors viewing the subreddit.
    ``currentlyViewingText``  The text displayed next to the view count. For example,
                              ``"users online"``.
    ``description``           The subreddit description.
    ``id``                    The widget ID.
    ``kind``                  The widget kind (always ``"id-card"``).
    ``shortName``             The short name of the widget.
    ``styles``                A ``dict`` with the keys ``"backgroundColor"`` and
                              ``"headerColor"``.
    ``subreddit``             The :class:`.Subreddit` the button widget belongs to.
    ``subscribersCount``      The number of subscribers to the subreddit.
    ``subscribersText``       The text displayed next to the subscriber count. For
                              example, "users subscribed".
    ========================= =======================================================

    """


class ImageWidget(Widget, BaseList):
    r"""Class to represent an image widget.

    Find an existing one:

    .. code-block:: python

        image_widget = None
        widgets = reddit.subreddit("test").widgets
        for widget in widgets.sidebar:
            if isinstance(widget, praw.models.ImageWidget):
                image_widget = widget
                break

        for image in image_widget:
            print(image.url)

    Create one:

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        image_paths = ["/path/to/image1.jpg", "/path/to/image2.png"]
        image_data = [
            {
                "width": 600,
                "height": 450,
                "linkUrl": "",
                "url": widgets.mod.upload_image(img_path),
            }
            for img_path in image_paths
        ]
        styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"}
        image_widget = widgets.mod.add_image_widget(
            short_name="My cool pictures", data=image_data, styles=styles
        )

    For more information on creation, see :meth:`.add_image_widget`.

    Update one:

    .. code-block:: python

        new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
        image_widget = image_widget.mod.update(shortName="My fav images", styles=new_styles)

    Delete one:

    .. code-block:: python

        image_widget.mod.delete()

    .. include:: ../../typical_attributes.rst

    ============= =====================================================================
    Attribute     Description
    ============= =====================================================================
    ``data``      A list of the :class:`.Image`\ s in this widget. Can be iterated over
                  by iterating over the :class:`.ImageWidget` (e.g., ``for img in
                  image_widget``).
    ``id``        The widget ID.
    ``kind``      The widget kind (always ``"image"``).
    ``shortName`` The short name of the widget.
    ``styles``    A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
    ``subreddit`` The :class:`.Subreddit` the button widget belongs to.
    ============= =====================================================================

    """

    CHILD_ATTRIBUTE = "data"


class Menu(Widget, BaseList):
    r"""Class to represent the top menu widget of a :class:`.Subreddit`.

    Menus can generally be found as the first item in a :class:`.Subreddit`'s top bar.

    .. code-block:: python

        topbar = reddit.subreddit("test").widgets.topbar
        if len(topbar) > 0:
            probably_menu = topbar[0]
            assert isinstance(probably_menu, praw.models.Menu)
            for item in probably_menu:
                if isinstance(item, praw.models.Submenu):
                    print(item.text)
                    for child in item:
                        print("\t", child.text, child.url)
                else:  # MenuLink
                    print(item.text, item.url)

    Create one:

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        menu_contents = [
            {"text": "My homepage", "url": "https://example.com"},
            {
                "text": "Python packages",
                "children": [
                    {"text": "PRAW", "url": "https://praw.readthedocs.io/"},
                    {"text": "requests", "url": "http://python-requests.org"},
                ],
            },
            {"text": "Reddit homepage", "url": "https://reddit.com"},
        ]
        menu = widgets.mod.add_menu(data=menu_contents)

    For more information on creation, see :meth:`.add_menu`.

    Update one:

    .. code-block:: python

        menu_items = list(menu)
        menu_items.reverse()
        menu = menu.mod.update(data=menu_items)

    Delete one:

    .. code-block:: python

        menu.mod.delete()

    .. include:: ../../typical_attributes.rst

    ============= ====================================================================
    Attribute     Description
    ============= ====================================================================
    ``data``      A list of the :class:`.MenuLink`\ s and :class:`.Submenu`\ s in this
                  widget. Can be iterated over by iterating over the :class:`.Menu`
                  (e.g., ``for item in menu``).
    ``id``        The widget ID.
    ``kind``      The widget kind (always ``"menu"``).
    ``subreddit`` The :class:`.Subreddit` the button widget belongs to.
    ============= ====================================================================

    """

    CHILD_ATTRIBUTE = "data"


class ModeratorsWidget(Widget, BaseList):
    r"""Class to represent a moderators widget.

    .. code-block:: python

        widgets = reddit.subreddit("test").widgets
        print(widgets.moderators_widget)

    Update one:

    .. code-block:: python

        new_styles = {"backgroundColor": "#FFFFFF", "headerColor": "#FF9900"}
        widgets.moderators_widget.mod.update(styles=new_styles)

    .. include:: ../../typical_attributes.rst

    ============= =====================================================================
    Attribute     Description
    ============= =====================================================================
    ``id``        The widget ID.
    ``kind``      The widget kind (always ``"moderators"``).
    ``mods``      A list of the :class:`.Redditor`\ s that moderate the subreddit. Can
                  be iterated over by iterating over the :class:`.ModeratorsWidget`
                  (e.g., ``for mod in widgets.moderators_widget``).
    ``styles``    A ``dict`` with the keys ``"backgroundColor"`` and ``"headerColor"``.
    ``subreddit`` The :class:`.Subreddit` the button widget belongs to.
    ``totalMods`` The total number of moderators in the subreddit.
    ============= =====================================================================

    """

    CHILD_ATTRIBUTE = "mods"

    def __init__(self, reddit: praw.Reddit, _data: dict[str, Any]) -> None:
        """Initialize a :class:`.ModeratorsWidget` instance."""
        if self.CHILD_ATTRIBUTE not in _data:
            # .mod.update() sometimes returns payload without "mods" field
            _data[self.CHILD_ATTRIBUTE] = []
        super().__init__(reddit, _data=_data)


class PostFlairWidget(Widget, BaseList):
    """Class to represent a post flair widget.

