"""
Regression tests for page content geometry (spec S2).

Content must be laid out inside the content box - the page box less its border
and padding - on all four sides. Horizontal padding was previously ignored on the
left, shifting every line left by padding_left and leaving a gutter of
padding_left + padding_right on the right, so lines appeared to break early.
"""

import pytest

from pyWebLayout.abstract.block import Paragraph
from pyWebLayout.abstract.inline import Word
from pyWebLayout.concrete.page import Page
from pyWebLayout.layout.document_layouter import DocumentLayouter
from pyWebLayout.style import Font
from pyWebLayout.style.page_style import PageStyle


PADDING = (40, 30, 40, 20)  # top, right, bottom, left - deliberately asymmetric


@pytest.fixture
def font():
    return Font(font_size=12)


def filled_page(size, style, font, word_count=120):
    page = Page(size=size, style=style)
    paragraph = Paragraph(font)
    for i in range(word_count):
        paragraph.add_word(Word(f"word{i}", font))
    DocumentLayouter(page).layout_paragraph(paragraph)
    return page


class TestContentBox:
    """content_origin / content_rect describe the box content lives in."""

    def test_content_origin_includes_border_and_padding(self):
        page = Page(size=(400, 300), style=PageStyle(border_width=2, padding=PADDING))
        assert page.content_origin == (2 + 20, 2 + 40)

    def test_content_rect_subtracts_both_paddings(self):
        page = Page(size=(400, 300), style=PageStyle(border_width=2, padding=PADDING))
        x, y, w, h = page.content_rect
        assert (x, y) == (22, 42)
        assert w == 400 - 2 * 2 - 20 - 30
        assert h == 300 - 2 * 2 - 40 - 40

    def test_page_origin_offsets_the_content_box(self):
        """A page placed inside another surface reports absolute coordinates."""
        page = Page(size=(100, 50), style=PageStyle(border_width=1, padding=(5, 5, 5, 5)),
                    origin=(200, 300))
        assert page.content_origin == (206, 306)

    def test_remaining_height_respects_bottom_padding(self, font):
        style = PageStyle(border_width=2, padding=PADDING)
        page = Page(size=(400, 300), style=style)
        # Nothing laid out yet: the whole content box is available.
        assert page.remaining_height == page.content_rect[3]


class TestLinePlacement:
    """Lines must start after the left padding and end before the right padding."""

    def test_first_line_starts_at_content_origin(self, font):
        style = PageStyle(border_width=2, padding=PADDING)
        page = filled_page((400, 300), style, font)

        line = page.children[0]
        assert int(line.origin[0]) == page.content_origin[0]
        assert int(line.origin[1]) == page.content_origin[1]

    def test_line_width_matches_content_width(self, font):
        style = PageStyle(border_width=2, padding=PADDING)
        page = filled_page((400, 300), style, font)

        line = page.children[0]
        assert int(line.size[0]) == page.content_rect[2]

    def test_no_line_extends_past_the_right_content_edge(self, font):
        style = PageStyle(border_width=2, padding=PADDING)
        page = filled_page((400, 300), style, font)
        right_edge = page.content_rect[0] + page.content_rect[2]

        for line in page.children:
            assert int(line.origin[0]) + int(line.size[0]) <= right_edge

    def test_ink_stays_inside_the_content_box(self, font):
        """The rendered pixels, not just the boxes, respect the padding."""
        style = PageStyle(border_width=0, padding=PADDING,
                          background_color=(255, 255, 255))
        page = filled_page((400, 300), style, font)
        image = page.render().convert("L")
        pixels = image.load()

        inked_x = [x for x in range(400) for y in range(300) if pixels[x, y] < 128]
        assert inked_x, "the page should have text on it"

        x0, _, w, _ = page.content_rect
        assert min(inked_x) >= x0
        assert max(inked_x) <= x0 + w

    def test_right_gutter_is_not_double_width(self, font):
        """
        The regression: text was shifted left by padding_left, so the right gutter
        was padding_left + padding_right wide while the left gutter was zero.
        """
        style = PageStyle(border_width=0, padding=(10, 30, 10, 30))
        page = filled_page((400, 300), style, font, word_count=200)
        image = page.render().convert("L")
        pixels = image.load()
        inked_x = [x for x in range(400) for y in range(300) if pixels[x, y] < 128]

        left_gutter = min(inked_x)
        right_gutter = 400 - max(inked_x)
        # Justification means the right edge is not always exactly flush, so allow
        # slack - but the two gutters must be comparable, not 0 vs 60.
        assert abs(left_gutter - right_gutter) < 25, \
            f"asymmetric gutters: left={left_gutter} right={right_gutter}"


class TestBlockBottomBoundary:
    """Blocks must not be placed into the bottom padding."""

    def test_lines_stop_before_bottom_padding(self, font):
        style = PageStyle(border_width=2, padding=PADDING)
        page = filled_page((400, 300), style, font, word_count=500)
        bottom_edge = page.content_rect[1] + page.content_rect[3]

        for line in page.children:
            assert int(line.origin[1]) <= bottom_edge
