"""The line and variation backfills on the fourteen 1 Million rows (Stream M, task M2).

Run against an in-memory SQLite holding the catalogue tables, like the account suites: no
server, no network. What is pinned is the brief's acceptance line, "one brand house, one
line, three variations, and the set kept apart", and that a second run changes nothing.
Before the Paco Rabanne alias is confirmed the same rows are two houses and two lines,
which is the honest state; the alias is rian's decision, never a rule's.
"""

from __future__ import annotations

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

from app import cli
from app.models import Account, Base, Brand, Product, ProductLine, VariationAlias
from app.services.normalize import match_key
from tests.test_lines import ONE_MILLION

TABLES = [Account.__table__, Brand.__table__, ProductLine.__table__, Product.__table__, VariationAlias.__table__]


@pytest.fixture
def db():
    engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
    Base.metadata.create_all(engine, tables=TABLES)
    factory = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False, future=True)
    with factory() as session:
        yield session


def seed(db, *, alias: bool):
    rabanne = Brand(slug="rabanne", name="Rabanne")
    paco = Brand(slug="paco-rabanne", name="Paco Rabanne")
    db.add_all([rabanne, paco])
    db.flush()
    if alias:
        paco.canonical_id = rabanne.id
    for n, (brand, name, size) in enumerate(ONE_MILLION, start=1):
        db.add(Product(
            id=n, name=name, brand=brand, brand_id=paco.id if brand == "Paco Rabanne" else rabanne.id,
            vertical="beauty", size_ml=size, match_key=match_key(brand, name, size), attributes={},
        ))
    db.commit()
    return rabanne, paco


class TestBeforeTheAlias:
    def test_two_houses_are_two_lines_and_a_second_run_changes_nothing(self, db):
        seed(db, alias=False)
        assert cli.backfill_lines(db) == "lines: 2 line(s) created, 14 product(s) pointed at their line"
        assert cli.backfill_lines(db) == "lines: 0 line(s) created, 0 product(s) pointed at their line"
        lines = db.scalars(select(ProductLine).order_by(ProductLine.slug)).all()
        assert [(line.slug, line.name, line.key) for line in lines] == [
            ("paco-rabanne-1-million", "1 Million", "1 million"), ("rabanne-1-million", "1 Million", "1 million"),
        ]


class TestWithTheAlias:
    def test_one_house_one_line_three_variations_and_the_set_apart(self, db):
        rabanne, _ = seed(db, alias=True)
        assert cli.backfill_lines(db) == "lines: 1 line(s) created, 14 product(s) pointed at their line"
        line = db.scalar(select(ProductLine))
        assert (line.brand_id, line.key, line.name, line.slug) == (rabanne.id, "1 million", "1 Million", "rabanne-1-million")
        assert cli.backfill_variations(db) == "variations: 4 wording(s) seeded, 11 product(s) stamped with their variation"
        assert cli.backfill_variations(db) == "variations: 0 wording(s) seeded, 0 product(s) stamped with their variation"
        products = db.scalars(select(Product).order_by(Product.id)).all()
        assert {p.line_id for p in products} == {line.id}
        variations = {(p.attributes or {}).get("variation") for p in products}
        assert variations == {"parfum", "edt", "elixir", None}
        assert sorted(a.raw for a in db.scalars(select(VariationAlias))) == [
            "edt", "elixir edp intense", "elixir parfum intense", "parfum"]
        from app.services.lines import form_of
        assert [p.name for p in products if form_of(p.name)] == ["Rabanne Set: Duo 1 Million Eau de Toilette 50 ml 100 ml"]

    def test_a_name_a_person_set_is_never_overwritten(self, db):
        seed(db, alias=True)
        cli.backfill_lines(db)
        line = db.scalar(select(ProductLine))
        line.name = "One Million"
        db.commit()
        cli.backfill_lines(db)
        assert db.scalar(select(ProductLine)).name == "One Million"

    def test_a_variation_a_person_decided_wins_over_the_rule(self, db):
        seed(db, alias=True)
        db.add(VariationAlias(vertical="beauty", raw="elixir parfum intense", canonical="parfum intense",
                              display="Parfum Intense", decided_by=None))
        db.commit()
        cli.backfill_variations(db)
        rows = db.scalars(select(Product).where(Product.name.like("%Elixir Parfum Intense%"))).all()
        assert {p.attributes["variation"] for p in rows} == {"parfum intense"}
