"""Performance benchmark comparing cached vs non-cached property access.

This demonstrates the performance improvement from using @cached_property
to avoid repeated list allocations.
"""

import time

from typing import Any

import pytest

from pinecone.models.inference import (
    Embedding,
    EmbedResponse,
    RerankResponse,
    RerankResult,
    Usage,
)


pytestmark = pytest.mark.unit


class NoCacheEmbedResponse:
    """Version without caching - creates new list on every access."""

    def __init__(self, data: list[Embedding], model: str, vector_type: str, usage: Usage) -> None:
        self.data = data
        self.model = model
        self.vector_type = vector_type
        self.usage = usage

    @property
    def embeddings(self) -> list[list[float]]:
        """Get embeddings (no caching - allocates new list each time)."""
        return [emb.values for emb in self.data]


class NoCacheRerankResponse:
    """Version without caching - creates new list/dicts on every access."""

    def __init__(self, data: list[RerankResult], model: str, usage: Usage) -> None:
        self.data = data
        self.model = model
        self.usage = usage

    @property
    def results(self) -> list[dict[str, Any]]:
        """Get results (no caching - allocates new list/dicts each time)."""
        return [
            {
                "index": result.index,
                "score": result.score,
                **({"document": result.document} if result.document is not None else {}),
            }
            for result in self.data
        ]


def test_embed_response_caching_performance_improvement() -> None:
    """Benchmark demonstrating performance improvement with caching."""
    # Setup test data
    data = [Embedding(values=[float(i)] * 768, index=i) for i in range(100)]
    usage = Usage(total_tokens=100)

    # Test without caching (old implementation)
    no_cache_response = NoCacheEmbedResponse(
        data=data, model="test-model", vector_type="dense", usage=usage
    )
    no_cache_start = time.perf_counter()
    for _ in range(1000):
        _ = no_cache_response.embeddings
    no_cache_time = time.perf_counter() - no_cache_start

    # Test with caching (new implementation)
    cached_response = EmbedResponse(data=data, model="test-model", vector_type="dense", usage=usage)
    cache_start = time.perf_counter()
    for _ in range(1000):
        _ = cached_response.embeddings
    cache_time = time.perf_counter() - cache_start

    # Calculate speedup
    speedup = no_cache_time / cache_time

    print(f"\n{'=' * 70}")
    print("EmbedResponse Performance Benchmark")
    print(f"{'=' * 70}")
    print("Data: 100 embeddings x 768 dimensions")
    print("Iterations: 1000 accesses to .embeddings property")
    print(f"\nWithout caching: {no_cache_time:.6f} seconds")
    print(f"With caching:    {cache_time:.6f} seconds")
    print(f"Speedup:         {speedup:.1f}x faster")
    print(f"{'=' * 70}\n")

    # Assert caching is significantly faster (at least 2x, accounting for system variability)
    assert speedup > 2, f"Expected >2x speedup, got {speedup:.1f}x"


def test_rerank_response_caching_performance_improvement() -> None:
    """Benchmark demonstrating performance improvement with caching."""
    # Setup test data
    data = [RerankResult(index=i, score=0.9 - i * 0.001, document=f"doc{i}") for i in range(100)]
    usage = Usage(total_tokens=100)

    # Test without caching (old implementation)
    no_cache_response = NoCacheRerankResponse(data=data, model="test-model", usage=usage)
    no_cache_start = time.perf_counter()
    for _ in range(1000):
        _ = no_cache_response.results
    no_cache_time = time.perf_counter() - no_cache_start

    # Test with caching (new implementation)
    cached_response = RerankResponse(data=data, model="test-model", usage=usage)
    cache_start = time.perf_counter()
    for _ in range(1000):
        _ = cached_response.results
    cache_time = time.perf_counter() - cache_start

    # Calculate speedup
    speedup = no_cache_time / cache_time

    print(f"\n{'=' * 70}")
    print("RerankResponse Performance Benchmark")
    print(f"{'=' * 70}")
    print("Data: 100 rerank results")
    print("Iterations: 1000 accesses to .results property")
    print(f"\nWithout caching: {no_cache_time:.6f} seconds")
    print(f"With caching:    {cache_time:.6f} seconds")
    print(f"Speedup:         {speedup:.1f}x faster")
    print(f"{'=' * 70}\n")

    # Assert caching is significantly faster (at least 2x, accounting for system variability)
    assert speedup > 2, f"Expected >2x speedup, got {speedup:.1f}x"


def test_realistic_usage_pattern() -> None:
    """Benchmark demonstrating benefit for code that assigns to variable once."""
    # Best practice: assign to variable if accessing repeatedly
    data = [Embedding(values=[float(i)] * 384, index=i) for i in range(50)]

    # Cached version makes it safe to access multiple times
    cached_response = EmbedResponse(
        data=data, model="test-model", vector_type="dense", usage=Usage(total_tokens=50)
    )

    # Multiple accesses - all efficient thanks to caching
    start = time.perf_counter()
    for _ in range(100):
        # User might access the property multiple times in their code
        embeddings = cached_response.embeddings
        _ = len(embeddings)
        _ = embeddings[0]
        _ = embeddings[-1]
    elapsed = time.perf_counter() - start

    print(f"\n{'=' * 70}")
    print("Realistic Usage Pattern Benchmark")
    print(f"{'=' * 70}")
    print("Scenario: Multiple property accesses in user code")
    print("Data: 50 embeddings x 384 dimensions")
    print("Pattern: 100 iterations with 3 property accesses each")
    print(f"\nTime with caching: {elapsed:.6f} seconds")
    print("Note: Without caching, each access would allocate a new list")
    print(f"{'=' * 70}\n")

    # Just verify it works and is reasonably fast
    assert elapsed < 0.1, f"Expected fast access, got {elapsed:.6f}s"
