"""
Smoke test for Task 3.1: Per-Provider Semaphores

Basic validation that the per-provider semaphore implementation works.
Run with: python backend/tests/test_per_provider_semaphores_smoke.py
"""

import sys
import os
import asyncio
import time

# Add parent directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from api.routes.complete import (
    _get_provider_semaphore,
    _provider_semaphores,
    PROVIDER_CONCURRENCY_LIMITS,
)


def test_provider_semaphore_limits():
    """Verify each provider has correct semaphore limits."""
    print("✓ Testing provider semaphore limits...")
    
    expected_providers = [
        "openai", "anthropic", "google", "xai", "fireworks",
        "novita", "deepseek", "mistral", "openrouter"
    ]
    
    for provider in expected_providers:
        assert provider in PROVIDER_CONCURRENCY_LIMITS, f"❌ Provider {provider} missing from limits"
        assert PROVIDER_CONCURRENCY_LIMITS[provider] > 0, f"❌ Provider {provider} has invalid limit"
        assert provider in _provider_semaphores, f"❌ Provider {provider} missing semaphore"
        print(f"  {provider}: {PROVIDER_CONCURRENCY_LIMITS[provider]} concurrent requests")
    
    print("✓ All providers have valid semaphore limits\n")


def test_get_provider_semaphore():
    """Test _get_provider_semaphore function."""
    print("✓ Testing _get_provider_semaphore...")
    
    # Known provider
    sem_openai = _get_provider_semaphore("openai")
    assert sem_openai is not None, "❌ No semaphore for openai"
    assert sem_openai == _provider_semaphores["openai"], "❌ Wrong semaphore for openai"
    
    # Case insensitive
    sem_upper = _get_provider_semaphore("OPENAI")
    assert sem_upper == sem_openai, "❌ Provider matching should be case-insensitive"
    
    # Whitespace
    sem_spaces = _get_provider_semaphore("  openai  ")
    assert sem_spaces == sem_openai, "❌ Should handle whitespace"
    
    # Unknown provider
    sem_unknown = _get_provider_semaphore("unknown_provider")
    assert sem_unknown is not None, "❌ Should provide default semaphore for unknown provider"
    
    print("✓ _get_provider_semaphore works correctly\n")


async def test_semaphore_acquire_release():
    """Test semaphore acquire and release."""
    print("✓ Testing semaphore acquire/release...")
    
    provider = "openai"
    sem = _get_provider_semaphore(provider)
    
    # Acquire and release
    await sem.acquire()
    sem.release()
    
    # Should be able to acquire again
    await sem.acquire()
    sem.release()
    
    print("✓ Semaphore acquire/release works\n")


async def test_different_providers_dont_block():
    """
    KEY TEST: Verify requests to different providers don't block each other.
    
    This is the main benefit of per-provider semaphores.
    """
    print("✓ Testing different providers don't block each other...")
    
    openai_sem = _get_provider_semaphore("openai")
    anthropic_sem = _get_provider_semaphore("anthropic")
    
    # Acquire all OpenAI slots
    openai_limit = PROVIDER_CONCURRENCY_LIMITS["openai"]
    for _ in range(openai_limit):
        await openai_sem.acquire()
    
    print(f"  Acquired all {openai_limit} OpenAI semaphore slots")
    
    # Anthropic should still be immediately available
    start = time.time()
    await anthropic_sem.acquire()
    elapsed = time.time() - start
    
    assert elapsed < 0.1, f"❌ Anthropic was blocked by OpenAI (took {elapsed:.2f}s)"
    
    print(f"  Anthropic acquired immediately ({elapsed*1000:.1f}ms) - not blocked by OpenAI ✓")
    
    # Cleanup
    anthropic_sem.release()
    for _ in range(openai_limit):
        openai_sem.release()
    
    print("✓ Different providers can run in parallel\n")


async def test_same_provider_respects_limit():
    """Verify same provider requests respect concurrency limit."""
    print("✓ Testing same provider respects limit...")
    
    provider = "xai"  # Use smaller limit for faster test
    sem = _get_provider_semaphore(provider)
    limit = PROVIDER_CONCURRENCY_LIMITS[provider]
    
    print(f"  {provider} limit: {limit}")
    
    # Acquire all slots
    for _ in range(limit):
        await sem.acquire()
    
    # Next acquire should block
    try:
        await asyncio.wait_for(sem.acquire(), timeout=0.1)
        sem.release()
        assert False, f"❌ Semaphore for {provider} didn't enforce limit of {limit}"
    except asyncio.TimeoutError:
        # Expected: semaphore blocked
        print(f"  Semaphore correctly blocked at limit of {limit} ✓")
    
    # Cleanup
    for _ in range(limit):
        sem.release()
    
    print("✓ Same provider respects concurrency limit\n")


async def test_concurrent_performance():
    """
    Load test: Verify different providers can run truly in parallel.
    """
    print("✓ Testing concurrent performance with multiple providers...")
    
    async def mock_request(provider: str, duration: float = 0.01):
        """Simulate a provider request."""
        sem = _get_provider_semaphore(provider)
        await sem.acquire()
        try:
            await asyncio.sleep(duration)
        finally:
            sem.release()
    
    start = time.time()
    
    # Launch concurrent requests to different providers
    tasks = []
    tasks.extend([mock_request("openai") for _ in range(50)])
    tasks.extend([mock_request("anthropic") for _ in range(30)])
    tasks.extend([mock_request("google") for _ in range(40)])
    
    print(f"  Launching {len(tasks)} concurrent requests (50 openai, 30 anthropic, 40 google)...")
    
    await asyncio.gather(*tasks)
    
    elapsed = time.time() - start
    
    print(f"  Completed in {elapsed:.2f}s")
    
    # With per-provider semaphores, should run in parallel (~0.01s)
    # If blocking each other: would take ~1.2s
    assert elapsed < 0.5, f"❌ Requests took {elapsed:.2f}s (too slow, not parallel)"
    
    print("✓ Concurrent requests to different providers run in parallel\n")


async def main():
    """Run all tests."""
    print("=" * 70)
    print("SMOKE TEST: Per-Provider Semaphores (Task 3.1)")
    print("=" * 70)
    print()
    
    # Sync tests
    test_provider_semaphore_limits()
    test_get_provider_semaphore()
    
    # Async tests
    await test_semaphore_acquire_release()
    await test_different_providers_dont_block()
    await test_same_provider_respects_limit()
    await test_concurrent_performance()
    
    print("=" * 70)
    print("✅ ALL TESTS PASSED")
    print("=" * 70)
    print()
    print("Summary:")
    print("  ✓ Per-provider semaphores configured correctly")
    print("  ✓ Different providers don't block each other")
    print("  ✓ Same provider respects concurrency limits")
    print("  ✓ Performance improved with parallel execution")
    print()
    print("Task 3.1 implementation is working correctly! 🎉")


if __name__ == "__main__":
    asyncio.run(main())
