#!/usr/bin/env python3
"""
Refactored main application entry point for pyPhotoAlbum

This version uses the mixin architecture with auto-generated ribbon configuration.
"""

import sys
from datetime import datetime
from pathlib import Path
from PyQt6.QtWidgets import (
    QApplication,
    QMainWindow,
    QVBoxLayout,
    QWidget,
    QStatusBar,
    QScrollBar,
    QHBoxLayout,
    QMessageBox,
)
from PyQt6.QtCore import Qt, QSize, QTimer
from PyQt6.QtGui import QIcon

from pyPhotoAlbum.project import Project
from pyPhotoAlbum.template_manager import TemplateManager
from pyPhotoAlbum.ribbon_widget import RibbonWidget
from pyPhotoAlbum.ribbon_builder import build_ribbon_config, print_ribbon_summary
from pyPhotoAlbum.gl_widget import GLWidget
from pyPhotoAlbum.autosave_manager import AutosaveManager
from pyPhotoAlbum.thumbnail_browser import ThumbnailBrowserDock

# Import mixins
from pyPhotoAlbum.mixins.base import ApplicationStateMixin
from pyPhotoAlbum.mixins.asset_path import AssetPathMixin
from pyPhotoAlbum.mixins.operations import (
    FileOperationsMixin,
    EditOperationsMixin,
    ElementOperationsMixin,
    PageOperationsMixin,
    TemplateOperationsMixin,
    ViewOperationsMixin,
    AlignmentOperationsMixin,
    DistributionOperationsMixin,
    SizeOperationsMixin,
    ZOrderOperationsMixin,
    MergeOperationsMixin,
    StyleOperationsMixin,
)


class MainWindow(
    QMainWindow,
    ApplicationStateMixin,
    AssetPathMixin,
    FileOperationsMixin,
    EditOperationsMixin,
    ElementOperationsMixin,
    PageOperationsMixin,
    TemplateOperationsMixin,
    ViewOperationsMixin,
    AlignmentOperationsMixin,
    DistributionOperationsMixin,
    SizeOperationsMixin,
    ZOrderOperationsMixin,
    MergeOperationsMixin,
    StyleOperationsMixin,
):
    """
    Main application window using mixin architecture.

    This class composes functionality from multiple mixins rather than
    implementing everything directly. The ribbon configuration is
    automatically generated from decorated methods in the mixins.
    """

    def __init__(self):
        super().__init__()

        # Initialize autosave manager
        self._autosave_manager = AutosaveManager()

        # Initialize shared state first
        self._init_state()

        # Initialize UI
        self._init_ui()

        # Check for checkpoint recovery
        self._check_checkpoint_recovery()

        # Setup autosave timer (every 5 minutes)
        self._autosave_timer = QTimer(self)
        self._autosave_timer.timeout.connect(self._perform_autosave)
        self._autosave_timer.start(5 * 60 * 1000)  # 5 minutes in milliseconds

        # Add a sample page for demonstration
        # self._add_sample_page()

    def _init_state(self):
        """Initialize shared application state"""
        # Initialize project
        self._project = Project("My Photo Album")

        # Set asset resolution context
        from pyPhotoAlbum.models import set_asset_resolution_context

        set_asset_resolution_context(self._project.folder_path)

        # Initialize template manager
        self._template_manager = TemplateManager()

    def _init_ui(self):
        """Initialize user interface"""
        # Basic window setup
        self.setWindowTitle("pyPhotoAlbum")
        self.resize(1200, 800)

        # Set window icon
        icon_path = Path(__file__).parent / "icons" / "icon.png"
        print(f"Window icon path: {icon_path}")
        print(f"Icon exists: {icon_path.exists()}")
        if icon_path.exists():
            icon = QIcon(str(icon_path))
            print(f"Icon is null: {icon.isNull()}")
            self.setWindowIcon(icon)

        # Create main widget with layout
        main_widget = QWidget()
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)
        main_widget.setLayout(main_layout)

        # Build ribbon config from decorated methods
        ribbon_config = build_ribbon_config(self.__class__)

        # Print summary (for debugging)
        print_ribbon_summary(ribbon_config)

        # Create ribbon with auto-generated config
        self.ribbon = RibbonWidget(self, ribbon_config)
        main_layout.addWidget(self.ribbon, 0)

        # Create canvas area with GL widget and scroll bars
        canvas_widget = QWidget()
        canvas_layout = QVBoxLayout()
        canvas_layout.setContentsMargins(0, 0, 0, 0)
        canvas_layout.setSpacing(0)

        # Top row: GL widget + vertical scrollbar
        top_layout = QHBoxLayout()
        top_layout.setContentsMargins(0, 0, 0, 0)
        top_layout.setSpacing(0)

        # Create OpenGL widget
        self._gl_widget = GLWidget(self)
        top_layout.addWidget(self._gl_widget, 1)

        # Vertical scrollbar
        self._v_scrollbar = QScrollBar(Qt.Orientation.Vertical)
        self._v_scrollbar.setRange(-10000, 10000)
        self._v_scrollbar.setValue(0)
        self._v_scrollbar.valueChanged.connect(self._on_vertical_scroll)
        top_layout.addWidget(self._v_scrollbar, 0)

        canvas_layout.addLayout(top_layout, 1)

        # Bottom row: horizontal scrollbar
        self._h_scrollbar = QScrollBar(Qt.Orientation.Horizontal)
        self._h_scrollbar.setRange(-10000, 10000)
        self._h_scrollbar.setValue(0)
        self._h_scrollbar.valueChanged.connect(self._on_horizontal_scroll)
        canvas_layout.addWidget(self._h_scrollbar, 0)

        canvas_widget.setLayout(canvas_layout)
        main_layout.addWidget(canvas_widget, 1)

        self.setCentralWidget(main_widget)

        # Create thumbnail browser dock
        self._thumbnail_browser = ThumbnailBrowserDock(self)
        self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self._thumbnail_browser)
        self._thumbnail_browser.hide()  # Initially hidden

        # Create status bar
        self._status_bar = QStatusBar()
        self.setStatusBar(self._status_bar)

        # Register keyboard shortcuts
        self._register_shortcuts()

        # Track scrollbar updates to prevent feedback loops
        self._updating_scrollbars = False
        # Track scrollbar visibility changes to prevent resize-triggered recentering
        self._updating_scrollbar_visibility = False

    def _on_vertical_scroll(self, value):
        """Handle vertical scrollbar changes"""
        if not self._updating_scrollbars:
            # Invert scrollbar value to pan offset (scrolling down = negative pan)
            self._gl_widget.pan_offset[1] = -value
            self._gl_widget.update()

    def _on_horizontal_scroll(self, value):
        """Handle horizontal scrollbar changes"""
        if not self._updating_scrollbars:
            # Invert scrollbar value to pan offset (scrolling right = negative pan)
            self._gl_widget.pan_offset[0] = -value
            self._gl_widget.update()

    def update_scrollbars(self):
        """Update scrollbar positions and ranges based on current content and pan offset"""
        self._updating_scrollbars = True

        # Block signals to prevent feedback loop
        self._v_scrollbar.blockSignals(True)
        self._h_scrollbar.blockSignals(True)

        # Get content bounds
        bounds = self._gl_widget.get_content_bounds()
        viewport_width = self._gl_widget.width()
        viewport_height = self._gl_widget.height()

        content_height = bounds["height"]
        content_width = bounds["width"]

        # Vertical scrollbar
        # Scrollbar value 0 = top of content
        # Scrollbar value max = bottom of content
        # Pan offset is inverted: positive pan = content moved down = view at top
        #                          negative pan = content moved up = view at bottom
        v_range = int(max(0, content_height - viewport_height))
        self._v_scrollbar.setRange(0, v_range)
        self._v_scrollbar.setPageStep(int(viewport_height))
        # Invert pan_offset for scrollbar position
        self._v_scrollbar.setValue(int(max(0, min(v_range, -self._gl_widget.pan_offset[1]))))

        # Show/hide vertical scrollbar based on whether scrolling is needed
        # Set flag to prevent resizeGL from recentering when scrollbar visibility changes
        self._updating_scrollbar_visibility = True
        self._v_scrollbar.setVisible(v_range > 0)

        # Horizontal scrollbar
        h_range = int(max(0, content_width - viewport_width))
        self._h_scrollbar.setRange(0, h_range)
        self._h_scrollbar.setPageStep(int(viewport_width))
        # Invert pan_offset for scrollbar position
        self._h_scrollbar.setValue(int(max(0, min(h_range, -self._gl_widget.pan_offset[0]))))

        # Show/hide horizontal scrollbar based on whether scrolling is needed
        self._h_scrollbar.setVisible(h_range > 0)
        self._updating_scrollbar_visibility = False

        # Unblock signals
        self._v_scrollbar.blockSignals(False)
        self._h_scrollbar.blockSignals(False)

        self._updating_scrollbars = False

    def _register_shortcuts(self):
        """Register keyboard shortcuts from decorated methods"""
        from PyQt6.QtGui import QShortcut, QKeySequence
        from pyPhotoAlbum.ribbon_builder import get_keyboard_shortcuts

        shortcuts = get_keyboard_shortcuts(self.__class__)

        for shortcut_str, method_name in shortcuts.items():
            if hasattr(self, method_name):
                shortcut = QShortcut(QKeySequence(shortcut_str), self)
                method = getattr(self, method_name)
                shortcut.activated.connect(method)
                print(f"Registered shortcut: {shortcut_str} -> {method_name}")

        # Register additional Ctrl+Shift+Z shortcut for redo
        if hasattr(self, "redo"):
            redo_shortcut = QShortcut(QKeySequence("Ctrl+Shift+Z"), self)
            redo_shortcut.activated.connect(self.redo)
            print("Registered shortcut: Ctrl+Shift+Z -> redo")

    def resizeEvent(self, event):
        """Handle window resize to reposition loading widget"""
        super().resizeEvent(event)
        if hasattr(self, "_loading_widget"):
            self._loading_widget.resizeParent()

    def _add_sample_page(self):
        """Add a sample page with some elements for demonstration"""
        from pyPhotoAlbum.project import Page
        from pyPhotoAlbum.page_layout import PageLayout, GridLayout
        from pyPhotoAlbum.models import ImageData, TextBoxData, PlaceholderData

        # Create a page with project default size
        width_mm, height_mm = self.project.page_size_mm
        page_layout = PageLayout(width=width_mm, height=height_mm)
        grid = GridLayout(rows=2, columns=2, spacing=20.0)
        page_layout.set_grid_layout(grid)

        # Add some sample elements (scaled to new default size)
        image = ImageData(image_path="sample.jpg", x=20, y=20, width=50, height=50)
        page_layout.add_element(image)

        text_box = TextBoxData(text_content="Sample Text", x=80, y=20, width=50, height=20)
        page_layout.add_element(text_box)

        placeholder = PlaceholderData(placeholder_type="image", x=20, y=80, width=50, height=50)
        page_layout.add_element(placeholder)

        # Create and add the page
        page = Page(layout=page_layout, page_number=1)
        page.manually_sized = False  # Not manually sized, uses defaults
        self.project.add_page(page)

    def _perform_autosave(self):
        """Perform automatic checkpoint save"""
        if self.project and self.project.is_dirty():
            success, message = self._autosave_manager.create_checkpoint(self.project)
            if success:
                print(f"Autosave: {message}")
            else:
                print(f"Autosave failed: {message}")

    def _check_checkpoint_recovery(self):
        """Check for available checkpoints on startup and offer recovery"""
        if not self._autosave_manager.has_checkpoints():
            return

        # Get the latest checkpoint
        checkpoint_info = self._autosave_manager.get_latest_checkpoint()
        if not checkpoint_info:
            return

        checkpoint_path, metadata = checkpoint_info
        project_name = metadata.get("project_name", "Unknown")
        timestamp_str = metadata.get("timestamp", "Unknown time")

        # Parse timestamp for better display
        try:
            timestamp = datetime.fromisoformat(timestamp_str)
            time_display = timestamp.strftime("%Y-%m-%d %H:%M:%S")
        except:
            time_display = timestamp_str

        # Ask user if they want to recover
        reply = QMessageBox.question(
            self,
            "Checkpoint Recovery",
            f"A checkpoint was found:\n\n"
            f"Project: {project_name}\n"
            f"Time: {time_display}\n\n"
            f"Would you like to recover this checkpoint?",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.Yes,
        )

        if reply == QMessageBox.StandardButton.Yes:
            # Load the checkpoint
            success, result = self._autosave_manager.load_checkpoint(checkpoint_path)

            if success:
                # Replace current project with recovered one
                if hasattr(self, "_project") and self._project:
                    self._project.cleanup()

                self._project = result
                self.gl_widget.current_page_index = 0
                self.update_view()

                self.show_status(f"Recovered checkpoint: {project_name}")
                print(f"Successfully recovered checkpoint: {project_name}")
            else:
                error_msg = f"Failed to recover checkpoint: {result}"
                self.show_error("Recovery Failed", error_msg)
                print(error_msg)

    def closeEvent(self, event):
        """Handle window close event"""
        # Check if project has unsaved changes
        if self.project and self.project.is_dirty():
            reply = QMessageBox.question(
                self,
                "Unsaved Changes",
                "You have unsaved changes. Would you like to save before exiting?",
                QMessageBox.StandardButton.Save
                | QMessageBox.StandardButton.Discard
                | QMessageBox.StandardButton.Cancel,
                QMessageBox.StandardButton.Save,
            )

            if reply == QMessageBox.StandardButton.Save:
                # Save is async — ignore event and let on_complete trigger close
                self._pending_close = True
                save_started = self.save_project()
                if not save_started:
                    # User cancelled the file dialog
                    self._pending_close = False
                event.ignore()
                return
            elif reply == QMessageBox.StandardButton.Cancel:
                # User cancelled exit
                event.ignore()
                return
            # If Discard, continue with exit

        # Clean up checkpoints on successful exit
        if self.project:
            self._autosave_manager.delete_all_checkpoints(self.project.name)
            self.project.cleanup()

        # Stop autosave timer
        if hasattr(self, "_autosave_timer"):
            self._autosave_timer.stop()

        # Cleanup old checkpoints
        self._autosave_manager.cleanup_old_checkpoints()

        event.accept()


def main():
    """Application entry point"""
    app = QApplication(sys.argv)

    # Set application identity for proper taskbar/window manager integration
    app.setApplicationName("pyPhotoAlbum")
    app.setApplicationDisplayName("pyPhotoAlbum")
    app.setDesktopFileName("pyphotoalbum.desktop")

    # Set application icon
    icon_path = Path(__file__).parent / "icons" / "icon.png"
    print(f"Application icon path: {icon_path}")
    print(f"Icon exists: {icon_path.exists()}")
    if icon_path.exists():
        icon = QIcon(str(icon_path))
        print(f"Icon is null: {icon.isNull()}")
        app.setWindowIcon(icon)

    # Enable high DPI scaling
    try:
        app.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling, True)
        app.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps, True)
    except AttributeError:
        pass  # Qt version doesn't support these attributes

    window = MainWindow()
    window.show()

    sys.exit(app.exec())


if __name__ == "__main__":
    main()
