"""Import performance benchmark for Pinecone SDK.

This benchmark measures the time to import the Pinecone class and identifies
opportunities for optimization. It uses subprocess for cold import measurements
to avoid cached imports.
"""

import subprocess
import sys
import time

import pytest


pytestmark = pytest.mark.unit


def measure_cold_import(import_statement: str) -> float:
    """Measure cold import time in a fresh subprocess.

    Args:
        import_statement: Python import statement to measure

    Returns:
        Import time in seconds
    """
    code = f"""
import time
start = time.perf_counter()
{import_statement}
elapsed = time.perf_counter() - start
print(elapsed)
"""
    result = subprocess.run(  # noqa: S603
        [sys.executable, "-c", code],
        capture_output=True,
        text=True,
        check=False,
    )

    if result.returncode != 0:
        raise RuntimeError(f"Import failed: {result.stderr}")

    return float(result.stdout.strip())


def test_cold_import_pinecone_class() -> None:
    """Measure cold import time for 'from pinecone import Pinecone'."""
    import_time = measure_cold_import("from pinecone import Pinecone")

    print(f"\n{'=' * 70}")
    print("Cold Import Performance")
    print(f"{'=' * 70}")
    print("Statement: from pinecone import Pinecone")
    print(f"Time:      {import_time * 1000:.2f}ms")
    print(f"{'=' * 70}\n")

    # Import should be reasonably fast (allowing for CI overhead)
    # Target: <50ms on CI, <10ms on developer machine
    assert import_time < 0.5, f"Import took {import_time * 1000:.2f}ms, expected <500ms"


def test_cold_import_just_exceptions() -> None:
    """Measure import time for just exception classes."""
    import_time = measure_cold_import("from pinecone import PineconeError, APIError")

    print(f"\nException import time: {import_time * 1000:.2f}ms")

    # Exception imports should be very fast
    assert import_time < 0.2, f"Exception import took {import_time * 1000:.2f}ms"


def test_cold_import_async_client() -> None:
    """Measure cold import time for AsyncPinecone."""
    import_time = measure_cold_import("from pinecone import AsyncPinecone")

    print(f"\nAsyncPinecone import time: {import_time * 1000:.2f}ms")

    assert import_time < 0.5, f"AsyncPinecone import took {import_time * 1000:.2f}ms"


def test_namespace_lazy_loading() -> None:
    """Verify that namespace classes are lazy-loaded."""
    # Measure importing Pinecone
    base_time = measure_cold_import("from pinecone import Pinecone")

    # Measure importing Pinecone + a namespace
    with_namespace_time = measure_cold_import("from pinecone import Pinecone, Inference")

    # Importing namespace should add minimal time (it should be lazy)
    overhead = with_namespace_time - base_time

    print(f"\n{'=' * 70}")
    print("Namespace Lazy Loading Verification")
    print(f"{'=' * 70}")
    print(f"Base import (Pinecone):           {base_time * 1000:.2f}ms")
    print(f"With namespace (Pinecone, Inference): {with_namespace_time * 1000:.2f}ms")
    print(f"Overhead:                         {overhead * 1000:.2f}ms")
    print(f"{'=' * 70}\n")

    # Namespace loading adds overhead from msgspec/orjson imports (~66ms+).
    # This is acceptable since namespaces are lazy-loaded only when accessed.
    # Allow up to 150ms overhead to account for CI variance.
    assert overhead < 0.15, f"Namespace added {overhead * 1000:.2f}ms overhead"


def test_model_lazy_loading() -> None:
    """Verify that model classes are lazy-loaded."""
    # Measure importing Pinecone
    base_time = measure_cold_import("from pinecone import Pinecone")

    # Measure importing Pinecone + a model
    with_model_time = measure_cold_import(
        "from pinecone import Pinecone; from pinecone.models import EmbedRequest"
    )

    # Model import should add some time (it loads the model module)
    overhead = with_model_time - base_time

    print(f"\n{'=' * 70}")
    print("Model Lazy Loading Verification")
    print(f"{'=' * 70}")
    print(f"Base import (Pinecone):              {base_time * 1000:.2f}ms")
    print(f"With model (Pinecone + EmbedRequest): {with_model_time * 1000:.2f}ms")
    print(f"Overhead:                            {overhead * 1000:.2f}ms")
    print(f"{'=' * 70}\n")

    # Model import should add reasonable overhead
    assert overhead < 0.2, f"Model added {overhead * 1000:.2f}ms overhead"


def test_instantiation_time() -> None:
    """Measure time to instantiate a Pinecone client (not just import)."""
    code = """
import time
from pinecone import Pinecone

start = time.perf_counter()
pc = Pinecone(api_key="test-key-12345678-1234-1234-1234-123456789012")
elapsed = time.perf_counter() - start
print(elapsed)
"""

    result = subprocess.run(  # noqa: S603
        [sys.executable, "-c", code],
        capture_output=True,
        text=True,
        check=False,
    )

    if result.returncode != 0:
        raise RuntimeError(f"Instantiation failed: {result.stderr}")

    instantiation_time = float(result.stdout.strip())

    print(f"\n{'=' * 70}")
    print("Client Instantiation Performance")
    print(f"{'=' * 70}")
    print(f"Time to create Pinecone(api_key=...): {instantiation_time * 1000:.2f}ms")
    print(f"{'=' * 70}\n")

    # Instantiation now pays the httpx import cost (~200-300ms on first instantiation).
    # This is the intended trade-off: fast imports, slower first instantiation.
    # Subsequent instantiations will be faster due to cached imports.
    assert instantiation_time < 0.4, f"Instantiation took {instantiation_time * 1000:.2f}ms"


def test_warm_import_performance() -> None:
    """Measure warm (cached) import time within same process."""
    iterations = 100

    # First import (cold)
    start = time.perf_counter()
    from pinecone import Pinecone

    cold_time = time.perf_counter() - start

    # Subsequent imports (warm - cached in sys.modules)
    start = time.perf_counter()
    for _ in range(iterations):
        from pinecone import Pinecone  # noqa: F401, F811
    warm_total = time.perf_counter() - start
    warm_avg = warm_total / iterations

    print(f"\n{'=' * 70}")
    print("Warm Import Performance")
    print(f"{'=' * 70}")
    print(f"Cold import:               {cold_time * 1000:.2f}ms")
    print(f"Warm import avg ({iterations} iter): {warm_avg * 1000:.4f}ms")
    print(f"Speedup:                   {cold_time / warm_avg:.0f}x")
    print(f"{'=' * 70}\n")

    # Warm imports should be extremely fast (just dict lookup)
    assert warm_avg < 0.001, f"Warm import took {warm_avg * 1000:.4f}ms"


def test_import_breakdown_with_importtime() -> None:
    """Use -X importtime to get detailed import breakdown."""
    result = subprocess.run(  # noqa: S603
        [sys.executable, "-X", "importtime", "-c", "from pinecone import Pinecone"],
        capture_output=True,
        text=True,
        check=False,
    )

    if result.returncode != 0:
        raise RuntimeError(f"Import failed: {result.stderr}")

    # Parse importtime output (goes to stderr)
    lines = result.stderr.split("\n")
    pinecone_lines = [line for line in lines if "pinecone" in line.lower()]

    print(f"\n{'=' * 70}")
    print("Detailed Import Breakdown (-X importtime)")
    print(f"{'=' * 70}")
    for line in pinecone_lines[:20]:  # Show first 20 pinecone-related imports
        print(line)
    print(f"{'=' * 70}\n")

    # This test just prints info, doesn't assert
    assert True


def test_httpx_import_timing() -> None:
    """Measure how much time httpx adds to import."""
    # Import httpx (heavy dependency that is lazily loaded)
    with_httpx_time = measure_cold_import("import httpx")

    print(f"\n{'=' * 70}")
    print("httpx Import Cost")
    print(f"{'=' * 70}")
    print(f"httpx import time: {with_httpx_time * 1000:.2f}ms")
    print(f"{'=' * 70}\n")

    # Just informational - httpx is a known heavy dependency
    assert with_httpx_time < 0.5, f"httpx import took {with_httpx_time * 1000:.2f}ms"


def test_msgspec_import_timing() -> None:
    """Measure how much time msgspec adds to import."""
    # Import msgspec
    msgspec_time = measure_cold_import("import msgspec")

    print(f"\n{'=' * 70}")
    print("msgspec Import Cost")
    print(f"{'=' * 70}")
    print(f"msgspec import time: {msgspec_time * 1000:.2f}ms")
    print(f"{'=' * 70}\n")

    # msgspec should be faster than httpx
    assert msgspec_time < 0.3, f"msgspec import took {msgspec_time * 1000:.2f}ms"


def test_orjson_import_timing() -> None:
    """Measure how much time orjson adds to import."""
    # Import orjson
    orjson_time = measure_cold_import("import orjson")

    print(f"\n{'=' * 70}")
    print("orjson Import Cost")
    print(f"{'=' * 70}")
    print(f"orjson import time: {orjson_time * 1000:.2f}ms")
    print(f"{'=' * 70}\n")

    # orjson is a C extension, should be fast
    assert orjson_time < 0.2, f"orjson import took {orjson_time * 1000:.2f}ms"
