"""Tests for profiling utilities."""

import time

import pytest

from llg3d.solvers.profiling import timeit


class MockSolver:
    """Mock solver for testing timeit decorator."""

    def __init__(self, profiling: bool = True):
        self.profiling = profiling
        self.profiling_stats = {
            "test_method": {"time": 0.0, "calls": 0},
        }

    @timeit
    def test_method(self):
        """Method to test timing."""
        time.sleep(0.01)  # Simulate some work
        return "result"


def test_timeit_enabled():
    """Test that timeit records stats when profiling is enabled."""
    solver = MockSolver(profiling=True)

    # Call the method
    result = solver.test_method()

    # Check the result
    assert result == "result"

    # Check profiling stats
    stats = solver.profiling_stats["test_method"]
    assert stats["calls"] == 1
    assert stats["time"] > 0.01  # Should have recorded at least 0.01s
    assert stats["time"] < 0.1  # But not too much overhead


def test_timeit_disabled():
    """Test that timeit doesn't record stats when profiling is disabled."""
    solver = MockSolver(profiling=False)

    # Call the method
    result = solver.test_method()

    # Check the result
    assert result == "result"

    # Check profiling stats are unchanged
    stats = solver.profiling_stats["test_method"]
    assert stats["calls"] == 0
    assert stats["time"] == 0.0


def test_timeit_multiple_calls():
    """Test that timeit accumulates stats over multiple calls."""
    solver = MockSolver(profiling=True)

    # Call the method multiple times
    for _ in range(3):
        solver.test_method()

    # Check profiling stats
    stats = solver.profiling_stats["test_method"]
    assert stats["calls"] == 3
    assert stats["time"] > 0.03  # Should have recorded at least 0.03s (3 x 0.01s)


def test_timeit_with_exception():
    """Test that timeit records time even when an exception is raised."""

    class MockSolverWithError(MockSolver):
        @timeit
        def error_method(self):
            time.sleep(0.01)
            raise ValueError("Test error")

    solver = MockSolverWithError(profiling=True)
    solver.profiling_stats["error_method"] = {"time": 0.0, "calls": 0}

    # Call the method and expect an exception
    with pytest.raises(ValueError, match="Test error"):
        solver.error_method()

    # Check profiling stats were still recorded
    stats = solver.profiling_stats["error_method"]
    assert stats["calls"] == 1
    assert stats["time"] > 0.01
