from __future__ import annotations

import json
from pathlib import Path


def test_db_serving_model_crud_and_events(tmp_path: Path) -> None:
    from argus.service.db import Db

    db = Db(str(tmp_path / "mvp.sqlite3"))
    db.init()

    m1 = db.create_serve_model(
        model_key="svc-001",
        user_id="alice",
        model_id_suffix="qwen-0.5b",
        model_id_prefix="alice-202601061235",
        model_id="alice-202601061235-qwen-0.5b",
        model_source="/private/hf/hub/models--Qwen--Qwen2.5-0.5B-Instruct/snapshots/sha",
        num_replicas=1,
        gpus_per_replica=1,
        engine_kwargs_json=json.dumps({"max_model_len": 8192}),
        spec_yaml="model_id: qwen-0.5b\nmodel_source: $HOME/common/hf/...\n",
        resolved_spec_yaml="model_id: alice-202601061235-qwen-0.5b\nmodel_source: /private/hf/...\n",
    )
    assert m1["model_key"] == "svc-001"
    assert m1["state"] == "QUEUED"

    # Same suffix may be created again; model_key is the identity.
    m2 = db.create_serve_model(
        model_key="svc-002",
        user_id="alice",
        model_id_suffix="qwen-0.5b",
        model_id_prefix="alice-202601061236",
        model_id="alice-202601061236-qwen-0.5b",
        model_source="/private/hf/hub/models--Qwen--Qwen2.5-0.5B-Instruct/snapshots/sha",
        num_replicas=1,
        gpus_per_replica=2,
        engine_kwargs_json=None,
        spec_yaml="model_id: qwen-0.5b\nmodel_source: $HOME/common/hf/...\n",
        resolved_spec_yaml="model_id: alice-202601061236-qwen-0.5b\nmodel_source: /private/hf/...\n",
    )
    assert m2["model_key"] == "svc-002"
    assert m2["model_id"] != m1["model_id"]

    got = db.get_serve_model("svc-001")
    assert got is not None
    assert got["gpus_per_replica"] == 1

    items = db.list_serve_models(user_id="alice")
    assert {i["model_key"] for i in items} == {"svc-001", "svc-002"}

    # State transition writes a serve event.
    db.set_serve_model_state(model_key="svc-001", state="DEPLOYING")
    got2 = db.get_serve_model("svc-001")
    assert got2 is not None
    assert got2["state"] == "DEPLOYING"

    events = db.list_serve_events("svc-001", limit=50)
    assert len(events) >= 2
    assert {e["event_type"] for e in events}.issuperset({"SERVE_MODEL_CREATED", "SERVE_STATE_UPDATE"})

    # Reconciler pick: QUEUED/DELETING only.
    picked = db.pick_next_runnable_serve_change()
    assert picked is not None
    assert picked["state"] == "QUEUED"

    db.set_serve_model_state(model_key="svc-002", state="DELETING")
    picked2 = db.pick_next_runnable_serve_change()
    assert picked2 is not None
    assert picked2["state"] in ("QUEUED", "DELETING")

    # Deleted models are hidden unless include_deleted.
    db.set_serve_model_state(model_key="svc-002", state="DELETED")
    items2 = db.list_serve_models(user_id="alice", include_deleted=False)
    assert {i["model_key"] for i in items2} == {"svc-001"}
    items3 = db.list_serve_models(user_id="alice", include_deleted=True)
    assert {i["model_key"] for i in items3} == {"svc-001", "svc-002"}

