"""Places and the comparison unit defined once (Stream K2; plan W19). What it cost: three
spellings of the unit in `catalog_queries.py` that agreed only because every shop was one
airport; a mall's sixty shops or a second shop at Heathrow would have counted sixty or two
airports. SQLite kit, no network."""
from __future__ import annotations

import pathlib
import re

import pytest
from sqlalchemy import create_engine, func, select
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool

from app import cli
from app.models import Account, Base, LEDGER_TABLES, Retailer, Shop
from app.models.places import Place, ShopPlace
from app.services import places

TABLES = [Account.__table__, Retailer.__table__, Shop.__table__, Place.__table__, ShopPlace.__table__, *LEDGER_TABLES]


@pytest.fixture
def db():
    engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
    Base.metadata.create_all(engine, tables=TABLES)
    with sessionmaker(bind=engine, autoflush=False, expire_on_commit=False)() as s:
        r = Retailer(slug="wdf", name="World Duty Free"); s.add(r); s.flush()
        s.add_all([Shop(id=1, retailer_id=r.id, code="LHR-T2", iata="LHR", name="Heathrow T2", city="London", currency="GBP"),
                   Shop(id=2, retailer_id=r.id, code="LHR-T5", iata="LHR", name="Heathrow T5", city="London", currency="GBP"),
                   Shop(id=3, retailer_id=r.id, code="CDG", iata="CDG", name="Extime", city="Paris", currency="EUR"),
                   Shop(id=4, retailer_id=r.id, code="ONLINE", iata=None, name="Catalogue", currency="EUR", is_catalogue_only=True)])
        s.flush()
        yield s


def units(db):
    return db.scalar(places.unit_join(select(places.unit_count()).select_from(Shop)))


def test_the_registry_knows_the_kinds_the_walk_through_stress_tested():
    assert places.KINDS["ship"].moves and not places.KINDS["inflight"].pages and not places.KINDS["online"].primary
    assert places.KINDS["airport"].address_prefix == "/airports/" and places.KINDS["mall"].comparable


def test_the_unit_is_the_primary_place_else_the_airport_code_else_the_shop(db):
    # Before the backfill: two Heathrow shops are one airport, the online catalogue is itself.
    assert units(db) == 3
    assert places.count_units([1, 2, 3, 4], {}, {1: "LHR", 2: "LHR", 3: "CDG", 4: None}) == 3
    assert places.count_units([1, 2, 3, 4], {}) == 4
    assert cli.backfill_places(db) == "places: 2 airport place(s) created, 3 shop(s) given their primary place"
    assert cli.backfill_places(db) == "places: 0 airport place(s) created, 0 shop(s) given their primary place"
    assert units(db) == 3
    primary = places.primary_of(db)
    assert primary[1] == primary[2] != primary[3] and 4 not in primary
    assert places.count_units([1, 2, 3, 4], primary) == 3
    lhr = places.place_of(db, db.get(Shop, 1))
    assert lhr.kind == "airport" and lhr.identifiers == [{"scheme": "iata", "value": "LHR"}] and lhr.slug.endswith("lhr-london") and lhr.name == "Heathrow T2"
    # A mall's two shops at Heathrow become one unit only through the place, never the code.
    mall = Place(slug="heathrow-mall", kind="mall", name="A mall"); db.add(mall); db.flush()
    row = db.scalar(select(ShopPlace).where(ShopPlace.shop_id == 3)); db.delete(row); db.flush()
    db.add(ShopPlace(shop_id=3, place_id=mall.id, role="primary")); db.flush()
    assert places.count_units([1, 3], places.primary_of(db)) == 2


def test_no_count_of_shops_bypasses_the_unit():
    """Grep-based, as K1's survivor test: a `COUNT(DISTINCT shop_id)`, `distinct(Shop.iata)` or
    `distinct(Shop.id)` in the catalogue queries is a second definition of the unit."""
    src = (pathlib.Path(__file__).resolve().parents[1] / "app" / "services" / "catalog_queries.py").read_text()
    hits = [line.strip() for line in src.splitlines()
            if re.search(r"distinct\(\s*(Listing\.shop_id|Shop\.iata|Shop\.id)\s*\)|COUNT\(DISTINCT\s+(listings\.shop_id|shops\.(iata|id))", line)
            and "# unit:" not in line]
    assert not hits, hits
