"""
Module: profiler.py
Description: Provides a performance profiler for identifying slow operations within the Genesis system.
"""

import time
import logging
from typing import Callable, Any, Dict, List, Tuple, Optional
from functools import wraps

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


class Profiler:
    """
    A performance profiler that measures the execution time of functions and methods.
    """

    def __init__(self, enabled: bool = True, threshold: float = 0.1) -> None:
        """
        Initializes the Profiler.

        Args:
            enabled: Whether the profiler is enabled or not. Defaults to True.
            threshold: The minimum execution time (in seconds) to consider an operation as "slow". Defaults to 0.1.
        """
        self.enabled = enabled
        self.threshold = threshold
        self.function_timings: Dict[str, List[float]] = {}  # Store timings for each function

    def profile(self, func: Callable[..., Any]) -> Callable[..., Any]:
        """
        A decorator that profiles the execution time of a function.

        Args:
            func: The function to be profiled.

        Returns:
            The decorated function.
        """

        @wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            """
            Wrapper function that measures the execution time of the decorated function.
            """
            if not self.enabled:
                return func(*args, **kwargs)

            start_time = time.time()
            try:
                result = func(*args, **kwargs)
                return result
            except Exception as e:
                logger.error(f"Error executing function {func.__name__}: {e}", exc_info=True)
                raise
            finally:
                end_time = time.time()
                execution_time = end_time - start_time

                self._log_timing(func.__name__, execution_time)

                if execution_time > self.threshold:
                    logger.warning(f"Slow operation: {func.__name__} took {execution_time:.4f} seconds")

        return wrapper

    def _log_timing(self, function_name: str, execution_time: float) -> None:
        """
        Logs the execution time of a function to the internal storage.

        Args:
            function_name: The name of the function.
            execution_time: The execution time in seconds.
        """
        if function_name not in self.function_timings:
            self.function_timings[function_name] = []
        self.function_timings[function_name].append(execution_time)


    def get_average_timings(self) -> Dict[str, float]:
        """
        Calculates and returns the average execution time for each profiled function.

        Returns:
            A dictionary where keys are function names and values are the average execution times.
        """
        average_timings: Dict[str, float] = {}
        for func_name, timings in self.function_timings.items():
            if timings:
                average_timings[func_name] = sum(timings) / len(timings)
            else:
                average_timings[func_name] = 0.0  # Or some other default value

        return average_timings


    def get_all_timings(self) -> Dict[str, List[float]]:
        """
        Returns all recorded timings for each profiled function.

        Returns:
            A dictionary where keys are function names and values are lists of execution times.
        """
        return self.function_timings

    def reset(self) -> None:
        """
        Resets the profiler, clearing all recorded timings.
        """
        self.function_timings = {}


# Example Usage (for testing):
if __name__ == '__main__':
    profiler = Profiler(enabled=True, threshold=0.05)

    @profiler.profile
    def slow_function(n: int) -> int:
        """
        A function that takes some time to execute.
        """
        time.sleep(0.1 * n)
        return n * 2

    @profiler.profile
    def fast_function() -> str:
        """
        A function that executes quickly.
        """
        return "Fast!"

    slow_function(1)
    slow_function(2)
    fast_function()

    average_times = profiler.get_average_timings()
    print("Average Timings:", average_times)

    all_times = profiler.get_all_timings()
    print("All Timings:", all_times)

    profiler.reset()
    print("Profiler Reset")
    print("Timings after reset:", profiler.get_all_timings())