from app.services.catalog.taxonomy import MAX_DEPTH, resolve_taxonomy


def sp(**kw):
    base = {"raw_category": None, "product_type": None,
            "collections": [], "tags": []}
    base.update(kw)
    return base


def test_native_category_wins():
    out = resolve_taxonomy(sp(
        raw_category="Apparel & Accessories > Shoes > Athletic",
        product_type="Shoes", collections=["Sale"], tags=["shoes"]))
    assert out["source"] == "native"
    assert out["path"] == ["Apparel & Accessories", "Shoes", "Athletic"]


def test_falls_back_to_product_type():
    out = resolve_taxonomy(sp(product_type="snowboard"))
    assert out["source"] == "product_type"
    assert out["path"] == ["snowboard"]
    assert out["raw"] == "snowboard"


def test_empty_product_type_is_not_a_match():
    # One live product has productType "" -- it must fall through, not resolve
    # to an empty path labelled product_type.
    out = resolve_taxonomy(sp(product_type="   "))
    assert out["source"] == "none"
    assert out["path"] == []


def test_falls_back_to_collections():
    out = resolve_taxonomy(sp(collections=["Snowboards"]))
    assert out["source"] == "collection"
    assert out["path"] == ["Snowboards"]


def test_merchandising_collections_are_skipped():
    # The live store has "Automated Collection" on 8 products and "Home page" on
    # one. Resolving to either makes unrelated products category siblings.
    out = resolve_taxonomy(sp(collections=["Home page", "Automated Collection"]))
    assert out["source"] == "none"


def test_merchandising_skipped_but_a_real_collection_still_wins():
    out = resolve_taxonomy(sp(collections=["Sale", "Snowboards"]))
    assert out["source"] == "collection"
    assert out["path"] == ["Snowboards"]


def test_falls_back_to_tags():
    out = resolve_taxonomy(sp(tags=["Snowboard"]))
    assert out["source"] == "tag"
    assert out["path"] == ["Snowboard"]


def test_unresolved():
    out = resolve_taxonomy(sp())
    assert out == {"path": [], "source": "none", "raw": None}


def test_depth_is_truncated():
    deep = " > ".join(f"L{i}" for i in range(8))
    out = resolve_taxonomy(sp(raw_category=deep))
    assert len(out["path"]) == MAX_DEPTH


def test_raw_category_is_preserved_for_debugging():
    out = resolve_taxonomy(sp(raw_category="Gift Cards"))
    assert out["raw"] == "Gift Cards"


def test_tag_resolution_is_order_independent():
    # taxonomy_path feeds content_hash; an order-dependent pick would re-embed
    # the catalog on every sync whenever a source reorders its tags.
    a = resolve_taxonomy(sp(tags=["Winter", "Sport"]))
    b = resolve_taxonomy(sp(tags=["Sport", "Winter"]))
    assert a["path"] == b["path"]


def test_collection_resolution_is_order_independent():
    a = resolve_taxonomy(sp(collections=["Winter Gear", "Sport Gear"]))
    b = resolve_taxonomy(sp(collections=["Sport Gear", "Winter Gear"]))
    assert a["path"] == b["path"]
