"""
Test HTTP Server Integration

Tests that @http decorated functions work properly with the WebAgents FastAPI server.
"""

import pytest
import asyncio
from typing import Dict, Any
from fastapi.testclient import TestClient

from webagents.agents.core.base_agent import BaseAgent
from webagents.agents.tools.decorators import tool, http, hook, handoff
from webagents.agents.skills.base import HandoffResult
from webagents.server.core.app import WebAgentsServer


# ===== TEST FIXTURES =====

@pytest.fixture
def weather_handler():
    """Weather API endpoint fixture"""
    @http("/weather", method="get", scope="owner")
    def get_weather(location: str, units: str = "celsius") -> Dict[str, Any]:
        """Weather API endpoint"""
        return {
            "location": location,
            "temperature": 25,
            "units": units,
            "condition": "sunny",
            "humidity": 65,
            "wind_speed": 10
        }
    return get_weather


@pytest.fixture
def data_handler():
    """Data submission endpoint fixture"""
    @http("/data", method="post")
    async def post_data(data: Dict[str, Any]) -> Dict[str, Any]:
        """Data submission endpoint"""
        return {
            "received": data,
            "status": "processed",
            "id": "req_123",
            "timestamp": "2024-01-01T12:00:00Z"
        }
    return post_data


@pytest.fixture
def admin_handler():
    """Admin-only statistics endpoint fixture"""
    @http("/admin/stats", method="get", scope="admin")
    def get_admin_stats() -> Dict[str, Any]:
        """Admin-only statistics endpoint"""
        return {
            "total_users": 150,
            "active_sessions": 23,
            "server_uptime": "2d 5h 30m",
            "memory_usage": "512MB",
            "cpu_usage": "15%"
        }
    return get_admin_stats


@pytest.fixture
def test_agent(weather_handler, data_handler, admin_handler):
    """Test agent with HTTP handlers"""
    @tool(scope="owner")
    def calculate_sum(a: int, b: int) -> int:
        """Calculate sum of two numbers"""
        return a + b

    @hook("on_request", priority=5)
    async def request_logger(context):
        """Log requests for testing"""
        return context

    @handoff(name="agent")
    async def escalate_issue(issue: str, priority: str = "normal") -> HandoffResult:
        """Escalate issue to specialist"""
        return HandoffResult(
            result=f"Issue '{issue}' escalated with priority {priority}",
            handoff_type="agent",
            success=True
        )

    return BaseAgent(
        name="test-agent",
        instructions="Test agent with HTTP handlers",
        scopes=["owner", "admin"],
        capabilities=[
            calculate_sum,
            weather_handler,
            data_handler,
            admin_handler,
            request_logger,
            escalate_issue
        ]
    )


@pytest.fixture
def test_server(test_agent):
    """Test server with HTTP-enabled agent"""
    return WebAgentsServer(
        agents=[test_agent],
        enable_cors=True,
        enable_monitoring=False,  # Disable for testing
        enable_request_logging=False
    )


@pytest.fixture
def test_client(test_server):
    """Test client for HTTP requests"""
    return TestClient(test_server.app)


# ===== HTTP HANDLER TESTS =====

class TestHTTPHandlerBasics:
    """Test basic HTTP handler functionality"""

    def test_agent_http_registration(self, test_agent):
        """Test that HTTP handlers are properly registered with agent"""
        handlers = test_agent.get_all_http_handlers()
        
        # 3 custom handlers + 3 auto-registered command handlers (list/execute/docs)
        assert len(handlers) >= 3
        
        # Check handler details
        handler_paths = {h['subpath'] for h in handlers}
        assert "/weather" in handler_paths
        assert "/data" in handler_paths
        assert "/admin/stats" in handler_paths
        
        handler_methods = {h['method'] for h in handlers}
        assert "get" in handler_methods
        assert "post" in handler_methods

    def test_scope_filtering(self, test_agent):
        """Test scope-based filtering of HTTP handlers"""
        # Test different scope access
        all_handlers = test_agent.get_http_handlers_for_scope("all")
        owner_handlers = test_agent.get_http_handlers_for_scope("owner")
        admin_handlers = test_agent.get_http_handlers_for_scope("admin")
        
        # All scope should only see public handlers
        all_paths = {h['subpath'] for h in all_handlers}
        assert "/data" in all_paths  # scope="all"
        assert "/weather" not in all_paths  # scope="owner"
        assert "/admin/stats" not in all_paths  # scope="admin"
        
        # Owner scope should see owner + public handlers
        owner_paths = {h['subpath'] for h in owner_handlers}
        assert "/data" in owner_paths
        assert "/weather" in owner_paths
        assert "/admin/stats" not in owner_paths
        
        # Admin scope should see all handlers
        admin_paths = {h['subpath'] for h in admin_handlers}
        assert "/data" in admin_paths
        assert "/weather" in admin_paths
        assert "/admin/stats" in admin_paths

    def test_handler_function_execution(self, weather_handler, data_handler):
        """Test that HTTP handler functions execute correctly"""
        # Test weather handler
        weather_result = weather_handler("Tokyo", "fahrenheit")
        assert weather_result["location"] == "Tokyo"
        assert weather_result["units"] == "fahrenheit"
        assert weather_result["temperature"] == 25
        
        # Test data handler (async)
        import asyncio
        data_result = asyncio.run(data_handler({"message": "test", "value": 42}))
        assert data_result["status"] == "processed"
        assert data_result["received"]["message"] == "test"
        assert data_result["received"]["value"] == 42


class TestServerIntegration:
    """Test HTTP handlers with FastAPI server"""

    def test_server_creation(self, test_server, test_agent):
        """Test that server is created successfully with HTTP handlers"""
        assert test_server is not None
        assert test_agent.name in test_server.static_agents
        
        # Check that HTTP endpoints were registered
        handlers = test_agent.get_all_http_handlers()
        assert len(handlers) > 0

    def test_core_endpoints(self, test_client, test_agent):
        """Test core agent endpoints still work"""
        agent_name = test_agent.name
        
        # Test agent info endpoint
        response = test_client.get(f"/{agent_name}")
        assert response.status_code == 200
        info = response.json()
        assert info.get('name') == agent_name
        
        # Test server discovery endpoint
        response = test_client.get("/")
        assert response.status_code == 200
        discovery = response.json()
        # agents can be a list of dicts or a list of strings
        agents = discovery.get('agents', [])
        if agents and isinstance(agents[0], dict):
            agent_names = [a.get('name') for a in agents]
            assert agent_name in agent_names
        else:
            assert agent_name in agents

    def test_health_endpoints(self, test_client):
        """Test health check endpoints"""
        # Basic health check
        response = test_client.get("/health")
        assert response.status_code == 200
        health = response.json()
        assert health.get('status') == 'healthy'
        # Note: /health/detailed and /ready endpoints are not yet implemented

    def test_custom_endpoints_registered(self, test_server):
        """Test that custom HTTP endpoints are registered with FastAPI"""
        # Check FastAPI routes
        routes = [route.path for route in test_server.app.routes]
        
        # Should have agent-specific routes
        agent_routes = [r for r in routes if "/test-agent/" in r]
        assert len(agent_routes) > 0
        
        # Check for specific custom endpoints
        assert any("/test-agent/weather" in route for route in routes)
        assert any("/test-agent/data" in route for route in routes)
        assert any("/test-agent/admin/stats" in route for route in routes)


class TestHTTPEndpointCalls:
    """Test actual HTTP endpoint calls"""

    def test_weather_endpoint(self, test_client):
        """Test weather endpoint with query parameters"""
        response = test_client.get("/test-agent/weather?location=NYC&units=fahrenheit")
        
        if response.status_code == 200:
            data = response.json()
            assert data["location"] == "NYC"
            assert data["units"] == "fahrenheit"
            assert data["temperature"] == 25
            assert "condition" in data
        else:
            # If endpoint not working, at least verify it was registered
            pytest.skip("Custom HTTP endpoints not fully functional in test environment")

    def test_data_endpoint(self, test_client):
        """Test data endpoint with JSON body"""
        test_data = {"message": "Hello from test", "value": 42}
        response = test_client.post("/test-agent/data", json=test_data)
        
        if response.status_code == 200:
            result = response.json()
            assert result["status"] == "processed"
            assert result["received"] == test_data
            assert "id" in result
        else:
            # If endpoint not working, at least verify it was registered
            pytest.skip("Custom HTTP endpoints not fully functional in test environment")

    def test_admin_endpoint_access(self, test_client):
        """Test admin-only endpoint"""
        # Note: In a real implementation, this would need proper authentication
        response = test_client.get("/test-agent/admin/stats")
        
        # Should work if authentication is not enforced in tests
        if response.status_code == 200:
            data = response.json()
            assert "total_users" in data
            assert "active_sessions" in data
        elif response.status_code == 403:
            # Expected if authentication is enforced
            pass
        else:
            pytest.skip("Admin endpoint test inconclusive")

    def test_nonexistent_endpoint(self, test_client):
        """Test that nonexistent endpoints return 404"""
        response = test_client.get("/test-agent/nonexistent")
        assert response.status_code == 404


class TestDirectRegistration:
    """Test direct registration methods (@agent.http)"""

    def test_direct_http_registration(self):
        """Test @agent.http direct registration"""
        agent = BaseAgent(
            name="direct-agent",
            instructions="Direct registration test"
        )
        
        @agent.http("/status")
        def get_status() -> Dict[str, Any]:
            """Agent status endpoint"""
            return {
                "status": "operational",
                "version": "2.0.0",
                "agent": agent.name
            }
        
        @agent.http("/metrics", method="get", scope="admin")
        def get_metrics() -> Dict[str, Any]:
            """Performance metrics endpoint"""
            return {
                "requests_per_second": 150,
                "average_response_time": "45ms"
            }
        
        # Verify registration (custom handlers + auto-registered command handlers)
        assert len(agent._registered_http_handlers) >= 2
        
        handlers = agent.get_all_http_handlers()
        paths = {h['subpath'] for h in handlers}
        assert "/status" in paths
        assert "/metrics" in paths
        
        # Test function execution
        status = get_status()
        assert status["status"] == "operational"
        assert status["agent"] == "direct-agent"

    def test_direct_tool_registration(self):
        """Test @agent.tool direct registration"""
        agent = BaseAgent(
            name="direct-agent",
            instructions="Direct registration test"
        )
        
        @agent.tool(scope="owner")
        def quick_calc(expression: str) -> str:
            """Quick calculation tool"""
            try:
                result = eval(expression.replace("^", "**"))
                return f"{expression} = {result}"
            except:
                return f"Error: Invalid expression '{expression}'"
        
        # Verify registration
        assert len(agent._registered_tools) == 1
        
        tool = agent._registered_tools[0]
        assert tool['name'] == "quick_calc"
        assert tool['scope'] == "owner"
        
        # Test function execution
        result = quick_calc("2 + 3 * 4")
        assert result == "2 + 3 * 4 = 14"


class TestCapabilitiesAutoRegistration:
    """Test the capabilities auto-registration system"""

    def test_mixed_capabilities_registration(self):
        """Test that capabilities are auto-registered based on decorator type"""
        @tool(scope="owner")
        def example_tool(message: str) -> str:
            """Example tool for capabilities testing"""
            return f"Tool says: {message}"

        @http("/process", method="post")
        def example_http(data: dict) -> dict:
            """Example HTTP handler for capabilities testing"""
            return {"processed": data, "status": "success"}

        @hook("on_request", priority=10)
        async def example_hook(context):
            """Example hook for capabilities testing"""
            return context

        @handoff(name="agent")
        async def example_handoff(target: str) -> HandoffResult:
            """Example handoff for capabilities testing"""
            return HandoffResult(
                result=f"Handoff to {target}",
                handoff_type="agent",
                success=True
            )

        capabilities = [example_tool, example_http, example_hook, example_handoff]
        
        agent = BaseAgent(
            name="capabilities-agent",
            instructions="Test capabilities",
            capabilities=capabilities
        )
        
        # Check that each capability was registered in the correct registry
        assert len(agent._registered_tools) == 1
        # 1 custom handler + 3 auto-registered command handlers
        assert len(agent._registered_http_handlers) >= 1
        assert len(agent._registered_hooks) == 1
        assert len(agent._registered_handoffs) == 1
        
        # Verify correct registration
        tool_names = {t['name'] for t in agent._registered_tools}
        assert "example_tool" in tool_names
        
        http_paths = {h['subpath'] for h in agent._registered_http_handlers}
        assert "/process" in http_paths
        
        hook_events = set(agent._registered_hooks.keys())
        assert "on_request" in hook_events
        
        handoff_targets = {h['config'].target for h in agent._registered_handoffs}
        # The handoff was declared with @handoff(name="agent"), so target is "agent"
        assert "agent" in handoff_targets

    def test_capabilities_with_explicit_params(self):
        """Test capabilities combined with explicit parameter registration"""
        @tool
        def capabilities_tool():
            return "capabilities"
        
        @tool
        def explicit_tool():
            return "explicit"
        
        agent = BaseAgent(
            name="mixed-agent",
            instructions="Mixed registration test",
            tools=[explicit_tool],  # Explicit registration
            capabilities=[capabilities_tool]  # Auto-registration
        )
        
        # Both should be registered
        assert len(agent._registered_tools) == 2
        tool_names = {t['name'] for t in agent._registered_tools}
        assert "explicit_tool" in tool_names
        assert "capabilities_tool" in tool_names


class TestHTTPIntegrationScenarios:
    """Test complex integration scenarios"""

    def test_comprehensive_agent_setup(self):
        """Test agent with all capability types working together"""
        @tool(scope="owner")
        def analyze_data(data: str) -> str:
            """Analyze data using ML models"""
            return f"Analysis complete: {data} shows positive trends"

        @http("/api/upload", method="post")
        def upload_data(file_data: dict) -> dict:
            """Upload data for analysis"""
            # Use the tool internally
            analysis = analyze_data(str(file_data))
            return {
                "uploaded": file_data.get("name", "unknown"),
                "analysis": analysis,
                "status": "complete"
            }

        @http("/api/status", method="get")
        def get_status() -> dict:
            """Get system status"""
            return {
                "system": "operational",
                "models_loaded": True,
                "queue_length": 0
            }

        @hook("on_request", priority=5)
        def log_requests(context):
            """Log all API requests"""
            # In real implementation, would log to file/database
            return context

        agent = BaseAgent(
            name="analysis-agent",
            instructions="Data analysis agent with HTTP API",
            scopes=["owner", "admin"],
            capabilities=[analyze_data, upload_data, get_status, log_requests]
        )
        
        # Verify comprehensive setup
        assert len(agent._registered_tools) == 1  # analyze_data
        # 2 custom handlers + 3 auto-registered command handlers
        assert len(agent._registered_http_handlers) >= 2  # upload_data, get_status
        assert len(agent._registered_hooks) == 1  # log_requests
        
        # Test integration - HTTP endpoint using tool
        test_data = {"name": "sales_data.csv", "rows": 1000}
        result = upload_data(test_data)
        
        assert result["uploaded"] == "sales_data.csv"
        assert "Analysis complete" in result["analysis"]
        assert result["status"] == "complete"

    def test_server_with_multiple_agents(self):
        """Test server with multiple HTTP-enabled agents"""
        # Create multiple agents with different HTTP endpoints
        # Note: http_handlers must use @http decorated functions, not lambdas
        agent1 = BaseAgent(
            name="weather-agent",
            instructions="Weather service"
        )
        
        agent2 = BaseAgent(
            name="news-agent", 
            instructions="News service"
        )
        
        # Create server with multiple agents
        server = WebAgentsServer(
            agents=[agent1, agent2],
            enable_monitoring=False
        )
        
        assert len(server.static_agents) == 2
        assert "weather-agent" in server.static_agents
        assert "news-agent" in server.static_agents
        
        # Test with client
        client = TestClient(server.app)
        
        # Test discovery endpoint shows both agents
        response = client.get("/")
        assert response.status_code == 200
        discovery = response.json()
        agents = discovery.get('agents', [])
        # agents can be list of dicts or strings
        if agents and isinstance(agents[0], dict):
            agent_names = [a.get('name') for a in agents]
            assert "weather-agent" in agent_names
            assert "news-agent" in agent_names
        else:
            assert "weather-agent" in agents
            assert "news-agent" in agents


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

@pytest.mark.integration
class TestHTTPServerIntegration:
    """Integration tests for HTTP server functionality"""

    def test_full_stack_integration(self):
        """Test complete integration from decorator to server to client"""
        # Define complete HTTP API
        @http("/users", method="get")
        def list_users(limit: int = 10) -> dict:
            """List users with pagination"""
            users = [{"id": i, "name": f"User{i}"} for i in range(1, limit + 1)]
            return {"users": users, "total": limit, "page": 1}

        @http("/users", method="post")
        def create_user(user_data: dict) -> dict:
            """Create a new user"""
            return {
                "created": True,
                "user": user_data,
                "id": "user_123"
            }

        @http("/users/{user_id}", method="get")
        def get_user(user_id: str) -> dict:
            """Get specific user"""
            return {
                "id": user_id,
                "name": f"User {user_id}",
                "email": f"user{user_id}@example.com"
            }

        # Create agent and server
        agent = BaseAgent(
            name="user-api",
            instructions="User management API",
            http_handlers=[list_users, create_user, get_user]
        )
        
        server = WebAgentsServer(agents=[agent], enable_monitoring=False)
        client = TestClient(server.app)
        
        # Test the complete flow - 3 custom handlers + 3 auto-registered command handlers
        assert len(agent._registered_http_handlers) >= 3
        
        # Test functions work
        users_result = list_users(5)
        assert len(users_result["users"]) == 5
        
        create_result = create_user({"name": "John", "email": "john@example.com"})
        assert create_result["created"] is True
        
        user_result = get_user("123")
        assert user_result["id"] == "123"


if __name__ == "__main__":
    pytest.main([__file__, "-v"]) 