from bec_qthemes import material_icon
from qtpy.QtGui import QAction  # type: ignore
from qtpy.QtWidgets import QApplication, QHBoxLayout, QStackedWidget, QWidget

from bec_widgets.applications.navigation_centre.reveal_animator import ANIMATION_DURATION
from bec_widgets.applications.navigation_centre.side_bar import SideBar
from bec_widgets.applications.navigation_centre.side_bar_components import NavigationItem
from bec_widgets.applications.views.admin_view.admin_view import AdminView
from bec_widgets.applications.views.developer_view.developer_view import DeveloperView
from bec_widgets.applications.views.device_manager_view.device_manager_view import DeviceManagerView
from bec_widgets.applications.views.dock_area_view.dock_area_view import DockAreaView
from bec_widgets.applications.views.view import ViewBase, WaveformViewInline, WaveformViewPopup
from bec_widgets.utils.colors import apply_theme
from bec_widgets.utils.guided_tour import GuidedTour
from bec_widgets.utils.name_utils import sanitize_namespace
from bec_widgets.utils.screen_utils import (
    apply_centered_size,
    available_screen_geometry,
    main_app_size_for_screen,
)
from bec_widgets.widgets.containers.main_window.main_window import BECMainWindow


class BECMainApp(BECMainWindow):
    RPC = False
    PLUGIN = False

    def __init__(
        self,
        parent=None,
        *args,
        anim_duration: int = ANIMATION_DURATION,
        show_examples: bool = False,
        **kwargs,
    ):
        super().__init__(parent=parent, *args, **kwargs)
        self._show_examples = bool(show_examples)

        # --- Compose central UI (sidebar + stack)
        self.sidebar = SideBar(parent=self, anim_duration=anim_duration)
        self.stack = QStackedWidget(self)

        container = QWidget(self)
        layout = QHBoxLayout(container)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.sidebar, 0)
        layout.addWidget(self.stack, 1)
        self.setCentralWidget(container)

        # Mapping for view switching
        self._view_index: dict[str, int] = {}
        self._current_view_id: str | None = None
        self.sidebar.view_selected.connect(self._on_view_selected)

        self._add_views()

        # Initialize guided tour
        self.guided_tour = GuidedTour(self)
        self._setup_guided_tour()

    def _add_views(self):
        self.add_section("BEC Applications", "bec_apps")
        self.dock_area = DockAreaView(self)
        self.device_manager = DeviceManagerView(self)
        # self.developer_view = DeveloperView(self) #TODO temporary disable until the bugs with BECShell are resolved
        self.admin_view = AdminView(self)

        self.add_view(icon="widgets", title="Dock Area", widget=self.dock_area, mini_text="Docks")
        self.add_view(
            icon="display_settings",
            title="Device Manager",
            widget=self.device_manager,
            mini_text="DM",
        )
        # TODO temporary disable until the bugs with BECShell are resolved
        # self.add_view(
        #     icon="code_blocks",
        #     title="IDE",
        #     widget=self.developer_view,
        #     mini_text="IDE",
        #     exclusive=True,
        # )
        self.add_view(
            icon="admin_panel_settings",
            title="Admin View",
            widget=self.admin_view,
            mini_text="Admin",
            from_top=False,
        )

        if self._show_examples:
            self.add_section("Examples", "examples")
            waveform_view_popup = WaveformViewPopup(
                parent=self, view_id="waveform_view_popup", title="Waveform Plot"
            )
            waveform_view_stack = WaveformViewInline(
                parent=self, view_id="waveform_view_stack", title="Waveform Plot"
            )

            self.add_view(
                icon="show_chart",
                title="Waveform With Popup",
                widget=waveform_view_popup,
                mini_text="Popup",
            )
            self.add_view(
                icon="show_chart",
                title="Waveform InLine Stack",
                widget=waveform_view_stack,
                mini_text="Stack",
            )

        self.set_current("dock_area")
        self.sidebar.add_dark_mode_item()

        # Add guided tour to Help menu
        self._add_guided_tour_to_menu()

    # --- Public API ------------------------------------------------------
    def add_section(self, title: str, id: str, position: int | None = None):
        return self.sidebar.add_section(title, id, position)

    def add_separator(self):
        return self.sidebar.add_separator()

    def add_dark_mode_item(self, id: str = "dark_mode", position: int | None = None):
        return self.sidebar.add_dark_mode_item(id=id, position=position)

    def add_view(
        self,
        *,
        icon: str,
        title: str,
        view_id: str | None = None,
        widget: QWidget,
        mini_text: str | None = None,
        position: int | None = None,
        from_top: bool = True,
        toggleable: bool = True,
        exclusive: bool = True,
    ) -> NavigationItem:
        """
        Register a view in the stack and create a matching nav item in the sidebar.

        Args:
            icon(str): Icon name for the nav item.
            title(str): Title for the nav item.
            view_id(str, optional): Unique ID for the view/item. If omitted, uses mini_text;
                if mini_text is also omitted, uses title.
            widget(QWidget): The widget to add to the stack.
            mini_text(str, optional): Short text for the nav item when sidebar is collapsed.
            position(int, optional): Position to insert the nav item.
            from_top(bool, optional): Whether to count position from the top or bottom.
            toggleable(bool, optional): Whether the nav item is toggleable.
            exclusive(bool, optional): Whether the nav item is exclusive.

        Returns:
            NavigationItem: The created navigation item.


        """
        resolved_id = sanitize_namespace(view_id or mini_text or title)
        item = self.sidebar.add_item(
            icon=icon,
            title=title,
            id=resolved_id,
            mini_text=mini_text,
            position=position,
            from_top=from_top,
            toggleable=toggleable,
            exclusive=exclusive,
        )
        # Wrap plain widgets into a ViewBase so enter/exit hooks are available
        if isinstance(widget, ViewBase):
            view_widget = widget
            view_widget.view_id = resolved_id
            view_widget.view_title = title
        else:
            view_widget = ViewBase(content=widget, parent=self, view_id=resolved_id, title=title)

        view_widget.change_object_name(resolved_id)

        idx = self.stack.addWidget(view_widget)
        self._view_index[resolved_id] = idx
        return item

    def set_current(self, id: str) -> None:
        if id in self._view_index:
            self.sidebar.activate_item(id)

    # Internal: route sidebar selection to the stack
    def _on_view_selected(self, vid: str) -> None:
        # Special handling for views that can not be switched to (e.g. dark mode toggle)
        # Not registered as proper view with a stack index, so we ignore any logic below
        # as it will anyways not result in a stack switch.
        idx = self._view_index.get(vid)
        if idx is None or not (0 <= idx < self.stack.count()):
            return
        # Determine current view
        current_index = self.stack.currentIndex()
        current_view = (
            self.stack.widget(current_index) if 0 <= current_index < self.stack.count() else None
        )

        # Ask current view whether we may leave
        if current_view is not None and hasattr(current_view, "on_exit"):
            may_leave = current_view.on_exit()
            if may_leave is False:
                # Veto: restore previous highlight without re-emitting selection
                if self._current_view_id is not None:
                    self.sidebar.activate_item(self._current_view_id, emit_signal=False)
                return

        # Proceed with switch
        idx = self._view_index.get(vid)
        if idx is None or not (0 <= idx < self.stack.count()):
            return
        self.stack.setCurrentIndex(idx)
        new_view = self.stack.widget(idx)
        self._current_view_id = vid
        if hasattr(new_view, "on_enter"):
            new_view.on_enter()

    def _setup_guided_tour(self):
        """
        Setup the guided tour for the main application.
        Registers key UI components and delegates to views for their internal components.
        """
        tour_steps = []

        # --- General Layout Components ---

        # Register the sidebar toggle button
        toggle_step = self.guided_tour.register_widget(
            widget=self.sidebar.toggle,
            title="Sidebar Toggle",
            text="Click this button to expand or collapse the sidebar. When expanded, you can see full navigation item titles and section names.",
        )
        tour_steps.append(toggle_step)

        # Register the sidebar icons
        sidebar_dock_area = self.sidebar.components.get("dock_area")
        if sidebar_dock_area:
            dock_step = self.guided_tour.register_widget(
                widget=sidebar_dock_area,
                title="Dock Area View",
                text="Click here to access the Dock Area view, where you can manage and arrange your dockable panels.",
            )
            tour_steps.append(dock_step)

        sidebar_device_manager = self.sidebar.components.get("device_manager")
        if sidebar_device_manager:
            device_manager_step = self.guided_tour.register_widget(
                widget=sidebar_device_manager,
                title="Device Manager View",
                text="Click here to open the Device Manager view, where you can view and manage device configs.",
            )
            tour_steps.append(device_manager_step)

        sidebar_developer_view = self.sidebar.components.get("developer_view")
        if sidebar_developer_view:
            developer_view_step = self.guided_tour.register_widget(
                widget=sidebar_developer_view,
                title="Developer View",
                text="Click here to access the Developer view to write scripts and macros.",
            )
            tour_steps.append(developer_view_step)

        # Register the dark mode toggle
        dark_mode_item = self.sidebar.components.get("dark_mode")
        if dark_mode_item:
            dark_mode_step = self.guided_tour.register_widget(
                widget=dark_mode_item,
                title="Theme Toggle",
                text="Switch between light and dark themes. The theme preference is saved and will be applied when you restart the application.",
            )
            tour_steps.append(dark_mode_step)

        # Register the client info label
        if hasattr(self, "_client_info_hover"):
            client_info_step = self.guided_tour.register_widget(
                widget=self._client_info_hover,
                title="Client Status",
                text="Displays status messages and information from the BEC Server.",
            )
            tour_steps.append(client_info_step)

        # Register the scan progress bar if available
        if hasattr(self, "_scan_progress_hover"):
            progress_step = self.guided_tour.register_widget(
                widget=self._scan_progress_hover,
                title="Scan Progress",
                text="Monitor the progress of ongoing scans. Hover over the progress bar to see detailed information including elapsed time and estimated completion.",
            )
            tour_steps.append(progress_step)

        # Register the notification indicator in the status bar
        if hasattr(self, "notification_indicator"):
            notif_step = self.guided_tour.register_widget(
                widget=self.notification_indicator,
                title="Notification Center",
                text="View system notifications, errors, and status updates. Click to filter notifications by type or expand to see all details.",
            )
            tour_steps.append(notif_step)

        # --- View-Specific Components ---

        # Register all views that can extend the tour
        for view_id, view_index in self._view_index.items():
            view_widget = self.stack.widget(view_index)
            if not view_widget or not hasattr(view_widget, "register_tour_steps"):
                continue

            # Get the view's tour steps
            view_tour = view_widget.register_tour_steps(self.guided_tour, self)
            if view_tour is None:
                if hasattr(view_widget.content, "register_tour_steps"):
                    view_tour = view_widget.content.register_tour_steps(self.guided_tour, self)
                if view_tour is None:
                    continue

            # Get the corresponding sidebar navigation item
            nav_item = self.sidebar.components.get(view_id)
            if not nav_item:
                continue

            # Use the view's title for the navigation button
            nav_step = self.guided_tour.register_widget(
                widget=nav_item,
                title=view_tour.view_title,
                text=f"Let's explore the features of the {view_tour.view_title}.",
            )
            tour_steps.append(nav_step)
            tour_steps.extend(view_tour.step_ids)

        # Create the tour with all registered steps
        if tour_steps:
            self.guided_tour.create_tour(tour_steps)

    def start_guided_tour(self):
        """
        Public method to start the guided tour.
        This can be called programmatically or connected to a menu/button action.
        """
        self.guided_tour.start_tour()

    def _add_guided_tour_to_menu(self):
        """
        Add a 'Guided Tour' action to the Help menu.
        """

        # Find the Help menu
        menu_bar = self.menuBar()
        help_menu = None
        for action in menu_bar.actions():
            if action.text() == "Help":
                help_menu = action.menu()
                break

        if help_menu:
            # Add separator before the tour action
            help_menu.addSeparator()

            # Create and add the guided tour action
            tour_action = QAction("Start Guided Tour", self)
            tour_action.setIcon(material_icon("help"))
            tour_action.triggered.connect(self.start_guided_tour)
            tour_action.setShortcut("F1")  # Add keyboard shortcut
            help_menu.addAction(tour_action)

    def cleanup(self):
        for view_id, idx in self._view_index.items():
            view = self.stack.widget(idx)
            view.close()
            view.deleteLater()
        super().cleanup()


def main():  # pragma: no cover
    """
    Main function to run the BEC main application, exposed as a script entry point through
    pyproject.toml.
    """
    # pylint: disable=import-outside-toplevel
    import argparse
    import sys

    parser = argparse.ArgumentParser(description="BEC Main Application")
    parser.add_argument(
        "--examples", action="store_true", help="Show the Examples section with waveform demo views"
    )
    # Let Qt consume the remaining args
    args, qt_args = parser.parse_known_args(sys.argv[1:])

    app = QApplication([sys.argv[0], *qt_args])
    app.setApplicationName("BEC")
    apply_theme("dark")
    w = BECMainApp(show_examples=args.examples)

    screen_geometry = available_screen_geometry()
    if screen_geometry is not None:
        width, height = main_app_size_for_screen(screen_geometry)
        apply_centered_size(w, width, height, available=screen_geometry)
    else:
        w.resize(w.minimumSizeHint())

    w.show()

    sys.exit(app.exec())


if __name__ == "__main__":  # pragma: no cover
    main()
