"""
Test file for document layouter functionality.

This test focuses on verifying that the document layouter properly
integrates word spacing constraints from the style system.
"""

from unittest.mock import Mock, patch

from pyWebLayout.layout.document_layouter import paragraph_layouter, table_layouter, DocumentLayouter
from pyWebLayout.style.abstract_style import AbstractStyle
from pyWebLayout.style.concrete_style import StyleResolver, RenderingContext
from pyWebLayout.abstract.block import Table
from pyWebLayout.concrete.table import TableStyle


class TestDocumentLayouter:
    """Test cases for document layouter functionality."""

    def setup_method(self):
        """Set up test fixtures before each test method."""
        # Create mock objects
        self.mock_page = Mock()
        self.mock_page.border_size = 20
        self.mock_page._current_y_offset = 50
        self.mock_page.available_width = 400
        # Content geometry: a 440x600 page with a 20px border and no padding, so
        # the content box starts at (20, 20) and is 400 wide.
        self.mock_page.size = (440, 600)
        self.mock_page.content_origin = (20, 20)
        self.mock_page.content_rect = (20, 20, 400, 560)
        self.mock_page.remaining_height = 530  # 20 + 560 - 50
        self.mock_page.draw = Mock()
        self.mock_page.can_fit_line = Mock(return_value=True)
        self.mock_page.add_child = Mock()

        # Create mock page style with all required numeric properties
        self.mock_page.style = Mock()
        self.mock_page.style.max_font_size = 72  # Reasonable maximum font size
        self.mock_page.style.line_spacing_multiplier = 1.2  # Standard line spacing

        # Create mock style resolver
        self.mock_style_resolver = Mock()
        self.mock_page.style_resolver = self.mock_style_resolver

        # Create mock paragraph
        self.mock_paragraph = Mock()
        self.mock_paragraph.line_height = 20
        self.mock_paragraph.style = AbstractStyle()

        # Create mock words
        self.mock_words = []
        for i in range(5):
            word = Mock()
            word.text = f"word{i}"
            self.mock_words.append(word)
        self.mock_paragraph.words = self.mock_words

        # Create mock concrete style with word spacing constraints
        self.mock_concrete_style = Mock()
        self.mock_concrete_style.word_spacing_min = 2.0
        self.mock_concrete_style.word_spacing_max = 8.0
        self.mock_concrete_style.text_align = "left"

        # Create mock font that returns proper numeric metrics (not Mock objects)
        mock_font = Mock()
        # CRITICAL: getmetrics() must return actual numeric values, not Mock objects
        # This prevents "TypeError: '>' not supported between instances of 'Mock'
        # and 'Mock'"
        # (ascent, descent) as actual integers
        mock_font.getmetrics.return_value = (12, 4)
        mock_font.font = mock_font  # For accessing .font property

        # Create mock font object that can be used by create_font
        mock_font_instance = Mock()
        mock_font_instance.font = mock_font
        mock_font_instance.font_size = 16
        mock_font_instance.colour = (0, 0, 0)
        mock_font_instance.background = (255, 255, 255, 0)
        self.mock_concrete_style.create_font = Mock(return_value=mock_font_instance)

        # Update mock words to have proper style with font
        for word in self.mock_words:
            word.style = Mock()
            word.style.font = mock_font
            word.style.font_size = 16
            word.style.colour = (0, 0, 0)
            word.style.background = None

    @patch('pyWebLayout.layout.document_layouter.StyleResolver')
    @patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
    @patch('pyWebLayout.layout.document_layouter.Line')
    def test_paragraph_layouter_basic_flow(
            self,
            mock_line_class,
            mock_style_registry_class,
            mock_style_resolver_class):
        """Test basic paragraph layouter functionality."""
        # Setup mocks for StyleResolver and ConcreteStyleRegistry
        mock_style_resolver = Mock()
        mock_style_resolver_class.return_value = mock_style_resolver

        mock_style_registry = Mock()
        mock_style_registry_class.return_value = mock_style_registry
        mock_style_registry.get_concrete_style.return_value = self.mock_concrete_style

        mock_line = Mock()
        mock_line_class.return_value = mock_line
        mock_line.add_word.return_value = (True, None)  # All words fit successfully

        # Call function
        result = paragraph_layouter(self.mock_paragraph, self.mock_page)

        # Verify results
        success, failed_word_index, remaining_pretext = result
        assert success is True
        assert failed_word_index is None
        assert remaining_pretext is None

        # Verify StyleResolver and ConcreteStyleRegistry were created correctly
        mock_style_resolver_class.assert_called_once()
        mock_style_registry_class.assert_called_once_with(mock_style_resolver)
        mock_style_registry.get_concrete_style.assert_called_once_with(
            self.mock_paragraph.style)

        # Verify Line was created with correct spacing constraints
        expected_spacing = (2, 8)  # From mock_concrete_style
        mock_line_class.assert_called_once()
        call_args = mock_line_class.call_args
        assert call_args[1]['spacing'] == expected_spacing

    @patch('pyWebLayout.layout.document_layouter.StyleResolver')
    @patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
    @patch('pyWebLayout.layout.document_layouter.Line')
    def test_paragraph_layouter_word_spacing_constraints_extraction(
            self, mock_line_class, mock_style_registry_class, mock_style_resolver_class):
        """Test that word spacing constraints are correctly extracted from style."""
        # Create concrete style with specific constraints
        concrete_style = Mock()
        concrete_style.word_spacing_min = 5.5
        concrete_style.word_spacing_max = 15.2
        concrete_style.text_align = "justify"

        # Create a mock font that concrete_style.create_font returns
        mock_font = Mock()
        mock_font.font = Mock()
        mock_font.font.getmetrics.return_value = (12, 4)
        mock_font.font_size = 16
        concrete_style.create_font = Mock(return_value=mock_font)

        # Setup StyleResolver and ConcreteStyleRegistry mocks
        mock_style_resolver = Mock()
        mock_style_resolver_class.return_value = mock_style_resolver

        mock_style_registry = Mock()
        mock_style_registry_class.return_value = mock_style_registry
        mock_style_registry.get_concrete_style.return_value = concrete_style

        mock_line = Mock()
        mock_line_class.return_value = mock_line
        mock_line.add_word.return_value = (True, None)

        # Call function
        paragraph_layouter(self.mock_paragraph, self.mock_page)

        # Verify spacing constraints were extracted correctly (converted to int)
        expected_spacing = (5, 15)  # int() conversion of 5.5 and 15.2
        call_args = mock_line_class.call_args
        assert call_args[1]['spacing'] == expected_spacing

    @patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
    @patch('pyWebLayout.layout.document_layouter.Line')
    @patch('pyWebLayout.layout.document_layouter.Text')
    def test_paragraph_layouter_line_overflow(
            self,
            mock_text_class,
            mock_line_class,
            mock_style_registry_class):
        """Test handling of line overflow when words don't fit."""
        # Setup mocks
        mock_style_registry = Mock()
        mock_style_registry_class.return_value = mock_style_registry
        mock_style_registry.get_concrete_style.return_value = self.mock_concrete_style

        # Create two mock lines with proper size attribute
        mock_line1 = Mock()
        mock_line1.size = (400, 20)  # (width, height)
        mock_line2 = Mock()
        mock_line2.size = (400, 20)  # (width, height)
        mock_line_class.side_effect = [mock_line1, mock_line2]

        # Mock Text.from_word to return mock text objects with numeric width
        mock_text = Mock()
        mock_text.width = 50  # Reasonable word width
        mock_text_class.from_word.return_value = mock_text

        # First line: first 2 words fit, third doesn't
        # Second line: remaining words fit
        mock_line1.add_word.side_effect = [
            (True, None),   # word0 fits
            (True, None),   # word1 fits
            (False, None),  # word2 doesn't fit
        ]
        mock_line2.add_word.side_effect = [
            (True, None),   # word2 fits on new line
            (True, None),   # word3 fits
            (True, None),   # word4 fits
        ]

        # Call function
        result = paragraph_layouter(self.mock_paragraph, self.mock_page)

        # Verify results
        success, failed_word_index, remaining_pretext = result
        assert success is True
        assert failed_word_index is None
        assert remaining_pretext is None

        # Verify two lines were created
        assert mock_line_class.call_count == 2
        assert self.mock_page.add_child.call_count == 2

    @patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
    @patch('pyWebLayout.layout.document_layouter.Line')
    def test_paragraph_layouter_page_full(
            self, mock_line_class, mock_style_registry_class):
        """Test handling when page runs out of space."""
        # Setup mocks
        mock_style_registry = Mock()
        mock_style_registry_class.return_value = mock_style_registry
        mock_style_registry.get_concrete_style.return_value = self.mock_concrete_style

        # Page can fit first line but not second
        self.mock_page.can_fit_line.side_effect = [True, False]

        mock_line = Mock()
        mock_line_class.return_value = mock_line
        mock_line.add_word.side_effect = [
            (True, None),   # word0 fits
            (False, None),  # word1 doesn't fit, need new line
        ]

        # Call function
        result = paragraph_layouter(self.mock_paragraph, self.mock_page)

        # Verify results indicate page is full
        success, failed_word_index, remaining_pretext = result
        assert success is False
        assert failed_word_index == 1  # word1 didn't fit
        assert remaining_pretext is None

    def test_paragraph_layouter_empty_paragraph(self):
        """Test handling of empty paragraph."""
        empty_paragraph = Mock()
        empty_paragraph.words = []

        result = paragraph_layouter(empty_paragraph, self.mock_page)

        success, failed_word_index, remaining_pretext = result
        assert success is True
        assert failed_word_index is None
        assert remaining_pretext is None

    def test_paragraph_layouter_invalid_start_word(self):
        """Test handling of invalid start_word index."""
        result = paragraph_layouter(self.mock_paragraph, self.mock_page, start_word=10)

        success, failed_word_index, remaining_pretext = result
        assert success is True
        assert failed_word_index is None
        assert remaining_pretext is None

    @patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
    def test_document_layouter_class(self, mock_style_registry_class):
        """Test DocumentLayouter class functionality."""
        # Setup mock
        mock_style_registry = Mock()
        mock_style_registry_class.return_value = mock_style_registry

        # Create layouter
        layouter = DocumentLayouter(self.mock_page)

        # Verify initialization
        assert layouter.page == self.mock_page
        mock_style_registry_class.assert_called_once_with(self.mock_page.style_resolver)

    @patch('pyWebLayout.layout.document_layouter.paragraph_layouter')
    def test_document_layouter_layout_paragraph(self, mock_paragraph_layouter):
        """Test DocumentLayouter.layout_paragraph method."""
        mock_paragraph_layouter.return_value = (True, None, None)

        with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
            layouter = DocumentLayouter(self.mock_page)

            result = layouter.layout_paragraph(
                self.mock_paragraph, start_word=2, pretext="test")

            # Verify the function was called correctly
            mock_paragraph_layouter.assert_called_once_with(
                self.mock_paragraph, self.mock_page, 2, "test"
            )
            assert result == (True, None, None)

    def test_document_layouter_layout_document_success(self):
        """Test DocumentLayouter.layout_document with successful layout."""
        from pyWebLayout.abstract import Paragraph

        # Create Mock paragraphs that pass isinstance checks
        paragraphs = [
            Mock(spec=Paragraph),
            Mock(spec=Paragraph),
            Mock(spec=Paragraph)
        ]

        with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
            layouter = DocumentLayouter(self.mock_page)

            # Mock the layout_paragraph method to return success
            layouter.layout_paragraph = Mock(return_value=(True, None, None))

            result = layouter.layout_document(paragraphs)

            assert result is True
            assert layouter.layout_paragraph.call_count == 3

    def test_document_layouter_layout_document_failure(self):
        """Test DocumentLayouter.layout_document with layout failure."""
        from pyWebLayout.abstract import Paragraph

        # Create Mock paragraphs that pass isinstance checks
        paragraphs = [
            Mock(spec=Paragraph),
            Mock(spec=Paragraph)
        ]

        with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
            layouter = DocumentLayouter(self.mock_page)

            # Mock the layout_paragraph method: first succeeds, second fails
            layouter.layout_paragraph = Mock(side_effect=[
                (True, None, None),      # First paragraph succeeds
                (False, 3, None),        # Second paragraph fails
            ])

            result = layouter.layout_document(paragraphs)

            assert result is False
            assert layouter.layout_paragraph.call_count == 2

    def test_real_style_integration(self):
        """Test integration with real style system."""
        # Create real style objects
        context = RenderingContext(base_font_size=16)
        resolver = StyleResolver(context)

        abstract_style = AbstractStyle(
            word_spacing=4.0,
            word_spacing_min=2.0,
            word_spacing_max=10.0
        )

        concrete_style = resolver.resolve_style(abstract_style)

        # Verify constraints are resolved correctly
        assert concrete_style.word_spacing_min == 2.0
        assert concrete_style.word_spacing_max == 10.0

        # This demonstrates the integration works end-to-end


class TestWordSpacingConstraintsInLayout:
    """Specific tests for word spacing constraints in layout context."""

    def test_different_spacing_scenarios(self):
        """Test various word spacing constraint scenarios."""
        context = RenderingContext(base_font_size=16)
        resolver = StyleResolver(context)

        test_cases = [
            # (word_spacing, word_spacing_min, word_spacing_max, expected_min, expected_max)
            (None, None, None, 2.0, 8.0),  # Default case
            (5.0, None, None, 5.0, 10.0),  # Only base specified
            (4.0, 2.0, 8.0, 2.0, 8.0),    # All specified
            # Min specified, max = max(word_spacing, min*2) = max(3.0, 2.0) = 3.0
            (3.0, 1.0, None, 1.0, 3.0),
            (6.0, None, 12.0, 6.0, 12.0),  # Max specified, min from base
        ]

        for word_spacing, min_spacing, max_spacing, expected_min, expected_max in test_cases:
            style_kwargs = {}
            if word_spacing is not None:
                style_kwargs['word_spacing'] = word_spacing
            if min_spacing is not None:
                style_kwargs['word_spacing_min'] = min_spacing
            if max_spacing is not None:
                style_kwargs['word_spacing_max'] = max_spacing

            abstract_style = AbstractStyle(**style_kwargs)
            concrete_style = resolver.resolve_style(abstract_style)

            assert concrete_style.word_spacing_min == expected_min, f"Failed for case: {style_kwargs}"
            assert concrete_style.word_spacing_max == expected_max, f"Failed for case: {style_kwargs}"


class TestMultiPageLayout:
    """Test cases for multi-page document layout scenarios."""

    def setup_method(self):
        """Set up test fixtures for multi-page tests."""
        # Create multiple mock pages
        self.mock_pages = []
        for i in range(3):
            page = Mock()
            page.page_number = i + 1
            page.border_size = 20
            page._current_y_offset = 50
            page.available_width = 400
            page.available_height = 600
            page.draw = Mock()
            page.add_child = Mock()
            page.style_resolver = Mock()
            self.mock_pages.append(page)

        # Create a long paragraph that will span multiple pages
        self.long_paragraph = Mock()
        self.long_paragraph.line_height = 25
        self.long_paragraph.style = AbstractStyle()

        # Create many words to ensure page overflow
        self.long_paragraph.words = []
        for i in range(50):  # 50 words should definitely overflow a page
            word = Mock()
            word.text = f"word_{i:02d}"
            self.long_paragraph.words.append(word)

        # Create mock concrete style
        self.mock_concrete_style = Mock()
        self.mock_concrete_style.word_spacing_min = 3.0
        self.mock_concrete_style.word_spacing_max = 12.0
        self.mock_concrete_style.text_align = "justify"
        self.mock_concrete_style.create_font = Mock()

    @patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
    def test_document_layouter_multi_page_scenario(self, mock_style_registry_class):
        """Test DocumentLayouter handling multiple pages with continuation."""
        # Setup style registry
        mock_style_registry = Mock()
        mock_style_registry_class.return_value = mock_style_registry
        mock_style_registry.get_concrete_style.return_value = self.mock_concrete_style

        # Create a multi-page document layouter
        class MultiPageDocumentLayouter(DocumentLayouter):
            def __init__(self, pages):
                self.pages = pages
                self.current_page_index = 0
                self.page = pages[0]
                self.style_registry = Mock()

            def get_next_page(self):
                """Get the next available page."""
                if self.current_page_index + 1 < len(self.pages):
                    self.current_page_index += 1
                    self.page = self.pages[self.current_page_index]
                    return self.page
                return None

            def layout_document_with_pagination(self, paragraphs):
                """Layout document with automatic pagination."""
                for paragraph in paragraphs:
                    start_word = 0
                    pretext = None

                    while start_word < len(paragraph.words):
                        complete, next_word, remaining_pretext = self.layout_paragraph(
                            paragraph, start_word, pretext
                        )

                        if complete:
                            # Paragraph finished
                            break

                        if next_word is None:
                            # Error condition
                            return False, f"Failed to layout paragraph at word {start_word}"

                        # Try to get next page
                        next_page = self.get_next_page()
                        if not next_page:
                            return False, f"Ran out of pages at word {next_word}"

                        # Continue with remaining words on next page
                        start_word = next_word
                        pretext = remaining_pretext

                return True, "All paragraphs laid out successfully"

        # Create layouter with multiple pages
        layouter = MultiPageDocumentLayouter(self.mock_pages)

        # Mock the layout_paragraph method to simulate page filling
        layouter.layout_paragraph
        call_count = [0]

        def mock_layout_paragraph(paragraph, start_word=0, pretext=None):
            call_count[0] += 1

            # Simulate different scenarios based on call count
            if call_count[0] == 1:
                # First page: can fit words 0-19, fails at word 20
                return (False, 20, None)
            elif call_count[0] == 2:
                # Second page: can fit words 20-39, fails at word 40
                return (False, 40, None)
            elif call_count[0] == 3:
                # Third page: can fit remaining words 40-49
                return (True, None, None)
            else:
                return (False, start_word, None)

        layouter.layout_paragraph = mock_layout_paragraph

        # Test multi-page layout
        success, message = layouter.layout_document_with_pagination(
            [self.long_paragraph])

        # Verify results
        assert success is True
        assert "successfully" in message
        assert call_count[0] == 3  # Should have made 3 layout attempts
        assert layouter.current_page_index == 2  # Should end on page 3 (index 2)

    def test_realistic_multi_page_scenario(self):
        """Test a realistic scenario with actual content and page constraints."""
        # Create realistic paragraph with varied content
        realistic_paragraph = Mock()
        realistic_paragraph.line_height = 20
        realistic_paragraph.style = AbstractStyle(
            word_spacing=4.0,
            word_spacing_min=2.0,
            word_spacing_max=8.0,
            text_align="justify"
        )

        # Create words of varying lengths (realistic text)
        words = [
            "The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog.",
            "This", "sentence", "contains", "words", "of", "varying", "lengths",
            "to", "simulate", "realistic", "text", "content", "that", "would",
            "require", "proper", "word", "spacing", "calculations", "and",
            "potentially", "multiple", "pages", "for", "layout.", "Each",
            "word", "represents", "a", "challenge", "for", "the", "layouter",
            "system", "to", "handle", "appropriately", "with", "the", "given",
            "constraints", "and", "spacing", "requirements."
        ]

        realistic_paragraph.words = []
        for word_text in words:
            word = Mock()
            word.text = word_text
            realistic_paragraph.words.append(word)

        # Create page with realistic constraints
        realistic_page = Mock()
        realistic_page.border_size = 30
        realistic_page._current_y_offset = 100
        realistic_page.available_width = 350  # Narrower page
        realistic_page.available_height = 500
        realistic_page.draw = Mock()
        realistic_page.add_child = Mock()
        realistic_page.style_resolver = Mock()

        # Simulate page that can fit approximately 20 lines
        lines_fitted = [0]
        max_lines = 20

        def realistic_can_fit_line(line_height):
            lines_fitted[0] += 1
            return lines_fitted[0] <= max_lines

        realistic_page.can_fit_line = realistic_can_fit_line

        # Test with real style system
        context = RenderingContext(base_font_size=14)
        resolver = StyleResolver(context)
        concrete_style = resolver.resolve_style(realistic_paragraph.style)

        # Verify realistic constraints were calculated
        assert concrete_style.word_spacing == 4.0
        assert concrete_style.word_spacing_min == 2.0
        assert concrete_style.word_spacing_max == 8.0

        # This test demonstrates the integration without mocking everything
        # In a real scenario, this would interface with actual Line and Text objects
        print("✓ Realistic scenario test completed")
        print(f"  - Words to layout: {len(realistic_paragraph.words)}")
        print(f"  - Page width: {realistic_page.available_width}px")
        print(
            f"  - Word spacing constraints: {concrete_style.word_spacing_min}-{concrete_style.word_spacing_max}px")


class TestTableLayouter:
    """Test cases for table layouter functionality."""

    def setup_method(self):
        """Set up test fixtures before each test method."""
        # Create mock page
        self.mock_page = Mock()
        self.mock_page.border_size = 20
        self.mock_page._current_y_offset = 50
        self.mock_page.available_width = 600
        self.mock_page.size = (800, 1000)
        # Content geometry: 800x1000 page, 20px border, no padding.
        self.mock_page.content_origin = (20, 20)
        self.mock_page.content_rect = (20, 20, 600, 960)
        self.mock_page.remaining_height = 930  # 20 + 960 - 50

        # Create mock draw and canvas
        self.mock_draw = Mock()
        self.mock_canvas = Mock()
        self.mock_page.draw = self.mock_draw
        self.mock_page._canvas = self.mock_canvas

        # Create mock table
        self.mock_table = Mock(spec=Table)

        # Create mock style resolver
        self.mock_style_resolver = Mock()
        self.mock_page.style_resolver = self.mock_style_resolver

    @patch('pyWebLayout.layout.document_layouter.TableRenderer')
    def test_table_layouter_success(self, mock_table_renderer_class):
        """Test table_layouter with successful table rendering."""
        # Setup mock renderer
        mock_renderer = Mock()
        mock_table_renderer_class.return_value = mock_renderer
        mock_renderer.size = (500, 200)  # Table fits on page

        # Call function
        result = table_layouter(self.mock_table, self.mock_page)

        # Verify results
        assert result is True

        # Verify TableRenderer was created with correct parameters
        mock_table_renderer_class.assert_called_once_with(
            table=self.mock_table,
            origin=(20, 50),  # (border_size, current_y_offset)
            available_width=600,
            draw=self.mock_draw,
            style=None,
            canvas=self.mock_canvas
        )

        # Verify render was called
        mock_renderer.render.assert_called_once()

        # Verify y_offset was updated
        assert self.mock_page._current_y_offset == 250  # 50 + 200

    @patch('pyWebLayout.layout.document_layouter.TableRenderer')
    def test_table_layouter_with_custom_style(self, mock_table_renderer_class):
        """Test table_layouter with custom TableStyle."""
        # Create custom style
        custom_style = TableStyle(
            border_width=2,
            border_color=(100, 100, 100),
            cell_padding=(10, 10, 10, 10)
        )

        # Setup mock renderer
        mock_renderer = Mock()
        mock_table_renderer_class.return_value = mock_renderer
        mock_renderer.size = (500, 150)

        # Call function with style
        result = table_layouter(self.mock_table, self.mock_page, style=custom_style)

        # Verify TableRenderer was created with custom style
        assert result is True
        call_args = mock_table_renderer_class.call_args
        assert call_args[1]['style'] == custom_style

    @patch('pyWebLayout.layout.document_layouter.TableRenderer')
    def test_table_layouter_table_too_large(self, mock_table_renderer_class):
        """Test table_layouter when table doesn't fit on page."""
        # Setup mock renderer with table larger than available space
        mock_renderer = Mock()
        mock_table_renderer_class.return_value = mock_renderer
        mock_renderer.size = (500, 1000)  # Table height exceeds available space

        # Available height = page_size[1] - y_offset - border_size
        # = 1000 - 50 - 20 = 930, but table is 1000 pixels tall

        # Call function
        result = table_layouter(self.mock_table, self.mock_page)

        # Verify failure
        assert result is False

        # Verify render was NOT called
        mock_renderer.render.assert_not_called()

        # Verify y_offset was NOT updated
        assert self.mock_page._current_y_offset == 50

    @patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
    def test_document_layouter_layout_table(self, mock_style_registry_class):
        """Test DocumentLayouter.layout_table method."""
        # Setup mocks
        mock_style_registry = Mock()
        mock_style_registry_class.return_value = mock_style_registry

        layouter = DocumentLayouter(self.mock_page)

        # Mock the table_layouter function
        with patch('pyWebLayout.layout.document_layouter.table_layouter') as mock_table_layouter:
            mock_table_layouter.return_value = True

            custom_style = TableStyle(border_width=1)
            result = layouter.layout_table(self.mock_table, style=custom_style)

            # Verify the function was called correctly
            mock_table_layouter.assert_called_once_with(
                self.mock_table, self.mock_page, custom_style
            )
            assert result is True

    def test_document_layouter_layout_document_with_table(self):
        """Test DocumentLayouter.layout_document with Table elements."""
        from pyWebLayout.abstract import Paragraph
        from pyWebLayout.abstract.block import Image as AbstractImage

        # Create mixed elements
        elements = [
            Mock(spec=Paragraph),
            Mock(spec=Table),
            Mock(spec=AbstractImage),
            Mock(spec=Table)
        ]

        with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
            layouter = DocumentLayouter(self.mock_page)

            # Mock the layout methods
            layouter.layout_paragraph = Mock(return_value=(True, None, None))
            layouter.layout_table = Mock(return_value=True)
            layouter.layout_image = Mock(return_value=True)

            result = layouter.layout_document(elements)

            # Verify all elements were laid out
            assert result is True
            assert layouter.layout_paragraph.call_count == 1
            assert layouter.layout_table.call_count == 2
            assert layouter.layout_image.call_count == 1

    def test_document_layouter_layout_document_table_failure(self):
        """Test DocumentLayouter.layout_document when table layout fails."""
        # Create elements with table that will fail
        elements = [
            Mock(spec=Table),
            Mock(spec=Table)
        ]

        with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
            layouter = DocumentLayouter(self.mock_page)

            # Mock layout_table: first succeeds, second fails
            layouter.layout_table = Mock(side_effect=[True, False])

            result = layouter.layout_document(elements)

            # Verify it stopped after failure
            assert result is False
            assert layouter.layout_table.call_count == 2


if __name__ == "__main__":
    # Run specific tests for debugging
    test = TestDocumentLayouter()
    test.setup_method()

    # Run a simple test
    with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry') as mock_registry:
        with patch('pyWebLayout.layout.document_layouter.Line') as mock_line:
            mock_style_registry = Mock()
            mock_registry.return_value = mock_style_registry
            mock_style_registry.get_concrete_style.return_value = test.mock_concrete_style

            mock_line_instance = Mock()
            mock_line.return_value = mock_line_instance
            mock_line_instance.add_word.return_value = (True, None)

            result = paragraph_layouter(test.mock_paragraph, test.mock_page)
            print(f"Test result: {result}")

    # Run multi-page tests
    multi_test = TestMultiPageLayout()
    multi_test.setup_method()
    multi_test.test_realistic_multi_page_scenario()

    print("Document layouter tests completed!")
