"""Identity rules v3: the key is brand | line | attribute | size, with a form marker.

The fourteen 1 Million rows (Stream M brief, M3): under one brand, the two 100 ml Eau de
Toilette spellings share a key and the two 200 ml Elixirs share a key even though their
parsed concentrations differ; the bare "1 Million 10cl" keys apart from both the Eau de
Toilette and the Parfum at 100 ml, because an unknown attribute is only ever a
suggestion, never a guess; the set keys apart from every bottle. The other cases are the
rows a looser rule once joined: a gift set folded into the bottle, a 2 x 1 L pack keyed
as a 2 L bottle. No database: the keying service is fed maps a session would.
"""

from app.services import keying
from app.services.ingest import attributes_disagree
from app.services.normalize import match_key
from tests.test_product_lines import ONE_MILLION


def key(listed_brand, name, size, *, brand="rabanne"):
    return match_key(listed_brand, name, size, vertical="beauty", brand=brand)


class TestOneMillionUnderOneHouse:
    def test_the_same_bottle_at_two_shops_shares_a_key_whatever_the_wording(self):
        edt_100 = {key(b, n, s) for b, n, s in ONE_MILLION if n in ("Rabanne 1 Million EDT 100ml", "1 Million Eau de Toilette 100 ml")}
        assert edt_100 == {"rabanne|1-million|concentration=edt|100ml"}
        elixir_200 = {key(b, n, s) for b, n, s in ONE_MILLION
                      if n in ("1 Million Elixir Parfum Intense 200 ml", "1 Million Elixir Eau de Parfum Intense 200 ml")}
        assert elixir_200 == {"rabanne|1-million|concentration=elixir|200ml"}

    def test_an_unknown_variation_keys_apart_and_is_never_guessed(self):
        bare = key("Paco Rabanne", "1 Million 10cl", 100)
        assert bare == "rabanne|1-million||100ml"
        assert bare != key("Paco Rabanne", "1 Million Parfum 10cl", 100) == "rabanne|1-million|concentration=parfum|100ml"
        assert bare != key("Rabanne", "Rabanne 1 Million EDT 100ml", 100)

    def test_the_set_never_shares_a_key_with_the_bottle(self):
        assert key("Rabanne", "Rabanne Set: Duo 1 Million Eau de Toilette 50 ml 100 ml", 50) == "rabanne|set-duo-1-million|concentration=edt|50ml+100ml|set"  # v6 keeps "Set: Duo": a format list no longer deletes
        assert key("Rabanne", "1 Million Eau de Toilette 50 ml", 50) == "rabanne|1-million|concentration=edt|50ml"

    def test_without_the_alias_the_two_houses_key_apart(self):
        assert match_key("Paco Rabanne", "1 Million 10cl", 100, vertical="beauty") == "paco-rabanne|1-million||100ml"
        assert match_key("Rabanne", "1 Million 10cl", 100, vertical="beauty") == "rabanne|1-million||100ml"


class TestTheVeto:
    def test_the_canonical_variation_subsumes_the_parsed_concentration(self):
        assert not attributes_disagree({"concentration": "parfum", "attribute": "elixir"},
                                       {"concentration": "edp", "attribute": "elixir"})
        assert attributes_disagree({"attribute": "edt"}, {"attribute": "parfum"})

    def test_without_a_variation_the_concentration_still_vetoes(self):
        assert attributes_disagree({"concentration": "edp"}, {"concentration": "edt"})
        assert not attributes_disagree({"attribute": "edt"}, {"concentration": "edt"})


class TestFormsAndDrinks:
    def test_a_gift_set_and_a_pack_key_apart_from_the_bottle(self):
        """A Good Girl gift set (80 ml + 10 ml) was joined to the 8cl bottle; a 2 x 1 L twin
        pack keyed as a 2 L bottle."""
        assert match_key("Carolina Herrera", "Good Girl Gift Set 80ml + 10ml", 90, vertical="beauty").endswith("|set")
        assert match_key("Chivas Regal", "Chivas Regal 12y 40% 2x1L", 2000, vertical="liquor") == "chivas-regal|12yo|abv=40|2000ml|pack"
        assert match_key("Chivas Regal", "Chivas Regal 12 Year Old 2L", 2000, vertical="liquor") == "chivas-regal|12yo||2000ml"

    def test_every_spelling_of_an_age_is_one_key_and_a_drink_has_no_variation(self):
        """Identity rules v6: the age's spellings fold to one token (`12yo`), and that is all a rule
        does alone. Under v5 these three were one key because a list deleted "Blended Scotch
        Whisky" and the strength was dropped; now the category words stay (the drink-words list
        proposes the join, `test_proposal_rules`) and a stated 40% keys apart from a silent name,
        because a stated value against none is not certain agreement."""
        keys = {match_key("Chivas Regal", n, 1000, vertical="liquor") for n in (
            "Chivas Regal 12 Year Old 1L", "Chivas Regal 12 Years Old 1L", "Chivas Regal 12y 1L", "Chivas Regal Aged 12 Years 1L")}
        assert keys == {"chivas-regal|12yo||1000ml"}
        assert match_key("Chivas Regal", "Chivas Regal 12 1L", 1000, vertical="liquor") == "chivas-regal|12||1000ml"  # a bare number states no age
        assert match_key("Chivas Regal", "Chivas Regal 12 Year Old Blended Scotch Whisky 1L", 1000, vertical="liquor") == \
            "chivas-regal|12yo-blended-scotch-whisky||1000ml"
        assert match_key("Chivas Regal", "Chivas Regal 12y 40% 1L", 1000, vertical="liquor") == "chivas-regal|12yo|abv=40|1000ml"
        assert match_key("Chivas Regal", "Chivas Regal 12y 43% 1L", 1000, vertical="liquor") == "chivas-regal|12yo|abv=43|1000ml"
        assert match_key("Calvin Klein", "Eternity For Men Eau de Toilette 100 ml", 100, vertical="beauty") != \
            match_key("Calvin Klein", "Eternity for Her Eau de Parfum 100 ml", 100, vertical="beauty")


class TestMaps:
    class _Brand:
        def __init__(self, id, slug, alias_of_id=None):
            self.id, self.slug, self.alias_of_id = id, slug, alias_of_id

    def test_the_house_follows_the_alias_and_the_alias_table_wins_over_the_rule(self):
        rabanne, paco = self._Brand(1, "rabanne"), self._Brand(2, "paco-rabanne", alias_of_id=1)
        maps = keying.Maps(brands={1: rabanne, 2: paco}, by_slug={"rabanne": rabanne, "paco-rabanne": paco},
                           aliases={("beauty", "elixir parfum intense"): "parfum intense"})
        assert maps.brand_of(2) is rabanne and maps.brand_of(listed_brand="PACO RABANNE") is rabanne
        assert keying.brand_words(maps.brand_of(2)) == "rabanne"
        assert maps.attribute("1 Million Elixir Parfum Intense 200 ml", "beauty") == "parfum intense"
        assert maps.attribute("1 Million Elixir Eau de Parfum Intense 200 ml", "beauty") == "elixir"
        assert maps.attribute("Chivas 12", "liquor") == ""
