"""
Tests for merge_dialog module
"""

import pytest
from unittest.mock import Mock, MagicMock, patch
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QRadioButton


class TestPagePreviewWidget:
    """Tests for PagePreviewWidget class"""

    def test_init(self, qtbot):
        """Test PagePreviewWidget initialization"""
        from pyPhotoAlbum.merge_dialog import PagePreviewWidget

        page_data = {
            "page_number": 1,
            "layout": {"elements": []},
            "last_modified": "2024-01-01 12:00:00",
        }

        widget = PagePreviewWidget(page_data)
        qtbot.addWidget(widget)

        assert widget.page_data == page_data
        assert widget.minimumSize().width() == 200
        assert widget.minimumSize().height() == 280

    def test_paint_event_basic(self, qtbot):
        """Test basic paint event rendering"""
        from pyPhotoAlbum.merge_dialog import PagePreviewWidget

        page_data = {
            "page_number": 2,
            "layout": {"elements": []},
            "last_modified": "2024-01-01",
        }

        widget = PagePreviewWidget(page_data)
        qtbot.addWidget(widget)
        widget.show()

        # Trigger paint event
        widget.repaint()

    def test_paint_event_with_elements(self, qtbot):
        """Test paint event with elements"""
        from pyPhotoAlbum.merge_dialog import PagePreviewWidget

        page_data = {
            "page_number": 3,
            "layout": {
                "elements": [
                    {"type": "image", "deleted": False},
                    {"type": "textbox", "deleted": False},
                    {"type": "shape", "deleted": True},
                ]
            },
            "last_modified": "2024-01-01 10:00:00",
        }

        widget = PagePreviewWidget(page_data)
        qtbot.addWidget(widget)
        widget.show()
        widget.repaint()

    def test_paint_event_many_elements(self, qtbot):
        """Test paint event with more than 5 elements (tests truncation)"""
        from pyPhotoAlbum.merge_dialog import PagePreviewWidget

        elements = [{"type": f"elem{i}", "deleted": False} for i in range(10)]

        page_data = {
            "page_number": 4,
            "layout": {"elements": elements},
            "last_modified": "2024-01-01",
        }

        widget = PagePreviewWidget(page_data)
        qtbot.addWidget(widget)
        widget.show()
        widget.repaint()


class TestConflictItemWidget:
    """Tests for ConflictItemWidget class"""

    @pytest.fixture
    def mock_conflict_page(self):
        """Create a mock page conflict"""
        from pyPhotoAlbum.merge_manager import ConflictInfo, ConflictType

        conflict = ConflictInfo(
            conflict_type=ConflictType.PAGE_MODIFIED_BOTH,
            page_uuid="page-uuid-1",
            element_uuid=None,
            description="Page 1 modified in both versions",
            our_version={
                "page_number": 1,
                "layout": {"elements": [{"type": "image"}]},
                "last_modified": "2024-01-01 10:00:00",
            },
            their_version={
                "page_number": 1,
                "layout": {"elements": [{"type": "image"}, {"type": "textbox"}]},
                "last_modified": "2024-01-01 11:00:00",
            },
        )
        return conflict

    @pytest.fixture
    def mock_conflict_element(self):
        """Create a mock element conflict"""
        from pyPhotoAlbum.merge_manager import ConflictInfo, ConflictType

        conflict = ConflictInfo(
            conflict_type=ConflictType.ELEMENT_MODIFIED_BOTH,
            page_uuid="page-uuid-1",
            element_uuid="element-uuid-1",
            description="Element modified in both versions",
            our_version={
                "type": "image",
                "position": (10, 20),
                "size": (100, 100),
                "deleted": False,
                "last_modified": "2024-01-01",
            },
            their_version={
                "type": "image",
                "position": (15, 25),
                "size": (120, 120),
                "deleted": False,
                "last_modified": "2024-01-02",
            },
        )
        return conflict

    @pytest.fixture
    def mock_conflict_settings(self):
        """Create a mock settings conflict"""
        from pyPhotoAlbum.merge_manager import ConflictInfo, ConflictType

        conflict = ConflictInfo(
            conflict_type=ConflictType.SETTINGS_MODIFIED_BOTH,
            page_uuid=None,
            element_uuid=None,
            description="Settings modified",
            our_version={"theme": "light", "font_size": 12, "last_modified": "2024-01-01"},
            their_version={"theme": "dark", "font_size": 14, "last_modified": "2024-01-02"},
        )
        return conflict

    def test_init_page_conflict(self, qtbot, mock_conflict_page):
        """Test initialization with page conflict"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(0, mock_conflict_page)
        qtbot.addWidget(widget)

        assert widget.conflict_index == 0
        assert widget.conflict == mock_conflict_page

    def test_init_element_conflict(self, qtbot, mock_conflict_element):
        """Test initialization with element conflict"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(1, mock_conflict_element)
        qtbot.addWidget(widget)

        assert widget.conflict_index == 1
        assert widget.conflict == mock_conflict_element

    def test_init_settings_conflict(self, qtbot, mock_conflict_settings):
        """Test initialization with settings conflict"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(2, mock_conflict_settings)
        qtbot.addWidget(widget)

        assert widget.conflict_index == 2

    def test_resolution_changed_signal_ours(self, qtbot, mock_conflict_page):
        """Test resolution_changed signal for 'ours'"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(0, mock_conflict_page)
        qtbot.addWidget(widget)

        signal_received = []

        def on_resolution_changed(index, choice):
            signal_received.append((index, choice))

        widget.resolution_changed.connect(on_resolution_changed)

        # First select "theirs", then select "ours" to trigger the signal
        for button in widget.button_group.buttons():
            if "Other" in button.text():
                button.setChecked(True)
                break

        # Clear the signal list
        signal_received.clear()

        # Now select "ours" to trigger the signal
        for button in widget.button_group.buttons():
            if "Your" in button.text():
                button.setChecked(True)
                break

        assert len(signal_received) == 1
        assert signal_received[0] == (0, "ours")

    def test_resolution_changed_signal_theirs(self, qtbot, mock_conflict_page):
        """Test resolution_changed signal for 'theirs'"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(0, mock_conflict_page)
        qtbot.addWidget(widget)

        signal_received = []

        def on_resolution_changed(index, choice):
            signal_received.append((index, choice))

        widget.resolution_changed.connect(on_resolution_changed)

        # Find "Use Other Version" button and click it
        for button in widget.button_group.buttons():
            if "Other" in button.text():
                button.setChecked(True)
                break

        assert len(signal_received) == 1
        assert signal_received[0] == (0, "theirs")

    def test_get_resolution_ours(self, qtbot, mock_conflict_page):
        """Test get_resolution returns 'ours' when selected"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(0, mock_conflict_page)
        qtbot.addWidget(widget)

        # Default is "ours"
        assert widget.get_resolution() == "ours"

    def test_get_resolution_theirs(self, qtbot, mock_conflict_page):
        """Test get_resolution returns 'theirs' when selected"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(0, mock_conflict_page)
        qtbot.addWidget(widget)

        # Select "theirs"
        for button in widget.button_group.buttons():
            if "Other" in button.text():
                button.setChecked(True)
                break

        assert widget.get_resolution() == "theirs"

    def test_create_element_details_image(self, qtbot, mock_conflict_element):
        """Test creating element details for image element"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(0, mock_conflict_element)
        qtbot.addWidget(widget)

        details = widget._create_element_details(mock_conflict_element.our_version)

        assert details is not None
        assert "Type: image" in details.toPlainText()

    def test_create_element_details_textbox(self, qtbot):
        """Test creating element details for textbox element"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget
        from pyPhotoAlbum.merge_manager import ConflictInfo, ConflictType

        element_data = {
            "type": "textbox",
            "position": (0, 0),
            "size": (100, 50),
            "deleted": False,
            "last_modified": "2024-01-01",
            "text_content": "This is a long text that should be truncated after 50 characters for display",
        }

        conflict = ConflictInfo(
            conflict_type=ConflictType.ELEMENT_MODIFIED_BOTH,
            page_uuid="page-uuid-1",
            element_uuid="element-uuid-2",
            description="Text element",
            our_version=element_data,
            their_version=element_data,
        )

        widget = ConflictItemWidget(0, conflict)
        qtbot.addWidget(widget)

        details = widget._create_element_details(element_data)
        text = details.toPlainText()

        assert "Type: textbox" in text
        assert "Text:" in text

    def test_create_settings_details(self, qtbot, mock_conflict_settings):
        """Test creating settings details"""
        from pyPhotoAlbum.merge_dialog import ConflictItemWidget

        widget = ConflictItemWidget(0, mock_conflict_settings)
        qtbot.addWidget(widget)

        details = widget._create_settings_details(mock_conflict_settings.our_version)

        assert details is not None
        text = details.toPlainText()
        assert "theme: light" in text
        assert "font_size: 12" in text
        assert "Modified:" in text


class TestMergeDialog:
    """Tests for MergeDialog class"""

    @pytest.fixture
    def our_project_data(self):
        """Create mock 'our' project data"""
        return {
            "name": "Our Project",
            "last_modified": "2024-01-01 10:00:00",
            "pages": [
                {
                    "page_number": 1,
                    "layout": {"elements": [{"type": "image"}]},
                    "last_modified": "2024-01-01 10:00:00",
                }
            ],
        }

    @pytest.fixture
    def their_project_data(self):
        """Create mock 'their' project data"""
        return {
            "name": "Their Project",
            "last_modified": "2024-01-01 11:00:00",
            "pages": [
                {
                    "page_number": 1,
                    "layout": {"elements": [{"type": "image"}, {"type": "textbox"}]},
                    "last_modified": "2024-01-01 11:00:00",
                }
            ],
        }

    @pytest.fixture
    def mock_conflicts(self):
        """Create mock conflicts"""
        from pyPhotoAlbum.merge_manager import ConflictInfo, ConflictType

        return [
            ConflictInfo(
                conflict_type=ConflictType.PAGE_MODIFIED_BOTH,
                page_uuid="page-uuid-conflict",
                element_uuid=None,
                description="Page conflict",
                our_version={"page_number": 1, "layout": {"elements": []}},
                their_version={"page_number": 1, "layout": {"elements": [{"type": "image"}]}},
            )
        ]

    def test_init(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test MergeDialog initialization"""
        from pyPhotoAlbum.merge_dialog import MergeDialog

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            assert dialog.our_project_data == our_project_data
            assert dialog.their_project_data == their_project_data
            assert len(dialog.conflicts) == 1
            assert len(dialog.resolutions) == 1
            assert dialog.resolutions[0] == "ours"  # Default

    def test_init_ui(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test UI initialization"""
        from pyPhotoAlbum.merge_dialog import MergeDialog

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            assert dialog.windowTitle() == "Merge Projects"
            assert dialog.strategy_combo is not None
            assert len(dialog.conflict_widgets) == 1

    def test_on_resolution_changed(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test handling resolution changes"""
        from pyPhotoAlbum.merge_dialog import MergeDialog

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            dialog._on_resolution_changed(0, "theirs")

            assert dialog.resolutions[0] == "theirs"

    def test_auto_resolve_latest_wins(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test auto-resolve with LATEST_WINS strategy"""
        from pyPhotoAlbum.merge_dialog import MergeDialog
        from pyPhotoAlbum.merge_manager import MergeStrategy

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            mock_manager.auto_resolve_conflicts.return_value = {0: "theirs"}
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            # Set strategy to LATEST_WINS
            dialog.strategy_combo.setCurrentIndex(0)

            dialog._auto_resolve()

            mock_manager.auto_resolve_conflicts.assert_called_once()
            assert dialog.resolutions[0] == "theirs"

    def test_auto_resolve_ours(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test auto-resolve with OURS strategy"""
        from pyPhotoAlbum.merge_dialog import MergeDialog
        from pyPhotoAlbum.merge_manager import MergeStrategy

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            mock_manager.auto_resolve_conflicts.return_value = {0: "ours"}
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            # Set strategy to OURS
            dialog.strategy_combo.setCurrentIndex(1)

            dialog._auto_resolve()

            assert dialog.resolutions[0] == "ours"

    def test_auto_resolve_theirs(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test auto-resolve with THEIRS strategy"""
        from pyPhotoAlbum.merge_dialog import MergeDialog
        from pyPhotoAlbum.merge_manager import MergeStrategy

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            mock_manager.auto_resolve_conflicts.return_value = {0: "theirs"}
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            # Set strategy to THEIRS
            dialog.strategy_combo.setCurrentIndex(2)

            dialog._auto_resolve()

            assert dialog.resolutions[0] == "theirs"

    def test_auto_resolve_updates_ui(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test that auto-resolve updates UI radio buttons"""
        from pyPhotoAlbum.merge_dialog import MergeDialog

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            mock_manager.auto_resolve_conflicts.return_value = {0: "theirs"}
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            dialog._auto_resolve()

            # Check that the "Other Version" button is now checked
            conflict_widget = dialog.conflict_widgets[0]
            resolution = conflict_widget.get_resolution()
            assert resolution == "theirs"

    def test_get_merged_project_data(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test getting merged project data"""
        from pyPhotoAlbum.merge_dialog import MergeDialog

        merged_data = {"name": "Merged", "pages": []}

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            mock_manager.apply_resolutions.return_value = merged_data
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            result = dialog.get_merged_project_data()

            mock_manager.apply_resolutions.assert_called_once_with(
                our_project_data, their_project_data, dialog.resolutions
            )
            assert result == merged_data

    def test_accept_button(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test clicking Accept button"""
        from pyPhotoAlbum.merge_dialog import MergeDialog

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            # This should trigger accept without errors
            dialog.accept()

    def test_reject_button(self, qtbot, our_project_data, their_project_data, mock_conflicts):
        """Test clicking Cancel button"""
        from pyPhotoAlbum.merge_dialog import MergeDialog

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = mock_conflicts
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            # This should trigger reject without errors
            dialog.reject()

    def test_no_conflicts(self, qtbot, our_project_data, their_project_data):
        """Test dialog with no conflicts"""
        from pyPhotoAlbum.merge_dialog import MergeDialog

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = []
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            assert len(dialog.conflicts) == 0
            assert len(dialog.conflict_widgets) == 0

    def test_multiple_conflicts(self, qtbot, our_project_data, their_project_data):
        """Test dialog with multiple conflicts"""
        from pyPhotoAlbum.merge_dialog import MergeDialog
        from pyPhotoAlbum.merge_manager import ConflictInfo, ConflictType

        conflicts = [
            ConflictInfo(
                conflict_type=ConflictType.PAGE_MODIFIED_BOTH,
                page_uuid=f"page-uuid-{i}",
                element_uuid=None,
                description=f"Conflict {i}",
                our_version={"page_number": i},
                their_version={"page_number": i},
            )
            for i in range(5)
        ]

        with patch("pyPhotoAlbum.merge_dialog.MergeManager") as MockMergeManager:
            mock_manager = Mock()
            mock_manager.detect_conflicts.return_value = conflicts
            MockMergeManager.return_value = mock_manager

            dialog = MergeDialog(our_project_data, their_project_data)
            qtbot.addWidget(dialog)

            assert len(dialog.conflicts) == 5
            assert len(dialog.conflict_widgets) == 5
            assert len(dialog.resolutions) == 5

            # All should default to "ours"
            for i in range(5):
                assert dialog.resolutions[i] == "ours"
