"""
Integration tests for cost tracking and billing simulation.

This module tests the cost tracking functionality in a real server environment,
including per-request cost calculation, API key tracking, model pricing, caching
savings, batch savings, budget management, and cost optimization features.
"""

import time
from typing import Any

import pytest

from .utils import FakeAIClient


@pytest.mark.integration
class TestPerRequestCostCalculation:
    """Test per-request cost calculation across different endpoints."""

    def test_chat_completion_cost_calculation(self, client: FakeAIClient):
        """Test cost calculation for chat completions."""
        response = client.chat_completion(
            model="gpt-4o",
            messages=[{"role": "user", "content": "Hello"}],
        )

        assert response["id"]
        assert "choices" in response

        # Check usage includes cost information if available
        usage = response.get("usage", {})
        assert "total_tokens" in usage

    def test_embedding_cost_calculation(self, client: FakeAIClient):
        """Test cost calculation for embeddings."""
        response = client.create_embedding(
            model="text-embedding-3-small",
            input="Test embedding input",
        )

        assert response["object"] == "list"
        assert "data" in response
        assert len(response["data"]) > 0

        # Verify usage tracking
        usage = response.get("usage", {})
        assert "total_tokens" in usage

    def test_image_generation_cost_calculation(self, client: FakeAIClient):
        """Test cost calculation for image generation."""
        response = client.create_image(
            model="dall-e-3",
            prompt="A beautiful sunset",
            size="1024x1024",
            quality="standard",
            n=1,
        )

        assert response["created"]
        assert "data" in response
        assert len(response["data"]) > 0

    def test_audio_speech_cost_calculation(self, client: FakeAIClient):
        """Test cost calculation for text-to-speech."""
        # Note: Audio endpoints return binary data
        try:
            response = client.post(
                "/v1/audio/speech",
                json={
                    "model": "tts-1",
                    "input": "Hello, this is a test of text to speech.",
                    "voice": "alloy",
                },
            )
            assert response.status_code == 200
        except Exception as e:
            # Audio endpoint might not be fully implemented
            if "404" not in str(e) and "not found" not in str(e).lower():
                raise


@pytest.mark.integration
class TestPerAPIKeyCostTracking:
    """Test cost tracking per API key."""

    def test_multiple_api_keys_separate_costs(
        self, server_function_scoped, sample_messages
    ):
        """Test that costs are tracked separately for different API keys."""
        # Create clients with different API keys
        key1 = "test-key-cost-1"
        key2 = "test-key-cost-2"

        client1 = FakeAIClient(
            base_url=server_function_scoped.base_url, api_key=key1, timeout=30.0
        )
        client2 = FakeAIClient(
            base_url=server_function_scoped.base_url, api_key=key2, timeout=30.0
        )

        try:
            # Make requests with different keys
            client1.chat_completion(model="gpt-4o", messages=sample_messages)
            client1.chat_completion(model="gpt-4o", messages=sample_messages)

            client2.chat_completion(model="gpt-4o", messages=sample_messages)

            # Both should succeed
            # Cost tracking is internal, but verify requests work
            assert True

        finally:
            client1.close()
            client2.close()

    def test_api_key_cost_accumulation(self, client: FakeAIClient, sample_messages):
        """Test that costs accumulate over multiple requests."""
        # Make multiple requests
        for _ in range(5):
            response = client.chat_completion(
                model="gpt-4o-mini", messages=sample_messages
            )
            assert response["id"]

        # Verify multiple requests succeeded
        assert True


@pytest.mark.integration
class TestPerModelPricing:
    """Test pricing varies correctly by model."""

    def test_different_model_pricing(self, client: FakeAIClient, sample_messages):
        """Test that different models have different costs."""
        # Test GPT-4o
        response1 = client.chat_completion(model="gpt-4o", messages=sample_messages)
        assert response1["model"]

        # Test GPT-4o-mini
        response2 = client.chat_completion(model="gpt-4o-mini", messages=sample_messages)
        assert response2["model"]

        # Test GPT-3.5-turbo
        response3 = client.chat_completion(
            model="gpt-3.5-turbo", messages=sample_messages
        )
        assert response3["model"]

        # All should succeed with different models
        assert response1["model"] != response2["model"]

    def test_fine_tuned_model_pricing(self, client: FakeAIClient, sample_messages):
        """Test pricing for fine-tuned models."""
        # Fine-tuned model format: ft:base:org::id
        response = client.chat_completion(
            model="ft:gpt-4o-2024-08-06:my-org::abc123",
            messages=sample_messages,
        )

        assert response["id"]
        assert "ft:" in response["model"]

    def test_embedding_model_pricing(self, client: FakeAIClient):
        """Test pricing for different embedding models."""
        # Test small model
        response1 = client.create_embedding(
            model="text-embedding-3-small",
            input="Test input",
        )
        assert response1["data"]

        # Test large model
        response2 = client.create_embedding(
            model="text-embedding-3-large",
            input="Test input",
        )
        assert response2["data"]


@pytest.mark.integration
class TestPerEndpointCosts:
    """Test cost tracking per endpoint."""

    def test_chat_endpoint_costs(self, client: FakeAIClient, sample_messages):
        """Test costs for chat completions endpoint."""
        response = client.chat_completion(model="gpt-4o", messages=sample_messages)
        assert response["id"]
        assert response["object"] == "chat.completion"

    def test_embeddings_endpoint_costs(self, client: FakeAIClient):
        """Test costs for embeddings endpoint."""
        response = client.create_embedding(
            model="text-embedding-3-small",
            input="Test input",
        )
        assert response["object"] == "list"

    def test_images_endpoint_costs(self, client: FakeAIClient):
        """Test costs for image generation endpoint."""
        response = client.create_image(
            model="dall-e-3",
            prompt="A test image",
            size="1024x1024",
        )
        assert response["created"]

    def test_multiple_endpoints_tracking(self, client: FakeAIClient, sample_messages):
        """Test that costs are tracked separately per endpoint."""
        # Make requests to different endpoints
        client.chat_completion(model="gpt-4o", messages=sample_messages)
        client.create_embedding(
            model="text-embedding-3-small",
            input="Test",
        )
        client.create_image(
            model="dall-e-3",
            prompt="Test",
        )

        # All should succeed
        assert True


@pytest.mark.integration
class TestBatchCostSavings:
    """Test batch processing cost savings (50% discount on completion tokens)."""

    def test_batch_creation_cost_tracking(self, client: FakeAIClient):
        """Test cost tracking for batch creation."""
        try:
            # Create a batch job
            response = client.post(
                "/v1/batches",
                json={
                    "input_file_id": "file-abc123",
                    "endpoint": "/v1/chat/completions",
                    "completion_window": "24h",
                },
            )

            # Batch should be created (or fail gracefully)
            assert response.get("id") or "error" in response

        except Exception as e:
            # Batch endpoint might require actual file
            if "404" not in str(e):
                pytest.skip("Batch endpoint requires file setup")

    def test_batch_vs_regular_cost_difference(self, client: FakeAIClient):
        """Test that batch API provides cost savings."""
        # This is more of a unit test, but verify endpoint works
        # The actual cost savings would be verified in unit tests
        try:
            response = client.get("/v1/batches")
            assert isinstance(response, dict)
        except Exception as e:
            if "404" not in str(e):
                pytest.skip("Batch endpoint not fully available")


@pytest.mark.integration
class TestCacheCostSavings:
    """Test prompt caching cost savings."""

    def test_cache_enabled_requests(self, client: FakeAIClient):
        """Test requests with caching enabled."""
        # Make repeated requests with same prompt
        messages = [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "What is 2+2?"},
        ]

        # First request (cache miss)
        response1 = client.chat_completion(model="gpt-4o", messages=messages)
        assert response1["id"]

        # Second request (potential cache hit)
        response2 = client.chat_completion(model="gpt-4o", messages=messages)
        assert response2["id"]

        # Both should succeed
        assert response1["id"] != response2["id"]

    def test_cache_metrics_tracking(self, client: FakeAIClient):
        """Test that cache metrics are tracked."""
        try:
            # Get KV cache metrics
            metrics = client.get_kv_cache_metrics()
            assert isinstance(metrics, dict)

            # Should have cache-related fields
            # The actual fields depend on implementation
        except Exception as e:
            # KV cache might not be available in this test
            if "404" not in str(e):
                raise


@pytest.mark.integration
class TestPromptCachingCostReduction:
    """Test prompt caching cost reduction."""

    def test_cached_prompt_tokens_cost_less(self, client: FakeAIClient):
        """Test that cached prompt tokens cost 50% less."""
        # Make request with caching header (if supported)
        messages = [
            {
                "role": "system",
                "content": "You are a helpful assistant. " * 100,  # Long prompt
            },
            {"role": "user", "content": "Hello"},
        ]

        response = client.chat_completion(model="gpt-4o", messages=messages)
        assert response["id"]

        usage = response.get("usage", {})
        assert "prompt_tokens" in usage

    def test_multiple_cached_requests(self, client: FakeAIClient):
        """Test cost savings accumulate over multiple cached requests."""
        # Same system message for all requests
        system_msg = "You are a helpful assistant with extensive knowledge."

        for i in range(5):
            messages = [
                {"role": "system", "content": system_msg},
                {"role": "user", "content": f"Question {i}?"},
            ]

            response = client.chat_completion(model="gpt-4o", messages=messages)
            assert response["id"]


@pytest.mark.integration
class TestCostAccumulationOverTime:
    """Test cost accumulation over time periods."""

    def test_cost_tracking_over_time(self, client: FakeAIClient, sample_messages):
        """Test that costs are tracked correctly over time."""
        # Make requests over time
        for i in range(3):
            response = client.chat_completion(
                model="gpt-4o-mini", messages=sample_messages
            )
            assert response["id"]
            time.sleep(0.1)  # Small delay between requests

        # All requests should succeed
        assert True

    def test_time_based_cost_queries(self, client: FakeAIClient, sample_messages):
        """Test querying costs for specific time periods."""
        # Make some requests
        client.chat_completion(model="gpt-4o-mini", messages=sample_messages)
        client.chat_completion(model="gpt-4o-mini", messages=sample_messages)

        # Cost queries would be via API if available
        # For now, just verify requests work
        assert True


@pytest.mark.integration
class TestCostLimitsAndAlerts:
    """Test budget limits and alerts."""

    @pytest.mark.server_config(
        env_overrides={
            "FAKEAI_BUDGET_ENABLED": "true",
            "FAKEAI_BUDGET_LIMIT": "1.0",
        }
    )
    def test_budget_limit_enforcement(self, server_function_scoped, sample_messages):
        """Test that budget limits are enforced."""
        client = FakeAIClient(
            base_url=server_function_scoped.base_url,
            api_key=server_function_scoped.api_keys[0],
            timeout=30.0,
        )

        try:
            # Make requests (should succeed initially)
            response = client.chat_completion(
                model="gpt-4o-mini", messages=sample_messages
            )
            assert response["id"]

        finally:
            client.close()

    @pytest.mark.server_config(
        env_overrides={
            "FAKEAI_BUDGET_ENABLED": "true",
            "FAKEAI_BUDGET_ALERT_THRESHOLD": "0.8",
        }
    )
    def test_budget_alert_threshold(self, server_function_scoped, sample_messages):
        """Test budget alert threshold."""
        client = FakeAIClient(
            base_url=server_function_scoped.base_url,
            api_key=server_function_scoped.api_keys[0],
            timeout=30.0,
        )

        try:
            # Make requests
            response = client.chat_completion(
                model="gpt-4o-mini", messages=sample_messages
            )
            assert response["id"]

            # Alerts would be in logs/metrics
        finally:
            client.close()


@pytest.mark.integration
class TestCostExportAndReporting:
    """Test cost export and reporting features."""

    def test_get_cost_summary(self, client: FakeAIClient, sample_messages):
        """Test getting cost summary via metrics endpoint."""
        # Make some requests first
        client.chat_completion(model="gpt-4o", messages=sample_messages)
        client.chat_completion(model="gpt-4o-mini", messages=sample_messages)
        client.create_embedding(model="text-embedding-3-small", input="Test")

        # Get metrics (which may include cost info)
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)

    def test_per_model_cost_reporting(self, client: FakeAIClient, sample_messages):
        """Test cost reporting broken down by model."""
        # Make requests with different models
        models = ["gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo"]

        for model in models:
            response = client.chat_completion(model=model, messages=sample_messages)
            assert response["id"]

        # Get metrics
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)

    def test_per_endpoint_cost_reporting(self, client: FakeAIClient, sample_messages):
        """Test cost reporting broken down by endpoint."""
        # Make requests to different endpoints
        client.chat_completion(model="gpt-4o", messages=sample_messages)
        client.create_embedding(model="text-embedding-3-small", input="Test")

        # Get metrics
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)

    def test_time_series_cost_data(self, client: FakeAIClient, sample_messages):
        """Test time-series cost data."""
        # Make requests over time
        for _ in range(5):
            client.chat_completion(model="gpt-4o-mini", messages=sample_messages)
            time.sleep(0.1)

        # Get metrics
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)


@pytest.mark.integration
class TestFreeTierTracking:
    """Test free tier usage tracking."""

    def test_free_tier_request_counting(self, client: FakeAIClient, sample_messages):
        """Test tracking requests against free tier limits."""
        # Make requests
        for _ in range(3):
            response = client.chat_completion(
                model="gpt-4o-mini", messages=sample_messages
            )
            assert response["id"]

        # Free tier tracking would be internal
        assert True

    def test_free_tier_token_counting(self, client: FakeAIClient, sample_messages):
        """Test tracking tokens against free tier limits."""
        response = client.chat_completion(model="gpt-4o-mini", messages=sample_messages)

        usage = response.get("usage", {})
        assert "total_tokens" in usage
        assert usage["total_tokens"] > 0


@pytest.mark.integration
class TestUsageBasedBillingSimulation:
    """Test usage-based billing simulation."""

    def test_token_based_billing(self, client: FakeAIClient, sample_messages):
        """Test token-based billing calculation."""
        response = client.chat_completion(model="gpt-4o", messages=sample_messages)

        usage = response.get("usage", {})
        assert "prompt_tokens" in usage
        assert "completion_tokens" in usage
        assert "total_tokens" in usage

        # Verify token counts are reasonable
        assert usage["prompt_tokens"] > 0
        assert usage["total_tokens"] > 0

    def test_request_based_billing(self, client: FakeAIClient):
        """Test request-based billing for image generation."""
        response = client.create_image(
            model="dall-e-3",
            prompt="A test image",
            size="1024x1024",
            quality="standard",
            n=1,
        )

        assert response["created"]
        assert len(response["data"]) == 1

    def test_character_based_billing(self, client: FakeAIClient):
        """Test character-based billing for audio."""
        try:
            # Audio TTS is character-based
            response = client.post(
                "/v1/audio/speech",
                json={
                    "model": "tts-1",
                    "input": "Hello, this is a test.",
                    "voice": "alloy",
                },
            )
            assert response.status_code == 200
        except Exception as e:
            if "404" not in str(e):
                pytest.skip("Audio endpoint not available")

    def test_mixed_billing_types(self, client: FakeAIClient, sample_messages):
        """Test mixed billing types in single session."""
        # Token-based (chat)
        client.chat_completion(model="gpt-4o", messages=sample_messages)

        # Token-based (embeddings)
        client.create_embedding(model="text-embedding-3-small", input="Test")

        # Request-based (images)
        client.create_image(model="dall-e-3", prompt="Test")

        # All should succeed
        assert True


@pytest.mark.integration
class TestCostOptimizationRecommendations:
    """Test cost optimization recommendations."""

    def test_cheaper_model_suggestions(self, client: FakeAIClient, sample_messages):
        """Test suggestions for using cheaper models."""
        # Make many expensive requests
        for _ in range(10):
            response = client.chat_completion(model="gpt-4", messages=sample_messages)
            assert response["id"]

        # Optimization suggestions would be in metrics/logs
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)

    def test_caching_recommendations(self, client: FakeAIClient):
        """Test recommendations for enabling caching."""
        # Make many requests with similar prompts
        for i in range(20):
            messages = [
                {"role": "system", "content": "You are a helpful assistant."},
                {"role": "user", "content": f"Question {i}?"},
            ]
            response = client.chat_completion(model="gpt-4o", messages=messages)
            assert response["id"]

        # Caching recommendations would be in optimization endpoint
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)

    def test_batch_processing_recommendations(self, client: FakeAIClient, sample_messages):
        """Test recommendations for batch processing."""
        # Make many individual requests
        for _ in range(15):
            response = client.chat_completion(
                model="gpt-4o-mini", messages=sample_messages
            )
            assert response["id"]

        # Batch processing recommendations would be generated
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)

    def test_optimization_based_on_usage_patterns(
        self, client: FakeAIClient, sample_messages
    ):
        """Test optimization recommendations based on usage patterns."""
        # Create diverse usage pattern
        for _ in range(5):
            client.chat_completion(model="gpt-4", messages=sample_messages)
            client.chat_completion(model="gpt-4o", messages=sample_messages)
            client.chat_completion(model="gpt-3.5-turbo", messages=sample_messages)

        # Get metrics
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)


@pytest.mark.integration
class TestCostTrackingWithStreaming:
    """Test cost tracking with streaming responses."""

    def test_streaming_chat_cost_tracking(self, client: FakeAIClient):
        """Test cost tracking for streaming chat completions."""
        messages = [{"role": "user", "content": "Count to 5"}]

        chunks = []
        for chunk in client.chat_completion_stream(model="gpt-4o", messages=messages):
            chunks.append(chunk)

        # Should receive multiple chunks
        assert len(chunks) > 0

        # Final chunk should have usage
        final_chunk = chunks[-1]
        if "usage" in final_chunk:
            assert "total_tokens" in final_chunk["usage"]

    def test_streaming_cost_vs_nonstreaming(self, client: FakeAIClient, sample_messages):
        """Test that streaming and non-streaming have same cost."""
        # Non-streaming request
        response1 = client.chat_completion(
            model="gpt-4o-mini", messages=sample_messages, stream=False
        )
        usage1 = response1.get("usage", {})

        # Streaming request
        chunks = list(
            client.chat_completion_stream(
                model="gpt-4o-mini", messages=sample_messages
            )
        )

        # Both should complete successfully
        assert response1["id"]
        assert len(chunks) > 0


@pytest.mark.integration
class TestMultiModalCostTracking:
    """Test cost tracking for multi-modal requests."""

    def test_vision_request_cost_tracking(self, client: FakeAIClient):
        """Test cost tracking for vision requests."""
        messages = [
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "What's in this image?"},
                    {
                        "type": "image_url",
                        "image_url": {"url": "https://example.com/image.jpg"},
                    },
                ],
            }
        ]

        response = client.chat_completion(model="gpt-4o", messages=messages)
        assert response["id"]

        usage = response.get("usage", {})
        assert "total_tokens" in usage

    def test_audio_input_cost_tracking(self, client: FakeAIClient):
        """Test cost tracking for audio input."""
        # Audio input would require actual audio file
        # For now, test the endpoint exists
        try:
            response = client.post(
                "/v1/audio/transcriptions",
                json={"model": "whisper-1", "file": "test.mp3"},
            )
            # May fail without actual file, that's okay
        except Exception:
            pass  # Expected without real file


@pytest.mark.integration
@pytest.mark.slow
class TestCostTrackingAtScale:
    """Test cost tracking at scale."""

    def test_high_volume_cost_tracking(self, client: FakeAIClient, sample_messages):
        """Test cost tracking with high request volume."""
        num_requests = 50

        for i in range(num_requests):
            response = client.chat_completion(
                model="gpt-4o-mini", messages=sample_messages
            )
            assert response["id"]

        # All requests should complete
        assert True

    def test_concurrent_cost_tracking(self, client: FakeAIClient, sample_messages):
        """Test cost tracking with concurrent requests."""
        import concurrent.futures

        def make_request():
            return client.chat_completion(model="gpt-4o-mini", messages=sample_messages)

        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
            futures = [executor.submit(make_request) for _ in range(20)]
            results = [f.result() for f in futures]

        # All requests should succeed
        assert len(results) == 20
        assert all(r["id"] for r in results)

    def test_cost_tracking_memory_efficiency(
        self, client: FakeAIClient, sample_messages
    ):
        """Test that cost tracking doesn't leak memory."""
        # Make many requests
        for _ in range(100):
            response = client.chat_completion(
                model="gpt-4o-mini", messages=sample_messages
            )
            assert response["id"]

        # Get metrics (should not cause memory issues)
        metrics = client.get_metrics()
        assert isinstance(metrics, dict)
