"""Test to understand dict allocation behavior with logging.

This test investigates when dict allocation happens with different logging patterns.
"""

from __future__ import annotations

import gc
import logging
import time

import pytest

from pinecone._internal.logging import get_logger


pytestmark = pytest.mark.unit


def count_allocations(func, iterations: int = 10000) -> int:
    """Count object allocations during function execution.

    Uses sys.gettotalrefcount() on debug Python builds, or falls back
    to a rough estimate based on timing.
    """
    # This is a simplified test - in production we'd use memory_profiler or tracemalloc
    gc.collect()

    # Run once to warm up
    func()

    gc.collect()
    before = len(gc.get_objects())

    for _ in range(iterations):
        func()

    gc.collect()
    after = len(gc.get_objects())

    return after - before


def test_stdlib_logger_allocates_dict_when_disabled():
    """Confirm that stdlib logger allocates extra dict even when logging is disabled."""
    logger = logging.getLogger("test_alloc_stdlib")
    logger.setLevel(logging.WARNING)  # DEBUG disabled

    def log_with_extra():
        logger.debug("Test", extra={"key": "value", "count": 123})

    # When logging is disabled, the extra dict is still allocated
    # We can't easily measure this without C-level instrumentation,
    # but we can verify the behavior exists
    assert not logger.isEnabledFor(logging.DEBUG)

    # Call it - the dict is created even though nothing is logged
    log_with_extra()


def test_structured_logger_still_allocates_kwargs():
    """Show that StructuredLogger still allocates **kwargs dict before level check."""
    logger = get_logger("test_alloc_structured")
    logger._logger.setLevel(logging.WARNING)  # DEBUG disabled

    def log_with_kwargs():
        logger.debug("Test", key="value", count=123)

    # When we call logger.debug(..., **kwargs), Python allocates the kwargs dict
    # BEFORE entering our function, so our level check doesn't prevent allocation
    assert not logger._logger.isEnabledFor(logging.DEBUG)

    # The **kwargs dict is still allocated
    log_with_kwargs()


def test_manual_guard_avoids_allocation():
    """Show that manual guards truly avoid dict allocation."""
    logger = logging.getLogger("test_guard")
    logger.setLevel(logging.WARNING)  # DEBUG disabled

    def log_with_guard():
        if logger.isEnabledFor(logging.DEBUG):
            # This code never runs, so dict never allocated
            logger.debug("Test", extra={"key": "value", "count": 123})

    # The guard prevents even constructing the dict
    assert not logger.isEnabledFor(logging.DEBUG)
    log_with_guard()


def test_realistic_performance_impact():
    """Measure realistic performance impact of dict allocation in hot path.

    This test simulates the HTTP client scenario: many requests with DEBUG logging
    disabled in production.
    """
    stdlib_logger = logging.getLogger("test_realistic_stdlib")
    stdlib_logger.setLevel(logging.WARNING)

    structured_logger = get_logger("test_realistic_structured")
    structured_logger._logger.setLevel(logging.WARNING)

    iterations = 100_000

    # Scenario 1: stdlib with extra dict (current code before fix)
    start = time.perf_counter()
    for i in range(iterations):
        stdlib_logger.debug("HTTP request", extra={"method": "POST", "url": "/api", "attempt": i})
    stdlib_time = time.perf_counter() - start

    # Scenario 2: structured logger with kwargs (our wrapper)
    start = time.perf_counter()
    for i in range(iterations):
        structured_logger.debug("HTTP request", method="POST", url="/api", attempt=i)
    structured_time = time.perf_counter() - start

    # Scenario 3: manual guard (SDK-568 proposal)
    start = time.perf_counter()
    for i in range(iterations):
        if stdlib_logger.isEnabledFor(logging.DEBUG):
            stdlib_logger.debug(
                "HTTP request", extra={"method": "POST", "url": "/api", "attempt": i}
            )
    guard_time = time.perf_counter() - start

    print(f"\nRealistic performance comparison ({iterations:,} iterations, DEBUG disabled):")
    print(f"  stdlib + extra=dict:  {stdlib_time:.4f}s  (baseline)")
    print(
        f"  StructuredLogger:     {structured_time:.4f}s  ({(structured_time / stdlib_time - 1) * 100:+.1f}%)"
    )
    print(
        f"  manual guard:         {guard_time:.4f}s  ({(guard_time / stdlib_time - 1) * 100:+.1f}%)"
    )
    print("\nConclusions:")
    print(f"  - Manual guards are fastest: {(stdlib_time / guard_time):.2f}x faster than baseline")
    print("  - StructuredLogger has similar/worse performance than baseline")
    print("  - The wrapper doesn't avoid **kwargs dict allocation")

    # Manual guards should be significantly faster
    assert guard_time < stdlib_time, "Guards should be faster than baseline"
