import pytest

from app.services.enrichment.job import is_extractable


def test_a_product_with_a_description_is_extractable():
    assert is_extractable({"name": "Oxford Shirt",
                           "description": "A white cotton shirt."}) is True


def test_a_long_title_alone_is_extractable():
    assert is_extractable({"name": "Brown Full Grain Leather Belt",
                           "description": None}) is True


def test_a_short_title_with_no_description_is_not():
    # No prompt fixes an absent input. Flag it rather than pay for a call that
    # can only guess.
    assert is_extractable({"name": "Belt", "description": None}) is False


def test_no_name_at_all_is_not_extractable():
    assert is_extractable({"name": None, "description": None}) is False


def test_batches_are_capped(monkeypatch):
    import app.services.enrichment.job as mod
    from app.services.enrichment.extractor import BATCH_SIZE

    seen = []
    monkeypatch.setattr(mod, "extract_batch",
                        lambda batch: seen.append(len(batch)) or {})
    monkeypatch.setattr(mod, "select_products", lambda t, f: [
        {"product_key": f"k{i}", "name": f"Product Number {i}",
         "description": "A description.", "attributes": [], "price_cents": 100,
         "taxonomy_path": [], "brand": None}
        for i in range(BATCH_SIZE + 5)])
    monkeypatch.setattr(mod, "compute_bands", lambda t: (100, 200))
    monkeypatch.setattr(mod, "persist_attributes", lambda *a, **kw: None)
    monkeypatch.setattr(mod, "migrate_products_table", lambda t: None)
    monkeypatch.setattr(mod, "flag_unextractable", lambda *a, **kw: None)
    monkeypatch.setattr(mod, "catalog_stats", lambda t: (BATCH_SIZE + 5, 0))

    mod.enrich_tenant("org_x")
    assert max(seen) <= BATCH_SIZE
    assert sum(seen) == BATCH_SIZE + 5


def test_a_failed_batch_does_not_abort_the_run(monkeypatch):
    import app.services.enrichment.job as mod
    from app.services.enrichment.extractor import BATCH_SIZE

    calls = {"n": 0}

    def half_fail(batch):
        calls["n"] += 1
        if calls["n"] == 1:
            return {}          # extract_batch already degrades to empty
        return {p["product_key"]: {"color": "white"} for p in batch}

    monkeypatch.setattr(mod, "extract_batch", half_fail)
    monkeypatch.setattr(mod, "select_products", lambda t, f: [
        {"product_key": f"k{i}", "name": f"Product Number {i}",
         "description": "A description.", "attributes": [], "price_cents": 100,
         "taxonomy_path": [], "brand": None}
        for i in range(BATCH_SIZE * 2)])
    monkeypatch.setattr(mod, "compute_bands", lambda t: (100, 200))
    monkeypatch.setattr(mod, "persist_attributes", lambda *a, **kw: None)
    monkeypatch.setattr(mod, "migrate_products_table", lambda t: None)
    monkeypatch.setattr(mod, "flag_unextractable", lambda *a, **kw: None)
    monkeypatch.setattr(mod, "catalog_stats", lambda t: (BATCH_SIZE * 2, 0))

    report = mod.enrich_tenant("org_x")
    assert report["extracted"] == BATCH_SIZE
    assert report["failed"] == BATCH_SIZE


def test_report_shape(monkeypatch):
    import app.services.enrichment.job as mod
    monkeypatch.setattr(mod, "select_products", lambda t, f: [])
    monkeypatch.setattr(mod, "compute_bands", lambda t: None)
    monkeypatch.setattr(mod, "migrate_products_table", lambda t: None)
    monkeypatch.setattr(mod, "catalog_stats", lambda t: (0, 0))

    report = mod.enrich_tenant("org_x")
    for key in ("products", "considered", "extracted", "skipped_unchanged",
                "skipped_unextractable", "failed", "conflicts",
                "attribute_coverage"):
        assert key in report


def test_an_empty_catalog_reports_zero_coverage_not_a_crash(monkeypatch):
    import app.services.enrichment.job as mod
    monkeypatch.setattr(mod, "select_products", lambda t, f: [])
    monkeypatch.setattr(mod, "compute_bands", lambda t: None)
    monkeypatch.setattr(mod, "migrate_products_table", lambda t: None)
    monkeypatch.setattr(mod, "catalog_stats", lambda t: (0, 0))

    assert mod.enrich_tenant("org_x")["attribute_coverage"] == 0.0
