"""
Regression tests for inline content inside block containers (spec S1).

Inline tags are registered to ignore_handler because they are meant to be
consumed by extract_text_content. Only <p> and <h1>-<h6> ever called it, so
every other container - div, li, td, th, blockquote - iterated its children as
blocks, and inline tags returned None. Their text was silently discarded, and
bare text nodes each became a separate paragraph.
"""

import pytest

from pyWebLayout.abstract.block import (
    HList,
    Paragraph,
    Quote,
    Table,
)
from pyWebLayout.abstract.inline import LinkedWord, Word
from pyWebLayout.io.readers.html_extraction import parse_html_string


def words_of(block):
    return [w.text for w in getattr(block, 'words', [])]


def all_words(blocks):
    out = []
    for block in blocks:
        out.extend(words_of(block))
    return out


def cell_blocks(table):
    for _, row in table.all_rows():
        for cell in row.cells():
            yield list(cell.blocks())


EXPECTED = ["hello", "world", "again"]


class TestInlineContentIsKept:
    """The same markup must survive in every container."""

    def test_paragraph_control(self):
        """<p> already worked - this is the reference behaviour."""
        blocks = parse_html_string("<p>hello <b>world</b> again</p>")
        assert all_words(blocks) == EXPECTED

    def test_div(self):
        blocks = parse_html_string("<div>hello <b>world</b> again</div>")
        assert all_words(blocks) == EXPECTED

    def test_list_item(self):
        blocks = parse_html_string("<ul><li>hello <b>world</b> again</li></ul>")
        hlist = next(b for b in blocks if isinstance(b, HList))
        item = list(hlist.items())[0]
        assert all_words(item.blocks()) == EXPECTED

    def test_table_cell(self):
        blocks = parse_html_string(
            "<table><tr><td>hello <b>world</b> again</td></tr></table>")
        table = next(b for b in blocks if isinstance(b, Table))
        assert all_words(next(cell_blocks(table))) == EXPECTED

    def test_table_header_cell(self):
        blocks = parse_html_string(
            "<table><tr><th>hello <b>world</b> again</th></tr></table>")
        table = next(b for b in blocks if isinstance(b, Table))
        assert all_words(next(cell_blocks(table))) == EXPECTED

    def test_blockquote(self):
        blocks = parse_html_string("<blockquote>hello <b>world</b> again</blockquote>")
        quote = next(b for b in blocks if isinstance(b, Quote))
        assert all_words(quote.blocks()) == EXPECTED


class TestInlineRunsCoalesce:
    """A run of inline content is one paragraph, not one per text node."""

    def test_div_yields_a_single_paragraph(self):
        blocks = parse_html_string("<div>a <b>b</b> c</div>")
        paragraphs = [b for b in blocks if isinstance(b, Paragraph)]
        assert len(paragraphs) == 1, f"expected one paragraph, got {len(blocks)} blocks"
        assert words_of(paragraphs[0]) == ["a", "b", "c"]

    def test_cell_yields_a_single_paragraph(self):
        blocks = parse_html_string("<table><tr><td>a <b>b</b> c</td></tr></table>")
        table = next(b for b in blocks if isinstance(b, Table))
        cell = next(cell_blocks(table))
        assert len(cell) == 1
        assert words_of(cell[0]) == ["a", "b", "c"]

    def test_block_child_splits_the_run(self):
        """Inline runs either side of a block child stay separate, in order."""
        blocks = parse_html_string(
            "<table><tr><td>before<p>middle</p>after</td></tr></table>")
        table = next(b for b in blocks if isinstance(b, Table))
        cell = next(cell_blocks(table))
        assert [words_of(b) for b in cell] == [["before"], ["middle"], ["after"]]

    def test_line_break_splits_the_run(self):
        blocks = parse_html_string("<div>first<br>second</div>")
        paragraphs = [b for b in blocks if isinstance(b, Paragraph)]
        assert [words_of(p) for p in paragraphs] == [["first"], ["second"]]

    def test_whitespace_between_blocks_makes_no_paragraph(self):
        blocks = parse_html_string("<div>\n  <p>one</p>\n  <p>two</p>\n</div>")
        assert [words_of(b) for b in blocks] == [["one"], ["two"]]


class TestLinksSurvive:
    """<a href> must produce LinkedWord wherever it appears."""

    def test_link_in_cell(self):
        blocks = parse_html_string(
            '<table><tr><td><a href="http://x">link</a> text</td></tr></table>')
        table = next(b for b in blocks if isinstance(b, Table))
        cell = next(cell_blocks(table))
        found = [w for b in cell for w in getattr(b, 'words', [])]

        assert [w.text for w in found] == ["link", "text"]
        linked = [w for w in found if isinstance(w, LinkedWord)]
        assert len(linked) == 1
        assert linked[0].location == "http://x"

    def test_link_in_div(self):
        blocks = parse_html_string('<div>see <a href="#s2">Section 2</a> now</div>')
        found = [w for b in blocks for w in getattr(b, 'words', [])]
        assert [w.text for w in found] == ["see", "Section", "2", "now"]
        assert all(isinstance(w, LinkedWord) for w in found[1:3])

    def test_link_in_list_item(self):
        blocks = parse_html_string('<ul><li><a href="u">click</a> here</li></ul>')
        hlist = next(b for b in blocks if isinstance(b, HList))
        item = hlist._items[0]
        found = [w for b in item.blocks() for w in getattr(b, 'words', [])]
        assert [w.text for w in found] == ["click", "here"]
        assert isinstance(found[0], LinkedWord)


class TestNestedContainers:

    def test_div_in_div(self):
        blocks = parse_html_string("<div>outer <div>inner</div> tail</div>")
        assert [words_of(b) for b in blocks] == [["outer"], ["inner"], ["tail"]]

    def test_block_children_still_pass_through(self):
        blocks = parse_html_string("<div><h1>Title</h1><p>Body</p></div>")
        assert len(blocks) == 2
        assert words_of(blocks[0]) == ["Title"]
        assert words_of(blocks[1]) == ["Body"]

    def test_cell_containing_a_list(self):
        blocks = parse_html_string(
            "<table><tr><td>intro<ul><li>item</li></ul></td></tr></table>")
        table = next(b for b in blocks if isinstance(b, Table))
        cell = next(cell_blocks(table))
        assert isinstance(cell[0], Paragraph)
        assert words_of(cell[0]) == ["intro"]
        assert isinstance(cell[1], HList)


class TestComments:

    def test_comment_text_is_not_content(self):
        blocks = parse_html_string("<div>real<!-- hidden note -->text</div>")
        assert all_words(blocks) == ["real", "text"]
