"""
Basic unit tests for Reno AI SDK
"""

import pytest
from unittest.mock import Mock, patch, MagicMock
import json

from renoai import Reno, RenoError, RenoConnectionError, RenoTimeoutError
from renoai.models import Completion, Choice, Usage, StreamChunk


class TestRenoClient:
    """Tests for Reno client initialization and basic functionality."""
    
    def test_client_initialization(self):
        """Test client can be initialized with API key."""
        client = Reno(api_key="reno_sk_test")
        assert client.base_url == "http://127.0.0.1:8000/api/v1"
        assert "Bearer reno_sk_test" in client.session.headers["Authorization"]
        client.close()
    
    def test_client_custom_base_url(self):
        """Test client accepts custom base URL."""
        client = Reno(
            api_key="reno_sk_test",
            base_url="https://custom.api.com/v1"
        )
        assert client.base_url == "https://custom.api.com/v1"
        client.close()
    
    def test_client_requires_api_key(self):
        """Test client raises error without API key."""
        with pytest.raises(ValueError):
            Reno(api_key="")
    
    def test_context_manager(self):
        """Test client works as context manager."""
        with Reno(api_key="reno_sk_test") as client:
            assert client is not None
        # Session should be closed after context


class TestExceptions:
    """Tests for exception classes."""
    
    def test_reno_error_basic(self):
        """Test RenoError initialization."""
        error = RenoError(code=1001, message="Test error")
        assert error.code == 1001
        assert error.message == "Test error"
        assert "[1001]" in str(error)
    
    def test_reno_error_with_details(self):
        """Test RenoError with details."""
        error = RenoError(
            code=2001,
            message="Test error",
            details="Additional info"
        )
        assert error.details == "Additional info"
    
    def test_user_friendly_error(self):
        """Test user-friendly error messages."""
        error = RenoError(code=1001, message="Invalid API key format")
        friendly = error.user_friendly()
        assert "Invalid API Key Format" in friendly
        assert "💡" in friendly
    
    def test_connection_error(self):
        """Test RenoConnectionError."""
        error = RenoConnectionError("Failed to connect")
        assert error.code == 6002
        assert "connect" in error.message.lower()
    
    def test_timeout_error(self):
        """Test RenoTimeoutError."""
        error = RenoTimeoutError("Request timed out")
        assert error.code == 6003
        assert "timeout" in error.message.lower()


class TestModels:
    """Tests for response model classes."""
    
    def test_usage_model(self):
        """Test Usage model."""
        data = {
            "prompt_tokens": 10,
            "completion_tokens": 20,
            "total_tokens": 30
        }
        usage = Usage(data)
        assert usage.prompt_tokens == 10
        assert usage.completion_tokens == 20
        assert usage.total_tokens == 30
    
    def test_choice_model(self):
        """Test Choice model."""
        data = {
            "index": 0,
            "message": {"role": "assistant", "content": "Hello!"},
            "finish_reason": "stop"
        }
        choice = Choice(data)
        assert choice.index == 0
        assert choice.content == "Hello!"
        assert choice.role == "assistant"
        assert choice.finish_reason == "stop"
    
    def test_completion_model(self):
        """Test Completion model."""
        data = {
            "id": "test-123",
            "created": 1234567890,
            "model": "gemma2:2b-instruct",
            "choices": [
                {
                    "index": 0,
                    "message": {"role": "assistant", "content": "Test response"},
                    "finish_reason": "stop"
                }
            ],
            "usage": {
                "prompt_tokens": 5,
                "completion_tokens": 10,
                "total_tokens": 15
            }
        }
        completion = Completion(data)
        assert completion.id == "test-123"
        assert completion.model == "gemma2:2b-instruct"
        assert completion.text == "Test response"
        assert completion.usage.total_tokens == 15
    
    def test_stream_chunk_model(self):
        """Test StreamChunk model."""
        data = {
            "id": "test-123",
            "choices": [
                {
                    "delta": {"content": "Hello"},
                    "finish_reason": None
                }
            ]
        }
        chunk = StreamChunk(data)
        assert chunk.delta == "Hello"
        assert chunk.finish_reason is None


class TestClientRequests:
    """Tests for client request methods."""
    
    @patch('requests.Session.post')
    def test_ask_method(self, mock_post):
        """Test ask method."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "id": "test",
            "model": "gemma2:2b-instruct",
            "choices": [
                {"message": {"content": "Answer"}, "finish_reason": "stop"}
            ],
            "usage": {"prompt_tokens": 5, "completion_tokens": 5, "total_tokens": 10}
        }
        mock_post.return_value = mock_response
        
        client = Reno(api_key="reno_sk_test")
        response = client.ask("Test question")
        
        assert response == "Answer"
        assert mock_post.called
        client.close()
    
    @patch('requests.Session.post')
    def test_chat_method(self, mock_post):
        """Test chat method."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "id": "test",
            "model": "gemma2:2b-instruct",
            "choices": [
                {"message": {"content": "Response"}, "finish_reason": "stop"}
            ],
            "usage": {"prompt_tokens": 10, "completion_tokens": 10, "total_tokens": 20}
        }
        mock_post.return_value = mock_response
        
        client = Reno(api_key="reno_sk_test")
        messages = [{"role": "user", "content": "Hello"}]
        response = client.chat(messages)
        
        assert isinstance(response, Completion)
        assert response.text == "Response"
        client.close()
    
    @patch('requests.Session.post')
    def test_error_handling(self, mock_post):
        """Test API error handling."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "error": {
                "code": 1002,
                "message": "Invalid API key",
                "details": "Check your key"
            }
        }
        mock_post.return_value = mock_response
        
        client = Reno(api_key="reno_sk_test")
        
        with pytest.raises(RenoError) as exc_info:
            client.ask("Test")
        
        assert exc_info.value.code == 1002
        assert "Invalid API key" in exc_info.value.message
        client.close()


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
