"""The image sourcing list: ranked by airports, in the unit the client can source, for client.view.

Pins what the client will act on: a product listed at one airport never appears (nothing to
compare), the product carried by the most airports leads its group, the perfume category
splits from the rest of beauty, a brand image is credited with every comparable product of
the brand, and the reads admit the client's level while a stranger is refused.
"""

from __future__ import annotations

import pytest

from app.models import RawRecord, AccountLevel, Base, Listing, Location, Product, ProductLine, Retailer
from app.services import accounts, image_ask
from tests import _accounts as T
from tests.kit import _env

CATALOGUE = [Retailer.__table__, Location.__table__, ProductLine.__table__, Product.__table__, Listing.__table__, RawRecord.__table__]

# (id, brand, name, vertical, category, image, airports)
PRODUCTS = [
    (1, "Johnnie Walker", "Johnnie Walker Blue Label 1L", "liquor", "Whisky", None, 4),
    (2, "Johnnie Walker", "Johnnie Walker Black Label 1L", "liquor", "Whisky", "https://img/jw-black.jpg", 3),
    (3, "Patrón", "Patrón Silver 1L", "liquor", "Tequila & Mezcal", None, 2),
    (4, "Lagavulin", "Lagavulin 16 700ml", "liquor", "Whisky", None, 1),
    (5, "Chanel", "Chanel No 5 EDP 100ml", "beauty", "Perfume", None, 3),
    (6, "Chanel", "Chanel Hydra Beauty Cream 50ml", "beauty", "Skincare", None, 2),
    (7, "Lancôme", "Lancôme Lash Idôle 8.5ml", "beauty", "Makeup", None, 2),
]


@pytest.fixture
def world(monkeypatch):
    T.fresh(monkeypatch)
    Base.metadata.drop_all(_env.engine, tables=CATALOGUE)
    Base.metadata.create_all(_env.engine, tables=CATALOGUE)
    T.person("rian")
    with _env.TestSessionLocal() as db:
        db.add(AccountLevel(name="admin", permissions=list(accounts.SEED_LEVELS["admin"]["permissions"]), assignable=[]))
        db.commit()
    T.person("adam", level="admin")
    with _env.TestSessionLocal() as db:
        retailer = Retailer(slug="shop", name="Shop")
        db.add(retailer)
        db.flush()
        locations = [Location(retailer_id=retailer.id, code=f"S{n}", iata=f"A{n:02d}", name=f"Shop {n}", currency="EUR",
                              visible=(n <= 2)) for n in range(1, 5)]
        db.add_all(locations)
        db.flush()
        for pid, brand, name, vertical, category, image, airports in PRODUCTS:
            db.add(Product(id=pid, name=name, brand=brand, vertical=vertical, category=category, image_url=image,
                           match_key=f"k{pid}", attributes={}))
            db.flush()
            for loc in locations[:airports]:
                db.add(Listing(product_id=pid, location_id=loc.id, source_sku=f"sku{pid}-{loc.id}"))
        db.commit()
    yield
    Base.metadata.drop_all(_env.engine, tables=CATALOGUE)


class TestRanking:
    def test_the_product_at_the_most_airports_leads_and_a_single_airport_never_appears(self, world):
        with _env.TestSessionLocal() as db:
            out = image_ask.recommended(db, mode="product")
        alcohol = next(g for g in out["groups"] if g["key"] == "alcohol")
        assert [r["id"] for r in alcohol["rows"]] == [1, 2, 3]
        assert alcohol["comparable"] == 3 and alcohol["without_image"] == 2
        assert alcohol["rows"][0]["codes"] == ["A01", "A02", "A03", "A04"]
        assert alcohol["rows"][0]["visible_airports"] == 2
        assert alcohol["rows"][1]["has_image"] is True

    def test_perfume_splits_from_the_rest_of_beauty(self, world):
        with _env.TestSessionLocal() as db:
            out = image_ask.recommended(db, mode="product")
        by = {g["key"]: [r["id"] for r in g["rows"]] for g in out["groups"]}
        assert by["perfume"] == [5]
        assert by["cosmetics"] == [6, 7]
        assert out["summary"]["alcohol"] == {"comparable": 3, "with_image": 1}

    def test_a_brand_image_is_credited_with_every_comparable_product_of_the_brand(self, world):
        with _env.TestSessionLocal() as db:
            out = image_ask.recommended(db, mode="brand")
        alcohol = next(g for g in out["groups"] if g["key"] == "alcohol")
        top = alcohol["rows"][0]
        assert top["brand"] == "Johnnie Walker" and top["products"] == 2 and top["without_image"] == 1
        assert top["airports"] == 4 and top["examples"][0] == "Johnnie Walker Blue Label 1L"
        assert alcohol["covers_products"] == 3

    def test_the_full_list_filters_and_pages(self, world):
        with _env.TestSessionLocal() as db:
            missing = image_ask.products(db, group="alcohol", brand=None, q=None, missing=True, page=1, per_page=10)
            searched = image_ask.products(db, group=None, brand=None, q="chanel", missing=False, page=1, per_page=10)
            paged = image_ask.products(db, group=None, brand=None, q=None, missing=False, page=2, per_page=2)
        assert [r["id"] for r in missing["rows"]] == [1, 3]
        assert [r["id"] for r in searched["rows"]] == [5, 6] and searched["total"] == 2
        assert paged["total"] == 6 and [r["rank"] for r in paged["rows"]] == [3, 4]


class TestAccess:
    def test_the_client_reads_it_and_a_stranger_is_refused(self, world):
        client = T.client()
        assert client.get("/api/images/recommended").status_code in (401, 403)
        T.login(client, "adam")
        r = client.get("/api/images/recommended?mode=brand")
        assert r.status_code == 200 and r.json()["mode"] == "brand"
        assert client.get("/api/images/products?group=perfume").status_code == 200
        assert client.get("/api/images/brands").status_code == 200
        assert client.get("/api/images/recommended?mode=logo").status_code == 422
