# 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 scene tools."""

import pytest
import tempfile
import json
from unittest.mock import Mock, patch, MagicMock
from pathlib import Path

from mcp.server.fastmcp import Context, Image

from src.blender_mcp.tools.scene_tools import (
    SceneInfoTool,
    ObjectInfoTool,
    ViewportScreenshotTool,
    CodeExecutionTool,
    get_scene_info,
    get_object_info,
    get_viewport_screenshot,
    execute_blender_code,
)
from src.blender_mcp.exceptions import ValidationError, SecurityError


class TestSceneInfoTool:
    """Test SceneInfoTool."""
    
    @patch('src.blender_mcp.tools.scene_tools.execute_blender_command')
    def test_execute_success(self, mock_execute):
        """Test successful scene info retrieval."""
        mock_execute.return_value = {"objects": ["Cube"], "camera": "Camera"}
        
        tool = SceneInfoTool()
        result = tool.execute()
        
        expected = json.dumps({"objects": ["Cube"], "camera": "Camera"}, indent=2)
        assert result == expected
        mock_execute.assert_called_once_with("get_scene_info")
    
    @patch('src.blender_mcp.tools.scene_tools.execute_blender_command')
    def test_mcp_wrapper(self, mock_execute):
        """Test MCP tool wrapper."""
        mock_execute.return_value = {"test": "data"}
        
        ctx = Context()
        result = get_scene_info(ctx)
        
        assert '"test": "data"' in result


class TestObjectInfoTool:
    """Test ObjectInfoTool."""
    
    @patch('src.blender_mcp.tools.scene_tools.execute_blender_command')
    def test_execute_success(self, mock_execute):
        """Test successful object info retrieval."""
        mock_execute.return_value = {"name": "Cube", "type": "MESH"}
        
        tool = ObjectInfoTool()
        result = tool.execute(object_name="Cube")
        
        expected = json.dumps({"name": "Cube", "type": "MESH"}, indent=2)
        assert result == expected
        mock_execute.assert_called_once_with("get_object_info", {"name": "Cube"})
    
    def test_invalid_object_name(self):
        """Test invalid object name validation."""
        tool = ObjectInfoTool()
        
        with pytest.raises(ValidationError):
            tool.execute(object_name="")
    
    @patch('src.blender_mcp.tools.scene_tools.execute_blender_command')
    def test_mcp_wrapper(self, mock_execute):
        """Test MCP tool wrapper."""
        mock_execute.return_value = {"name": "TestObj"}
        
        ctx = Context()
        result = get_object_info(ctx, "TestObj")
        
        assert '"name": "TestObj"' in result


class TestViewportScreenshotTool:
    """Test ViewportScreenshotTool."""
    
    @patch('src.blender_mcp.tools.scene_tools.execute_blender_command')
    @patch('builtins.open', create=True)
    @patch('pathlib.Path.exists')
    @patch('pathlib.Path.stat')
    @patch('pathlib.Path.unlink')
    def test_execute_success(self, mock_unlink, mock_stat, mock_exists, mock_open, mock_execute):
        """Test successful screenshot capture."""
        # Mock file operations
        mock_exists.return_value = True
        mock_stat.return_value = Mock(st_size=1024 * 1024)  # 1MB file
        mock_execute.return_value = {"success": True}
        
        # Mock file reading
        mock_file = Mock()
        mock_file.read.return_value = b"fake_image_data"
        mock_open.return_value.__enter__.return_value = mock_file
        
        tool = ViewportScreenshotTool()
        result = tool.execute(max_size=800)
        
        assert isinstance(result, Image)
        assert result.data == b"fake_image_data"
        assert result.format == "png"
        mock_unlink.assert_called_once()
    
    def test_invalid_max_size(self):
        """Test invalid max_size validation."""
        tool = ViewportScreenshotTool()
        
        with pytest.raises(ValidationError):
            tool.execute(max_size=50)  # Too small
        
        with pytest.raises(ValidationError):
            tool.execute(max_size=5000)  # Too large
    
    @patch('src.blender_mcp.tools.scene_tools.execute_blender_command')
    def test_blender_error(self, mock_execute):
        """Test Blender error handling."""
        mock_execute.return_value = {"error": "Screenshot failed"}
        
        tool = ViewportScreenshotTool()
        
        with pytest.raises(Exception):
            tool.execute()


class TestCodeExecutionTool:
    """Test CodeExecutionTool."""
    
    @patch('src.blender_mcp.config.config')
    @patch('src.blender_mcp.tools.scene_tools.execute_blender_command')
    def test_execute_success(self, mock_execute, mock_config):
        """Test successful code execution."""
        mock_config.enable_code_execution = True
        mock_execute.return_value = {"result": "Success"}
        
        tool = CodeExecutionTool()
        result = tool.execute(code="x = 1 + 1")
        
        assert "Code executed successfully" in result
        mock_execute.assert_called_once_with("execute_code", {"code": "x = 1 + 1"})
    
    @patch('src.blender_mcp.config.config')
    def test_code_execution_disabled(self, mock_config):
        """Test disabled code execution."""
        mock_config.enable_code_execution = False
        
        tool = CodeExecutionTool()
        
        with pytest.raises(SecurityError):
            tool.execute(code="x = 1")
    
    @patch('src.blender_mcp.config.config')
    def test_dangerous_code(self, mock_config):
        """Test dangerous code rejection."""
        mock_config.enable_code_execution = True
        
        tool = CodeExecutionTool()
        
        with pytest.raises(SecurityError):
            tool.execute(code="import os")
    
    def test_empty_code(self):
        """Test empty code validation."""
        tool = CodeExecutionTool()
        
        with pytest.raises(ValidationError):
            tool.execute(code="   ")
    
    @patch('src.blender_mcp.config.config')
    @patch('src.blender_mcp.tools.scene_tools.execute_blender_command')
    def test_mcp_wrapper(self, mock_execute, mock_config):
        """Test MCP tool wrapper."""
        mock_config.enable_code_execution = True
        mock_execute.return_value = {"result": "OK"}
        
        ctx = Context()
        result = execute_blender_code(ctx, "print('hello')")
        
        assert "Code executed successfully" in result
