# #region Test.Schemas.Agent [C:2] [TYPE Module] [SEMANTICS test,schema,agent]
# @BRIEF Tests for schemas/agent.py — ConversationItem, ConversationListResponse, ToolCall,
#        AttachmentMeta, MessageItem, HistoryResponse, DeleteResponse, ServiceTokenRequest,
#        ServiceTokenResponse, SaveConversationRequest.
# @RELATION BINDS_TO -> [Schemas.Agent]
# @TEST_EDGE: missing_field -> optional fields default correctly
# @TEST_EDGE: invalid_type -> Pydantic validation rejects bad types
# @TEST_EDGE: serialization -> round-trip via model_dump/model_validate

from datetime import datetime, timezone
import pytest
from pydantic import ValidationError

from src.schemas.agent import (
    AttachmentMeta,
    ConversationItem,
    ConversationListResponse,
    DeleteResponse,
    HistoryResponse,
    MessageItem,
    SaveConversationRequest,
    ServiceTokenRequest,
    ServiceTokenResponse,
    ToolCall,
)


class TestConversationItem:
    """ConversationItem — required fields and defaults."""

    def test_minimal(self):
        ts = datetime.now(timezone.utc)
        item = ConversationItem(id="c1", title="Chat", updated_at=ts, message_count=5)
        assert item.id == "c1"
        assert item.title == "Chat"
        assert item.message_count == 5

    def test_serialize_roundtrip(self):
        ts = datetime.now(timezone.utc)
        item = ConversationItem(id="c1", title="Chat", updated_at=ts, message_count=5)
        data = item.model_dump()
        restored = ConversationItem.model_validate(data)
        assert restored.id == item.id
        assert restored.title == item.title

    def test_missing_required_raises(self):
        with pytest.raises(ValidationError):
            ConversationItem()


class TestConversationListResponse:
    """ConversationListResponse — items list and optional counters."""

    def test_defaults(self):
        resp = ConversationListResponse(items=[])
        assert resp.items == []
        assert resp.has_next is False
        assert resp.active_total == 0
        assert resp.archived_total == 0

    def test_with_items(self):
        ts = datetime.now(timezone.utc)
        items = [ConversationItem(id="c1", title="A", updated_at=ts, message_count=1)]
        resp = ConversationListResponse(items=items, has_next=True, active_total=10, archived_total=2)
        assert len(resp.items) == 1
        assert resp.has_next is True
        assert resp.active_total == 10

    def test_serialize_roundtrip(self):
        ts = datetime.now(timezone.utc)
        items = [ConversationItem(id="c1", title="A", updated_at=ts, message_count=1)]
        resp = ConversationListResponse(items=items, has_next=True)
        data = resp.model_dump()
        restored = ConversationListResponse.model_validate(data)
        assert restored.has_next is True
        assert len(restored.items) == 1


class TestToolCall:
    """ToolCall — tool name, input/output/error, status defaults."""

    def test_defaults(self):
        tc = ToolCall(tool="test_tool")
        assert tc.tool == "test_tool"
        assert tc.input == {}
        assert tc.output is None
        assert tc.error is None
        assert tc.status == "executing"

    def test_with_all_fields(self):
        tc = ToolCall(
            tool="query", input={"q": "1"}, output={"result": "ok"},
            error=None, status="completed",
        )
        assert tc.tool == "query"
        assert tc.output == {"result": "ok"}

    def test_serialize_roundtrip(self):
        tc = ToolCall(tool="calc", input={"expr": "2+2"}, status="completed")
        data = tc.model_dump()
        restored = ToolCall.model_validate(data)
        assert restored.tool == "calc"
        assert restored.input == {"expr": "2+2"}


class TestAttachmentMeta:
    """AttachmentMeta — name, type, size, optional preview_url."""

    def test_minimal(self):
        am = AttachmentMeta(name="file.pdf", type="pdf", size=1024)
        assert am.name == "file.pdf"
        assert am.type == "pdf"
        assert am.size == 1024
        assert am.preview_url is None

    def test_with_preview(self):
        am = AttachmentMeta(name="img.png", type="png", size=2048, preview_url="/preview/img.png")
        assert am.preview_url == "/preview/img.png"

    def test_serialize_roundtrip(self):
        am = AttachmentMeta(name="data.csv", type="csv", size=512)
        data = am.model_dump()
        restored = AttachmentMeta.model_validate(data)
        assert restored.name == "data.csv"


class TestMessageItem:
    """MessageItem — all fields including nested ToolCall and AttachmentMeta."""

    def test_minimal(self):
        ts = datetime.now(timezone.utc)
        msg = MessageItem(id="m1", conversation_id="c1", role="user", created_at=ts)
        assert msg.text is None
        assert msg.state is None
        assert msg.tool_calls is None
        assert msg.attachments is None

    def test_with_nested_objects(self):
        ts = datetime.now(timezone.utc)
        tc = ToolCall(tool="search", status="completed")
        am = AttachmentMeta(name="doc.txt", type="txt", size=100)
        msg = MessageItem(
            id="m1", conversation_id="c1", role="assistant",
            text="Here are results", state="done",
            tool_calls=[tc], attachments=[am], created_at=ts,
        )
        assert len(msg.tool_calls) == 1
        assert len(msg.attachments) == 1
        assert msg.tool_calls[0].tool == "search"

    def test_serialize_roundtrip(self):
        ts = datetime.now(timezone.utc)
        msg = MessageItem(
            id="m1", conversation_id="c1", role="user",
            text="hello", created_at=ts,
        )
        data = msg.model_dump()
        restored = MessageItem.model_validate(data)
        assert restored.text == "hello"


class TestHistoryResponse:
    """HistoryResponse — items list and pagination fields."""

    def test_defaults(self):
        resp = HistoryResponse(items=[])
        assert resp.items == []
        assert resp.has_next is False
        assert resp.conversation_id is None

    def test_with_conversation_id(self):
        resp = HistoryResponse(items=[], conversation_id="c1")
        assert resp.conversation_id == "c1"

    def test_serialize_roundtrip(self):
        ts = datetime.now(timezone.utc)
        items = [MessageItem(id="m1", conversation_id="c1", role="user", created_at=ts)]
        resp = HistoryResponse(items=items, has_next=True)
        data = resp.model_dump()
        restored = HistoryResponse.model_validate(data)
        assert restored.has_next is True


class TestDeleteResponse:
    """DeleteResponse — just a deleted flag defaulting True."""

    def test_default(self):
        resp = DeleteResponse()
        assert resp.deleted is True

    def test_false(self):
        resp = DeleteResponse(deleted=False)
        assert resp.deleted is False

    def test_serialize_roundtrip(self):
        resp = DeleteResponse()
        data = resp.model_dump()
        restored = DeleteResponse.model_validate(data)
        assert restored.deleted is True


class TestServiceTokenRequest:
    """ServiceTokenRequest — just service_secret."""

    def test_required(self):
        req = ServiceTokenRequest(service_secret="s3cr3t")
        assert req.service_secret == "s3cr3t"

    def test_missing_raises(self):
        with pytest.raises(ValidationError):
            ServiceTokenRequest()

    def test_serialize_roundtrip(self):
        req = ServiceTokenRequest(service_secret="key")
        data = req.model_dump()
        restored = ServiceTokenRequest.model_validate(data)
        assert restored.service_secret == "key"


class TestServiceTokenResponse:
    """ServiceTokenResponse — token response with defaults."""

    def test_defaults(self):
        resp = ServiceTokenResponse(access_token="jwt")
        assert resp.access_token == "jwt"
        assert resp.token_type == "bearer"
        assert resp.expires_in == 86400
        assert resp.role == "agent"

    def test_custom_values(self):
        resp = ServiceTokenResponse(access_token="tok", token_type="Bearer", expires_in=3600, role="admin")
        assert resp.expires_in == 3600
        assert resp.role == "admin"

    def test_serialize_roundtrip(self):
        resp = ServiceTokenResponse(access_token="jwt-token")
        data = resp.model_dump()
        restored = ServiceTokenResponse.model_validate(data)
        assert restored.access_token == "jwt-token"


class TestSaveConversationRequest:
    """SaveConversationRequest — conversation save payload."""

    def test_defaults(self):
        req = SaveConversationRequest(conversation_id="c1")
        assert req.title == ""
        assert req.user_id == "admin"
        assert req.messages == []

    def test_with_messages(self):
        req = SaveConversationRequest(conversation_id="c1", title="Chat", user_id="u1", messages=[{"role": "user", "text": "hi"}])
        assert req.title == "Chat"
        assert len(req.messages) == 1

    def test_missing_required_raises(self):
        with pytest.raises(ValidationError):
            SaveConversationRequest()

    def test_serialize_roundtrip(self):
        req = SaveConversationRequest(conversation_id="c1", title="Test", messages=[{"role": "user", "text": "hello"}])
        data = req.model_dump()
        restored = SaveConversationRequest.model_validate(data)
        assert restored.title == "Test"
        assert restored.messages == [{"role": "user", "text": "hello"}]
# #endregion Test.Schemas.Agent
