"""The header search suggests product variants as a shopper types.

Suggestions first reused `list_product_variants`, which spent about half a second per
search on staging (11 Sep) aggregating prices over the whole catalogue: every
keystroke would have waited on it. `suggest_product_variants` matches the same way but
fetches only what a suggestion shows, so these pin the match, the ranking, and
that too short a term asks the database nothing.
"""

from sqlalchemy.dialects import postgresql

from app.services import catalog_queries

#: The row's tail since Stream AW3: product_line_id, brand_id, image_url, image_source, image_level
#: (what the picture cascade reads); None throughout for a variant with no line, brand or picture.
NO_PICTURE = (None, None, None, None, None)


def _sql(clause) -> str:
    # Literal binds render a % as %% (the driver's escaping); read it as SQL.
    sql = str(clause.compile(dialect=postgresql.dialect(), compile_kwargs={"literal_binds": True}))
    return sql.replace("%%", "%")


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

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


class _Session:
    def __init__(self, rows, lines=()):
        self.info: dict = {}
        self.rows = list(rows)
        self.lines = list(lines)
        self.statements: list[str] = []

    def execute(self, stmt):
        sql = _sql(stmt)
        if sql.startswith("SELECT product_variants.id, product_lines.id"):
            return _Result(self.lines)  # the line lookup (Stream K5) is not the suggestion query
        self.statements.append(sql)
        return _Result(self.rows)

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


def test_a_suggestion_carries_what_it_shows(monkeypatch):
    monkeypatch.setattr(catalog_queries.verify, "blocked_sources", lambda db: {})
    db = _Session([(52, "Tanqueray London Dry Gin 1L", "Tanqueray", "Gin", None, 9, *NO_PICTURE)])
    (s,) = catalog_queries.suggest_product_variants(db, "tanq")
    assert (s.id, s.name, s.brand, s.category, s.shop_count) == (52, "Tanqueray London Dry Gin 1L", "Tanqueray", "Gin", 9)


def test_the_match_is_the_browse_pages_and_the_ranking_is_for_typing(monkeypatch):
    monkeypatch.setattr(catalog_queries.verify, "blocked_sources", lambda db: {})
    db = _Session([])
    catalog_queries.suggest_product_variants(db, "  johnnie ", limit=6)
    (statement,) = db.statements
    # The words anywhere in the name or the brand, accents folded (as list_product_variants).
    assert "unaccent(product_variants.name) ILIKE unaccent('%johnnie%')" in statement
    assert "unaccent(coalesce(product_variants.brand, '')) ILIKE unaccent('%johnnie%')" in statement
    # Names that begin with the words, then brands that do, then the widest stocked.
    assert "WHEN (unaccent(product_variants.name) ILIKE unaccent('johnnie%')" in statement
    assert "shops.visible IS true" in statement
    assert "LIMIT 18" in statement
    # No price aggregation: that is what made the browse query too slow per keystroke.
    assert "price_observations" not in statement


def test_a_shoppers_wildcards_are_characters(monkeypatch):
    monkeypatch.setattr(catalog_queries.verify, "blocked_sources", lambda db: {})
    db = _Session([])
    catalog_queries.suggest_product_variants(db, "50%_off")
    statement = db.statements[0]
    assert r"unaccent('%50\\%\\_off%') ESCAPE" in statement


def test_one_character_asks_nothing():
    db = _Session([])
    assert catalog_queries.suggest_product_variants(db, "a") == []
    assert catalog_queries.suggest_product_variants(db, "   ") == []
    assert db.statements == []


def test_one_suggestion_per_product_line_the_representative_opening_the_line(monkeypatch):
    """Stream K5: a search for "1 million" listed the EDT and the Elixir as two bottles of one
    page. Now the line is suggested once, as its variant at the most places (then the lowest id),
    at the rank of its best match, and the suggestion opens the line with that variant chosen."""
    monkeypatch.setattr(catalog_queries.verify, "blocked_sources", lambda db: {})
    db = _Session(
        [(102, "1 Million Elixir 100 ml", "Rabanne", "Perfume", None, 2, *NO_PICTURE), (101, "1 Million EDT 100 ml", "Rabanne", "Perfume", None, 2, *NO_PICTURE),
         (7, "1 Million Lucky", "Rabanne", "Perfume", None, 5, *NO_PICTURE), (103, "1 Million Parfum 50 ml", "Rabanne", "Perfume", None, 1, *NO_PICTURE)],
        lines=[(101, 1, "rabanne-1-million", None, False), (102, 1, "rabanne-1-million", None, False),
               (103, 1, "rabanne-1-million", None, False), (7, 2, "rabanne-1-million-lucky", None, False)],
    )
    out = catalog_queries.suggest_product_variants(db, "1 million")
    assert [(s.id, s.path) for s in out] == [(101, "/products/rabanne-1-million?variant=101"),
                                             (7, "/products/rabanne-1-million-lucky?variant=7")]
