"""
Base mixin providing shared application state access
"""

from typing import Any, Optional, cast
from PyQt6.QtWidgets import QStatusBar, QMessageBox, QWidget


class ApplicationStateMixin:
    """
    Base mixin providing access to shared application state.

    This mixin provides properties and helper methods for accessing
    core application objects that are shared across all operation mixins.

    Required attributes (must be set by MainWindow):
        _project: Project instance
        _gl_widget: GLWidget instance
        _status_bar: QStatusBar instance
        _template_manager: TemplateManager instance
    """

    @property
    def project(self):
        """Access to current project"""
        if not hasattr(self, "_project"):
            raise AttributeError("MainWindow must set _project attribute")
        return self._project

    @project.setter
    def project(self, value):
        """Set the current project"""
        self._project = value

    @property
    def gl_widget(self):
        """Access to GL rendering widget"""
        if not hasattr(self, "_gl_widget"):
            raise AttributeError("MainWindow must set _gl_widget attribute")
        return self._gl_widget

    @property
    def status_bar(self) -> QStatusBar:
        """Access to status bar"""
        if not hasattr(self, "_status_bar"):
            raise AttributeError("MainWindow must set _status_bar attribute")
        return cast(QStatusBar, self._status_bar)

    @property
    def template_manager(self):
        """Access to template manager"""
        if not hasattr(self, "_template_manager"):
            raise AttributeError("MainWindow must set _template_manager attribute")
        return self._template_manager

    # Common helper methods

    def _get_most_visible_page_index(self):
        """
        Determine which page is most visible in the current viewport.

        Returns:
            int: Index of the most visible page
        """
        if not hasattr(self.gl_widget, "_page_renderers") or not self.gl_widget._page_renderers:
            return self.gl_widget.current_page_index

        # Get viewport dimensions
        viewport_height = self.gl_widget.height()
        viewport_center_y = viewport_height / 2

        # Find which page's center is closest to viewport center
        min_distance = float("inf")
        best_page_index = self.gl_widget.current_page_index

        for renderer, page in self.gl_widget._page_renderers:
            # Get page center Y position in screen coordinates
            page_height_mm = page.layout.size[1]
            page_height_px = page_height_mm * self.project.working_dpi / 25.4
            page_center_y_offset = renderer.screen_y + (page_height_px * self.gl_widget.zoom_level / 2)

            # Calculate distance from viewport center
            distance = abs(page_center_y_offset - viewport_center_y)

            if distance < min_distance:
                min_distance = distance
                # Find the page index in project.pages
                try:
                    best_page_index = self.project.pages.index(page)
                except ValueError:
                    pass

        return best_page_index

    def get_current_page(self):
        """
        Get currently visible page (most visible in viewport).

        Returns:
            Page instance or None if no page is selected
        """
        if not self.project or not self.project.pages:
            return None

        index = self._get_most_visible_page_index()
        if 0 <= index < len(self.project.pages):
            return self.project.pages[index]

        return None

    def get_current_page_index(self) -> int:
        """
        Get current page index.

        Returns:
            Current page index, or -1 if no page
        """
        if not self.project or not self.project.pages:
            return -1
        return int(self.gl_widget.current_page_index)

    def show_status(self, message: str, timeout: int = 2000):
        """
        Show message in status bar.

        Args:
            message: Message to display
            timeout: Display duration in milliseconds
        """
        if self.status_bar:
            self.status_bar.showMessage(message, timeout)

    def show_error(self, title: str, message: str):
        """
        Show error dialog.

        Args:
            title: Dialog title
            message: Error message
        """
        QMessageBox.critical(cast(QWidget, self), title, message)

    def show_warning(self, title: str, message: str):
        """
        Show warning dialog.

        Args:
            title: Dialog title
            message: Warning message
        """
        QMessageBox.warning(cast(QWidget, self), title, message)

    def show_info(self, title: str, message: str):
        """
        Show information dialog.

        Args:
            title: Dialog title
            message: Information message
        """
        QMessageBox.information(cast(QWidget, self), title, message)

    def require_page(self, show_warning: bool = True) -> bool:
        """
        Check if a page is available and optionally show warning.

        Args:
            show_warning: Whether to show warning dialog if no page exists

        Returns:
            True if page exists, False otherwise
        """
        current_page = self.get_current_page()

        if current_page is None:
            if show_warning:
                self.show_warning("No Page", "Please create a page first.")
            return False

        return True

    def require_selection(self, min_count: int = 1, show_warning: bool = True) -> bool:
        """
        Check if required number of elements are selected.

        Args:
            min_count: Minimum number of selected elements required
            show_warning: Whether to show warning dialog if requirement not met

        Returns:
            True if requirements met, False otherwise
        """
        selected_count = len(self.gl_widget.selected_elements)

        if selected_count < min_count:
            if show_warning:
                if min_count == 1:
                    self.show_info("No Selection", "Please select an element.")
                else:
                    self.show_info("Selection Required", f"Please select at least {min_count} elements.")
            return False

        return True

    def update_view(self):
        """Trigger GL widget update to refresh the view"""
        if self.gl_widget:
            self.gl_widget.update()

            # Update scrollbars to reflect new content
            if hasattr(self, "update_scrollbars"):
                self.update_scrollbars()
