"""Tests for the metadata block added to /api/results/<id>/export.

Pins the contract introduced by the migration-0014 PR:
- Response includes a top-level ``metadata`` dict with ``ldr_version``,
  ``started_at`` (ISO-8601), and ``settings_snapshot``.
- Pre-0014 rows return null for the new fields without crashing the JS.
- ``success: True`` envelope is preserved (5 JS callsites depend on it —
  see benchmark_results.html lines 95, 348, 556, 661, 722).
- ``started_at`` falls back to ``created_at`` when ``start_time`` is null
  (e.g. a run that was created but never reached IN_PROGRESS).
- ``settings_snapshot`` round-trips as a JSON object — secrets are
  expected to already be redacted at insert time, but this endpoint
  doesn't re-redact, so we assert the round-trip preserves the dict.
"""
# allow: no-sut-import — exercises the SUT indirectly through helpers
# (_make_app, _make_export_query_router, _patch_auth_and_db, …) imported
# from tests.benchmarks.web_api.test_benchmark_routes_coverage, which in
# turn imports from local_deep_research.

from datetime import datetime, UTC
from unittest.mock import MagicMock

# Import helpers from the existing coverage suite.
from tests.benchmarks.web_api.test_benchmark_routes_coverage import (
    _FakeDatasetType,
    _make_app,
    _make_export_query_router,
    _make_run_mock,
    _patch_auth_and_db,
)


def _result_row():
    r = MagicMock()
    r.example_id = "ex0"
    r.dataset_type = _FakeDatasetType.SIMPLEQA
    r.question = "Q?"
    r.correct_answer = "A"
    r.extracted_answer = "A"
    r.is_correct = True
    r.confidence = 0.9
    r.processing_time = 5.0
    r.completed_at = datetime(2025, 1, 1, tzinfo=UTC)
    return r


def test_metadata_includes_ldr_version():
    app = _make_app()
    with _patch_auth_and_db() as (_svc, _mgr, mock_db):
        mock_db.query.side_effect = _make_export_query_router(
            run=_make_run_mock(ldr_version="1.6.10"),
            results=[_result_row()],
        )
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["username"] = "testuser"
            resp = client.get("/benchmark/api/results/1/export")
        assert resp.status_code == 200
        body = resp.get_json()
        assert body["metadata"]["ldr_version"] == "1.6.10"


def test_metadata_started_at_uses_start_time_when_set():
    app = _make_app()
    start_dt = datetime(2026, 5, 1, 12, 30, 0, tzinfo=UTC)
    with _patch_auth_and_db() as (_svc, _mgr, mock_db):
        mock_db.query.side_effect = _make_export_query_router(
            run=_make_run_mock(start_time=start_dt),
            results=[_result_row()],
        )
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["username"] = "testuser"
            resp = client.get("/benchmark/api/results/1/export")
        body = resp.get_json()
        assert body["metadata"]["started_at"] == start_dt.isoformat()


def test_metadata_started_at_falls_back_to_created_at():
    """If a run was created but never started, started_at falls back to
    created_at so the YAML still has a date_tested anchor."""
    app = _make_app()
    run = _make_run_mock()  # start_time defaults to None
    with _patch_auth_and_db() as (_svc, _mgr, mock_db):
        mock_db.query.side_effect = _make_export_query_router(
            run=run, results=[_result_row()]
        )
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["username"] = "testuser"
            resp = client.get("/benchmark/api/results/1/export")
        body = resp.get_json()
        assert body["metadata"]["started_at"] == run.created_at.isoformat()


def test_metadata_settings_snapshot_round_trip():
    """The snapshot is returned only when opted in via ?include_settings=1."""
    app = _make_app()
    snapshot = {
        "llm.model": {"value": "qwen3.6:27b", "ui_element": "select"},
        "search.fetch.mode": {
            "value": "summary_focus_query",
            "ui_element": "select",
        },
    }
    with _patch_auth_and_db() as (_svc, _mgr, mock_db):
        mock_db.query.side_effect = _make_export_query_router(
            run=_make_run_mock(settings_snapshot=snapshot),
            results=[_result_row()],
        )
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["username"] = "testuser"
            resp = client.get(
                "/benchmark/api/results/1/export?include_settings=1"
            )
        body = resp.get_json()
        assert body["metadata"]["settings_snapshot"] == snapshot


def test_metadata_settings_snapshot_omitted_by_default():
    """Without the opt-in flag, the snapshot is NOT transferred — the
    default summary download stays lean and doesn't ship the full config."""
    app = _make_app()
    snapshot = {"llm.model": {"value": "qwen3.6:27b", "ui_element": "select"}}
    with _patch_auth_and_db() as (_svc, _mgr, mock_db):
        mock_db.query.side_effect = _make_export_query_router(
            run=_make_run_mock(settings_snapshot=snapshot),
            results=[_result_row()],
        )
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["username"] = "testuser"
            resp = client.get("/benchmark/api/results/1/export")
        body = resp.get_json()
        assert body["metadata"]["settings_snapshot"] is None


def test_pre_0014_row_returns_null_metadata_fields():
    """A row with all new columns NULL must serialize as JSON null, not
    crash, and not omit the metadata block."""
    app = _make_app()
    run = _make_run_mock()  # all provenance fields default to None
    run.created_at = None  # full pre-0014 — even created_at unset
    with _patch_auth_and_db() as (_svc, _mgr, mock_db):
        mock_db.query.side_effect = _make_export_query_router(
            run=run, results=[]
        )
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["username"] = "testuser"
            resp = client.get("/benchmark/api/results/1/export")
        body = resp.get_json()
        assert body["metadata"] == {
            "ldr_version": None,
            "started_at": None,
            "settings_snapshot": None,
        }


def test_run_not_found_returns_null_metadata():
    """Missing run still returns a metadata block (all None) so the JS
    code path doesn't need a separate not-found branch."""
    app = _make_app()
    with _patch_auth_and_db() as (_svc, _mgr, mock_db):
        mock_db.query.side_effect = _make_export_query_router(
            run=None, results=[]
        )
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["username"] = "testuser"
            resp = client.get("/benchmark/api/results/1/export")
        assert resp.status_code == 200
        body = resp.get_json()
        assert body["success"] is True
        assert body["metadata"]["ldr_version"] is None
        assert body["metadata"]["settings_snapshot"] is None
        assert body["results"] == []


def test_success_field_preserved_for_back_compat():
    """5 JS callsites in benchmark_results.html check `data.success` —
    removing it would silently break the UI."""
    app = _make_app()
    with _patch_auth_and_db() as (_svc, _mgr, mock_db):
        mock_db.query.side_effect = _make_export_query_router(
            run=_make_run_mock(ldr_version="1.6.10"),
            results=[_result_row()],
        )
        with app.test_client() as client:
            with client.session_transaction() as sess:
                sess["username"] = "testuser"
            resp = client.get("/benchmark/api/results/1/export")
        body = resp.get_json()
        assert body["success"] is True
