"""
Unit tests for DynamicPage class.
"""

import pytest
from PIL import Image
from pyWebLayout.concrete.dynamic_page import DynamicPage, SizeConstraints
from pyWebLayout.concrete.text import Line, Text
from pyWebLayout.style.fonts import Font
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.style import Alignment


class TestSizeConstraints:
    """Test SizeConstraints dataclass."""

    def test_default_constraints(self):
        """Test default constraint values."""
        constraints = SizeConstraints()
        assert constraints.min_width is None
        assert constraints.max_width is None
        assert constraints.min_height is None
        assert constraints.max_height is None

    def test_custom_constraints(self):
        """Test custom constraint values."""
        constraints = SizeConstraints(
            min_width=100,
            max_width=500,
            min_height=50,
            max_height=1000
        )
        assert constraints.min_width == 100
        assert constraints.max_width == 500
        assert constraints.min_height == 50
        assert constraints.max_height == 1000


class TestDynamicPage:
    """Test DynamicPage class."""

    def test_initialization(self):
        """Test DynamicPage initialization."""
        page = DynamicPage()

        assert page.size == (0, 0)  # Starts with zero size
        assert not page._is_measured
        assert not page._is_laid_out
        assert page._render_offset == 0
        assert page.constraints is not None

    def test_initialization_with_constraints(self):
        """Test initialization with custom constraints."""
        constraints = SizeConstraints(min_width=200, max_width=800)
        page = DynamicPage(constraints=constraints)

        assert page.constraints.min_width == 200
        assert page.constraints.max_width == 800

    def test_initialization_with_style(self):
        """Test initialization with custom style."""
        style = PageStyle(border_width=2, padding=(10, 20, 10, 20))
        page = DynamicPage(style=style)

        assert page.style.border_width == 2
        assert page.style.padding_top == 10

    def test_measure_empty_page(self):
        """Test measuring an empty page."""
        page = DynamicPage()
        width, height = page.measure()

        # Empty page should have minimal size (just padding/borders)
        assert width > 0  # At least padding/borders
        assert height > 0
        assert page._is_measured

    def test_measure_with_constraints(self):
        """Test measuring respects constraints."""
        constraints = SizeConstraints(min_width=300, min_height=200)
        page = DynamicPage(constraints=constraints)

        width, height = page.measure()

        assert width >= 300
        assert height >= 200

    def test_measure_caching(self):
        """Test that measurement is cached."""
        page = DynamicPage()

        # First measurement
        size1 = page.measure()

        # Second measurement should return cached value
        size2 = page.measure()

        assert size1 == size2
        assert page._is_measured

    def test_get_min_width(self):
        """Test get_min_width."""
        page = DynamicPage()
        min_width = page.get_min_width()

        assert min_width > 0
        assert isinstance(min_width, int)

    def test_get_preferred_width(self):
        """Test get_preferred_width."""
        page = DynamicPage()
        pref_width = page.get_preferred_width()

        assert pref_width > 0
        assert isinstance(pref_width, int)

    def test_measure_content_height(self):
        """Test measure_content_height."""
        page = DynamicPage()
        content_height = page.measure_content_height()

        assert content_height > 0
        assert isinstance(content_height, int)

    def test_layout(self):
        """Test layout method."""
        page = DynamicPage()
        target_size = (400, 600)

        page.layout(target_size)

        assert page.size == target_size
        assert page._is_laid_out
        assert page._dirty  # Should be marked for re-render

    def test_render_without_layout(self):
        """Test rendering without explicit layout (auto-sizing)."""
        page = DynamicPage()
        image = page.render()

        assert isinstance(image, Image.Image)
        assert image.size[0] > 0
        assert image.size[1] > 0

    def test_render_with_layout(self):
        """Test rendering after explicit layout."""
        page = DynamicPage()
        page.layout((500, 700))

        image = page.render()

        assert isinstance(image, Image.Image)
        assert image.size == (500, 700)

    def test_add_child_invalidates_cache(self):
        """Test that adding a child invalidates measurement caches."""
        page = DynamicPage()

        # Measure to populate cache
        page.measure()
        assert page._is_measured

        # Add a child (mock renderable)
        class MockRenderable:
            def __init__(self):
                self.size = (100, 50)
                self._origin = (0, 0)

            @property
            def origin(self):
                return self._origin

            def render(self):
                pass

        page.add_child(MockRenderable())

        # Caches should be invalidated
        assert not page._is_measured
        assert page._intrinsic_size is None

    def test_clear_children_invalidates_cache(self):
        """Test that clearing children invalidates caches."""
        page = DynamicPage()

        # Measure to populate cache
        page.measure()
        assert page._is_measured

        # Clear children
        page.clear_children()

        # Caches should be invalidated
        assert not page._is_measured

    def test_pagination_reset(self):
        """Test pagination reset."""
        page = DynamicPage()
        page._render_offset = 100

        page.reset_pagination()

        assert page._render_offset == 0

    def test_has_more_content_false(self):
        """Test has_more_content when all content is rendered."""
        page = DynamicPage()

        # Set render offset to total height
        total_height = page.measure_content_height()
        page._render_offset = total_height

        assert not page.has_more_content()

    def test_has_more_content_true(self):
        """Test has_more_content when content remains."""
        page = DynamicPage()

        # Offset is less than total
        page._render_offset = 0

        assert page.has_more_content()

    def test_min_width_measurement(self):
        """Test min width measures longest word."""
        page = DynamicPage()

        # Min width should be at least padding/borders
        min_width = page.get_min_width()
        assert min_width > 0

    def test_invalidate_caches(self):
        """Test cache invalidation."""
        page = DynamicPage()

        # Populate caches
        page.measure()
        page.get_min_width()
        page.get_preferred_width()
        page.measure_content_height()

        assert page._is_measured
        assert page._intrinsic_size is not None
        assert page._min_width_cache is not None
        assert page._preferred_width_cache is not None
        assert page._content_height_cache is not None

        # Invalidate
        page.invalidate_caches()

        assert not page._is_measured
        assert page._intrinsic_size is None
        assert page._min_width_cache is None
        assert page._preferred_width_cache is None
        assert page._content_height_cache is None
        assert not page._is_laid_out

    def test_measure_with_available_width(self):
        """Test measurement with available_width constraint."""
        page = DynamicPage()

        width, height = page.measure(available_width=300)

        # Width should respect available_width
        assert width <= 300

    def test_constraints_override_available_width(self):
        """Test that constraints override available_width."""
        constraints = SizeConstraints(min_width=400)
        page = DynamicPage(constraints=constraints)

        width, height = page.measure(available_width=300)

        # Should use min_width constraint, not available_width
        assert width >= 400

    def test_render_partial_empty_page(self):
        """Test partial rendering on empty page."""
        page = DynamicPage()

        rendered = page.render_partial(available_height=100)

        assert rendered >= 0
        assert isinstance(rendered, int)

    def test_method_chaining_add_child(self):
        """Test that add_child returns self for chaining."""
        page = DynamicPage()

        class MockRenderable:
            def __init__(self):
                self.size = (50, 50)
                self._origin = (0, 0)

            @property
            def origin(self):
                return self._origin

        result = page.add_child(MockRenderable())

        assert result is page

    def test_method_chaining_clear_children(self):
        """Test that clear_children returns self for chaining."""
        page = DynamicPage()

        result = page.clear_children()

        assert result is page


if __name__ == '__main__':
    pytest.main([__file__, '-v'])
