"""read_one(): reading one published listing back from its source.

Verification (Stream Q) compares what we published with what the page says
now, so each read must take the MAIN offer of a page and nothing else. The
trap on the Avolta platform is real: a product page ends in a rail of
related-product tiles, and the first price on the page is not always the
product's own.
"""

import json
from pathlib import Path

import pytest

from app.services.collectors.ari import listing_from_page
from app.services.collectors.avolta import STORES, AvoltaCollector, parse_product_page
from app.services.collectors.base import ListingRef, gone
from app.services.collectors.dubai import DubaiDutyFree
from app.services.collectors.extime import Extime
from app.services.collectors.fetch import FetchError, SourceBlocked
from app.services.collectors.heinemann import HeinemannGlobal
from app.services.collectors.heinemann_platform import heinemann_platform_collectors
from app.services.collectors.registry import COLLECTORS
from app.services.collectors.shopify import SHOPS, ShopifyCollector

FIXTURES = Path(__file__).parent / "fixtures"
PDP = (FIXTURES / "avolta_product_page.html").read_text()
CONFIGURABLE = (FIXTURES / "avolta_configurable_product.html").read_text()
ATH = next(s for s in STORES if s.code == "ATH")


class TestEveryCollectorAnswers:
    def test_read_one_exists_on_every_registered_collector(self):
        assert all(callable(getattr(c, "read_one", None)) for c in COLLECTORS.values())

    def test_hosts_that_refuse_us_say_so_without_fetching(self):
        ref = ListingRef("1", "https://x/p", "DXB")
        with pytest.raises(SourceBlocked):
            DubaiDutyFree().read_one(ref)
        with pytest.raises(SourceBlocked):
            HeinemannGlobal().read_one(ref)
        for collector in heinemann_platform_collectors():
            with pytest.raises(SourceBlocked):
                collector.read_one(ref)

    def test_a_404_or_410_is_gone_and_anything_else_is_not(self):
        assert gone(FetchError("https://x/p returned HTTP 404"))
        assert gone(FetchError("https://x/p returned HTTP 410"))
        assert not gone(FetchError("https://x/p returned HTTP 500"))
        assert not gone(FetchError("https://x/p failed: timed out"))


class TestAvoltaProductPage:
    def test_the_main_offer_is_read_not_the_related_tile(self):
        """The fixture's related rail prices Azzaro Chrome at 44.50; the
        product is Azzaro Sport at 19.95."""
        item = parse_product_page(PDP)
        assert item["sku"] == "5074177" and item["price"] == 19.95
        assert item["currency"] == "EUR" and item["in_stock"] is True
        assert item["brand"] == "Azzaro" and item["name"] == "Azzaro Sport 100ml Eau de Toilette"

    def test_a_published_row_is_read_back_as_a_listing(self):
        ref = ListingRef("5074177", "https://athens.shopdutyfree.com/en/137/azzaro-sport-100ml-eau-de-toilette", "ATH")
        row = AvoltaCollector(ATH).listing_from_product_page(ref, PDP)
        assert row.price == 19.95 and row.currency == "EUR" and row.size_ml == 100
        assert row.raw["tile"]["sku"] == "5074177"

    def test_a_page_now_serving_another_sku_is_gone(self):
        ref = ListingRef("9999999", "https://athens.shopdutyfree.com/en/137/x", "ATH")
        assert AvoltaCollector(ATH).listing_from_product_page(ref, PDP) is None

    def test_a_per_size_row_takes_its_own_size_from_the_family_page(self):
        """The fixture prices 70cl and 1L variants of one configurable product."""
        page = PDP.replace("</body>", CONFIGURABLE + "</body>")
        ref = ListingRef("5074177-P::1000", "https://athens.shopdutyfree.com/en/137/x", "ATH")
        row = AvoltaCollector(ATH).listing_from_product_page(ref, page)
        assert row is not None and row.source_sku == "5074177-P::1000" and row.size_ml == 1000
        assert row.gtin is None
        assert row.raw["variant"]["size_ml"] == 1000

    def test_a_size_the_page_no_longer_prices_is_gone(self):
        page = PDP.replace("</body>", CONFIGURABLE + "</body>")
        ref = ListingRef("5074177-P::350", "https://athens.shopdutyfree.com/en/137/x", "ATH")
        assert AvoltaCollector(ATH).listing_from_product_page(ref, page) is None

    def test_a_page_without_an_offer_is_none(self):
        assert parse_product_page("<html><body>maintenance</body></html>") is None


class TestShopifyProductJson:
    def collector(self):
        shop, slug, name, op = SHOPS[0]
        return ShopifyCollector(shop, slug, name, op)

    def payload(self):
        return {"product": {"id": 1, "title": "Grey Goose Vodka", "vendor": "Grey Goose",
                            "product_type": "Vodka", "tags": ["spirits"], "handle": "939092",
                            "body_html": "<p>copy</p>", "images": [],
                            "variants": [{"id": 42339720298573, "title": "1L", "price": "45.00",
                                          "available": True, "barcode": "", "sku": "GG1"},
                                         {"id": 7, "title": "70cl", "price": "35.00", "available": False}]}}

    def test_the_published_variant_is_picked_by_id(self):
        ref = ListingRef("42339720298573", "https://www.montrealdutyfree.ca/products/939092", "YUL")
        row = self.collector().listing_from_product_json(ref, self.payload())
        assert row.price == 45.0 and row.name == "Grey Goose Vodka 1L" and row.in_stock is True
        assert "body_html" not in json.dumps(row.raw)

    def test_a_variant_no_longer_listed_is_gone(self):
        ref = ListingRef("123", "https://www.montrealdutyfree.ca/products/939092", "YUL")
        assert self.collector().listing_from_product_json(ref, self.payload()) is None


class TestAriAndExtime:
    def test_ari_reads_the_page_back_and_extime_picks_its_size(self):
        page = '''<script type="application/ld+json">{"@type": "Product", "name": "Irish Mist 1L",
          "sku": "018000", "brand": {"name": "Irish Mist"},
          "offers": [{"price": "29.00", "priceCurrency": "EUR"}, {"price": "35.00"}]}</script>'''
        row = listing_from_page("https://www.dublinandcorkdutyfree.ie/alcohol/x/018000.html", page)
        assert row.source_sku == "018000" and row.price == 29.0
        extime_page = (FIXTURES / "extime_product.html").read_text()
        url = "https://www.extime.com/en/paris/product/single-malt-whisky-ten-years-old-107222991"
        rows = Extime()._listings(url, extime_page)
        assert rows and rows[0].source_sku == "76594"
