"""
Test sampling tools using standardized client testing framework.

This test suite validates the 4 enhanced sampling demo tools:
1. intelligent_email_composer - Email composition with macro suggestions
2. smart_workflow_assistant - Workflow suggestions with historical patterns
3. template_rendering_demo - Template rendering demonstrations
4. resource_discovery_assistant - Resource discovery and usage examples

Tests implement proper sampling handler as per FastMCP client2 documentation.

NOTE: These sampling tools are NOT part of the current supported tool surface area.
The tools (intelligent_email_composer, smart_workflow_assistant, template_rendering_demo,
resource_discovery_assistant) have been removed from the server.
"""

import pytest

# Skip the entire module - sampling tools are not in the supported tool surface area
pytestmark = pytest.mark.skip(
    reason="Sampling tools (intelligent_email_composer, smart_workflow_assistant, etc.) "
    "are not part of the supported tool surface area. These experimental features "
    "have been removed from the server."
)
from dataclasses import dataclass
from typing import List

import pytest_asyncio

# Import FastMCP client sampling types
from fastmcp.client.sampling import (
    RequestContext,
    SamplingMessage,
    SamplingParams,
)

# Import standardized test framework components
from .base_test_config import TEST_EMAIL, create_test_client

# ============================================================================
# SAMPLING HANDLER IMPLEMENTATION
# ============================================================================


@dataclass
class MockSamplingResponse:
    """Mock response that simulates LLM sampling output."""

    text: str


class SamplingHandler:
    """
    Sample implementation of a sampling handler for testing.

    This handler simulates LLM responses based on the sampling request,
    providing contextually appropriate responses for each tool test case.
    """

    def __init__(self, enable_debug: bool = False):
        self.enable_debug = enable_debug
        self.call_count = 0
        self.last_request = None

    async def handle_sampling(
        self,
        messages: List[SamplingMessage],
        params: SamplingParams,
        context: RequestContext,
    ) -> str:
        """
        Handle sampling requests from the server.

        Args:
            messages: List of sampling messages
            params: Sampling parameters including temperature, max_tokens, etc.
            context: Request context with request ID

        Returns:
            Generated text response simulating LLM output
        """
        self.call_count += 1
        self.last_request = {"messages": messages, "params": params, "context": context}

        # Extract the conversation content
        conversation = []
        for message in messages:
            content = (
                message.content.text
                if hasattr(message.content, "text")
                else str(message.content)
            )
            conversation.append(f"{message.role}: {content}")

        conversation_text = "\n".join(conversation)

        if self.enable_debug:
            print(f"\n{'=' * 80}")
            print(f"🎯 SAMPLING REQUEST #{self.call_count}")
            print(f"{'=' * 80}")
            print(f"\n📨 Messages Received ({len(messages)} message(s)):")
            for i, msg in enumerate(messages, 1):
                content = (
                    msg.content.text
                    if hasattr(msg.content, "text")
                    else str(msg.content)
                )
                print(f"\n  Message {i}:")
                print(f"    Role: {msg.role}")
                print(
                    f"    Content: {content[:200]}{'...' if len(content) > 200 else ''}"
                )

            print("\n⚙️  Sampling Parameters:")
            print(f"    Request ID: {context.request_id}")
            print(f"    Temperature: {params.temperature}")
            print(f"    Max Tokens: {params.maxTokens}")
            print(
                f"    Stop Sequences: {params.stopSequences if hasattr(params, 'stopSequences') else 'None'}"
            )

            if params.systemPrompt:
                print("\n📋 System Prompt:")
                print(f"    {params.systemPrompt[:300]}...")

            if hasattr(params, "modelPreferences") and params.modelPreferences:
                print(f"\n🤖 Model Preferences: {params.modelPreferences}")

        # Generate contextually appropriate responses based on the request
        response_text = self._generate_contextual_response(conversation_text, params)

        if self.enable_debug:
            print("\n💬 Generated Response:")
            print(f"    {response_text[:300]}...")
            print(f"\n{'=' * 80}\n")

        return response_text

    def _generate_contextual_response(
        self, conversation: str, params: SamplingParams
    ) -> str:
        """Generate contextually appropriate response based on conversation content."""

        conversation_lower = conversation.lower()

        # Email composition responses
        if "compose" in conversation_lower and "email" in conversation_lower:
            return """Here's a professional email composition using the beautiful_email macro:

{{ render_beautiful_email(
    title="Professional Update",
    content_sections=[{
        'type': 'text',
        'content': 'Dear recipient, I wanted to reach out regarding our recent discussion...'
    }],
    user_name="{{user://current/email.name}}",
    user_email="{{user://current/email.email}}",
    signature_style="professional"
) }}

This email uses your actual user profile data from the resource system and applies professional styling with gradient backgrounds. You can also add Gmail label chips using:

{{ render_gmail_labels_chips(service://gmail/labels, 'Label context') }}
"""

        # Workflow assistance responses
        elif "workflow" in conversation_lower or "task" in conversation_lower:
            return """Based on your workspace context, here's a recommended workflow:

**Step-by-Step Workflow:**

1. **Data Collection**
   - Use `service://gmail/labels` to understand email organization
   - Use `recent://drive/7` to access files from the last week
   - Use `tools://enhanced/list` to see available automation tools

2. **Processing**
   - Leverage the search_drive_files tool with mime_type filters
   - Use Gmail filters for automated email organization
   - Apply Calendar event creation for scheduling

3. **Integration**
   - Combine Drive file operations with email notifications
   - Use template macros for professional formatting
   - Track patterns with Qdrant historical analysis

**Recommended Tools:**
- search_drive_files (for file discovery)
- create_gmail_filter (for automation)
- create_event (for scheduling)

This workflow leverages your current workspace setup and available resources.
"""

        # Template rendering responses
        elif "template" in conversation_lower or "rendering" in conversation_lower:
            return """**Template Rendering Demonstration**

Available template macros with examples:

1. **Beautiful Email Template:**
```jinja2
{{ render_beautiful_email(
    title="Status Update",
    content_sections=[{'type': 'text', 'content': 'Content here'}],
    user_name="{{user://current/email.name}}",
    signature_style="professional"
) }}
```

2. **Gmail Labels Visualization:**
```jinja2
{{ render_gmail_labels_chips(
    service://gmail/labels,
    'Organization Summary'
) }}
```

3. **Calendar Dashboard:**
```jinja2
{{ render_calendar_dashboard(
    service://calendar/calendars,
    service://calendar/events
) }}
```

**Usage Tips:**
- Use {{ }} for variable interpolation
- Access resources via URI patterns like service://gmail/labels
- Combine macros for complex templates
- All macros handle real user data from the resource system
"""

        # Resource discovery responses
        elif "resource" in conversation_lower or "discover" in conversation_lower:
            return """**Resource Discovery Guide**

**Available Resource Patterns:**

1. **User Context Resources:**
   - `user://current/email` - Current user email and name
   - `user://current/profile` - Complete user profile

2. **Service Resources:**
   - `service://gmail/labels` - Gmail labels and organization
   - `service://drive/items` - Drive files and folders
   - `service://calendar/events` - Calendar events
   - `service://gmail/filters` - Gmail filter rules

3. **Recent Activity Resources:**
   - `recent://drive/7` - Files from last 7 days
   - `recent://all` - Recent items across all services

4. **Template Resources:**
   - `template://macros` - Available Jinja2 macros
   - `template://user_email` - Simple email template

5. **Analytics Resources:**
   - `qdrant://search/{query}` - Vector database search
   - `qdrant://collection/mcp_tool_responses/info` - Collection info

**Integration Example:**
```python
# Send email with dynamic labels
send_gmail_message(
    html_body="{{ render_gmail_labels_chips(service://gmail/labels) }}"
)
```

These resources provide dynamic access to your workspace data and enable sophisticated integrations.
"""

        # Default response for other cases
        else:
            return f"""Based on your request, I've analyzed the available context and resources.

**Key Insights:**
- User profile and authentication status: Available via user://current/email
- Workspace activity: Accessible via recent://all
- Available tools: Listed in tools://enhanced/list
- Historical patterns: Searchable via Qdrant

**Recommendations:**
1. Leverage resource URIs for dynamic data access
2. Use template macros for professional formatting
3. Apply historical patterns for personalized workflows
4. Integrate multiple services for comprehensive solutions

Temperature setting ({params.temperature}) indicates a {"creative" if params.temperature > 0.5 else "precise"} response style.
System prompt context: {params.systemPrompt[:100] if params.systemPrompt else "Standard"}...
"""


# ============================================================================
# PYTEST FIXTURES
# ============================================================================


@pytest_asyncio.fixture
async def client_with_sampling():
    """Create test client with sampling handler.

    Uses the shared connection logic (HTTP/HTTPS + untrusted cert handling), then
    attaches the sampling handler.
    """
    from .base_test_config import TEST_EMAIL

    handler = SamplingHandler(enable_debug=True)

    try:
        client = await create_test_client(TEST_EMAIL)
    except Exception as e:
        pytest.skip(f"MCP server not reachable for integration tests: {e}")

    # FastMCP client supports setting sampling handler at init; if this version
    # allows setting after creation, attach it here for test usage.
    if hasattr(client, "sampling_handler"):
        client.sampling_handler = handler.handle_sampling
    else:
        # Fallback: stash the handler; tests can still access it.
        client._sampling_handler = handler.handle_sampling

    async with client:
        client._test_sampling_handler = handler
        yield client


# ============================================================================
# TEST CLASS
# ============================================================================


@pytest.mark.service("sampling")
class TestSamplingTools:
    """Tests for enhanced sampling demo tools."""

    # ========================================================================
    # TOOL AVAILABILITY TESTS
    # ========================================================================

    @pytest.mark.asyncio
    async def test_sampling_tools_available(self, client_with_sampling):
        """Test that all 4 sampling demo tools are available."""
        expected_tools = [
            "intelligent_email_composer",
            "smart_workflow_assistant",
            "template_rendering_demo",
            "resource_discovery_assistant",
        ]

        tools = await client_with_sampling.list_tools()
        tool_names = [tool.name for tool in tools]

        for tool_name in expected_tools:
            assert tool_name in tool_names, (
                f"Sampling tool {tool_name} should be available"
            )

        print(f"✅ All {len(expected_tools)} sampling demo tools are available")

    # ========================================================================
    # INTELLIGENT EMAIL COMPOSER TESTS
    # ========================================================================

    @pytest.mark.asyncio
    async def test_intelligent_email_composer_basic(self, client_with_sampling):
        """Test intelligent_email_composer with basic parameters."""
        result = await client_with_sampling.call_tool(
            "intelligent_email_composer",
            {
                "recipient": "test@example.com",
                "topic": "Weekly Status Update",
                "style": "professional",
                "user_google_email": TEST_EMAIL,
            },
        )

        assert result is not None, "Should receive response from email composer"

        # Extract content
        content = result.content[0].text if result.content else str(result)
        content_lower = content.lower()

        # Validate response structure
        assert (
            "success" in content_lower
            or "composed" in content_lower
            or "email" in content_lower
        ), "Response should indicate email composition"

        # Verify sampling was called
        handler = client_with_sampling._test_sampling_handler
        assert handler.call_count > 0, "Sampling handler should have been called"

        print(f"✅ Email composer called sampling {handler.call_count} time(s)")

    @pytest.mark.asyncio
    async def test_intelligent_email_composer_styles(self, client_with_sampling):
        """Test email composer with different styles."""
        styles = ["professional", "friendly", "formal"]

        for style in styles:
            result = await client_with_sampling.call_tool(
                "intelligent_email_composer",
                {
                    "recipient": f"test_{style}@example.com",
                    "topic": f"Test {style} email",
                    "style": style,
                    "user_google_email": TEST_EMAIL,
                },
            )

            assert result is not None, f"Should handle {style} style"
            content = result.content[0].text if result.content else str(result)
            assert "success" in content.lower() or "email" in content.lower(), (
                f"Should compose email with {style} style"
            )

        print(f"✅ Tested {len(styles)} different email styles")

    # ========================================================================
    # SMART WORKFLOW ASSISTANT TESTS
    # ========================================================================

    @pytest.mark.asyncio
    async def test_smart_workflow_assistant_basic(self, client_with_sampling):
        """Test smart_workflow_assistant with basic task."""
        result = await client_with_sampling.call_tool(
            "smart_workflow_assistant",
            {
                "task_description": "Organize my Drive files by project",
                "include_history": True,
                "user_google_email": TEST_EMAIL,
            },
        )

        assert result is not None, "Should receive workflow suggestions"

        content = result.content[0].text if result.content else str(result)
        content_lower = content.lower()

        # Validate workflow suggestions
        assert any(
            keyword in content_lower
            for keyword in ["workflow", "step", "tool", "recommend"]
        ), "Response should contain workflow suggestions"

        # Verify sampling was called
        handler = client_with_sampling._test_sampling_handler
        assert handler.call_count > 0, (
            "Sampling handler should provide workflow assistance"
        )

        print("✅ Workflow assistant provided suggestions")

    @pytest.mark.asyncio
    async def test_smart_workflow_assistant_with_and_without_history(
        self, client_with_sampling
    ):
        """Test workflow assistant with and without historical patterns."""
        # Test with history
        result_with_history = await client_with_sampling.call_tool(
            "smart_workflow_assistant",
            {
                "task_description": "Automate email responses",
                "include_history": True,
                "user_google_email": TEST_EMAIL,
            },
        )

        # Test without history
        result_without_history = await client_with_sampling.call_tool(
            "smart_workflow_assistant",
            {
                "task_description": "Automate email responses",
                "include_history": False,
                "user_google_email": TEST_EMAIL,
            },
        )

        assert result_with_history is not None, "Should work with history enabled"
        assert result_without_history is not None, "Should work with history disabled"

        print("✅ Workflow assistant works with and without historical context")

    # ========================================================================
    # TEMPLATE RENDERING DEMO TESTS
    # ========================================================================

    @pytest.mark.asyncio
    async def test_template_rendering_demo_email(self, client_with_sampling):
        """Test template rendering demo with email templates."""
        result = await client_with_sampling.call_tool(
            "template_rendering_demo",
            {
                "template_type": "email",
                "render_examples": False,
                "user_google_email": TEST_EMAIL,
            },
        )

        assert result is not None, "Should provide template examples"

        content = result.content[0].text if result.content else str(result)
        content_lower = content.lower()

        # Validate template information
        assert any(
            keyword in content_lower
            for keyword in ["template", "macro", "email", "example"]
        ), "Response should contain template information"

        print("✅ Template demo provided email template examples")

    @pytest.mark.asyncio
    async def test_template_rendering_demo_document(self, client_with_sampling):
        """Test template rendering demo with document templates."""
        result = await client_with_sampling.call_tool(
            "template_rendering_demo",
            {
                "template_type": "document",
                "render_examples": True,
                "user_google_email": TEST_EMAIL,
            },
        )

        assert result is not None, "Should provide document template examples"

        content = result.content[0].text if result.content else str(result)

        # Should contain document-related template info
        assert "document" in content.lower() or "template" in content.lower(), (
            "Response should contain document template information"
        )

        print("✅ Template demo provided document template examples")

    # ========================================================================
    # RESOURCE DISCOVERY ASSISTANT TESTS
    # ========================================================================

    @pytest.mark.asyncio
    async def test_resource_discovery_assistant_basic(self, client_with_sampling):
        """Test resource discovery assistant with basic use case."""
        result = await client_with_sampling.call_tool(
            "resource_discovery_assistant",
            {
                "use_case": "Send automated weekly reports",
                "user_google_email": TEST_EMAIL,
            },
        )

        assert result is not None, "Should provide resource discovery information"

        content = result.content[0].text if result.content else str(result)
        content_lower = content.lower()

        # Validate resource discovery
        assert any(
            keyword in content_lower
            for keyword in ["resource", "uri", "service://", "user://"]
        ), "Response should contain resource URIs and patterns"

        # Verify sampling was called
        handler = client_with_sampling._test_sampling_handler
        assert handler.call_count > 0, (
            "Sampling handler should provide resource guidance"
        )

        print("✅ Resource discovery provided guidance on available resources")

    @pytest.mark.asyncio
    async def test_resource_discovery_various_use_cases(self, client_with_sampling):
        """Test resource discovery with various use cases."""
        use_cases = [
            "Email automation workflows",
            "Drive file organization",
            "Calendar event management",
            "Cross-service data integration",
        ]

        for use_case in use_cases:
            result = await client_with_sampling.call_tool(
                "resource_discovery_assistant",
                {"use_case": use_case, "user_google_email": TEST_EMAIL},
            )

            assert result is not None, f"Should handle use case: {use_case}"
            content = result.content[0].text if result.content else str(result)
            assert "resource" in content.lower() or "service://" in content.lower(), (
                f"Should provide resource guidance for: {use_case}"
            )

        print(f"✅ Tested {len(use_cases)} different resource discovery use cases")

    # ========================================================================
    # SAMPLING HANDLER BEHAVIOR TESTS
    # ========================================================================

    @pytest.mark.asyncio
    async def test_sampling_handler_receives_parameters(self, client_with_sampling):
        """Test that sampling handler receives proper parameters."""
        # Call a sampling tool
        await client_with_sampling.call_tool(
            "intelligent_email_composer",
            {
                "recipient": "test@example.com",
                "topic": "Parameter Test",
                "style": "professional",
                "user_google_email": TEST_EMAIL,
            },
        )

        # Check handler received parameters
        handler = client_with_sampling._test_sampling_handler
        assert handler.last_request is not None, "Handler should have received request"

        last_params = handler.last_request["params"]
        assert last_params.maxTokens > 0, "Should receive max_tokens parameter"
        assert last_params.temperature is not None, (
            "Should receive temperature parameter"
        )

        print("✅ Sampling handler received proper parameters:")
        print(f"   - max_tokens: {last_params.maxTokens}")
        print(f"   - temperature: {last_params.temperature}")
        print(
            f"   - system_prompt: {'Present' if last_params.systemPrompt else 'None'}"
        )

    @pytest.mark.asyncio
    async def test_sampling_handler_call_count(self, client_with_sampling):
        """Test that sampling handler is called for each tool invocation."""
        handler = client_with_sampling._test_sampling_handler
        initial_count = handler.call_count

        # Call multiple sampling tools
        await client_with_sampling.call_tool(
            "intelligent_email_composer",
            {
                "recipient": "test1@example.com",
                "topic": "Test 1",
                "style": "professional",
                "user_google_email": TEST_EMAIL,
            },
        )

        await client_with_sampling.call_tool(
            "smart_workflow_assistant",
            {
                "task_description": "Test workflow",
                "include_history": False,
                "user_google_email": TEST_EMAIL,
            },
        )

        final_count = handler.call_count
        calls_made = final_count - initial_count

        assert calls_made >= 2, (
            f"Should have made at least 2 sampling calls, got {calls_made}"
        )
        print(f"✅ Sampling handler called {calls_made} times for 2 tool invocations")

    # ========================================================================
    # ERROR HANDLING TESTS
    # ========================================================================

    @pytest.mark.asyncio
    async def test_sampling_tools_without_sampling_handler(self, client):
        """Test sampling tools behavior when client lacks sampling handler."""
        # Note: Using regular client fixture without sampling handler

        result = await client.call_tool(
            "intelligent_email_composer",
            {
                "recipient": "test@example.com",
                "topic": "No Handler Test",
                "style": "professional",
                "user_google_email": TEST_EMAIL,
            },
        )

        # Should still return a response (may indicate sampling not supported)
        assert result is not None, "Should handle missing sampling handler gracefully"

        content = result.content[0].text if result.content else str(result)
        # May contain error about sampling not supported or fallback behavior
        print(f"✅ Tool handled missing sampling handler: {content[:100]}...")

    @pytest.mark.asyncio
    async def test_sampling_tool_with_missing_parameters(self, client_with_sampling):
        """Test sampling tool error handling with missing parameters."""
        # Try calling tool with missing required parameters
        try:
            result = await client_with_sampling.call_tool(
                "intelligent_email_composer",
                {
                    "user_google_email": TEST_EMAIL
                    # Missing recipient and topic
                },
            )

            # If it doesn't raise, check for error in response
            if result:
                content = result.content[0].text if result.content else str(result)
                # Should indicate error or missing parameters
                assert any(
                    keyword in content.lower()
                    for keyword in ["error", "required", "missing"]
                ), "Should indicate missing parameters"
        except Exception as e:
            # Expected - tool should validate parameters
            assert "required" in str(e).lower() or "missing" in str(e).lower(), (
                "Error should indicate missing required parameters"
            )
            print(f"✅ Tool properly validates required parameters: {str(e)[:100]}")

    # ========================================================================
    # INTEGRATION TESTS
    # ========================================================================

    @pytest.mark.asyncio
    async def test_sampling_tool_integration_sequence(self, client_with_sampling):
        """Test a realistic sequence of sampling tool calls."""
        # 1. Discover resources for a use case
        discovery_result = await client_with_sampling.call_tool(
            "resource_discovery_assistant",
            {
                "use_case": "Automated weekly team updates",
                "user_google_email": TEST_EMAIL,
            },
        )
        assert discovery_result is not None, "Resource discovery should succeed"

        # 2. Get workflow suggestions based on the use case
        workflow_result = await client_with_sampling.call_tool(
            "smart_workflow_assistant",
            {
                "task_description": "Create automated weekly team update workflow",
                "include_history": True,
                "user_google_email": TEST_EMAIL,
            },
        )
        assert workflow_result is not None, "Workflow suggestions should succeed"

        # 3. Get template examples for email formatting
        template_result = await client_with_sampling.call_tool(
            "template_rendering_demo",
            {
                "template_type": "email",
                "render_examples": True,
                "user_google_email": TEST_EMAIL,
            },
        )
        assert template_result is not None, "Template demo should succeed"

        # 4. Compose the actual email
        compose_result = await client_with_sampling.call_tool(
            "intelligent_email_composer",
            {
                "recipient": "team@example.com",
                "topic": "Weekly Team Update",
                "style": "professional",
                "user_google_email": TEST_EMAIL,
            },
        )
        assert compose_result is not None, "Email composition should succeed"

        # Verify all steps completed
        handler = client_with_sampling._test_sampling_handler
        print(
            f"✅ Integration sequence completed with {handler.call_count} sampling calls"
        )

    @pytest.mark.asyncio
    async def test_sampling_performance(self, client_with_sampling):
        """Test sampling handler performance with concurrent calls."""
        import asyncio

        # Create multiple concurrent sampling tool calls
        tasks = [
            client_with_sampling.call_tool(
                "intelligent_email_composer",
                {
                    "recipient": f"test{i}@example.com",
                    "topic": f"Performance Test {i}",
                    "style": "professional",
                    "user_google_email": TEST_EMAIL,
                },
            )
            for i in range(3)
        ]

        # Execute concurrently
        results = await asyncio.gather(*tasks, return_exceptions=True)

        # Verify all succeeded or handled gracefully
        successful = sum(1 for r in results if not isinstance(r, Exception))
        assert successful >= len(tasks) // 2, (
            f"At least half of concurrent calls should succeed, got {successful}/{len(tasks)}"
        )

        handler = client_with_sampling._test_sampling_handler
        print(
            f"✅ Handled {len(tasks)} concurrent sampling requests, {successful} successful"
        )


# ============================================================================
# UTILITY FUNCTIONS
# ============================================================================


def validate_sampling_response_structure(content: str) -> bool:
    """Validate that sampling response has expected structure."""
    content_lower = content.lower()

    # Check for key indicators of valid sampling response
    indicators = [
        "success" in content_lower,
        any(
            keyword in content_lower
            for keyword in ["email", "workflow", "template", "resource"]
        ),
        len(content) > 50,  # Should have meaningful content
    ]

    return sum(indicators) >= 2  # At least 2 indicators should be present


if __name__ == "__main__":
    # Run tests with verbose output
    pytest.main([__file__, "-v", "-s", "--tb=short"])
