"""Regression: _save_job must tolerate nested NaN values in list-valued columns.

Ported from crystallise-master commit 3430709. A 9.2h prod run on
crystallise-master silently lost its results when score_list = [0.5, nan, 0.7]
crashed json.dumps in the persistence boundary. netready-mvp shares the
same _save_job structure and would hit the same bug without this scrub.
"""
from __future__ import annotations

import json
from datetime import datetime, timezone

import pytest


@pytest.fixture(autouse=True)
def _reset_screening_table_flag():
    from api.routers import screening as scr
    scr._table_created = False
    scr._db_available = True
    scr._active_jobs.clear()
    yield


def test_save_job_accepts_results_with_nested_nan_in_score_list():
    """The pre-fix bug: a NaN inside a list-valued column escaped json.dumps
    and dropped the whole save. The fix scrubs NaN at the persistence boundary."""
    from api.routers import screening as scr

    job_id = "nested-nan-job"
    now = datetime.now(timezone.utc).isoformat()
    job = {
        "id": job_id,
        "project_id": 1,
        "status": "completed",
        "progress": 1.0,
        "stage": "Complete",
        "config": {"model": "gpt-5-nano", "papers_count": 2, "repetitions": 3},
        "results": [
            {
                "id": "p1",
                "mean_score": 0.72,
                "score_list": [0.7, 0.8, 0.66],
                "ai_decision": "include",
            },
            {
                "id": "p2",
                "mean_score": float("nan"),
                "score_list": [float("nan"), 0.5, float("nan")],
                "ai_decision": "uncertain",
            },
        ],
        "clusters": [],
        "stage_timings": {},
        "duration_ms": 1234,
        "estimated_cost_usd": 0.0001,
        "error": None,
        "error_category": None,
        "error_retryable": None,
        "model_version": "gpt-5-nano",
        "created_at": now,
        "completed_at": now,
    }

    # Pre-fix: raises ValueError("Out of range float values are not JSON compliant: nan")
    scr._save_job(job)

    reloaded = scr._load_job(job_id)
    assert reloaded is not None
    assert reloaded["status"] == "completed"
    results = reloaded["results"]
    assert len(results) == 2
    nan_row = next(r for r in results if r["id"] == "p2")
    # NaNs must serialize as null (Python None).
    assert nan_row["mean_score"] is None
    assert nan_row["score_list"] == [None, 0.5, None]
    # Strict-mode JSON round-trip — any leftover NaN would raise here.
    json.dumps(reloaded, allow_nan=False)
