"""PageGroup + GroupedPaginator — select-menu driven paginated embeds."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Sequence

from discord import Embed, Interaction, InteractionMessage, SelectOption
from discord.ui import Button, Select, View, button


# ---------------------------------------------------------------------------
# PageGroup — a named collection of pages
# ---------------------------------------------------------------------------


@dataclass
class PageGroup:
    """A named group of pages that appears as one option in a select dropdown.

    Parameters
    ----------
    label : str
        The human-readable name shown in the select menu.
    pages : list[Embed]
        Ordered list of embeds belonging to this group.
    description : str | None
        Optional short description shown under the label in the dropdown.
    emoji : str | None
        Optional emoji displayed next to the label.
    default : bool
        Whether this group is selected by default when the paginator is first shown.

    Examples
    --------
    ::

        stats_group = PageGroup(
            label="Stats",
            pages=[stats_page_1, stats_page_2],
            description="View base statistics",
            emoji="\U0001f4ca",
        )
    """

    label: str
    pages: list[Embed] = field(default_factory=list)
    description: str | None = None
    emoji: str | None = None
    default: bool = False


# ---------------------------------------------------------------------------
# Internal select component
# ---------------------------------------------------------------------------


class _GroupSelect(Select):
    """Internal select menu that switches between PageGroups."""

    def __init__(self, paginator: GroupedPaginator) -> None:
        self._paginator = paginator
        options = []
        for idx, group in enumerate(paginator.groups):
            options.append(
                SelectOption(
                    label=group.label,
                    value=str(idx),
                    description=group.description,
                    emoji=group.emoji,
                    default=idx == paginator._active_group_index,
                )
            )
        super().__init__(
            placeholder="Select a section\u2026",
            min_values=1,
            max_values=1,
            options=options,
        )

    async def callback(self, interaction: Interaction) -> None:
        await interaction.response.defer()
        idx = int(self.values[0])
        await self._paginator._switch_group(idx)


# ---------------------------------------------------------------------------
# GroupedPaginator — the top-level view
# ---------------------------------------------------------------------------


class GroupedPaginator(View):
    """A paginated view with a ``Select`` dropdown to switch between :class:`PageGroup` sections.

    Each group has its own independent page index and Prev/Next navigation.

    Parameters
    ----------
    groups : Sequence[PageGroup]
        Two or more page groups.  At least one is required.
    timeout : float | None
        View timeout in seconds.  Defaults to ``600``.
    wrap : bool
        Wrap navigation at page boundaries.  Defaults to ``False``.
    show_page_count : bool
        Show a page-counter button between Prev/Next.  Defaults to ``True``.
    prev_label : str
        Label for the previous button.
    next_label : str
        Label for the next button.

    Examples
    --------
    ::

        info = PageGroup(label="Info", pages=[info_page])
        stats = PageGroup(label="Stats", pages=[stats_p1, stats_p2])
        evos = PageGroup(label="Evolutions", pages=evo_pages)

        paginator = GroupedPaginator(groups=[info, stats, evos])
        await paginator.send(message)
    """

    def __init__(
        self,
        groups: Sequence[PageGroup],
        *,
        timeout: float | None = 600,
        wrap: bool = False,
        show_page_count: bool = True,
        prev_label: str = "Prev",
        next_label: str = "Next",
    ) -> None:
        super().__init__(timeout=timeout)
        if not groups:
            raise ValueError("At least one PageGroup is required.")
        for g in groups:
            if not g.pages:
                raise ValueError(f"PageGroup '{g.label}' has no pages.")

        self.groups: list[PageGroup] = list(groups)
        self.wrap: bool = wrap
        self.show_page_count: bool = show_page_count

        # Per-group page indices (independent navigation per section)
        self._page_indices: list[int] = [0] * len(self.groups)

        # Determine initial active group
        self._active_group_index: int = next(
            (i for i, g in enumerate(self.groups) if g.default),
            0,
        )

        # Button labels
        self.prev_button.label = prev_label
        self.next_button.label = next_label

        # Attach the select menu
        self._select = _GroupSelect(self)
        self.add_item(self._select)

        self.message: InteractionMessage | None = None

    # -- properties ------------------------------------------------------------

    @property
    def active_group(self) -> PageGroup:
        """The currently active PageGroup."""
        return self.groups[self._active_group_index]

    @property
    def current_page_index(self) -> int:
        """Page index within the active group."""
        return self._page_indices[self._active_group_index]

    @current_page_index.setter
    def current_page_index(self, value: int) -> None:
        self._page_indices[self._active_group_index] = value

    @property
    def current_embed(self) -> Embed:
        """The embed for the current page."""
        return self.active_group.pages[self.current_page_index]

    @property
    def page_count(self) -> int:
        """Total pages in the active group."""
        return len(self.active_group.pages)

    # -- internal helpers ------------------------------------------------------

    def _update_buttons(self) -> None:
        if self.wrap:
            self.prev_button.disabled = False
            self.next_button.disabled = False
        else:
            self.prev_button.disabled = self.current_page_index == 0
            self.next_button.disabled = self.current_page_index >= self.page_count - 1

        if self.show_page_count:
            self.page_indicator.label = (
                f"{self.current_page_index + 1} / {self.page_count}"
            )

    def _rebuild_select(self) -> None:
        """Rebuild the select menu to reflect the newly active group."""
        self.remove_item(self._select)
        self._select = _GroupSelect(self)
        self.add_item(self._select)

    async def _switch_group(self, index: int) -> None:
        """Switch to a different PageGroup by index."""
        self._active_group_index = index
        self._rebuild_select()
        self._update_buttons()
        if self.message:
            await self.message.edit(embed=self.current_embed, view=self)

    # -- public API ------------------------------------------------------------

    async def send(self, message: InteractionMessage) -> None:
        """Edit *message* to show the first page of the default group.

        Parameters
        ----------
        message : InteractionMessage
            The message to edit (from ``await interaction.original_response()``).
        """
        self.message = message
        self._update_buttons()
        await self.message.edit(embed=self.current_embed, view=self)

    async def goto(self, page: int) -> None:
        """Jump to a page within the currently active group.

        Parameters
        ----------
        page : int
            Zero-based page index.
        """
        self.current_page_index = max(0, min(page, self.page_count - 1))
        self._update_buttons()
        if self.message:
            await self.message.edit(embed=self.current_embed, view=self)

    # -- buttons ---------------------------------------------------------------

    @button(label="Prev", style=2, row=1)
    async def prev_button(self, interaction: Interaction, btn: Button) -> None:
        await interaction.response.defer()
        if self.wrap:
            self.current_page_index = (
                self.current_page_index - 1
            ) % self.page_count
        else:
            self.current_page_index = max(0, self.current_page_index - 1)
        self._update_buttons()
        await self.message.edit(embed=self.current_embed, view=self)

    @button(label="\u00b7", style=2, disabled=True, row=1)
    async def page_indicator(self, interaction: Interaction, btn: Button) -> None:
        await interaction.response.defer()

    @button(label="Next", style=2, row=1)
    async def next_button(self, interaction: Interaction, btn: Button) -> None:
        await interaction.response.defer()
        if self.wrap:
            self.current_page_index = (
                self.current_page_index + 1
            ) % self.page_count
        else:
            self.current_page_index = min(
                self.page_count - 1, self.current_page_index + 1
            )
        self._update_buttons()
        await self.message.edit(embed=self.current_embed, view=self)

    # -- lifecycle -------------------------------------------------------------

    async def on_timeout(self) -> None:
        """Disable all components when the view times out."""
        for item in self.children:
            if isinstance(item, (Button, Select)):
                item.disabled = True
        if self.message:
            try:
                await self.message.edit(view=self)
            except Exception:
                pass
