"""Tests for the Mistral runtime adapter.

Fake mistralai-shaped client (no SDK dependency). Mistral wraps
``client.chat.complete`` and shares OpenAI's response shape.
"""
from __future__ import annotations

import pytest

from abom import sign
from abom.adapters import instrument
from abom.adapters.mistral import MistralAdapter
from abom.audit import verify_chain
from abom.bom import build_composition
from abom.gate import ActionDenied


class _Usage:
    def __init__(self, total):
        self.total_tokens = total


class _Msg:
    def __init__(self, content):
        self.content = content


class _Choice:
    def __init__(self, content):
        self.message = _Msg(content)


class _Response:
    def __init__(self, model, text, total=18):
        self.model = model
        self.choices = [_Choice(text)]
        self.usage = _Usage(total)


class _Chat:
    def complete(self, *, model, messages, **kw):
        return _Response(model, f"reply from {model}")


class FakeMistral:
    __module__ = "mistralai"  # so the adapter auto-detects it

    def __init__(self):
        self.chat = _Chat()


MODEL = "mistral-large-latest"


def _manifest(model=MODEL):
    key = sign.load_or_create_key()
    return build_composition(
        agent={"name": "test-agent", "version": "1.0.0"},
        components=[{"type": "model", "name": model}],
        controls={},
        key=key,
    )


def test_adapter_autodetects_mistral():
    assert MistralAdapter().matches(FakeMistral())


def test_records_model_call_into_valid_chain():
    client = instrument(FakeMistral(), manifest=_manifest())
    resp = client.chat.complete(model=MODEL, messages=[{"role": "user", "content": "hi"}])

    assert "reply from" in resp.choices[0].message.content
    chain = client.abom.chain
    assert len(chain) == 1
    mc = chain[0]["data"]["model_calls"][0]
    assert mc["model"] == MODEL
    assert mc["tokens"] == 18
    assert mc["output_sha256"] is not None
    assert verify_chain(chain)["valid"]


def test_gate_allows_declared_and_denies_undeclared():
    ok = instrument(FakeMistral(), manifest=_manifest(), gate=True)
    ok.chat.complete(model=MODEL, messages=[])
    assert len(ok.abom.chain) == 1

    bad = instrument(FakeMistral(), manifest=_manifest(model="other"), gate=True)
    with pytest.raises(ActionDenied):
        bad.chat.complete(model=MODEL, messages=[])
    assert bad.abom.chain == []


def test_multiple_calls_extend_the_chain():
    client = instrument(FakeMistral(), manifest=_manifest())
    for _ in range(3):
        client.chat.complete(model=MODEL, messages=[])
    assert [r["seq"] for r in client.abom.chain] == [0, 1, 2]
    assert verify_chain(client.abom.chain)["valid"]
