"""
Tests for KeyboardNavigationMixin
"""

import pytest
from unittest.mock import Mock, MagicMock, patch
from PyQt6.QtCore import Qt
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
from pyPhotoAlbum.mixins.keyboard_navigation import KeyboardNavigationMixin
from pyPhotoAlbum.mixins.viewport import ViewportMixin
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.page_layout import PageLayout
from pyPhotoAlbum.models import ImageData, TextBoxData


# Create test widget combining necessary mixins
class KeyboardNavWidget(KeyboardNavigationMixin, ViewportMixin, QOpenGLWidget):
    """Test widget combining keyboard navigation and viewport mixins"""

    def __init__(self):
        super().__init__()
        self.selected_elements = set()


class TestNavigateToNextPage:
    """Test _navigate_to_next_page method"""

    def test_navigate_next_no_project_attribute(self, qtbot):
        """Test does nothing when main window has no project attribute"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        mock_window = Mock()
        del mock_window.project
        widget.window = Mock(return_value=mock_window)

        # Should not raise exception
        widget._navigate_to_next_page()

    def test_navigate_next_no_project(self, qtbot):
        """Test does nothing when project is None"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        mock_window = Mock()
        mock_window.project = None
        widget.window = Mock(return_value=mock_window)

        widget._navigate_to_next_page()

    def test_navigate_next_empty_pages(self, qtbot):
        """Test does nothing when project has no pages"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.project.pages = []
        widget.window = Mock(return_value=mock_window)

        widget._navigate_to_next_page()

    def test_navigate_next_at_last_page(self, qtbot):
        """Test does nothing when already at last page"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.project.working_dpi = 96
        page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
        page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
        mock_window.project.pages = [page1, page2]
        mock_window._get_most_visible_page_index = Mock(return_value=1)  # Last page
        widget.window = Mock(return_value=mock_window)

        initial_pan = widget.pan_offset.copy()
        widget._navigate_to_next_page()

        # Should not navigate (already at last page)

    def test_navigate_next_to_next_page(self, qtbot):
        """Test navigates to next page successfully"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        # Need to set widget size for calculations
        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
            page3 = Page(layout=PageLayout(width=210, height=297), page_number=3)
            mock_window.project.pages = [page1, page2, page3]
            mock_window._get_most_visible_page_index = Mock(return_value=0)  # First page
            mock_window.project.get_page_display_name = Mock(return_value="Page 2")
            mock_window.show_status = Mock()
            mock_window.update_scrollbars = Mock()
            widget.window = Mock(return_value=mock_window)

            # Mock update method
            widget.update = Mock()

            widget._navigate_to_next_page()

            # Should have scrolled to page 2
            mock_window.show_status.assert_called_once_with("Navigated to Page 2", 2000)
            widget.update.assert_called()

    def test_navigate_next_without_status_bar(self, qtbot):
        """Test navigation works even without show_status method"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
            mock_window.project.pages = [page1, page2]
            mock_window._get_most_visible_page_index = Mock(return_value=0)
            mock_window.update_scrollbars = Mock()
            # No show_status attribute
            del mock_window.show_status
            widget.window = Mock(return_value=mock_window)

            widget.update = Mock()

            # Should not raise exception
            widget._navigate_to_next_page()
            widget.update.assert_called()


class TestNavigateToPreviousPage:
    """Test _navigate_to_previous_page method"""

    def test_navigate_prev_no_project_attribute(self, qtbot):
        """Test does nothing when main window has no project attribute"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        mock_window = Mock()
        del mock_window.project
        widget.window = Mock(return_value=mock_window)

        widget._navigate_to_previous_page()

    def test_navigate_prev_no_project(self, qtbot):
        """Test does nothing when project is None"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        mock_window = Mock()
        mock_window.project = None
        widget.window = Mock(return_value=mock_window)

        widget._navigate_to_previous_page()

    def test_navigate_prev_empty_pages(self, qtbot):
        """Test does nothing when project has no pages"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.project.pages = []
        widget.window = Mock(return_value=mock_window)

        widget._navigate_to_previous_page()

    def test_navigate_prev_at_first_page(self, qtbot):
        """Test does nothing when already at first page"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.project.working_dpi = 96
        page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
        page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
        mock_window.project.pages = [page1, page2]
        mock_window._get_most_visible_page_index = Mock(return_value=0)  # First page
        widget.window = Mock(return_value=mock_window)

        widget._navigate_to_previous_page()

        # Should not navigate (already at first page)

    def test_navigate_prev_to_previous_page(self, qtbot):
        """Test navigates to previous page successfully"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
            page3 = Page(layout=PageLayout(width=210, height=297), page_number=3)
            mock_window.project.pages = [page1, page2, page3]
            mock_window._get_most_visible_page_index = Mock(return_value=2)  # Third page
            mock_window.project.get_page_display_name = Mock(return_value="Page 2")
            mock_window.show_status = Mock()
            mock_window.update_scrollbars = Mock()
            widget.window = Mock(return_value=mock_window)

            widget.update = Mock()

            widget._navigate_to_previous_page()

            # Should have scrolled to page 2
            mock_window.show_status.assert_called_once_with("Navigated to Page 2", 2000)
            widget.update.assert_called()

    def test_navigate_prev_without_status_bar(self, qtbot):
        """Test navigation works even without show_status method"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
            mock_window.project.pages = [page1, page2]
            mock_window._get_most_visible_page_index = Mock(return_value=1)
            mock_window.update_scrollbars = Mock()
            del mock_window.show_status
            widget.window = Mock(return_value=mock_window)

            widget.update = Mock()

            widget._navigate_to_previous_page()
            widget.update.assert_called()


class TestScrollToPage:
    """Test _scroll_to_page method"""

    def test_scroll_to_page_no_project_attribute(self, qtbot):
        """Test does nothing when main window has no project attribute"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        mock_window = Mock()
        del mock_window.project
        widget.window = Mock(return_value=mock_window)

        page = Page(layout=PageLayout(width=210, height=297), page_number=1)
        widget._scroll_to_page(page, 0)

    def test_scroll_to_page_first_page(self, qtbot):
        """Test scrolling to first page"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
            mock_window.project.pages = [page1, page2]
            mock_window.update_scrollbars = Mock()
            widget.window = Mock(return_value=mock_window)

            widget.update = Mock()

            widget._scroll_to_page(page1, 0)

            # Should have updated pan offset
            assert widget.pan_offset[1] != 0  # Y offset should be set
            widget.update.assert_called()
            mock_window.update_scrollbars.assert_called()

    def test_scroll_to_page_second_page(self, qtbot):
        """Test scrolling to second page accounts for first page height"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
            mock_window.project.pages = [page1, page2]
            mock_window.update_scrollbars = Mock()
            widget.window = Mock(return_value=mock_window)

            widget.update = Mock()

            # Scroll to first page
            widget._scroll_to_page(page1, 0)
            first_page_offset = widget.pan_offset[1]

            # Scroll to second page
            widget._scroll_to_page(page2, 1)
            second_page_offset = widget.pan_offset[1]

            # Second page offset should be different (accounting for first page height + spacing)
            assert second_page_offset != first_page_offset

    def test_scroll_to_page_with_different_zoom(self, qtbot):
        """Test scrolling respects zoom level"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]

        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
            mock_window.project.pages = [page1, page2]
            mock_window.update_scrollbars = Mock()
            widget.window = Mock(return_value=mock_window)

            widget.update = Mock()

            # Test at 100% zoom
            widget.zoom_level = 1.0
            widget._scroll_to_page(page2, 1)
            offset_100 = widget.pan_offset[1]

            # Test at 50% zoom
            widget.zoom_level = 0.5
            widget._scroll_to_page(page2, 1)
            offset_50 = widget.pan_offset[1]

            # Offsets should be different due to zoom
            assert offset_100 != offset_50

    def test_scroll_to_page_calls_clamp_if_available(self, qtbot):
        """Test that clamp_pan_offset is called if available"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]
        widget.clamp_pan_offset = Mock()

        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            mock_window.project.pages = [page1]
            mock_window.update_scrollbars = Mock()
            widget.window = Mock(return_value=mock_window)

            widget.update = Mock()

            widget._scroll_to_page(page1, 0)

            # clamp_pan_offset should have been called
            widget.clamp_pan_offset.assert_called()

    def test_scroll_to_page_without_update_scrollbars(self, qtbot):
        """Test scrolling works even without update_scrollbars method"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.zoom_level = 1.0
        widget.pan_offset = [0, 0]

        with patch.object(widget, "height", return_value=800):
            mock_window = Mock()
            mock_window.project = Project(name="Test")
            mock_window.project.working_dpi = 96
            page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
            mock_window.project.pages = [page1]
            del mock_window.update_scrollbars
            widget.window = Mock(return_value=mock_window)

            widget.update = Mock()

            # Should not raise exception
            widget._scroll_to_page(page1, 0)
            widget.update.assert_called()


class TestMoveViewportWithArrowKeys:
    """Test _move_viewport_with_arrow_keys method"""

    def test_move_viewport_up(self, qtbot):
        """Test moving viewport up with up arrow"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]
        widget.update = Mock()

        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)

        # Pan Y should increase by 50
        assert widget.pan_offset[1] == 50
        assert widget.pan_offset[0] == 0
        widget.update.assert_called()

    def test_move_viewport_down(self, qtbot):
        """Test moving viewport down with down arrow"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]
        widget.update = Mock()

        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Down)

        # Pan Y should decrease by 50
        assert widget.pan_offset[1] == -50
        assert widget.pan_offset[0] == 0
        widget.update.assert_called()

    def test_move_viewport_left(self, qtbot):
        """Test moving viewport left with left arrow"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]
        widget.update = Mock()

        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Left)

        # Pan X should increase by 50
        assert widget.pan_offset[0] == 50
        assert widget.pan_offset[1] == 0
        widget.update.assert_called()

    def test_move_viewport_right(self, qtbot):
        """Test moving viewport right with right arrow"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]
        widget.update = Mock()

        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Right)

        # Pan X should decrease by 50
        assert widget.pan_offset[0] == -50
        assert widget.pan_offset[1] == 0
        widget.update.assert_called()

    def test_move_viewport_multiple_moves(self, qtbot):
        """Test multiple consecutive moves accumulate"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]
        widget.update = Mock()

        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)
        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Right)

        # Should have moved up (Y+50) and right (X-50)
        assert widget.pan_offset[0] == -50
        assert widget.pan_offset[1] == 50

    def test_move_viewport_calls_clamp_if_available(self, qtbot):
        """Test that clamp_pan_offset is called if available"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]
        widget.clamp_pan_offset = Mock()
        widget.update = Mock()

        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)

        widget.clamp_pan_offset.assert_called()

    def test_move_viewport_updates_scrollbars_if_available(self, qtbot):
        """Test that update_scrollbars is called if available"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.project.pages = []
        mock_window.update_scrollbars = Mock()
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)

        mock_window.update_scrollbars.assert_called()

    def test_move_viewport_without_scrollbars(self, qtbot):
        """Test movement works even without update_scrollbars method"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.pan_offset = [0, 0]

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.project.pages = []
        del mock_window.update_scrollbars
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        # Should not raise exception
        widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)
        assert widget.pan_offset[1] == 50


class TestMoveSelectedElementsWithArrowKeys:
    """Test _move_selected_elements_with_arrow_keys method"""

    def test_move_elements_no_project_attribute(self, qtbot):
        """Test does nothing when main window has no project attribute"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        mock_window = Mock()
        del mock_window.project
        widget.window = Mock(return_value=mock_window)

        # Should not raise exception
        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

    def test_move_elements_no_selected_elements(self, qtbot):
        """Test works when no elements are selected (just updates view)"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)
        widget.selected_elements = set()

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

        # Update is called even with no selected elements
        widget.update.assert_called()

    def test_move_elements_up(self, qtbot):
        """Test moving elements up with up arrow"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        # Create element without parent page (no snapping)
        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

        # Y should decrease by 1mm
        assert element.position == (5, 4)
        widget.update.assert_called()

    def test_move_elements_down(self, qtbot):
        """Test moving elements down with down arrow"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Down)

        # Y should increase by 1mm
        assert element.position == (5, 6)
        widget.update.assert_called()

    def test_move_elements_left(self, qtbot):
        """Test moving elements left with left arrow"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Left)

        # X should decrease by 1mm
        assert element.position == (4, 5)
        widget.update.assert_called()

    def test_move_elements_right(self, qtbot):
        """Test moving elements right with right arrow"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Right)

        # X should increase by 1mm
        assert element.position == (6, 5)
        widget.update.assert_called()

    def test_move_multiple_elements(self, qtbot):
        """Test moving multiple selected elements"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element1 = ImageData(image_path="test1.jpg", width=10, height=10, x=5, y=5)
        element2 = TextBoxData(text_content="Test", width=20, height=20, x=10, y=10)
        widget.selected_elements = {element1, element2}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.show_status = Mock()
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

        # Both elements should move
        assert element1.position == (5, 4)
        assert element2.position == (10, 9)
        mock_window.show_status.assert_called_once_with("Moved 2 elements", 1000)

    def test_move_elements_with_snapping(self, qtbot):
        """Test moving elements with snapping enabled"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        # Create element with parent page
        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)

        # Create mock page with snapping system
        mock_page = Mock()
        mock_snapping_system = Mock()
        mock_snapping_system.snap_position = Mock(return_value=(5.5, 4.5))  # Snapped position
        mock_layout = Mock()
        mock_layout.snapping_system = mock_snapping_system
        mock_layout.size = (210, 297)
        mock_page.layout = mock_layout

        element._parent_page = mock_page
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.project.working_dpi = 96
        mock_window.show_status = Mock()
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

        # Position should be the snapped position
        assert element.position == (5.5, 4.5)
        # Snapping system should have been called
        mock_snapping_system.snap_position.assert_called_once()

    def test_move_elements_without_snapping(self, qtbot):
        """Test moving elements without parent page (no snapping)"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        # No _parent_page attribute
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.show_status = Mock()
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

        # Position should be directly set (no snapping)
        assert element.position == (5, 4)

    def test_move_elements_shows_status_single_element(self, qtbot):
        """Test status message for single element"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.show_status = Mock()
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

        mock_window.show_status.assert_called_once_with("Moved 1 element", 1000)

    def test_move_elements_shows_status_multiple_elements(self, qtbot):
        """Test status message for multiple elements"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element1 = ImageData(image_path="test1.jpg", width=10, height=10, x=5, y=5)
        element2 = ImageData(image_path="test2.jpg", width=10, height=10, x=10, y=10)
        element3 = ImageData(image_path="test3.jpg", width=10, height=10, x=15, y=15)
        widget.selected_elements = {element1, element2, element3}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.show_status = Mock()
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Right)

        mock_window.show_status.assert_called_once_with("Moved 3 elements", 1000)

    def test_move_elements_without_status_bar(self, qtbot):
        """Test movement works even without show_status method"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        del mock_window.show_status
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        # Should not raise exception
        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
        assert element.position == (5, 4)
        widget.update.assert_called()

    def test_move_elements_element_without_parent_page_attribute(self, qtbot):
        """Test moving elements that don't have _parent_page attribute"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        # Ensure no _parent_page attribute exists
        if hasattr(element, "_parent_page"):
            delattr(element, "_parent_page")
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.show_status = Mock()
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

        # Should use direct positioning (no snapping)
        assert element.position == (5, 4)

    def test_move_elements_with_none_parent_page(self, qtbot):
        """Test moving elements with _parent_page set to None"""
        widget = KeyboardNavWidget()
        qtbot.addWidget(widget)

        element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
        element._parent_page = None
        widget.selected_elements = {element}

        mock_window = Mock()
        mock_window.project = Project(name="Test")
        mock_window.show_status = Mock()
        widget.window = Mock(return_value=mock_window)
        widget.update = Mock()

        widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)

        # Should use direct positioning (no snapping)
        assert element.position == (5, 4)
