import pytest

from app.services.enrichment.extractor import (
    BATCH_SIZE, GENDERS, PRICE_TIERS, SIZE_SYSTEMS, build_prompt, extract_batch,
    validate_one,
)

PRODUCTS = [
    {"product_key": "shopify:1", "name": "Oxford Shirt",
     "description": "A white cotton formal shirt.", "brand": "Brooks",
     "taxonomy_path": ["Apparel", "Shirts"], "attributes": []},
    {"product_key": "http_api:2", "name": "Brown Leather Belt",
     "description": "Full-grain leather belt.", "brand": None,
     "taxonomy_path": ["Apparel", "Belts"], "attributes": []},
]


def test_batch_size_is_twenty():
    assert BATCH_SIZE == 20


def test_prompt_carries_every_product_key():
    prompt = build_prompt(PRODUCTS)
    for p in PRODUCTS:
        assert p["product_key"] in prompt


def test_prompt_does_not_carry_a_whole_description_when_long():
    # A single record can be kilobytes of prose that teaches the model nothing.
    long_one = dict(PRODUCTS[0], description="x" * 5000)
    assert len(build_prompt([long_one])) < 3000


def test_validate_keeps_known_fields():
    out = validate_one({
        "color": "white", "material": "cotton", "style": "formal",
        "gender": "male", "size_system": "alpha", "use_case": "office",
        "is_accessory": False, "price_tier": "mid",
        "key_features": ["button-down", "breathable"],
    })
    assert out["color"] == "white"
    assert out["is_accessory"] is False
    assert out["key_features"] == ["button-down", "breathable"]


def test_validate_discards_unknown_fields():
    out = validate_one({"color": "white", "invented_field": "nonsense"})
    assert "invented_field" not in out


@pytest.mark.parametrize("field,bad", [
    ("gender", "attack helicopter"),
    ("size_system", "furlongs"),
    ("price_tier", "luxury"),
])
def test_validate_discards_values_outside_a_closed_set(field, bad):
    # These reach columns a later phase filters on; one bad value would make a
    # filter silently miss products.
    assert field not in validate_one({field: bad})


@pytest.mark.parametrize("field,good", [
    ("gender", "female"), ("size_system", "uk"), ("price_tier", "premium"),
])
def test_validate_keeps_values_inside_a_closed_set(field, good):
    assert validate_one({field: good})[field] == good


def test_is_accessory_must_be_a_real_boolean():
    assert "is_accessory" not in validate_one({"is_accessory": "yes"})
    assert validate_one({"is_accessory": True})["is_accessory"] is True


def test_key_features_capped_at_five():
    out = validate_one({"key_features": ["a", "b", "c", "d", "e", "f", "g"]})
    assert len(out["key_features"]) == 5


def test_key_features_must_be_a_list_of_strings():
    assert "key_features" not in validate_one({"key_features": "not a list"})
    assert "key_features" not in validate_one({"key_features": [1, 2, 3]})


def test_extract_batch_keys_by_product_key(monkeypatch):
    import app.services.enrichment.extractor as mod
    monkeypatch.setattr(mod, "_call_llm", lambda prompt: {
        "products": [
            {"product_key": "shopify:1", "color": "white"},
            {"product_key": "http_api:2", "color": "brown", "is_accessory": True},
        ]})

    out = extract_batch(PRODUCTS)
    assert set(out) == {"shopify:1", "http_api:2"}
    assert out["http_api:2"]["is_accessory"] is True


def test_extract_batch_ignores_a_key_it_did_not_ask_about(monkeypatch):
    # A model that invents a product must not create a row for it.
    import app.services.enrichment.extractor as mod
    monkeypatch.setattr(mod, "_call_llm", lambda prompt: {
        "products": [{"product_key": "invented:99", "color": "red"}]})
    assert extract_batch(PRODUCTS) == {}


def test_extract_batch_returns_empty_on_failure(monkeypatch):
    # One bad batch must not abort a whole catalog.
    import app.services.enrichment.extractor as mod

    def boom(prompt):
        raise RuntimeError("model unavailable")

    monkeypatch.setattr(mod, "_call_llm", boom)
    assert extract_batch(PRODUCTS) == {}


def test_extract_batch_survives_a_non_dict_response(monkeypatch):
    import app.services.enrichment.extractor as mod
    monkeypatch.setattr(mod, "_call_llm", lambda prompt: "not a dict")
    assert extract_batch(PRODUCTS) == {}
