from __future__ import annotations

import pytest

from argus.service.serving_spec import ServingSpec, parse_serving_spec, validate_model_id_suffix


@pytest.mark.parametrize(
    "suffix",
    [
        "a",
        "qwen-0.5b",
        "Qwen2.5-0.5B",
        "a_b",
        "a.b-c",
        "a" * 64,
    ],
)
def test_validate_model_id_suffix_accepts(suffix: str):
    validate_model_id_suffix(suffix)


@pytest.mark.parametrize(
    "suffix",
    [
        "",
        " a",
        "a ",
        "-bad",
        ".bad",
        "bad/",
        "bad..",
        "bad\n",
        "bad\t",
        "a" * 65,
    ],
)
def test_validate_model_id_suffix_rejects(suffix: str):
    with pytest.raises(ValueError):
        validate_model_id_suffix(suffix)


def test_parse_serving_spec_smoke_defaults():
    spec = parse_serving_spec(
        {
            "model_id": "qwen-0.5b",
            "model_source": "/private/hf/x",
        }
    )
    assert isinstance(spec, ServingSpec)
    assert spec.num_replicas == 1
    assert spec.gpus_per_replica == 1
    assert spec.engine_kwargs is None


def test_parse_serving_spec_rejects_missing_fields():
    with pytest.raises(ValueError, match="missing required field: model_id"):
        parse_serving_spec({"model_source": "/private/hf/x"})
    with pytest.raises(ValueError, match="missing required field: model_source"):
        parse_serving_spec({"model_id": "x"})


def test_parse_serving_spec_rejects_bad_types():
    with pytest.raises(ValueError, match="serving spec must be a mapping"):
        parse_serving_spec(["nope"])
    with pytest.raises(ValueError, match="num_replicas"):
        parse_serving_spec({"model_id": "x", "model_source": "/private/hf/x", "num_replicas": 0})
    with pytest.raises(ValueError, match="gpus_per_replica"):
        parse_serving_spec({"model_id": "x", "model_source": "/private/hf/x", "gpus_per_replica": 0})
    with pytest.raises(ValueError, match="engine_kwargs"):
        parse_serving_spec({"model_id": "x", "model_source": "/private/hf/x", "engine_kwargs": "nope"})

