"""`compile_model`: a plan's field declarations become a Pydantic model, with
grounding and the not-present case appended no matter what was declared.

Assertions read `model_dump()` because the compiled model's fields are
dynamic — unknown to the type checker by design.
"""

import pytest
from pydantic import ValidationError

from quber.playground.answers.planned import FieldSpec, Plan, compile_model


def test_each_declared_field_type():
    model = compile_model(
        Plan(
            wants_value=True,
            fields=[
                FieldSpec(name="figure", type="string", description="as printed"),
                FieldSpec(name="rate", type="number", description="a number"),
                FieldSpec(name="labels", type="string_list", description="names"),
                FieldSpec(name="rates", type="number_list", description="numbers"),
            ],
        )
    )
    got = model(figure="$1", rate=1.5, labels=["a"], rates=[0.1, 0.2]).model_dump()
    assert got["figure"] == "$1" and got["rate"] == 1.5
    assert got["labels"] == ["a"] and got["rates"] == [0.1, 0.2]


def test_required_against_optional():
    model = compile_model(
        Plan(
            wants_value=True,
            fields=[
                FieldSpec(name="figure", type="string", description="d"),
                FieldSpec(name="period", type="string", description="d", required=False),
            ],
        )
    )
    assert model(figure="$1").model_dump()["period"] is None
    with pytest.raises(ValidationError):
        model()


def test_cited_ids_and_not_found_appended_to_every_model():
    got = compile_model(Plan(wants_value=True, fields=[]))().model_dump()
    assert got["cited_ids"] == []
    assert got["not_found"] is None


def test_declared_collision_loses_to_the_appended_field():
    model = compile_model(
        Plan(
            wants_value=True,
            fields=[
                FieldSpec(name="cited_ids", type="string", description="hijack"),
                FieldSpec(name="not_found", type="number", description="hijack"),
            ],
        )
    )
    got = model().model_dump()
    # The appended definitions won: list default and None default, not the
    # planner's required string/number.
    assert got["cited_ids"] == []
    assert got["not_found"] is None
