"""Recorded merges and the match key (rules v2, now v3), with the rows that went wrong.

The first rehearsal of `backfill merges` on a copy of the 5 Sep dump (task A9) folded
"Nina" into "Ricci Ricci", a PSG "paris" cap into the plain cap, and a Boss Bottled
bottle into a Boss Bottled gift set. Each is pinned here; the fold itself was proven on
the copy (44 rows forwarded, a second pass changed nothing).
"""

from app.models import Product
from app.services.merges import _conflicts, pick_survivor
from app.services.normalize import looks_like_set, match_key
from app.services.taxonomy import classify


def product(pid, name, brand, *, gtin=None, size=700, attributes=None):
    return Product(id=pid, name=name, brand=brand, gtin=gtin, size_ml=size,
                   match_key=match_key(brand, name, size), attributes=attributes or {})


class TestMatchKeyVersionTwo:
    def test_every_spelling_of_a_house_shares_the_key(self):
        keys = {
            match_key("Moët & Chandon", "Moët & Chandon Impérial Brut 750ml", 750),
            match_key("MOET ET CHANDON", "Moet Chandon Imperial Brut 75cl", 750),
            match_key("Moet and Chandon", "Imperial Brut", 750),
        }
        assert keys == {"moet-chandon|imperial||750"}  # "brut" is the style every Impérial has
        assert match_key("Don Julio Tequila", "Don Julio 1942 70cl", 700) == "don-julio|1942||700"
        assert match_key("Don Julio®", "Don Julio 1942® Tequila 750 ml", 750) == match_key("Don Julio", "Don Julio 1942 Tequila 750ml", 750)

    def test_a_scent_named_after_the_house_is_not_the_house(self):
        """Stripping every leading brand word emptied "Nina Ricci Ricci Ricci" and merged it with "Nina"."""
        assert match_key("Nina Ricci", "Nina Ricci Ricci Ricci", 50) != match_key("Nina Ricci", "Nina 5cl", 50)
        assert match_key("Nina Ricci", "Nina Ricci Nina 5cl", 50) == match_key("Nina Ricci", "Nina 5cl", 50)

    def test_only_the_brand_prefix_leaves_the_name(self):
        """The second "paris" in PSG's "paris" cap tells it from the plain cap."""
        assert match_key("Paris Saint-Germain - PSG", 'Paris saint-germain - "paris" cap', None) != \
            match_key("Paris Saint-Germain - PSG", "Paris saint germain - cap", None)

    def test_the_last_brand_word_alone_still_strips(self):
        assert match_key("GIORGIO ARMANI", "Armani Code 7.5cl", 75) == match_key("GIORGIO ARMANI", "Code 7.5cl", 75)

    def test_a_name_that_is_only_the_brand_is_the_house_namesake(self):
        """Version 2 kept the word ("gin-gin|700"); version 3 keys the namesake line as the
        house with an empty line, which still meets no other house."""
        assert match_key("Gin", "Gin", 700) == "gin|||700"
        assert match_key("Gin", "Gin", 700) != match_key("Gin Mare", "Gin Mare", 700)


class TestConflicts:
    def test_two_barcodes_never_merge(self):
        group = [product(1, "Dior Homme", "Dior", gtin="3348901484893", size=100),
                 product(2, "Dior Homme", "Dior", gtin="3348901419246", size=100)]
        assert _conflicts(group) == ["gtin_differs"]

    def test_one_barcode_or_none_may_merge(self):
        assert _conflicts([product(1, "Hendrick's Gin 1L", "Hendrick's", gtin="5010327703053", size=1000),
                           product(2, "Gin 1L", "Hendricks", size=1000)]) == []

    def test_a_set_never_folds_into_the_bottle(self):
        """A Boss Bottled 10cl once merged into "Bottled Gift Set" because "gift set" is key noise."""
        assert looks_like_set("Bottled Gift Set") and not looks_like_set("Boss Bottled 10cl")
        assert _conflicts([product(1, "Boss Bottled 10cl", "Hugo Boss", size=100),
                           product(2, "Bottled Gift Set", "Hugo Boss", gtin="3616301623373", size=100)]) == ["set_vs_single"]

    def test_declared_concentrations_that_differ_keep_the_rows_apart(self):
        assert _conflicts([product(1, "Sauvage 100ml", "Dior", size=100, attributes={"concentration": "edt"}),
                           product(2, "Sauvage 100ml", "Dior", size=100, attributes={"concentration": "edp"})]) == ["attribute_differs"]
        assert _conflicts([product(1, "Sauvage 100ml", "Dior", size=100, attributes={"concentration": "edt"}),
                           product(2, "Sauvage 100ml", "Dior", size=100)]) == []


class TestSurvivor:
    def test_the_barcode_holder_survives_else_the_most_listed_else_the_oldest(self):
        a, b, c = product(3, "X", "B"), product(2, "X", "B", gtin="5010327703053"), product(1, "X", "B")
        assert pick_survivor([a, b, c], {3: 5}) is b
        assert pick_survivor([a, c], {3: 5, 1: 1}) is a
        assert pick_survivor([a, c], {}) is c


class TestUncategorisedBackfillRules:
    """Launch rows the 3 Sep count listed as uncategorised, and where they now land."""

    def test_the_brand_settles_a_name_that_says_nothing(self):
        assert classify("Don Papa Masskara 70cl", "Don Papa") == "Rum"
        assert classify("Cîroc Pineapple 1l", "Cîroc") == "Vodka"
        assert classify("Bombay Sapphire Sunset Special Edition 1l", "Bombay Sapphire") == "Gin"
        assert classify("Gran Centenario Plata 99cl", "Gran Centenario") == "Tequila & Mezcal"

    def test_widened_name_rules(self):
        assert classify("Disaronno Amaretto Originale 1l", "Disaronno") == "Liqueurs & Aperitifs"
        assert classify("Cream Liqueurs 1L", "Coole Swan") == "Liqueurs & Aperitifs"
        assert classify("Kosk. Espresso Liq.21% 0.5L", "Koskenkorva") == "Liqueurs & Aperitifs"
        assert classify("Lancaster Sun Beauty Velvet Milk Spf30 400ml", "Lancaster") == "Skincare"
        assert classify("OPI Nail Lacquer Berlin There Done That 15ml", "OPI") == "Makeup"
        assert classify("Kylie Brow Highlighter", "Kylie Cosmetics") == "Makeup"

    def test_a_house_that_makes_two_categories_is_left_to_the_name(self):
        assert classify("Torres 10 Imp.Gr.Res. 38% 1L", "Torres") is None
        assert classify("Milka & Oreo Tablet 300g", "Milka") is None


class TestBrandDisplaySpelling:
    def test_mixed_case_beats_shouting_even_when_rarer(self):
        from collections import Counter
        from app.cli import display_spelling
        assert display_spelling(Counter({"SISLEY": 195, "Sisley": 3})) == "Sisley"
        assert display_spelling(Counter({"DIOR": 149, "Dior": 4, "Dior Parfums": 1})) == "Dior"
        assert display_spelling(Counter({"MOET": 2})) == "MOET"
        assert display_spelling(Counter({"Jameson®": 3, "Jameson": 2})) == "Jameson"
