"""The ground K9 stands on: which brand rows hold several listed spellings because the
trailer list folded them, taken from the real catalogue and pinned here.

`tests/fixtures/brand_folds.json` is the 23 brand rows a copy of the 17 Sep post-chain dump
holds (450 live variants, 52 distinct spellings) whose spellings agree ONLY because
`normalize.brand_key` dropped a trailing listed word: without the list they would be 23
extra brand rows with their own slugs and pages. Measured 2026-09-17 on `dfp_k9`, read from
that database; every fold in it is right, which is why the list was kept for launch and why
the split (K9.2) rather than an un-fold is the answer.

The fixture is the real record, so a change to the list that would move one of these rows
fails here first.
"""

import json
import pathlib

import pytest

from app.services.normalize import brand_key
from app.services.proposal_rules import trailer_words

FIXTURE = json.loads((pathlib.Path(__file__).parent / "fixtures" / "brand_folds.json").read_text())
CASES = [(row["brand_slug"], row["spellings"]) for row in FIXTURE]


class TestTheGroundAsMeasured:
    def test_the_fixture_is_the_measured_catalogue(self):
        assert len(FIXTURE) == 23
        assert sum(row["variants"] for row in FIXTURE) == 450
        assert sum(len(row["spellings"]) for row in FIXTURE) == 52
        assert {row["vertical"] for row in FIXTURE} == {"liquor", "beauty"}

    @pytest.mark.parametrize(("slug", "spellings"), CASES, ids=[c[0] for c in CASES])
    def test_every_spelling_of_a_folded_row_lands_on_one_key(self, slug, spellings):
        assert len({brand_key(s) for s in spellings}) == 1

    @pytest.mark.parametrize(("slug", "spellings"), CASES, ids=[c[0] for c in CASES])
    def test_a_listed_word_is_what_joined_them(self, slug, spellings):
        """Not merely that they agree: that they agree BECAUSE a listed word was dropped."""
        assert any(trailer_words(s) for s in spellings)
        assert len({" ".join(s.lower().split()) for s in spellings}) > 1


class TestTheTwoNamedInTheBrief:
    """Appleton's four spellings and Balvenie's three, the cases rian named."""

    def test_appleton_holds_four_spellings_on_one_key(self):
        spellings = ["Appleton", "Appleton Estate", "Appleton Estate®", "Appleton Rum"]
        assert {brand_key(s) for s in spellings} == {"appleton"}
        assert trailer_words("Appleton Rum") == ["rum"]
        assert trailer_words("Appleton Estate") == ["estate"]
        assert trailer_words("Appleton") == []

    def test_balvenie_folds_on_the_article_not_on_a_trailer(self):
        """"The Balvenie" is the leading-article strip, a different rule from the trailer
        list: no trailer word is involved, so scoping the trailer list cannot move it."""
        assert {brand_key(s) for s in ["Balvenie", "The Balvenie", "The Balvenie®"]} == {"balvenie"}
        assert trailer_words("The Balvenie") == []


class TestTheListIsScopedPerVertical:
    """Rian, 17 Sep: *"are we still using those stop words like estate? in future crawl on other
    categories like clothing, will it strip words like estate?"* It did: 45 words written for
    drinks and beauty, applied to every vertical, acting at ingest with no decision behind it.

    Scoped (K9.5): the drink and producer words in liquor, the house and city words in beauty,
    the corporate suffixes and the article everywhere, and NOTHING else for a vertical nobody
    has written a list for. The fixture above is the proof that no row this catalogue holds
    moves: every one of the 23 is liquor or beauty.
    """

    def test_no_row_in_the_catalogue_re_slugs_under_the_scoped_list(self):
        for row in FIXTURE:
            scoped = {brand_key(s, row["vertical"]) for s in row["spellings"]}
            assert scoped == {brand_key(s) for s in row["spellings"]}, row["brand_slug"]
            assert len(scoped) == 1 and scoped.pop() == row["brand_slug"].replace("-", " ")

    @pytest.mark.parametrize(("spelling", "vertical", "key"), [
        ("Tanqueray Gin", "liquor", "tanqueray"),
        ("Tanqueray Gin", "beauty", "tanqueray gin"),
        ("Jo Malone London", "beauty", "jo malone"),
        ("Jo Malone London", "liquor", "jo malone london"),
        ("Dior Parfums", "beauty", "dior"),
        ("Appleton Estate", "liquor", "appleton"),
    ])
    def test_a_word_is_removed_only_in_the_vertical_its_list_was_written_for(self, spelling, vertical, key):
        assert brand_key(spelling, vertical) == key

    @pytest.mark.parametrize("spelling", ["Acme London", "Acme Paris", "Acme Estate", "Acme Gin"])
    def test_an_unlisted_vertical_folds_nothing(self, spelling):
        """The case rian named, and the one that has not happened yet: the first clothing shop.
        Two companies whose names differ only by a listed word stay two brands."""
        assert brand_key(spelling, "fashion") == spelling.lower()
        assert brand_key(spelling, "fashion") != brand_key("Acme", "fashion")

    @pytest.mark.parametrize("spelling", ["Acme Ltd", "Acme Company", "Acme Brands", "The Acme"])
    def test_the_corporate_suffixes_and_the_article_still_apply_everywhere(self, spelling):
        """A company's legal form is not its brand in any vertical, and never was a judgement."""
        assert brand_key(spelling, "fashion") == "acme"

    def test_a_caller_with_no_vertical_still_gets_every_word(self):
        """An audit tally, a collector's target match and the listings table's "brand differs"
        compare spellings; they do not decide identity, and scoping must not move them."""
        assert brand_key("Acme London") == "acme" and brand_key("Acme Gin") == "acme"
