"""The category primer: what a category is, on a page whose job is prices.

What it pins: that a guide only exists for a category that has an address; that the primer
sits below the exclusives and above the full list, at both renderers, because that order is
the decision (the browser pass measured the first price two screens down on a phone when it
sat above them); that every word of the primer is in the page a crawler reads, open or not;
that a category nobody has written up renders nothing at all; and that the component and the
server body use the same classes, which is the only thing keeping the two pages the same.

No database and no network: the detail object is the pairing test's stub.
"""

import pathlib
import re

from app.models.hubs import CategoryGuide, CategorySection
from app.services import category_guides, seo, taxonomy, urls
from test_airport_category import pair

WEB = pathlib.Path(__file__).resolve().parents[1] / "web"
COMPONENT = WEB / "src" / "components" / "CategoryPrimer.tsx"

GUIDE = CategoryGuide(
    summary="What it is, in a sentence or two.",
    takeaway="The one line worth knowing at the shelf.",
    sections=[
        CategorySection(title="A short history", teaser="Five words", body="The longer part."),
        CategorySection(title="Where it comes from", teaser="Five more", body="Another longer part."),
    ],
)


class TestTheGuides:
    def test_every_guide_names_a_category_that_has_a_page(self):
        """A primer for a category with no address could never be read: the page it belongs
        to does not exist. Keyed by the taxonomy's name, like the address table."""
        for category in category_guides.GUIDES:
            assert category in taxonomy.VERTICAL_OF_CATEGORY, category
            assert urls.category_slug(category), category

    def test_a_category_nobody_has_written_up_has_no_guide(self):
        assert category_guides.guide_for("Twinpack") is None
        assert category_guides.guide_for(None) is None
        assert category_guides.guide_for("") is None

    def test_a_guide_leads_with_the_two_parts_that_are_always_shown(self):
        """`summary` and `takeaway` are the parts the page shows unprompted, so a guide that
        carries only sections would render an empty card with three closed rows."""
        for category, guide in category_guides.GUIDES.items():
            assert guide.summary.strip(), category
            for section in guide.sections:
                assert section.title.strip() and section.teaser.strip() and section.body.strip(), category

    def test_the_teaser_is_short_enough_for_the_row_it_shares(self):
        """Title and teaser sit on one line together; a long teaser is clipped mid-word,
        which is what the first browser pass showed."""
        for category, guide in category_guides.GUIDES.items():
            for section in guide.sections:
                assert len(section.teaser) <= 34, f"{category}: {section.teaser}"

    def test_the_house_style_holds_in_every_word_a_shopper_reads(self):
        for category, guide in category_guides.GUIDES.items():
            words = [guide.summary, guide.takeaway or ""]
            words += [s.title + s.teaser + s.body for s in guide.sections]
            for text in words:
                assert "—" not in text, category
                assert not re.search(r"\bcheap\b", text), category


class TestTheServerBody:
    def test_the_primer_reads_without_javascript(self):
        body = seo.airport_category_body(pair(guide=GUIDE))
        assert '<section class="category-primer" aria-label="About whisky">' in body
        assert '<span class="eyebrow">About whisky</span>' in body
        assert '<p class="category-primer__summary">What it is, in a sentence or two.</p>' in body
        assert '<span class="category-primer__label">What decides the price</span>' in body
        assert "The one line worth knowing at the shelf." in body

    def test_every_part_is_in_the_page_whether_or_not_a_reader_opens_it(self):
        """The rows are closed by default; a crawler and an assistant still read all of it."""
        body = seo.airport_category_body(pair(guide=GUIDE))
        assert body.count('<details class="primer-section" name="category-primer">') == 2
        for section in GUIDE.sections:
            assert section.title in body and section.teaser in body and section.body in body

    def test_one_row_opens_at_a_time(self):
        """The shared `name` is what keeps the card from growing a tall column beside a
        short one when every part is open."""
        body = seo.airport_category_body(pair(guide=GUIDE))
        assert 'name="category-primer"' in body

    def test_it_sits_below_the_exclusives_and_above_the_full_list(self):
        body = seo.airport_category_body(pair(guide=GUIDE))
        assert body.index("airport-category__exclusives") < body.index("category-primer")
        assert body.index("category-primer") < body.index('id="products"')

    def test_a_category_with_no_guide_renders_nothing(self):
        body = seo.airport_category_body(pair())
        assert "category-primer" not in body and "primer-section" not in body
        assert seo.category_primer_html(pair()) == ""

    def test_the_words_are_escaped_like_the_rest_of_the_page(self):
        guide = CategoryGuide(summary="Cognac & <brandy>", sections=[])
        body = seo.airport_category_body(pair(guide=guide))
        assert "Cognac &amp; &lt;brandy&gt;" in body and "<brandy>" not in body


class TestTheTwoRenderersAgree:
    def test_the_component_uses_the_classes_the_server_body_writes(self):
        """The SPA and the crawler's body must stay the same markup, class for class, or the
        page moves under a reader when React mounts."""
        component = COMPONENT.read_text()
        body = seo.airport_category_body(pair(guide=GUIDE))
        for name in (
            "category-primer", "category-primer__intro", "category-primer__summary",
            "category-primer__takeaway", "category-primer__label", "category-primer__sections",
            "primer-section", "primer-section__head", "primer-section__name",
            "primer-section__teaser", "primer-section__body",
        ):
            assert f'"{name}"' in body or f'{name} ' in body or f'"{name}"' in body, name
            assert name in component, name
        assert 'name="category-primer"' in component

    def test_the_component_imports_its_own_stylesheet(self):
        """A stylesheet nothing imports fails silently: the block renders unstyled."""
        assert 'import "./CategoryPrimer.css"' in COMPONENT.read_text()
        assert (WEB / "src" / "components" / "CategoryPrimer.css").exists()
