"""
Performance tests for the OpenNotion backend.
Tests API response times, concurrent load, and database query performance.
"""
import asyncio
import time
from typing import List
import pytest
from httpx import AsyncClient


# Performance thresholds (in seconds)
RESPONSE_TIME_THRESHOLD = 0.5  # 500ms
BULK_OPERATION_THRESHOLD = 2.0  # 2s for bulk operations


class TestApiPerformance:
    """Performance tests for API endpoints."""

    @pytest.mark.asyncio
    async def test_list_memos_response_time(self, async_client: AsyncClient):
        """Test that listing memos responds within threshold."""
        start = time.perf_counter()
        response = await async_client.get("/api/v1/memos")
        elapsed = time.perf_counter() - start
        
        assert response.status_code == 200
        assert elapsed < RESPONSE_TIME_THRESHOLD, f"List memos took {elapsed:.3f}s (threshold: {RESPONSE_TIME_THRESHOLD}s)"

    @pytest.mark.asyncio
    async def test_get_user_stats_response_time(self, async_client: AsyncClient):
        """Test that user stats API responds within threshold."""
        start = time.perf_counter()
        response = await async_client.get("/api/v1/users/1/stats")
        elapsed = time.perf_counter() - start
        
        assert response.status_code == 200
        assert elapsed < RESPONSE_TIME_THRESHOLD, f"User stats took {elapsed:.3f}s (threshold: {RESPONSE_TIME_THRESHOLD}s)"

    @pytest.mark.asyncio
    async def test_create_memo_response_time(self, async_client: AsyncClient):
        """Test memo creation performance."""
        memo_data = {
            "content": "Performance test memo #perf #test",
            "visibility": "PUBLIC",
        }
        
        start = time.perf_counter()
        response = await async_client.post("/api/v1/memos", json=memo_data)
        elapsed = time.perf_counter() - start
        
        # May fail due to auth, but we're testing response time
        assert elapsed < RESPONSE_TIME_THRESHOLD, f"Create memo took {elapsed:.3f}s"

    @pytest.mark.asyncio
    async def test_concurrent_requests(self, async_client: AsyncClient):
        """Test handling of concurrent requests."""
        num_requests = 10
        
        async def make_request():
            return await async_client.get("/api/v1/memos")
        
        start = time.perf_counter()
        tasks = [make_request() for _ in range(num_requests)]
        responses = await asyncio.gather(*tasks)
        elapsed = time.perf_counter() - start
        
        # All should succeed
        success_count = sum(1 for r in responses if r.status_code == 200)
        
        assert success_count == num_requests, f"Only {success_count}/{num_requests} succeeded"
        assert elapsed < BULK_OPERATION_THRESHOLD, f"Concurrent requests took {elapsed:.3f}s"


class TestDatabasePerformance:
    """Performance tests for database operations."""

    @pytest.mark.asyncio
    async def test_memo_query_with_filters(self, async_client: AsyncClient):
        """Test memo listing with tag filter performance."""
        start = time.perf_counter()
        response = await async_client.get("/api/v1/memos?tag=test")
        elapsed = time.perf_counter() - start
        
        assert response.status_code == 200
        assert elapsed < RESPONSE_TIME_THRESHOLD, f"Filtered query took {elapsed:.3f}s"

    @pytest.mark.asyncio
    async def test_memo_query_with_date_range(self, async_client: AsyncClient):
        """Test memo listing with date range filter."""
        start = time.perf_counter()
        response = await async_client.get(
            "/api/v1/memos?start_date=2026-01-01&end_date=2026-12-31"
        )
        elapsed = time.perf_counter() - start
        
        assert response.status_code == 200
        assert elapsed < RESPONSE_TIME_THRESHOLD, f"Date range query took {elapsed:.3f}s"


class TestVersionsApiPerformance:
    """Performance tests for version history API."""

    @pytest.mark.asyncio
    async def test_get_versions_response_time(self, async_client: AsyncClient):
        """Test version history API performance."""
        # First get a memo ID
        memos_response = await async_client.get("/api/v1/memos")
        if memos_response.status_code == 200 and len(memos_response.json()) > 0:
            memo_id = memos_response.json()[0].get("id")
            
            start = time.perf_counter()
            response = await async_client.get(f"/api/v1/memos/{memo_id}/versions")
            elapsed = time.perf_counter() - start
            
            assert response.status_code == 200
            assert elapsed < RESPONSE_TIME_THRESHOLD, f"Versions API took {elapsed:.3f}s"


class TestMemoryAndResources:
    """Test for memory leaks and resource management."""

    @pytest.mark.asyncio
    async def test_repeated_requests_no_memory_leak(self, async_client: AsyncClient):
        """Test that repeated requests don't cause memory issues."""
        import psutil
        import os
        
        process = psutil.Process(os.getpid())
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        # Make many requests
        for _ in range(50):
            await async_client.get("/api/v1/memos")
        
        final_memory = process.memory_info().rss / 1024 / 1024  # MB
        memory_increase = final_memory - initial_memory
        
        # Memory shouldn't increase by more than 50MB
        assert memory_increase < 50, f"Memory increased by {memory_increase:.1f}MB"


class TestLoadStress:
    """Load and stress tests."""

    @pytest.mark.asyncio
    async def test_sustained_load(self, async_client: AsyncClient):
        """Test handling sustained load over time."""
        num_requests = 30
        request_times: List[float] = []
        
        for _ in range(num_requests):
            start = time.perf_counter()
            response = await async_client.get("/api/v1/memos")
            elapsed = time.perf_counter() - start
            request_times.append(elapsed)
            
            assert response.status_code == 200
        
        avg_time = sum(request_times) / len(request_times)
        max_time = max(request_times)
        
        assert avg_time < RESPONSE_TIME_THRESHOLD, f"Average response time {avg_time:.3f}s exceeds threshold"
        assert max_time < RESPONSE_TIME_THRESHOLD * 2, f"Max response time {max_time:.3f}s is too high"
