import time
import pytest
import random

from digit_counter import DigitCounter
from digit_counter.utils import measure_performance


class TestPerformance:
    @pytest.fixture
    def counter_with_cache(self):
        return DigitCounter(precision=15, enable_cache=True, cache_size=2000)

    @pytest.fixture
    def counter_without_cache(self):
        return DigitCounter(precision=15, enable_cache=False)

    @pytest.fixture
    def test_data(self):
        data = []

        data.extend([random.randint(-1_000_000, 1_000_000) for _ in range(800)])
        data.extend([random.uniform(-1000, 1000) for _ in range(800)])
        data.extend([
            0,
            123.456,
            -987.654,
            1000000,
            0.000001,
            3.141592653589793,
        ])

        return data

    def test_cache_performance(self, counter_with_cache, counter_without_cache, test_data):
        # Without cache
        start = time.perf_counter()
        for value in test_data[:200]:
            counter_without_cache.count_digits(value)
        time_no_cache = time.perf_counter() - start

        # With cache
        start = time.perf_counter()
        for _ in range(5):
            for value in test_data[:200]:
                counter_with_cache.count_digits(value)
        time_cache = time.perf_counter() - start

        assert time_cache < time_no_cache * 5

    def test_batch_performance(self, counter_with_cache, test_data):
        result = counter_with_cache.batch_process(test_data, mode="total")
        assert result.successful_items == len(test_data)
        assert result.average_time_per_item_ms < 10

    def test_large_integer_performance(self, counter_with_cache):
        values = [10**10, 10**100, 10**1000]

        for num in values:
            start = time.perf_counter()
            result = counter_with_cache.count_digits(num)
            elapsed = (time.perf_counter() - start) * 1000

            assert elapsed < 100  # <100 ms
            assert result > 0

    def test_measure_performance_utility(self, counter_with_cache):
        stats = measure_performance(
            counter_with_cache.count_digits,
            123.456,
            mode="total",
            iterations=300,
            warmup=30,
        )
        assert stats["iterations"] == 300
        assert stats["average_time_ms"] > 0
