"""
Tests for ApplicationStateMixin (base mixin)
"""

import pytest
from unittest.mock import Mock, MagicMock
from PyQt6.QtWidgets import QMainWindow, QStatusBar, QMessageBox
from pyPhotoAlbum.mixins.base import ApplicationStateMixin
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.page_layout import PageLayout


class TestAppStateWindow(ApplicationStateMixin, QMainWindow):
    """Test window with application state mixin"""

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


class TestPropertyAccess:
    """Test property access methods"""

    def test_project_property_get(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        window._project = project

        assert window.project == project

    def test_project_property_set(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        window.project = project

        assert window._project == project

    def test_project_property_missing_raises_error(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        with pytest.raises(AttributeError):
            _ = window.project

    def test_gl_widget_property_get(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        gl_widget = Mock()
        window._gl_widget = gl_widget

        assert window.gl_widget == gl_widget

    def test_gl_widget_property_missing_raises_error(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        with pytest.raises(AttributeError):
            _ = window.gl_widget

    def test_status_bar_property_get(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        status_bar = QStatusBar()
        window._status_bar = status_bar

        assert window.status_bar == status_bar

    def test_status_bar_property_missing_raises_error(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        with pytest.raises(AttributeError):
            _ = window.status_bar

    def test_template_manager_property_get(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        template_manager = Mock()
        window._template_manager = template_manager

        assert window.template_manager == template_manager

    def test_template_manager_property_missing_raises_error(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        with pytest.raises(AttributeError):
            _ = window.template_manager


class TestGetCurrentPage:
    """Test get_current_page method"""

    def test_get_current_page_success(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        # Setup project with pages
        project = Mock(spec=Project)
        project.working_dpi = 96
        page1 = Mock(spec=Page)
        page2 = Mock(spec=Page)
        project.pages = [page1, page2]

        gl_widget = Mock()
        gl_widget.current_page_index = 0
        gl_widget._page_renderers = []  # No renderers, so it will use current_page_index

        window._project = project
        window._gl_widget = gl_widget

        assert window.get_current_page() == page1

    def test_get_current_page_second_page(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        project.working_dpi = 96
        page1 = Mock(spec=Page)
        page2 = Mock(spec=Page)
        project.pages = [page1, page2]

        gl_widget = Mock()
        gl_widget.current_page_index = 1
        gl_widget._page_renderers = []  # No renderers, so it will use current_page_index

        window._project = project
        window._gl_widget = gl_widget

        assert window.get_current_page() == page2

    def test_get_current_page_no_project(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        window._project = None
        window._gl_widget = Mock()

        assert window.get_current_page() is None

    def test_get_current_page_no_pages(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        project.pages = []

        window._project = project
        window._gl_widget = Mock()

        assert window.get_current_page() is None

    def test_get_current_page_invalid_index(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        project.working_dpi = 96
        project.pages = [Mock(spec=Page)]

        gl_widget = Mock()
        gl_widget.current_page_index = 5  # Out of range
        gl_widget._page_renderers = []  # No renderers, so it will use current_page_index

        window._project = project
        window._gl_widget = gl_widget

        assert window.get_current_page() is None


class TestGetCurrentPageIndex:
    """Test get_current_page_index method"""

    def test_get_current_page_index_success(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        project.pages = [Mock(), Mock()]

        gl_widget = Mock()
        gl_widget.current_page_index = 1

        window._project = project
        window._gl_widget = gl_widget

        assert window.get_current_page_index() == 1

    def test_get_current_page_index_no_project(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        window._project = None
        window._gl_widget = Mock()

        assert window.get_current_page_index() == -1

    def test_get_current_page_index_no_pages(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        project.pages = []

        window._project = project
        window._gl_widget = Mock()

        assert window.get_current_page_index() == -1


class TestShowStatus:
    """Test show_status method"""

    def test_show_status_with_status_bar(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        status_bar = Mock(spec=QStatusBar)
        window._status_bar = status_bar

        window.show_status("Test message", 3000)

        status_bar.showMessage.assert_called_once_with("Test message", 3000)

    def test_show_status_default_timeout(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        status_bar = Mock(spec=QStatusBar)
        window._status_bar = status_bar

        window.show_status("Test message")

        status_bar.showMessage.assert_called_once_with("Test message", 2000)


class TestDialogMethods:
    """Test dialog methods (error, warning, info)"""

    def test_show_error(self, qtbot, monkeypatch):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        mock_critical = Mock()
        monkeypatch.setattr(QMessageBox, "critical", mock_critical)

        window.show_error("Error Title", "Error message")

        mock_critical.assert_called_once_with(window, "Error Title", "Error message")

    def test_show_warning(self, qtbot, monkeypatch):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        mock_warning = Mock()
        monkeypatch.setattr(QMessageBox, "warning", mock_warning)

        window.show_warning("Warning Title", "Warning message")

        mock_warning.assert_called_once_with(window, "Warning Title", "Warning message")

    def test_show_info(self, qtbot, monkeypatch):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        mock_info = Mock()
        monkeypatch.setattr(QMessageBox, "information", mock_info)

        window.show_info("Info Title", "Info message")

        mock_info.assert_called_once_with(window, "Info Title", "Info message")


class TestRequirePage:
    """Test require_page method"""

    def test_require_page_with_page(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        project.working_dpi = 96
        project.pages = [Mock(spec=Page)]

        gl_widget = Mock()
        gl_widget.current_page_index = 0
        gl_widget._page_renderers = []  # No renderers, so it will use current_page_index

        window._project = project
        window._gl_widget = gl_widget

        assert window.require_page() is True

    def test_require_page_no_page_with_warning(self, qtbot, monkeypatch):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        project.pages = []

        window._project = project
        window._gl_widget = Mock()

        mock_warning = Mock()
        monkeypatch.setattr(QMessageBox, "warning", mock_warning)

        result = window.require_page(show_warning=True)

        assert result is False
        mock_warning.assert_called_once()

    def test_require_page_no_page_without_warning(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        project = Mock(spec=Project)
        project.pages = []

        window._project = project
        window._gl_widget = Mock()

        result = window.require_page(show_warning=False)

        assert result is False


class TestRequireSelection:
    """Test require_selection method"""

    def test_require_selection_one_element(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        gl_widget = Mock()
        gl_widget.selected_elements = {Mock()}

        window._gl_widget = gl_widget

        assert window.require_selection(min_count=1) is True

    def test_require_selection_multiple_elements(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        gl_widget = Mock()
        gl_widget.selected_elements = {Mock(), Mock(), Mock()}

        window._gl_widget = gl_widget

        assert window.require_selection(min_count=3) is True

    def test_require_selection_insufficient_with_warning(self, qtbot, monkeypatch):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        gl_widget = Mock()
        gl_widget.selected_elements = set()

        window._gl_widget = gl_widget

        mock_info = Mock()
        monkeypatch.setattr(QMessageBox, "information", mock_info)

        result = window.require_selection(min_count=1, show_warning=True)

        assert result is False
        mock_info.assert_called_once()
        # Check it shows "No Selection" message
        call_args = mock_info.call_args[0]
        assert "No Selection" in call_args

    def test_require_selection_insufficient_multiple_with_warning(self, qtbot, monkeypatch):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        gl_widget = Mock()
        gl_widget.selected_elements = {Mock()}

        window._gl_widget = gl_widget

        mock_info = Mock()
        monkeypatch.setattr(QMessageBox, "information", mock_info)

        result = window.require_selection(min_count=3, show_warning=True)

        assert result is False
        mock_info.assert_called_once()
        # Check it shows "at least N elements" message
        call_args = mock_info.call_args[0]
        assert "at least 3" in call_args[2]

    def test_require_selection_insufficient_without_warning(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        gl_widget = Mock()
        gl_widget.selected_elements = set()

        window._gl_widget = gl_widget

        result = window.require_selection(min_count=1, show_warning=False)

        assert result is False


class TestUpdateView:
    """Test update_view method"""

    def test_update_view(self, qtbot):
        window = TestAppStateWindow()
        qtbot.addWidget(window)

        gl_widget = Mock()
        window._gl_widget = gl_widget

        window.update_view()

        gl_widget.update.assert_called_once()
