"""`brand_slug` on a product card: the brand page's slug, only when the page exists.

Brand pages (B5) are keyed by `brands.slug` and held back under two published
products, while the product API carried only the brand's text, so every product
page linked its brand to a catalogue search. Handing out the slug blindly would
have been worse: on a copy of the launch data about a quarter of the visible
products belong to a house too thin for a page, and each would have linked to a
404. `catalog_queries.brand_page_slugs` resolves an alias to its house and hands
a slug only for a house `list_brands` would list.
"""

from sqlalchemy.dialects import postgresql

from app.models.schemas import ProductDetail, ProductSummary
from app.services import catalog_queries, verify


def _sql(clause) -> str:
    return str(clause.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}))


class _Session:
    """Answers the house lookup and the page count from fixtures and records both."""

    def __init__(self, houses=(), counted=()):
        self.info: dict = {}
        self.houses = list(houses)  # (brand_id, house_id, house_slug)
        self.counted = list(counted)  # (house_id, products, last_observed)
        self.statements: list[str] = []

    def execute(self, stmt):
        text = _sql(stmt)
        self.statements.append(text)
        rows = self.houses if "brands_1.slug" in text else self.counted
        return _Result(rows)

    def scalars(self, stmt):  # the block lookup never runs: nothing is blocked
        raise AssertionError(_sql(stmt))


class _Result:
    def __init__(self, rows):
        self.rows = rows

    def all(self):
        return list(self.rows)


class TestBrandPageSlugs:
    def test_an_alias_resolves_to_its_house_and_gets_the_house_slug(self, monkeypatch):
        monkeypatch.setattr(verify, "blocked_sources", lambda db: {})
        db = _Session(houses=[(5, 3, "moet-chandon"), (3, 3, "moet-chandon")], counted=[(3, 21, None)])
        assert catalog_queries.brand_page_slugs(db, [5, 3, None]) == {5: "moet-chandon", 3: "moet-chandon"}
        lookup, count = db.statements
        assert "coalesce(brands.canonical_id, brands.id)" in lookup
        assert "brands.id IN (3, 5)" in lookup

    def test_a_house_too_thin_for_a_page_gets_no_slug(self, monkeypatch):
        monkeypatch.setattr(verify, "blocked_sources", lambda db: {})
        db = _Session(houses=[(7, 7, "drumshanbo"), (5, 3, "moet-chandon")], counted=[(3, 21, None)])
        assert catalog_queries.brand_page_slugs(db, [7, 5]) == {5: "moet-chandon"}

    def test_the_page_count_is_the_brand_index_rule_narrowed_to_these_houses(self, monkeypatch):
        """The same statement `list_brands` runs (visible airport shops, not
        blocked, at least BRAND_PAGE_MIN_PRODUCTS products), plus the house filter."""
        monkeypatch.setattr(verify, "blocked_sources", lambda db: {})
        db = _Session(houses=[(5, 3, "moet-chandon"), (7, 7, "drumshanbo")], counted=[])
        catalog_queries.brand_page_slugs(db, [5, 7])
        _, count = db.statements
        assert "locations.visible IS true" in count
        assert "locations.is_catalogue_only IS false" in count
        assert f"count(DISTINCT products.id) >= {catalog_queries.BRAND_PAGE_MIN_PRODUCTS}" in count
        assert f"count(DISTINCT locations.iata) >= {catalog_queries.BRAND_PAGE_MIN_AIRPORTS}" in count
        assert "coalesce(brands.canonical_id, brands.id) IN (3, 7)" in count
        # And the index's own statement is the same one without the filter.
        db2 = _Session(counted=[])
        catalog_queries.list_brands(db2)
        (index,) = db2.statements
        assert "coalesce(brands.canonical_id, brands.id) IN" not in index
        assert count.replace(" AND coalesce(brands.canonical_id, brands.id) IN (3, 7)", "") == index

    def test_no_brands_asks_nothing(self):
        db = _Session()
        assert catalog_queries.brand_page_slugs(db, [None, None]) == {}
        assert catalog_queries.brand_page_slugs(db, []) == {}
        assert db.statements == []


class TestSchema:
    def test_brand_slug_defaults_to_none_on_summary_and_detail(self):
        """Every existing response stays identical until the read layer fills it."""
        summary = ProductSummary(id=1, name="Red Label 1L", brand="Johnnie Walker", location_count=2)
        assert summary.brand_slug is None
        detail = ProductDetail(id=1, name="Red Label 1L", location_count=2, brand_slug="johnnie-walker")
        assert detail.brand_slug == "johnnie-walker"


class TestTheFloorIsThreeProductsAtTwoAirports:
    def test_the_review_rule_and_its_reading(self, monkeypatch):
        """Structure review, resolved 10 Sep (Adam: "three or more where stocked
        at two or more airports"; Mark: two products makes a page too thin).
        Read as three or more published products, priced between them at two
        or more distinct airports, counted by IATA so two shops at one airport
        are one airport. The floor had been two products alone since 5 Sep;
        the change is these two constants and one HAVING clause, and every
        consumer (index, page, sitemap, product-page brand link) shares it."""
        assert catalog_queries.BRAND_PAGE_MIN_PRODUCTS == 3
        assert catalog_queries.BRAND_PAGE_MIN_AIRPORTS == 2
        monkeypatch.setattr(verify, "blocked_sources", lambda db: {})
        db = _Session(counted=[])
        catalog_queries.list_brands(db)
        (index,) = db.statements
        assert "count(DISTINCT products.id) >= 3" in index
        assert "count(DISTINCT locations.iata) >= 2" in index

    def test_the_page_itself_applies_the_same_floor(self, monkeypatch):
        """brand_detail used to count the brand's products itself; now it asks
        _house_counts, so a page can never exist that the index would not list."""
        monkeypatch.setattr(verify, "blocked_sources", lambda db: {})
        monkeypatch.setattr(catalog_queries, "brand_by_slug", lambda db, slug: type("B", (), {"id": 9, "slug": slug, "name": "X", "canonical_id": None})())
        db = _Session(counted=[])
        assert catalog_queries.brand_detail(db, "thin-house") is None
        (count,) = db.statements
        assert "coalesce(brands.canonical_id, brands.id) IN (9)" in count
        assert "count(DISTINCT locations.iata) >= 2" in count
