"""Tests for export/import tools module."""

from unittest.mock import AsyncMock, MagicMock

import pytest

from freecad_mcp.bridge.base import ExecutionResult


class TestExportTools:
    """Tests for export/import tools."""

    @pytest.fixture
    def mock_mcp(self):
        """Create a mock MCP server that captures tool registrations."""
        mcp = MagicMock()
        mcp._registered_tools = {}

        def tool_decorator():
            def wrapper(func):
                mcp._registered_tools[func.__name__] = func
                return func

            return wrapper

        mcp.tool = tool_decorator
        return mcp

    @pytest.fixture
    def mock_bridge(self):
        """Create a mock FreeCAD bridge."""
        return AsyncMock()

    @pytest.fixture
    def register_tools(self, mock_mcp, mock_bridge):
        """Register export tools and return the registered functions."""
        from freecad_mcp.tools.export import register_export_tools

        async def get_bridge():
            return mock_bridge

        register_export_tools(mock_mcp, get_bridge)
        return mock_mcp._registered_tools

    @pytest.mark.asyncio
    async def test_export_step(self, register_tools, mock_bridge):
        """export_step should export to STEP format via execute_python."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "path": "/tmp/output.step",
                    "object_count": 2,
                },
                stdout="",
                stderr="",
                execution_time_ms=50.0,
            )
        )

        export_step = register_tools["export_step"]
        result = await export_step(
            file_path="/tmp/output.step", object_names=["Box", "Cylinder"]
        )

        assert result["success"] is True
        assert result["path"] == "/tmp/output.step"
        assert result["object_count"] == 2
        mock_bridge.execute_python.assert_called_once()

    @pytest.mark.asyncio
    async def test_export_step_all_visible(self, register_tools, mock_bridge):
        """export_step should export all visible objects when no names given."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "path": "/tmp/output.step",
                    "object_count": 5,
                },
                stdout="",
                stderr="",
                execution_time_ms=75.0,
            )
        )

        export_step = register_tools["export_step"]
        result = await export_step(file_path="/tmp/output.step")

        assert result["success"] is True
        assert result["object_count"] == 5

    @pytest.mark.asyncio
    async def test_export_stl(self, register_tools, mock_bridge):
        """export_stl should export to STL format via execute_python."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "path": "/tmp/output.stl",
                    "object_count": 1,
                },
                stdout="",
                stderr="",
                execution_time_ms=30.0,
            )
        )

        export_stl = register_tools["export_stl"]
        result = await export_stl(file_path="/tmp/output.stl", object_names=["Box"])

        assert result["success"] is True
        assert result["path"] == "/tmp/output.stl"
        mock_bridge.execute_python.assert_called_once()

    @pytest.mark.asyncio
    async def test_export_stl_with_tolerance(self, register_tools, mock_bridge):
        """export_stl should accept mesh tolerance parameter."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "path": "/tmp/fine.stl",
                    "object_count": 1,
                },
                stdout="",
                stderr="",
                execution_time_ms=45.0,
            )
        )

        export_stl = register_tools["export_stl"]
        result = await export_stl(file_path="/tmp/fine.stl", mesh_tolerance=0.01)

        assert result["success"] is True
        mock_bridge.execute_python.assert_called_once()

    @pytest.mark.asyncio
    async def test_export_3mf(self, register_tools, mock_bridge):
        """export_3mf should export to 3MF format via execute_python."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "path": "/tmp/output.3mf",
                    "object_count": 1,
                },
                stdout="",
                stderr="",
                execution_time_ms=40.0,
            )
        )

        export_3mf = register_tools["export_3mf"]
        result = await export_3mf(file_path="/tmp/output.3mf")

        assert result["success"] is True
        mock_bridge.execute_python.assert_called_once()

    @pytest.mark.asyncio
    async def test_export_obj(self, register_tools, mock_bridge):
        """export_obj should export to OBJ format via execute_python."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "path": "/tmp/output.obj",
                    "object_count": 1,
                },
                stdout="",
                stderr="",
                execution_time_ms=35.0,
            )
        )

        export_obj = register_tools["export_obj"]
        result = await export_obj(file_path="/tmp/output.obj")

        assert result["success"] is True
        mock_bridge.execute_python.assert_called_once()

    @pytest.mark.asyncio
    async def test_export_iges(self, register_tools, mock_bridge):
        """export_iges should export to IGES format via execute_python."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "path": "/tmp/output.iges",
                    "object_count": 1,
                },
                stdout="",
                stderr="",
                execution_time_ms=55.0,
            )
        )

        export_iges = register_tools["export_iges"]
        result = await export_iges(file_path="/tmp/output.iges")

        assert result["success"] is True
        mock_bridge.execute_python.assert_called_once()

    @pytest.mark.asyncio
    async def test_import_step(self, register_tools, mock_bridge):
        """import_step should import STEP files via execute_python."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "document": "Imported",
                    "objects": ["Part", "Assembly"],
                },
                stdout="",
                stderr="",
                execution_time_ms=100.0,
            )
        )

        import_step = register_tools["import_step"]
        result = await import_step(file_path="/tmp/input.step")

        assert result["success"] is True
        assert result["document"] == "Imported"
        assert len(result["objects"]) == 2
        mock_bridge.execute_python.assert_called_once()

    @pytest.mark.asyncio
    async def test_import_stl(self, register_tools, mock_bridge):
        """import_stl should import STL files via execute_python."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "document": "Mesh",
                    "object": "Mesh001",
                },
                stdout="",
                stderr="",
                execution_time_ms=80.0,
            )
        )

        import_stl = register_tools["import_stl"]
        result = await import_stl(file_path="/tmp/input.stl")

        assert result["success"] is True
        assert result["object"] == "Mesh001"
        mock_bridge.execute_python.assert_called_once()

    @pytest.mark.asyncio
    async def test_export_step_failure(self, register_tools, mock_bridge):
        """export_step should raise ValueError on failure."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=False,
                result=None,
                stdout="",
                stderr="FileNotFoundError: Directory does not exist",
                execution_time_ms=5.0,
                error_type="FileNotFoundError",
                error_traceback="Traceback: FileNotFoundError: Directory does not exist",
            )
        )

        export_step = register_tools["export_step"]
        with pytest.raises(ValueError) as exc_info:
            await export_step(file_path="/nonexistent/output.step")

        assert "FileNotFoundError" in str(exc_info.value) or "Traceback" in str(
            exc_info.value
        )

    @pytest.mark.asyncio
    async def test_import_step_into_document(self, register_tools, mock_bridge):
        """import_step should import into specified document."""
        mock_bridge.execute_python = AsyncMock(
            return_value=ExecutionResult(
                success=True,
                result={
                    "success": True,
                    "document": "MyDoc",
                    "objects": ["ImportedPart"],
                },
                stdout="",
                stderr="",
                execution_time_ms=90.0,
            )
        )

        import_step = register_tools["import_step"]
        result = await import_step(file_path="/tmp/part.step", doc_name="MyDoc")

        assert result["success"] is True
        assert result["document"] == "MyDoc"
