# Generated by Copilot
# Maintained by Dinesh Kumar C (https://github.com/Dinesh210805/blender-mcp)
# Forked from original work by Siddharth Ahuja (https://github.com/ahujasid/blender-mcp)

"""
Tests for Sketchfab integration tools.

This module contains comprehensive tests for Sketchfab model search,
download, and status checking functionality, including validation,
error handling, and security measures.
"""

import pytest
from unittest.mock import Mock, patch, MagicMock
from mcp.server.fastmcp import Context

from blender_mcp.tools.sketchfab_tools import (
    get_sketchfab_status,
    search_sketchfab_models,
    download_sketchfab_model,
    _format_model_results,
)
from blender_mcp.exceptions import BlenderMCPError, ConnectionError, ValidationError


class TestSketchfabStatus:
    """Test cases for Sketchfab status checking."""
    
    def test_get_sketchfab_status_success(self, mock_context, mock_blender_connection):
        """Test successful Sketchfab status check."""
        # Arrange
        mock_blender_connection.send_command.return_value = {
            "enabled": True,
            "message": "Sketchfab integration is enabled."
        }
        
        # Act
        result = get_sketchfab_status()
        
        # Assert
        assert "Sketchfab integration is enabled." in result
        assert "Sketchfab excels at realistic models" in result
        mock_blender_connection.send_command.assert_called_once_with("get_sketchfab_status")
    
    def test_get_sketchfab_status_disabled(self, mock_context, mock_blender_connection):
        """Test Sketchfab status check when disabled."""
        # Arrange
        mock_blender_connection.send_command.return_value = {
            "enabled": False,
            "message": "Sketchfab integration is disabled."
        }
        
        # Act
        result = get_sketchfab_status()
        
        # Assert
        assert result == "Sketchfab integration is disabled."
        assert "excels at realistic models" not in result
    
    def test_get_sketchfab_status_invalid_response(self, mock_context, mock_blender_connection):
        """Test handling of invalid response from Blender."""
        # Arrange
        mock_blender_connection.send_command.return_value = "invalid response"
        
        # Act & Assert
        with pytest.raises(BlenderMCPError, match="Invalid response from Blender"):
            get_sketchfab_status()
    
    def test_get_sketchfab_status_connection_error(self, mock_context):
        """Test handling of connection errors."""
        # Arrange
        with patch('blender_mcp.tools.sketchfab_tools.get_blender_connection') as mock_get_conn:
            mock_get_conn.side_effect = ConnectionError("Connection failed")
            
            # Act & Assert
            with pytest.raises(ConnectionError):
                get_sketchfab_status()


class TestSketchfabSearch:
    """Test cases for Sketchfab model searching."""
    
    def test_search_sketchfab_models_success(self, mock_context, mock_blender_connection):
        """Test successful model search."""
        # Arrange
        mock_models = [
            {
                "name": "Medieval Sword",
                "uid": "abc123",
                "user": {"username": "artist1"},
                "license": {"label": "CC BY 4.0"},
                "faceCount": 2500,
                "isDownloadable": True
            },
            {
                "name": "Shield",
                "uid": "def456",
                "user": {"username": "artist2"},
                "license": {"label": "CC BY-SA 4.0"},
                "faceCount": 1800,
                "isDownloadable": False
            }
        ]
        mock_blender_connection.send_command.return_value = {"results": mock_models}
        
        # Act
        result = search_sketchfab_models("medieval sword", categories="weapons", count=10)
        
        # Assert
        assert "Found 2 models matching 'medieval sword'" in result
        assert "Medieval Sword (UID: abc123)" in result
        assert "Shield (UID: def456)" in result
        assert "Author: artist1" in result
        assert "License: CC BY 4.0" in result
        assert "Downloadable: Yes" in result
        assert "Downloadable: No" in result
        
        mock_blender_connection.send_command.assert_called_once_with(
            "search_sketchfab_models",
            {
                "query": "medieval sword",
                "categories": "weapons",
                "count": 10,
                "downloadable": True
            }
        )
    
    def test_search_sketchfab_models_no_results(self, mock_context, mock_blender_connection):
        """Test search with no results."""
        # Arrange
        mock_blender_connection.send_command.return_value = {"results": []}
        
        # Act
        result = search_sketchfab_models("nonexistent model")
        
        # Assert
        assert "No models found matching 'nonexistent model'" in result
    
    def test_search_sketchfab_models_validation_errors(self, mock_context):
        """Test input validation errors."""
        # Test empty query
        with pytest.raises(ValidationError, match="query.*cannot be empty"):
            search_sketchfab_models("")
        
        # Test invalid count
        with pytest.raises(ValidationError, match="count must be an integer"):
            search_sketchfab_models("test", count=150)
        
        # Test invalid downloadable type
        with pytest.raises(ValidationError, match="downloadable must be a boolean"):
            search_sketchfab_models("test", downloadable="yes")
    
    def test_search_sketchfab_models_api_error(self, mock_context, mock_blender_connection):
        """Test API error handling."""
        # Arrange
        mock_blender_connection.send_command.return_value = {
            "error": "API key not configured"
        }
        
        # Act & Assert
        with pytest.raises(BlenderMCPError, match="Sketchfab search error"):
            search_sketchfab_models("test")
    
    def test_search_sketchfab_models_malformed_data(self, mock_context, mock_blender_connection):
        """Test handling of malformed model data."""
        # Arrange
        mock_models = [
            {"name": "Valid Model", "uid": "valid123"},
            None,  # Invalid model entry
            {"name": "Another Model"},  # Missing UID
        ]
        mock_blender_connection.send_command.return_value = {"results": mock_models}
        
        # Act
        result = search_sketchfab_models("test")
        
        # Assert
        assert "Valid Model" in result
        assert "Another Model" in result
        # Should handle None entries gracefully


class TestSketchfabDownload:
    """Test cases for Sketchfab model downloading."""
    
    def test_download_sketchfab_model_success(self, mock_context, mock_blender_connection):
        """Test successful model download."""
        # Arrange
        mock_blender_connection.send_command.return_value = {
            "success": True,
            "imported_objects": ["Sword_Mesh", "Sword_Handle"]
        }
        
        # Act
        result = download_sketchfab_model("abc123")
        
        # Assert
        assert "Successfully imported model" in result
        assert "Sword_Mesh, Sword_Handle" in result
        mock_blender_connection.send_command.assert_called_once_with(
            "download_sketchfab_model",
            {"uid": "abc123"}
        )
    
    def test_download_sketchfab_model_failure(self, mock_context, mock_blender_connection):
        """Test failed model download."""
        # Arrange
        mock_blender_connection.send_command.return_value = {
            "success": False,
            "message": "Model not found or not downloadable"
        }
        
        # Act
        result = download_sketchfab_model("invalid123")
        
        # Assert
        assert "Failed to download model" in result
        assert "Model not found or not downloadable" in result
    
    def test_download_sketchfab_model_validation_errors(self, mock_context):
        """Test UID validation errors."""
        # Test empty UID
        with pytest.raises(ValidationError, match="uid.*cannot be empty"):
            download_sketchfab_model("")
        
        # Test invalid UID characters
        with pytest.raises(ValidationError, match="Invalid UID format"):
            download_sketchfab_model("test<script>alert('xss')</script>")
        
        # Test UID too long
        with pytest.raises(ValidationError, match="uid.*too long"):
            download_sketchfab_model("a" * 200)
    
    def test_download_sketchfab_model_api_error(self, mock_context, mock_blender_connection):
        """Test API error during download."""
        # Arrange
        mock_blender_connection.send_command.return_value = {
            "error": "Insufficient permissions"
        }
        
        # Act & Assert
        with pytest.raises(BlenderMCPError, match="Sketchfab download error"):
            download_sketchfab_model("abc123")
    
    def test_download_sketchfab_model_no_objects(self, mock_context, mock_blender_connection):
        """Test successful download with no imported objects."""
        # Arrange
        mock_blender_connection.send_command.return_value = {
            "success": True,
            "imported_objects": []
        }
        
        # Act
        result = download_sketchfab_model("abc123")
        
        # Assert
        assert "Created objects: none" in result


class TestFormatModelResults:
    """Test cases for model result formatting."""
    
    def test_format_model_results_valid_data(self):
        """Test formatting of valid model data."""
        # Arrange
        models = [
            {
                "name": "Test Model",
                "uid": "test123",
                "user": {"username": "testuser"},
                "license": {"label": "CC BY 4.0"},
                "faceCount": 1000,
                "isDownloadable": True
            }
        ]
        
        # Act
        result = _format_model_results(models, "test query")
        
        # Assert
        assert "Found 1 models matching 'test query'" in result
        assert "Test Model (UID: test123)" in result
        assert "Author: testuser" in result
        assert "License: CC BY 4.0" in result
        assert "Face count: 1000" in result
        assert "Downloadable: Yes" in result
    
    def test_format_model_results_missing_data(self):
        """Test formatting with missing data fields."""
        # Arrange
        models = [
            {
                "name": "Incomplete Model",
                "uid": "incomplete123"
                # Missing user, license, faceCount, isDownloadable
            }
        ]
        
        # Act
        result = _format_model_results(models, "test")
        
        # Assert
        assert "Incomplete Model (UID: incomplete123)" in result
        assert "Author: Unknown author" in result
        assert "License: Unknown" in result
        assert "Face count: Unknown" in result
        assert "Downloadable: No" in result
    
    def test_format_model_results_malicious_data(self):
        """Test formatting with potentially malicious data."""
        # Arrange
        models = [
            {
                "name": "<script>alert('xss')</script>",
                "uid": "malicious123",
                "user": {"username": "<img src=x onerror=alert(1)>"},
                "license": {"label": "javascript:alert('xss')"}
            }
        ]
        
        # Act
        result = _format_model_results(models, "malicious")
        
        # Assert
        # Should sanitize malicious content
        assert "<script>" not in result
        assert "onerror=" not in result
        assert "javascript:" not in result
    
    def test_format_model_results_formatting_error(self):
        """Test handling of formatting errors."""
        # Arrange
        models = [{"name": "Test"}]  # Minimal valid data
        
        # Act
        with patch('blender_mcp.tools.sketchfab_tools.sanitize_text_input', side_effect=Exception("Format error")):
            result = _format_model_results(models, "test")
        
        # Assert
        assert "encountered formatting error" in result
        assert "Format error" in result


# Integration test fixtures would go here
@pytest.fixture
def sample_sketchfab_models():
    """Sample Sketchfab model data for testing."""
    return [
        {
            "name": "Medieval Sword",
            "uid": "abc123def456",
            "user": {"username": "medieval_artist"},
            "license": {"label": "CC BY 4.0"},
            "faceCount": 2500,
            "isDownloadable": True
        },
        {
            "name": "Fantasy Shield",
            "uid": "def456ghi789", 
            "user": {"username": "fantasy_creator"},
            "license": {"label": "CC BY-SA 4.0"},
            "faceCount": 1800,
            "isDownloadable": False
        }
    ]
