import traceback
from typing import Dict, List, Optional

from supervisely._utils import logger
from supervisely.app import StateJson
from supervisely.app.content import DataJson
from supervisely.app.widgets import Widget


class RadioTabs(Widget):
    """Tabs-like widget that switches between panes and notifies on selection changes."""

    class Routes:
        """Callback route names used by the widget frontend to notify Python."""

        VALUE_CHANGED = "value_changed_cb"

    class RadioTabPane:
        """One selectable tab pane (title + content widget + optional subtitle)."""

        def __init__(
            self,
            title: str,
            content: Widget,
            subtitle: Optional[str] = "",
        ):
            """:param title: Tab title.
            :type title: str
            :param content: Widget to display when tab is active.
            :type content: Widget
            :param subtitle: Optional subtitle.
            :type subtitle: str, optional
            """
            self.title = title
            self.subtitle = subtitle
            self.name = title  # identifier corresponding to the active tab
            self.content = content

    def __init__(
        self,
        titles: List[str],
        contents: List[Widget],
        descriptions: Optional[List[str]] = None,
        widget_id=None,
    ):
        """:param titles: List of tab titles (unique, max 10).
        :type titles: List[str]
        :param contents: List of widgets, one per tab.
        :type contents: List[Widget]
        :param descriptions: Optional descriptions per tab.
        :type descriptions: List[str], optional
        :param widget_id: Unique widget identifier.

        :raises ValueError: If titles/contents lengths differ, or titles not unique.
        """
        if len(titles) != len(contents):
            raise ValueError(
                "titles length must be equal to contents length in RadioTabs widget."
            )
        if len(titles) > 10:
            raise ValueError("You can specify up to 10 tabs.")
        if descriptions is None:
            descriptions = [""] * len(titles)
        else:
            if len(titles) != len(descriptions):
                raise ValueError(
                    "descriptions length must be equal to titles length in RadioTabs widget."
                )
        if len(set(titles)) != len(titles):
            raise ValueError("All of tab labels should be unique.")
        self._items = []
        for title, widget, description in zip(titles, contents, descriptions):
            self._items.append(
                RadioTabs.RadioTabPane(
                    title=title, content=widget, subtitle=description
                )
            )
        self._value = titles[0]
        self._changes_handled = False
        super().__init__(widget_id=widget_id, file_path=__file__)

    def value_changed(self, func):
        route_path = self.get_route_path(RadioTabs.Routes.VALUE_CHANGED)
        server = self._sly_app.get_server()
        self._changes_handled = True

        @server.post(route_path)
        def _value_changed():
            res = self.get_active_tab()
            func(res)

        return _value_changed

    def get_json_data(self) -> Dict:
        return {"tabsOptions": {item.name: {"disabled": False} for item in self._items}}

    def get_json_state(self) -> Dict:
        return {"value": self._value}

    def set_active_tab(self, value: str):
        self._value = value
        StateJson()[self.widget_id]["value"] = self._value
        StateJson().send_changes()

    def get_active_tab(self) -> str:
        return StateJson()[self.widget_id]["value"]

    def disable_tab(self, tab_name: str):
        if tab_name not in [item.name for item in self._items]:
            raise ValueError(f"Tab with name '{tab_name}' does not exist.")
        DataJson()[self.widget_id]["tabsOptions"][tab_name]["disabled"] = True
        DataJson().send_changes()

    def enable_tab(self, tab_name: str):
        if tab_name not in [item.name for item in self._items]:
            raise ValueError(f"Tab with name '{tab_name}' does not exist.")
        DataJson()[self.widget_id]["tabsOptions"][tab_name]["disabled"] = False
        DataJson().send_changes()
