"""What a merge inherits: the barcode and the image, never invented (Stream M, task M7).

Over the in-memory SQLite, as the alias tests. Pinned: a survivor without a barcode gains
the merged row's and is marked as having done so; a survivor with its own keeps it; two
different barcodes reach a merge only with a person's note, and the record says which won,
which was dropped and why; two rows without a barcode merge into one without a barcode;
the image passes with its provenance and a survivor's own image is kept.
"""

from __future__ import annotations

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

from app.models import Account, Award, Base, Brand, Listing, Shop, Suggestion, ProductVariant, ProductLine, Merge, Retailer
from app.models.decisions import LEDGER_TABLES
from app.models.places import Place, ShopPlace
from app.services import merge_session, merges

TABLES = [Account.__table__, Brand.__table__, ProductLine.__table__, ProductVariant.__table__, Retailer.__table__,
          Shop.__table__, Listing.__table__, Award.__table__, Merge.__table__, Suggestion.__table__,
    *LEDGER_TABLES, Place.__table__, ShopPlace.__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:
        session.add(Account(id=1, username="rian", display_name="rian"))
        yield session


def product(db, pid, name, **kw):
    row = ProductVariant(id=pid, name=name, brand="Dior", vertical="beauty", quantity_ml=100, match_key=f"k{pid}", attributes={}, **kw)
    db.add(row)
    db.flush()
    return row


class TestBarcode:
    def test_a_survivor_without_a_barcode_gains_the_merged_rows_and_is_marked(self, db):
        keeper = product(db, 1, "Sauvage 100ml")
        other = product(db, 2, "Sauvage EDT 100ml", gtin="3348901250412")
        assert merges.merge_product_variants(db, keeper, [other], reason="confirmed", merged_by=1) == 1
        assert (keeper.gtin, keeper.gtin_source) == ("3348901250412", "merge")
        assert other.gtin is None and other.merged_into_id == 1
        record = db.scalar(select(Merge))
        assert record.detail["barcode"] == {"gained": "3348901250412", "from": 2}

    def test_a_survivor_with_its_own_keeps_it_and_two_different_ones_are_recorded_with_why(self, db):
        keeper = product(db, 1, "Sauvage 100ml", gtin="3348901250412")
        other = product(db, 2, "Sauvage EDT 100ml", gtin="3348901250429")
        merges.merge_product_variants(db, keeper, [other], reason="confirmed", merged_by=1, note="the shop page shows 0412")
        assert (keeper.gtin, keeper.gtin_source) == ("3348901250412", None)
        record = db.scalar(select(Merge))
        assert record.detail["barcode"] == {"kept": "3348901250412", "dropped": "3348901250429", "why": "the shop page shows 0412"}
        assert record.detail["note"] == "the shop page shows 0412"

    def test_two_rows_without_a_barcode_never_gain_one(self, db):
        keeper, other = product(db, 1, "Sauvage 100ml"), product(db, 2, "Sauvage 100 ml")
        merges.merge_product_variants(db, keeper, [other], reason="confirmed", merged_by=1)
        assert keeper.gtin is None and keeper.gtin_source is None
        assert "barcode" not in db.scalar(select(Merge)).detail

    def test_the_session_refuses_two_barcodes_without_a_note(self, db):
        product(db, 1, "Sauvage 100ml", gtin="3348901250412")
        product(db, 2, "Sauvage EDT 100ml", gtin="3348901250429")
        db.add(Suggestion(level="product", left_id=1, right_id=2, reason="gtin_differs", score=0.5))
        db.commit()
        with pytest.raises(merge_session.Refused) as refused:
            merge_session.confirm(db, 1, decided_by=1)
        assert refused.value.code == "NOTE_REQUIRED"
        result = merge_session.confirm(db, 1, decided_by=1, keep="left", note="the shop page shows 0412")
        assert result["applied"]["rows_merged"] == 1
        assert db.get(Suggestion, 1).decision == "same"


class TestImage:
    def test_the_image_passes_with_its_provenance_and_an_own_image_is_kept(self, db):
        keeper = product(db, 1, "Sauvage 100ml")
        other = product(db, 2, "Sauvage EDT 100ml", image_url="https://img/x.jpg", thumb_url="https://img/x_t.jpg", image_source="barcode")
        merges.merge_product_variants(db, keeper, [other], reason="confirmed", merged_by=1)
        assert (keeper.image_url, keeper.thumb_url, keeper.image_source) == ("https://img/x.jpg", "https://img/x_t.jpg", "barcode")
        assert db.scalar(select(Merge)).detail["image"] == {"from": 2, "source": "barcode"}
        own = product(db, 3, "Sauvage Parfum 100ml", image_url="https://img/own.jpg", image_source="name")
        third = product(db, 4, "Sauvage Parfum 100 ml", image_url="https://img/other.jpg", image_source="barcode")
        merges.merge_product_variants(db, own, [third], reason="confirmed", merged_by=1)
        assert (own.image_url, own.image_source) == ("https://img/own.jpg", "name")
