"""
Testes do motor principal
"""
import pytest
import asyncio
from unittest.mock import Mock, AsyncMock

from core.brain import Brain, ProcessingResult
from core.memory import ConversationMemory
from core.context import ContextManager
from services.llm_service import LLMService
from config.settings import LLMConfig, MemoryConfig


@pytest.fixture
def mock_llm():
    llm = Mock(spec=LLMService)
    llm.generate = AsyncMock(return_value=("Resposta de teste", 10))
    llm.generate_stream = AsyncMock(return_value=async_iter(["Resposta ", "de ", "teste"]))
    return llm


@pytest.fixture
def brain(mock_llm):
    b = Brain()
    b.llm = mock_llm
    return b


def async_iter(items):
    """Helper para criar async iterator"""
    async def _iter():
        for item in items:
            yield item
    return _iter()


class TestBrain:
    """Testes do Brain"""

    @pytest.mark.asyncio
    async def test_process_basic_message(self, brain, mock_llm):
        """Testa processamento básico"""
        mock_llm.generate = AsyncMock(return_value=("Olá! Como posso ajudar?", 15))

        result = await brain.process(
            message="Oi",
            user_id="user_123",
            session_id="session_1"
        )

        assert isinstance(result, ProcessingResult)
        assert result.response == "Olá! Como posso ajudar?"
        assert result.tokens_used == 15
        assert result.processing_time > 0

    @pytest.mark.asyncio
    async def test_process_with_tools(self, brain, mock_llm):
        """Testa processamento com ferramentas"""
        # Registrar ferramenta mock
        async def mock_tool(**kwargs):
            return "resultado da ferramenta"

        brain.register_tool("test_tool", mock_tool, "Ferramenta de teste")

        mock_llm.generate = AsyncMock(side_effect=[
            ("acao_sistema", 5),  # classificação
            ("TOOL: test_tool\nPARAMS: {}", 5),  # decisão de tool
            ("Resposta usando ferramenta", 10)  # resposta final
        ])

        result = await brain.process(
            message="Use a ferramenta",
            user_id="user_123"
        )

        assert "test_tool" in result.used_tools

    @pytest.mark.asyncio
    async def test_stream_process(self, brain):
        """Testa streaming"""
        chunks = []
        async for chunk in brain.stream_process(
            message="Teste",
            user_id="user_123"
        ):
            chunks.append(chunk)

        assert len(chunks) > 0

    @pytest.mark.asyncio
    async def test_classify_intent(self, brain, mock_llm):
        """Testa classificação de intenção"""
        mock_llm.generate = AsyncMock(return_value("pergunta_geral", 3))

        intent = await brain._classify_intent("Qual é a capital do Brasil?")
        assert intent == "pergunta_geral"

    def test_build_prompt(self, brain):
        """Testa construção do prompt"""
        prompt = brain._build_prompt(
            message="Olá",
            history=[],
            context={},
            relevant_info=["Info 1"],
            tool_results=None,
            system_context=None
        )

        assert "Olá" in prompt
        assert "Info 1" in prompt
        assert "Assistente:" in prompt


class TestIntegration:
    """Testes de integração"""

    @pytest.mark.asyncio
    async def test_full_conversation_flow(self, brain, mock_llm):
        """Testa fluxo completo de conversa"""
        responses = [
            "Olá! Como posso ajudar?",
            "Posso ajudar com isso.",
            "Mais alguma coisa?"
        ]

        mock_llm.generate = AsyncMock(side_effect=[
            (resp, 10) for resp in responses
        ])

        for i, msg in enumerate(["Oi", "Preciso de ajuda", "Obrigado"]):
            result = await brain.process(
                message=msg,
                user_id="user_test",
                session_id="session_test"
            )
            assert result.response == responses[i]

        # Verificar histórico
        history = await brain.memory.get_history("session_test")
        assert len(history) == 6  # 3 user + 3 assistant