"""The header search suggests products as a shopper types.

Suggestions first reused `list_products`, 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_products` 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


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):
        self.info: dict = {}
        self.rows = list(rows)
        self.statements: list[str] = []

    def execute(self, stmt):
        self.statements.append(_sql(stmt))
        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)])
    (s,) = catalog_queries.suggest_products(db, "tanq")
    assert (s.id, s.name, s.brand, s.category, s.location_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_products(db, "  johnnie ", limit=6)
    (statement,) = db.statements
    # The words anywhere in the name or the brand, accents folded (as list_products).
    assert "unaccent(products.name) ILIKE unaccent('%johnnie%')" in statement
    assert "unaccent(coalesce(products.brand, '')) ILIKE unaccent('%johnnie%')" in statement
    # Names that begin with the words, then brands that do, then the widest stocked.
    assert "WHEN (unaccent(products.name) ILIKE unaccent('johnnie%')" in statement
    assert "locations.visible IS true" in statement
    assert "LIMIT 6" 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_products(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_products(db, "a") == []
    assert catalog_queries.suggest_products(db, "   ") == []
    assert db.statements == []
