#!/usr/bin/env python3
"""Synthetic collectibles snapshot; Python 3 standard library, no network.

The fixture URLs identify fictional records, not actual eBay listings.
Review fields and thresholds are owned by the example application.
"""
from collections import Counter
from copy import deepcopy
from datetime import datetime, timedelta, timezone
from decimal import Decimal, InvalidOperation
import json
from statistics import median

NOW = datetime(2026, 10, 27, 12, tzinfo=timezone.utc)
MAX_AGE = timedelta(hours=24)
COHORT = "sealed-set-example-v1"


def instant(value):
    parsed = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
    if parsed.tzinfo is None or parsed.utcoffset() is None:
        raise ValueError("Timestamp must include its UTC offset")
    return parsed


def snapshot(rows, now=NOW, cohort=COHORT, site="ebay.com", currency="USD",
             minimum_count=5, max_age=MAX_AGE):
    if now.tzinfo is None or now.utcoffset() is None:
        raise ValueError("now must have a UTC offset")
    if minimum_count < 1 or max_age <= timedelta(0):
        raise ValueError("Positive sample size and freshness window required")
    seen, accepted, excluded = set(), [], Counter()
    for record in rows:
        row = record["listing"]
        key = (record.get("ebaySite"), row.get("itemId"))
        reason = None
        if not key[1]:
            reason = "missing_id"
        elif key in seen:
            reason = "duplicate"
        else:
            seen.add(key)
            if record.get("reviewStatus") != "accepted":
                reason = "unreviewed"
            elif record.get("cohortId") != cohort or key[0] != site:
                reason = "different_group"
            elif row.get("listingType") != "sold":
                reason = "listing_type"
            elif row.get("soldCurrency") != currency:
                reason = "currency"
            elif row.get("quality", {}).get("priceBasis") != "displayed_listing_price":
                reason = "price_basis"
            else:
                try:
                    amount = Decimal(str(row.get("soldPrice")))
                    if not amount.is_finite() or amount <= 0:
                        raise ValueError("Positive finite amount required")
                except (InvalidOperation, ValueError):
                    reason = "amount"
                if reason is None:
                    try:
                        observed = instant(row.get("scrapedAt"))
                        if not timedelta(0) <= now - observed <= max_age:
                            raise ValueError("Outside observation window")
                    except (ValueError, TypeError, OverflowError):
                        reason = "freshness"
        if reason:
            excluded[reason] += 1
        else:
            accepted.append((amount, observed, row))
    ready = len(accepted) >= minimum_count
    return {
        "status": "ready" if ready else "insufficient_evidence",
        "cohortId": cohort, "marketplace": site, "currency": currency,
        "reviewedCount": len(accepted), "excluded": dict(excluded),
        "medianDisplayedItemPrice": (
            format(median([x[0] for x in accepted]), ".2f") if ready else None),
        "oldestObservedAt": min((x[1] for x in accepted), default=None),
        "newestObservedAt": max((x[1] for x in accepted), default=None),
        "supportingExamples": [
            {"title": x[2]["title"], "url": x[2]["url"],
             "displayedPrice": format(x[0], ".2f"), "currency": currency}
            for x in accepted[:3]
        ] if ready else [],
        "priceBasis": "Displayed item amounts, excluding shipping",
        "limitations": ["Final accepted-offer amounts are unverified",
                        "Source-reported sale dates are unverified"],
    }


def candidate(item_id, amount, **changes):
    record = {
        "cohortId": COHORT, "ebaySite": "ebay.com", "reviewStatus": "accepted",
        "listing": {"itemId": item_id, "title": "Fictional sealed-set comparable",
                    "url": "https://example.com/synthetic/" + item_id,
                    "soldPrice": amount, "soldCurrency": "USD", "listingType": "sold",
                    "scrapedAt": "2026-10-27T08:00:00Z",
                    "quality": {"priceBasis": "displayed_listing_price"}},
    }
    record.update(changes)
    return record


def fixtures():
    rows = [candidate("comp-" + str(i), str(price))
            for i, price in enumerate([38, 40, 42, 44, 46], start=1)]
    other = candidate("other-edition", "98", cohortId="open-set-example-v1")
    ranged = candidate("range", "20")
    ranged["listing"]["quality"]["priceBasis"] = "range_lower_bound"
    old = candidate("old", "100")
    old["listing"]["scrapedAt"] = "2026-10-24T08:00:00Z"
    return rows + [other, ranged, deepcopy(rows[0]), old]


def checks():
    rows = fixtures()
    result = snapshot(rows)
    assert result["medianDisplayedItemPrice"] == "42.00"
    assert result["reviewedCount"] == 5
    assert result["excluded"] == {
        "different_group": 1, "price_basis": 1, "duplicate": 1, "freshness": 1}
    assert snapshot(rows[:4])["medianDisplayedItemPrice"] is None
    assert snapshot([])["status"] == "insufficient_evidence"
    for bad in [None, "NaN", "Infinity", "0", "-1", "invalid"]:
        assert snapshot([candidate("bad", bad)])["reviewedCount"] == 0
    active = candidate("active", "42")
    active["listing"]["listingType"] = "active"
    assert snapshot([active])["excluded"] == {"listing_type": 1}
    wrong_currency = candidate("eur", "42")
    wrong_currency["listing"]["soldCurrency"] = "EUR"
    assert snapshot([wrong_currency])["excluded"] == {"currency": 1}
    for timestamp in ["not-a-date", "2026-10-28T08:00:00Z", "2026-10-27T08:00:00"]:
        bad_date = candidate("bad-date", "42")
        bad_date["listing"]["scrapedAt"] = timestamp
        assert snapshot([bad_date])["excluded"] == {"freshness": 1}
    assert snapshot([candidate("pending", "42", reviewStatus="pending")])["reviewedCount"] == 0
    assert snapshot(rows, now=NOW + timedelta(days=2))["supportingExamples"] == []
    assert result["oldestObservedAt"] == instant("2026-10-27T08:00:00Z")
    assert len(result["supportingExamples"]) == 3


if __name__ == "__main__":
    checks()
    print(json.dumps(snapshot(fixtures()), default=lambda value: value.isoformat(), indent=2))
    print("Offline checks passed; records and amounts are synthetic.")
