import json

OUTCOMES = ("ready_for_review", "hold", "invalid")

def signals(batch):
    if batch.get("parse_error"):
        return {"batch_outcome": "invalid", "rows_matched": 0,
                "rows_for_review": 0, "rows_observed": None}
    rows = batch.get("rows")
    if not isinstance(rows, list) or not rows:
        raise ValueError("An empty/missing batch is not a ready batch")
    counts = {"matched": 0, "review": 0}
    for row in rows:
        status = row.get("status")
        if status not in counts:
            raise ValueError("Unknown row status")
        counts[status] += 1
    outcome = "hold" if counts["review"] else "ready_for_review"
    return {"batch_outcome": outcome, "rows_matched": counts["matched"],
            "rows_for_review": counts["review"], "rows_observed": len(rows)}

fixtures = [
    {"rows": [{"status": "matched", "code": "0012"},
              {"status": "matched", "code": "SECRET-CUSTOMER-SKU"}]},
    {"rows": [{"status": "matched"}, {"status": "review"},
              {"status": "review"}]},
    {"parse_error": True},
]
results = [signals(fixture) for fixture in fixtures]
assert [r["batch_outcome"] for r in results] == list(OUTCOMES)
assert results[1]["rows_matched"] + results[1]["rows_for_review"] == 3
assert results[2]["rows_observed"] is None
assert "SECRET-CUSTOMER-SKU" not in json.dumps(results)
for invalid in ({"rows": []}, {"rows": [{"status": "new_state"}]}):
    try:
        signals(invalid)
    except ValueError:
        pass
    else:
        raise AssertionError("Invalid state silently classified")
print(json.dumps(results, indent=2))
