"""Unit tests for document management tools."""

import json
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from mcp.server.fastmcp import Context

from src.mcp_server.features.documents.document_tools import register_document_tools


@pytest.fixture
def mock_mcp():
    """Create a mock MCP server for testing."""
    mock = MagicMock()
    # Store registered tools
    mock._tools = {}

    def tool_decorator():
        def decorator(func):
            mock._tools[func.__name__] = func
            return func

        return decorator

    mock.tool = tool_decorator
    return mock


@pytest.fixture
def mock_context():
    """Create a mock context for testing."""
    return MagicMock(spec=Context)


@pytest.mark.asyncio
async def test_create_document_success(mock_mcp, mock_context):
    """Test successful document creation."""
    # Register tools with mock MCP
    register_document_tools(mock_mcp)

    # Get the create_document function from registered tools
    create_document = mock_mcp._tools.get("create_document")
    assert create_document is not None, "create_document tool not registered"

    # Mock HTTP response
    mock_response = MagicMock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "document": {"id": "doc-123", "title": "Test Doc"},
        "message": "Document created successfully",
    }

    with patch("src.mcp_server.features.documents.document_tools.httpx.AsyncClient") as mock_client:
        mock_async_client = AsyncMock()
        mock_async_client.post.return_value = mock_response
        mock_client.return_value.__aenter__.return_value = mock_async_client

        # Test the function
        result = await create_document(
            mock_context,
            project_id="project-123",
            title="Test Document",
            document_type="spec",
            content={"test": "content"},
        )

        result_data = json.loads(result)
        assert result_data["success"] is True
        assert result_data["document_id"] == "doc-123"
        assert "Document created successfully" in result_data["message"]


@pytest.mark.asyncio
async def test_list_documents_success(mock_mcp, mock_context):
    """Test successful document listing."""
    register_document_tools(mock_mcp)

    # Get the list_documents function from registered tools
    list_documents = mock_mcp._tools.get("list_documents")
    assert list_documents is not None, "list_documents tool not registered"

    # Mock HTTP response
    mock_response = MagicMock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "documents": [
            {"id": "doc-1", "title": "Doc 1", "document_type": "spec"},
            {"id": "doc-2", "title": "Doc 2", "document_type": "design"},
        ]
    }

    with patch("src.mcp_server.features.documents.document_tools.httpx.AsyncClient") as mock_client:
        mock_async_client = AsyncMock()
        mock_async_client.get.return_value = mock_response
        mock_client.return_value.__aenter__.return_value = mock_async_client

        result = await list_documents(mock_context, project_id="project-123")

        result_data = json.loads(result)
        assert result_data["success"] is True
        assert len(result_data["documents"]) == 2
        assert result_data["count"] == 2


@pytest.mark.asyncio
async def test_update_document_partial_update(mock_mcp, mock_context):
    """Test partial document update."""
    register_document_tools(mock_mcp)

    # Get the update_document function from registered tools
    update_document = mock_mcp._tools.get("update_document")
    assert update_document is not None, "update_document tool not registered"

    # Mock HTTP response
    mock_response = MagicMock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "doc": {"id": "doc-123", "title": "Updated Title"},
        "message": "Document updated successfully",
    }

    with patch("src.mcp_server.features.documents.document_tools.httpx.AsyncClient") as mock_client:
        mock_async_client = AsyncMock()
        mock_async_client.put.return_value = mock_response
        mock_client.return_value.__aenter__.return_value = mock_async_client

        # Update only title
        result = await update_document(
            mock_context, project_id="project-123", doc_id="doc-123", title="Updated Title"
        )

        result_data = json.loads(result)
        assert result_data["success"] is True
        assert "Document updated successfully" in result_data["message"]

        # Verify only title was sent in update
        call_args = mock_async_client.put.call_args
        sent_data = call_args[1]["json"]
        assert sent_data == {"title": "Updated Title"}


@pytest.mark.asyncio
async def test_delete_document_not_found(mock_mcp, mock_context):
    """Test deleting a non-existent document."""
    register_document_tools(mock_mcp)

    # Get the delete_document function from registered tools
    delete_document = mock_mcp._tools.get("delete_document")
    assert delete_document is not None, "delete_document tool not registered"

    # Mock 404 response
    mock_response = MagicMock()
    mock_response.status_code = 404
    mock_response.text = "Document not found"

    with patch("src.mcp_server.features.documents.document_tools.httpx.AsyncClient") as mock_client:
        mock_async_client = AsyncMock()
        mock_async_client.delete.return_value = mock_response
        mock_client.return_value.__aenter__.return_value = mock_async_client

        result = await delete_document(
            mock_context, project_id="project-123", doc_id="non-existent"
        )

        result_data = json.loads(result)
        assert result_data["success"] is False
        # Error must be structured format (dict), not string
        assert "error" in result_data
        assert isinstance(result_data["error"], dict), (
            "Error should be structured format, not string"
        )
        assert result_data["error"]["type"] == "not_found"
        assert "not found" in result_data["error"]["message"].lower()
