"""`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
product variants, 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
product variants belong to a brand too thin for a page, and each would have linked to a
404. `catalog_queries.brand_page_slugs` resolves an alias to its brand and hands
a slug only for a brand `list_brands` would list.
"""

from sqlalchemy.dialects import postgresql

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


#: The one spelling of the comparison unit (K2: shops counted once per primary place).
UNIT = f"count(DISTINCT {places.unit().compile(compile_kwargs={'literal_binds': True})})"


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


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

    def __init__(self, brands=(), counted=()):
        self.info: dict = {}
        self.brands = list(brands)  # (brand_id, brand_of_id, brand_slug)
        self.counted = list(counted)  # (brand_of_id, product variants, last_observed)
        self.statements: list[str] = []

    def execute(self, stmt):
        text = _sql(stmt)
        self.statements.append(text)
        rows = self.brands 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(brands=[(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,) = db.statements
        assert "coalesce(brands.alias_of_id, brands.id)" in lookup
        assert "brands.id IN (3, 5)" in lookup

    def test_a_thin_house_has_a_page_from_discovery_and_only_a_hidden_one_gets_no_slug(self, monkeypatch):
        """Plan W18 (Stream K6): the floor no longer decides existence, so a thin brand's name
        links to its page; a brand a person hid links to a catalogue search instead. Before,
        the floor's count ran here and a thin brand got no slug."""
        monkeypatch.setattr(verify, "blocked_sources", lambda db: {})
        db = _Session(brands=[(7, 7, "drumshanbo"), (5, 3, "moet-chandon")], counted=[])
        assert catalog_queries.brand_page_slugs(db, [7, 5]) == {7: "drumshanbo", 5: "moet-chandon"}
        assert len(db.statements) == 1, "no count: existence is not a rule"
        monkeypatch.setattr(catalog_queries, "hidden_brand_ids", lambda db: [3])
        assert catalog_queries.brand_page_slugs(db, [7, 5]) == {7: "drumshanbo"}

    def test_the_floor_is_one_statement_narrowed_or_not(self, monkeypatch):
        """`_brand_counts` (visible airport shops, not blocked, at least BRAND_PAGE_MIN_PRODUCTS
        product variants) is the candidate rule `publish.brand_candidates` reads, and the index's
        statement when it is asked for the floor."""
        monkeypatch.setattr(verify, "blocked_sources", lambda db: {})
        db = _Session(counted=[])
        catalog_queries._brand_counts(db, brand_ids=[3, 7])
        (count,) = db.statements
        assert "shops.visible IS true" in count
        assert "shops.is_catalogue_only IS false" in count
        assert f"count(DISTINCT product_variants.id) >= {catalog_queries.BRAND_PAGE_MIN_PRODUCTS}" in count
        assert f"{UNIT} >= {catalog_queries.BRAND_PAGE_MIN_AIRPORTS}" in count
        assert "coalesce(brands.alias_of_id, brands.id) IN (3, 7)" in count
        db2 = _Session(counted=[])
        catalog_queries.list_brands(db2)
        (index,) = db2.statements
        assert "coalesce(brands.alias_of_id, brands.id) IN" not in index
        assert count.replace(" AND coalesce(brands.alias_of_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", shop_count=2)
        assert summary.brand_slug is None
        detail = ProductDetail(id=1, name="Red Label 1L", shop_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 product variants makes a page too thin).
        Read as three or more published product variants, priced between them at two
        or more distinct airports, counted by place so two shops at one airport
        are one airport. Since plan W18 (Stream K6) it is the rule that SUGGESTS a brand
        page for indexing and lists it on the brands index, not the rule of its existence:
        a thin brand has its page, linked from its variants, kept out of search by noindex."""
        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 product_variants.id) >= 3" in index and f"{UNIT} >= 2" in index

    def test_the_page_exists_under_the_floor_and_a_hidden_one_does_not(self, monkeypatch):
        """brand_detail asked `_brand_counts` and answered None under the floor, so a thin brand
        was a 404. Now it asks nothing about size; only a hidden brand has no detail (its page
        route answers 302). A caller may still pass the floor and get the old gate."""
        monkeypatch.setattr(verify, "blocked_sources", lambda db: {})
        hidden = type("B", (), {"id": 9, "slug": "wrong", "name": "X", "alias_of_id": None, "hidden": True})()
        monkeypatch.setattr(catalog_queries, "brand_by_slug", lambda db, slug: hidden)
        db = _Session(counted=[])
        assert catalog_queries.brand_detail(db, "wrong") is None and db.statements == []
        hidden.hidden = False
        assert catalog_queries.brand_detail(db, "thin-brand", min_product_variants=3, min_airports=2) is None
        (count,) = db.statements
        assert "coalesce(brands.alias_of_id, brands.id) IN (9)" in count
