"""Performance Tests for GEO-SEO Optimizer.

Agent: Agent-2 (性能测试专家)
Scope: 并发性能、限流器压力、缓存性能
"""

import asyncio
import time
import tracemalloc
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock

import pytest

from src.keyword_research.batch_processor import BatchProcessor
from src.keyword_research.google_planner import KeywordData
from src.utils.rate_limiter import TokenBucketRateLimiter


def create_keyword_data(keyword: str = "test") -> KeywordData:
    """Create a sample KeywordData object."""
    return KeywordData(
        keyword=keyword,
        avg_monthly_searches=1000,
        competition="LOW",
        low_cpc=0.5,
        high_cpc=1.5,
        trend=[1000] * 12,
        fetched_at=datetime.now(),
    )


@pytest.mark.performance
@pytest.mark.asyncio
async def test_batch_processing_100_keywords():
    """性能: 批量处理100个关键词 < 10秒."""
    # Mock planner
    planner = MagicMock()
    planner.get_keyword_data = AsyncMock(return_value=create_keyword_data())

    processor = BatchProcessor(planner, max_concurrent=10)
    keywords = [f"keyword_{i}" for i in range(100)]

    start_time = time.time()
    results = await processor.process(keywords)
    elapsed = time.time() - start_time

    assert len(results) == 100
    assert elapsed < 10.0, f"100关键词处理时间 {elapsed:.2f}s 超过10秒限制"


@pytest.mark.performance
@pytest.mark.asyncio
async def test_batch_processing_1000_keywords():
    """性能: 批量处理1000个关键词 < 60秒."""
    planner = MagicMock()
    planner.get_keyword_data = AsyncMock(return_value=create_keyword_data())

    processor = BatchProcessor(planner, max_concurrent=20)
    keywords = [f"keyword_{i}" for i in range(1000)]

    start_time = time.time()
    results = await processor.process(keywords)
    elapsed = time.time() - start_time

    assert len(results) == 1000
    assert elapsed < 60.0, f"1000关键词处理时间 {elapsed:.2f}s 超过60秒限制"


@pytest.mark.performance
@pytest.mark.asyncio
async def test_rate_limiter_under_load():
    """性能: 限流器在100并发下的表现.

    Scenario:
        - 100并发请求
        - 初始只有10个令牌
        - 验证无超限错误
        - 验证限流器正常工作
    """
    limiter = TokenBucketRateLimiter()
    limiter.config.requests = 10
    limiter.config.window_seconds = 1
    limiter.tokens = 10.0  # Only 10 tokens initially

    async def make_request():
        async with limiter:
            await asyncio.sleep(0.01)  # Simulate work
            return True

    start_time = time.time()
    tasks = [make_request() for _ in range(50)]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    elapsed = time.time() - start_time

    # Check no errors
    errors = [r for r in results if isinstance(r, Exception)]
    assert len(errors) == 0, f"出现 {len(errors)} 个错误"

    # With rate limiting, 50 requests with 10 tokens per second should take ~4 seconds
    assert elapsed > 2.0, f"限流器可能没有正确限制速率 (elapsed: {elapsed:.2f}s)"


@pytest.mark.performance
def test_cache_hit_rate():
    """性能: 缓存命中率 > 80%."""
    # Simple cache implementation test
    cache = {}
    hits = 0
    misses = 0

    def get_from_cache(key):
        nonlocal hits, misses
        if key in cache:
            hits += 1
            return cache[key]
        misses += 1
        # Simulate data fetch
        value = f"data_for_{key}"
        cache[key] = value
        return value

    # Simulate access pattern (80% of requests hit 20% of data)
    hot_keys = ["key_1", "key_2", "key_3", "key_4", "key_5"]
    all_keys = hot_keys + [f"cold_{i}" for i in range(20)]

    import random

    random.seed(42)
    for _ in range(100):
        key = random.choice(hot_keys) if random.random() < 0.8 else random.choice(all_keys)
        get_from_cache(key)

    hit_rate = hits / (hits + misses)
    assert hit_rate > 0.80, f"缓存命中率 {hit_rate:.2%} 低于80%"


@pytest.mark.performance
def test_memory_usage():
    """性能: 内存使用 < 500MB.

    Requirements:
        - 长时间运行后内存 < 500MB
    """
    tracemalloc.start()

    # Simulate workload
    data = []
    for i in range(10000):
        data.append({"id": i, "content": "x" * 100})

    current, peak = tracemalloc.get_traced_memory()
    tracemalloc.stop()

    # Convert to MB
    current_mb = current / 1024 / 1024
    peak_mb = peak / 1024 / 1024

    assert current_mb < 500, f"当前内存使用 {current_mb:.2f}MB 超过500MB限制"
    assert peak_mb < 500, f"峰值内存使用 {peak_mb:.2f}MB 超过500MB限制"

    # Cleanup
    del data
