"""Reading Singapore (iShopChangi) off the page's own inventory response.

The traps here were measured on 2026-09-05 (`.logs/runs/changi-probe-2026-09-05.log`):
the same offer carries three channel prices and the non-traveller one is a fifth
higher; the API labels SGD "en" and CNY "zh" with only the string form carrying the
symbol; the size qualifier is a bare number with no unit; and nothing anywhere
carries a barcode. The fixture is the Johnnie Walker Blue Label 1L record as the
page received it, facts only.
"""

import json
from pathlib import Path

from app.services.collectors.changi import (
    PRICE_TYPE,
    Changi,
    channel_price,
    listings_from_inventory,
    load_targets,
    product_code,
    product_urls,
    quantity_of_variant,
    size_ml_of,
)
from app.services.collectors.registry import COLLECTORS

FIXTURES = Path(__file__).parent / "fixtures"
INVENTORY = json.loads((FIXTURES / "changi_inventory.json").read_text())
URL = "https://www.ishopchangi.com/en/product/johnnie-walker-blue-label-1000ml-40--mp00089136"


def offer() -> dict:
    return INVENTORY["product"]["variantOptions"][0]["offers"][0]


class TestChannelPrice:
    def test_the_departure_price_is_published_not_the_non_traveller_one(self):
        rows = listings_from_inventory(INVENTORY, URL)
        assert [r.price for r in rows] == [271.10]
        assert rows[0].price_type == PRICE_TYPE == "departure"
        assert 333.87 not in (r.price for r in rows)

    def test_a_channel_the_offer_does_not_list_is_none_never_another_channels_figure(self):
        assert channel_price(offer(), "ARRIVAL") == (271.10, None)
        assert channel_price(offer(), "SGDELIVERY") == (None, None)

    def test_only_a_price_that_reads_in_sgd_is_accepted(self):
        """The en/zh split is a naming convention; the symbol is the declaration."""
        o = json.loads(json.dumps(offer()))
        o["channelPrices"][1]["enDiscountedPrice"] = "¥1,409.72"
        assert channel_price(o) == (None, None)

    def test_a_promotion_keeps_the_crossed_out_price(self):
        o = json.loads(json.dumps(offer()))
        o["channelPrices"][1]["enOriginalValue"] = 300.0
        o["channelPrices"][1]["enOriginalPrice"] = "S$300.00"
        assert channel_price(o) == (271.10, 300.0)


class TestListing:
    def test_the_record_reads_as_the_page_shows_it(self):
        row = listings_from_inventory(INVENTORY, URL)[0]
        assert row.source_sku == "mp00089137"  # the variant, not the product
        assert row.name == "JOHNNIE WALKER BLUE LABEL 1000ML 40%"
        assert row.brand == "JOHNNIE WALKER"
        assert (row.currency, row.location_code) == ("SGD", "SIN")
        assert (row.size_ml, row.abv, row.in_stock) == (1000, 40.0, True)
        assert row.feed_categories == ["Wines & Spirits", "Whisky", "Blended Whisky"]
        assert row.vertical == "liquor"
        assert row.gtin is None

    def test_the_raw_record_keeps_the_seller_and_no_copy(self):
        row = listings_from_inventory(INVENTORY, URL)[0]
        assert row.raw["offer"]["shopName"] == "Lotte Duty Free"
        assert "descr" not in json.dumps(row.raw).lower()

    def test_size_from_the_name_first_then_the_bare_qualifier(self):
        variant = INVENTORY["product"]["variantOptions"][0]
        assert size_ml_of(variant, "JOHNNIE WALKER BLUE LABEL 1000ML 40%") == 1000
        assert size_ml_of(variant, "JOHNNIE WALKER BLUE LABEL") == 1000
        weight_variant = {"saleMeasureType": "Weight", **{k: v for k, v in variant.items() if k != "saleMeasureType"}}
        assert size_ml_of(weight_variant, "X") is None
        quantity, text = quantity_of_variant(weight_variant)
        assert (quantity.value, quantity.unit) == (1000, "g")
        assert text == "1000 g"

    def test_a_volume_qualifier_yields_a_millilitre_quantity(self):
        variant = INVENTORY["product"]["variantOptions"][0]
        quantity, text = quantity_of_variant(variant)
        assert quantity.unit == "ml" and quantity.value == 1000
        assert text == "1000 ml"
        row = listings_from_inventory(INVENTORY, URL)[0]
        assert row.quantity.unit == "ml"

    def test_an_offer_without_the_channel_is_skipped_not_guessed(self):
        payload = json.loads(json.dumps(INVENTORY))
        payload["product"]["variantOptions"][0]["offers"][0]["channelPrices"] = []
        assert listings_from_inventory(payload, URL) == []


class TestDiscovery:
    def test_product_urls_and_codes(self):
        xml = f"<urlset><url><loc>{URL}</loc></url><url><loc>https://www.ishopchangi.com/en/brand/diageo</loc></url><url><loc>{URL}</loc></url></urlset>"
        assert product_urls(xml) == [URL]
        assert product_code(URL) == "mp00089136"
        assert product_code("https://www.ishopchangi.com/en/product/x-mp00349682.html") == "mp00349682"

    def test_targets_file_shape(self, tmp_path):
        f = tmp_path / "t.json"
        f.write_text(json.dumps({"urls": [URL, 3]}))
        assert load_targets(f) == [URL]
        assert load_targets(tmp_path / "absent.json") == []

    def test_the_collector_is_registered_and_targeted_by_default(self):
        assert isinstance(COLLECTORS["ishopchangi-sin"], Changi)
        assert COLLECTORS["ishopchangi-sin"].full_catalogue is False
