"""Brand pages (B5): keyed by the brands table's slug, never a folded name.

An alias row 301s to its house, a brand with fewer than two published products
has no page (the agreed structure's threshold), and the body and structured
data come from one BrandDetail the API also returns. The fixtures below stand
in for the database; the route tests in test_site_routes.py mount the real
routes over them.
"""

import json
import re
from datetime import UTC, datetime

from app.models.hubs import BrandAirport, BrandDetail, BrandSummary
from app.models.schemas import CategoryCount
from app.services import seo, urls
from test_seo_airport import SHELL, summary

CHECKED = datetime(2026, 8, 25, 23, 51, tzinfo=UTC)


def brand_summary(**over) -> BrandSummary:
    base = dict(slug="johnnie-walker", path="/brands/johnnie-walker", name="Johnnie Walker",
                products=21, last_collected_at=CHECKED)
    base.update(over)
    return BrandSummary(**base)


def brand(**over) -> BrandDetail:
    base = dict(
        **brand_summary().model_dump(),
        airports=[
            BrandAirport(iata="LHR", path="/airports/heathrow-lhr-london", name="London Heathrow", products=14,
                         cheapest_for=4, last_collected_at=CHECKED),
            BrandAirport(iata="HKG", path="/airports/hong-kong-hkg", name="Hong Kong International", products=5,
                         cheapest_for=4, last_collected_at=CHECKED),
            BrandAirport(iata="EZE", path="/airports/buenos-aires-ezeiza-eze", name="Buenos Aires Ezeiza", products=1,
                         cheapest_for=0, last_collected_at=None),
        ],
        comparable=12, exclusives=0,
        categories=[CategoryCount(category="Whisky", count=21)],
        total=21, limit=24, offset=0,
        items=[summary(id=4, name="Johnnie Walker Red Label 1L", brand="Johnnie Walker", category="Whisky")],
    )
    base.update(over)
    return BrandDetail(**base)


class TestBrandUrls:
    def test_page_path_is_the_table_slug_verbatim(self):
        assert urls.brand_page_path("moet-chandon") == "/brands/moet-chandon"

    def test_brand_link_prefers_the_page_and_falls_back_to_search(self):
        assert urls.brand_path("Moët & Chandon", "moet-chandon") == "/brands/moet-chandon"
        assert urls.brand_path("Moët & Chandon") == "/products?q=Mo%C3%ABt%20%26%20Chandon"
        assert urls.brand_path("Moët & Chandon", None) == "/products?q=Mo%C3%ABt%20%26%20Chandon"


class TestBrandHead:
    def test_title_description_canonical_and_seed(self):
        head = seo.head_for_brand(brand())
        assert head.title == "Johnnie Walker duty-free prices | Duty Free Professor"
        assert head.description == (
            "21 Johnnie Walker duty-free prices at 3 airports, checked 25 Aug 2026. "
            "12 compared across airports: see where Johnnie Walker is the better buy."
        )
        assert head.canonical_path == "/brands/johnnie-walker"
        assert head.seed_key == "__DFP_BRAND__" and head.seed["slug"] == "johnnie-walker"
        assert head.last_modified == CHECKED
        assert seo.head_for_brand(brand(category="Whisky", offset=24)).canonical_path == "/brands/johnnie-walker"

    def test_collection_page_about_a_brand_with_an_item_list(self):
        page = seo.head_for_brand(brand()).apply(SHELL, "https://s.example")
        blocks = [json.loads(m) for m in re.findall(r'<script type="application/ld\+json">(.*?)</script>', page)]
        collection = next(b for b in blocks if b["@type"] == "CollectionPage")
        assert collection["@id"] == "https://s.example/brands/johnnie-walker#page"
        assert collection["about"] == {
            "@type": "Brand", "@id": "https://s.example/brands/johnnie-walker#brand",
            "name": "Johnnie Walker", "url": "https://s.example/brands/johnnie-walker",
        }
        items = collection["mainEntity"]
        assert items["@type"] == "ItemList" and items["numberOfItems"] == 21
        assert items["itemListElement"][0]["url"] == "https://s.example/products/johnnie-walker-red-label-1l-4"
        crumbs = next(b for b in blocks if b["@type"] == "BreadcrumbList")
        assert crumbs["itemListElement"] == [
            {"@type": "ListItem", "position": 1, "name": "Products", "item": "https://s.example/products"},
            {"@type": "ListItem", "position": 2, "name": "Johnnie Walker"},
        ]


class TestBrandBody:
    def test_reads_without_javascript(self):
        body = seo.brand_body(brand())
        assert '<h1 class="airports-hero__title">Johnnie Walker in duty free</h1>' in body
        assert ("21 Johnnie Walker products priced at 3 airports we track, checked 25 Aug 2026. "
                "12 of them are sold at more than one, so you can see which airport is the better buy; "
                "most often it is LHR.") in body
        for label, value in (("Products priced", "21"), ("Airports stocking it", "3"),
                             ("Compared across airports", "12"), ("Travel exclusives", "0")):
            assert f"<dt>{label}</dt><dd>{value}</dd>" in body, label
        assert 'href="/products">Products</a>' in body
        assert "Where to buy Johnnie Walker" in body
        assert ('<a class="brand-airport__name" href="/airports/heathrow-lhr-london">London Heathrow (LHR)</a>'
                '<span class="brand-airport__facts">14 products · cheapest for 4 · checked 25 Aug 2026</span>') in body
        assert '<span class="brand-airport__facts">1 product · checked not yet</span>' in body
        assert "Every Johnnie Walker product we price" in body
        assert 'href="/products/johnnie-walker-red-label-1l-4" class="product-card"' in body

    def test_ties_in_the_cheapest_count_pick_the_larger_range(self):
        """LHR and HKG are cheapest for four each; the sentence names the one
        that stocks more, and stays silent when no airport wins anything."""
        assert "most often it is LHR." in seo.brand_lede(brand())
        flat = brand(airports=[BrandAirport(iata="LHR", path="/airports/heathrow-lhr-london", name="LHR", products=2, cheapest_for=0)])
        assert seo.brand_lede(flat).endswith("which airport is the better buy.")

    def test_category_chips_only_for_a_house_with_several(self):
        assert "Johnnie Walker by category" not in seo.brand_body(brand())
        many = brand(categories=[CategoryCount(category="Champagne & Sparkling", count=9), CategoryCount(category="Wine", count=2)])
        body = seo.brand_body(many)
        assert "Johnnie Walker by category" in body
        assert 'href="/brands/johnnie-walker?category=Champagne%20%26%20Sparkling#products">Champagne &amp; Sparkling' in body
        assert "Johnnie Walker whisky" in seo.brand_body(brand(category="Whisky"))

    def test_pager_keeps_the_brand_path(self):
        body = seo.brand_body(brand(total=30))
        assert 'href="/brands/johnnie-walker?page=2#products">Next</a>' in body
        assert '<span class="pager__status">1-24 of 30</span>' in body

    def test_house_style(self):
        """No em dashes, and no "cheap" or "free" as a claim, in what this page
        says (the header mirror is the site's own chrome, checked elsewhere)."""
        body = seo.brand_body(brand()).split("<main>", 1)[1]
        head = seo.head_for_brand(brand())
        assert "—" not in body and "—" not in head.title and "—" not in head.description
        words = re.findall(r"[a-z]+", re.sub(r"<[^>]+>", " ", body).lower().replace("duty free", "").replace("duty-free", ""))
        assert "cheap" not in words and "free" not in words

    def test_untrusted_names_are_escaped(self):
        evil = brand(name='JW <b>"Blue"</b>', items=[summary(name="Red </script><script>alert(1)")])
        page = seo.head_for_brand(evil).apply(SHELL, "https://s.example")
        assert "<b>" not in page.split("<h1")[1].split("</h1>")[0]
        assert "</script><script>alert(1)" not in page and "\\u003c/script" in page
