"""
Tests for utility functions.
"""

import time

import pytest

from gattopy.core import ProfileResult
from gattopy.utils import (
    aggregate_results,
    benchmark,
    compare_functions,
    format_bytes,
    format_time,
    validate_input,
)


class TestFormatBytes:
    """Test cases for format_bytes function."""

    def test_bytes(self):
        """Test formatting bytes."""
        assert "B" in format_bytes(500)

    def test_kilobytes(self):
        """Test formatting kilobytes."""
        assert "KB" in format_bytes(1024 * 10)

    def test_megabytes(self):
        """Test formatting megabytes."""
        assert "MB" in format_bytes(1024 * 1024 * 5)

    def test_gigabytes(self):
        """Test formatting gigabytes."""
        assert "GB" in format_bytes(1024 * 1024 * 1024 * 2)


class TestFormatTime:
    """Test cases for format_time function."""

    def test_nanoseconds(self):
        """Test formatting nanoseconds."""
        assert "ns" in format_time(1e-9)

    def test_microseconds(self):
        """Test formatting microseconds."""
        assert "μs" in format_time(1e-6)

    def test_milliseconds(self):
        """Test formatting milliseconds."""
        assert "ms" in format_time(0.001)

    def test_seconds(self):
        """Test formatting seconds."""
        result = format_time(5.5)
        assert "s" in result

    def test_minutes(self):
        """Test formatting minutes."""
        result = format_time(65)
        assert "m" in result

    def test_hours(self):
        """Test formatting hours."""
        result = format_time(3700)
        assert "h" in result


class TestBenchmark:
    """Test cases for benchmark function."""

    def test_basic_benchmark(self):
        """Test basic benchmarking."""

        def simple_function():
            return sum(range(100))

        results = benchmark(simple_function, iterations=10)

        assert "iterations" in results
        assert results["iterations"] == 10
        assert "average" in results
        assert "min" in results
        assert "max" in results
        assert "median" in results
        assert "p95" in results
        assert "p99" in results
        assert results["min"] <= results["average"] <= results["max"]

    def test_benchmark_consistency(self):
        """Test that benchmark results are consistent."""

        def constant_function():
            return 42

        results = benchmark(constant_function, iterations=100)

        # All times should be very small and relatively consistent
        assert results["min"] >= 0
        assert results["max"] > results["min"]


class TestCompareFunctions:
    """Test cases for compare_functions."""

    def test_compare_two_functions(self):
        """Test comparing two functions."""

        def func1():
            return sum(range(100))

        def func2():
            return list(range(100))

        results = compare_functions(func1, func2, iterations=10)

        assert "func1" in results
        assert "func2" in results
        assert "average" in results["func1"]
        assert "average" in results["func2"]

    def test_compare_multiple_functions(self):
        """Test comparing multiple functions."""

        def func_a():
            return 1

        def func_b():
            return 2

        def func_c():
            return 3

        results = compare_functions(func_a, func_b, func_c, iterations=10)

        assert len(results) == 3
        assert all(name in results for name in ["func_a", "func_b", "func_c"])


class TestValidateInput:
    """Test cases for validate_input function."""

    def test_valid_input(self):
        """Test validation with valid input."""
        data = [1, 2, 3]
        checks = [lambda x: len(x) > 0, lambda x: isinstance(x, list)]

        assert validate_input(data, checks) is True

    def test_empty_input(self):
        """Test validation with empty input."""
        with pytest.raises(ValueError, match="Invalid input data"):
            validate_input(None, [])

    def test_failed_check(self):
        """Test validation with failed check."""
        data = [1, 2, 3]
        checks = [lambda x: len(x) > 10]  # This will fail

        with pytest.raises(ValueError, match="Validation check"):
            validate_input(data, checks)


class TestAggregateResults:
    """Test cases for aggregate_results function."""

    def test_empty_results(self):
        """Test aggregation with empty results."""
        results = aggregate_results([])
        assert results == {}

    def test_aggregate_time_results(self):
        """Test aggregating time results."""
        results = [
            ProfileResult(function_name="func1", execution_time=1.0),
            ProfileResult(function_name="func2", execution_time=2.0),
            ProfileResult(function_name="func3", execution_time=3.0),
        ]

        aggregated = aggregate_results(results)

        assert "time" in aggregated
        assert aggregated["time"]["count"] == 3
        assert aggregated["time"]["total"] == 6.0
        assert aggregated["time"]["average"] == 2.0
        assert aggregated["time"]["min"] == 1.0
        assert aggregated["time"]["max"] == 3.0

    def test_aggregate_memory_results(self):
        """Test aggregating memory results."""
        results = [
            ProfileResult(function_name="func1", memory_peak=10.0),
            ProfileResult(function_name="func2", memory_peak=20.0),
            ProfileResult(function_name="func3", memory_peak=30.0),
        ]

        aggregated = aggregate_results(results)

        assert "memory" in aggregated
        assert aggregated["memory"]["count"] == 3
        assert aggregated["memory"]["average_peak"] == 20.0
        assert aggregated["memory"]["min_peak"] == 10.0
        assert aggregated["memory"]["max_peak"] == 30.0

    def test_aggregate_mixed_results(self):
        """Test aggregating results with both time and memory."""
        results = [
            ProfileResult(function_name="func1", execution_time=1.0, memory_peak=10.0),
            ProfileResult(function_name="func2", execution_time=2.0, memory_peak=20.0),
        ]

        aggregated = aggregate_results(results)

        assert "time" in aggregated
        assert "memory" in aggregated
