from __future__ import annotations

from datetime import datetime
from decimal import Decimal
from types import MethodType
from uuid import UUID, uuid4

import pytest

from core.auth.models import CurrentUser
from models.agent_chat_message import AgentChatMessage, AgentChatMessageRole
from models.agent_chat_session import AgentChatSession, AgentChatSessionStatus
from v1.agent.schemas import AgentChatRunRequest
from v1.agent.service import AgentChatService


class _FakeAsyncSession:
    def __init__(self) -> None:
        self.added: list[object] = []
        self.committed = False
        self.rolled_back = False

    def add(self, obj: object) -> None:
        self.added.append(obj)

    async def flush(self) -> None:
        return None

    async def commit(self) -> None:
        self.committed = True

    async def rollback(self) -> None:
        self.rolled_back = True

    async def refresh(self, obj: object) -> None:
        if isinstance(obj, AgentChatSession) and obj.id is None:
            obj.id = uuid4()
        if isinstance(obj, AgentChatMessage) and obj.id is None:
            obj.id = uuid4()


@pytest.mark.asyncio
async def test_run_persists_messages_and_emits_ordered_events() -> None:
    fake_db = _FakeAsyncSession()
    service = AgentChatService(
        session=fake_db,  # type: ignore[arg-type]
        current_user=CurrentUser(id=UUID("00000000-0000-0000-0000-000000000001")),
    )

    async def _resolve_session(
        self: AgentChatService,
        *,
        session_id: object | None,
        user_id: UUID,
        first_message: str,
        now: datetime,
    ) -> AgentChatSession:
        assert session_id is None
        assert first_message == "hello"
        return AgentChatSession(
            id=UUID("00000000-0000-0000-0000-000000000111"),
            user_id=user_id,
            title="hello",
            status=AgentChatSessionStatus.RUNNING,
            last_activity_at=now,
            message_count=0,
            total_tokens=0,
            total_cost=Decimal("0"),
            created_at=now,
            updated_at=now,
            deleted_at=None,
        )

    async def _next_seq_base(self: AgentChatService, session_id: object) -> int:
        assert session_id == UUID("00000000-0000-0000-0000-000000000111")
        return 2

    service._resolve_session = MethodType(_resolve_session, service)  # type: ignore[method-assign]
    service._next_seq_base = MethodType(_next_seq_base, service)  # type: ignore[method-assign]

    response = await service.run(AgentChatRunRequest(message="hello"))

    assert fake_db.committed is True
    inserted_messages = [
        item for item in fake_db.added if isinstance(item, AgentChatMessage)
    ]
    assert len(inserted_messages) == 2
    assert [msg.seq for msg in inserted_messages] == [3, 4]
    assert [msg.role for msg in inserted_messages] == [
        AgentChatMessageRole.USER,
        AgentChatMessageRole.ASSISTANT,
    ]
    assert [event.type for event in response.events] == [
        "run.started",
        "message.delta",
        "run.completed",
    ]
