"""Performance monitoring and metrics for Mnemex."""

import time
from collections import defaultdict
from collections.abc import Callable
from functools import wraps
from typing import Any, ParamSpec, TypeVar

P = ParamSpec("P")
R = TypeVar("R")


class PerformanceMetrics:
    """Collects and tracks performance metrics."""

    def __init__(self) -> None:
        self.metrics: dict[str, list[float]] = defaultdict(list)
        self.counters: dict[str, int] = defaultdict(int)
        self.timers: dict[str, float] = {}

    def record_timing(self, operation: str, duration: float) -> None:
        """Record timing for an operation."""
        self.metrics[operation].append(duration)
        # Keep only last 1000 measurements to prevent memory growth
        if len(self.metrics[operation]) > 1000:
            self.metrics[operation] = self.metrics[operation][-1000:]

    def increment_counter(self, operation: str) -> None:
        """Increment counter for an operation."""
        self.counters[operation] += 1

    def start_timer(self, operation: str) -> None:
        """Start timing an operation."""
        self.timers[operation] = time.time()

    def end_timer(self, operation: str) -> float:
        """End timing an operation and return duration."""
        if operation not in self.timers:
            return 0.0

        duration = time.time() - self.timers[operation]
        self.record_timing(operation, duration)
        del self.timers[operation]
        return duration

    def get_stats(self) -> dict[str, Any]:
        """Get performance statistics."""
        stats: dict[str, Any] = {}

        for operation, timings in self.metrics.items():
            if timings:
                stats[operation] = {
                    "count": len(timings),
                    "avg_ms": sum(timings) * 1000 / len(timings),
                    "min_ms": min(timings) * 1000,
                    "max_ms": max(timings) * 1000,
                    "total_ms": sum(timings) * 1000,
                }

        for operation, count in self.counters.items():
            stats[f"{operation}_count"] = int(count)

        return stats

    def reset(self) -> None:
        """Reset all metrics."""
        self.metrics.clear()
        self.counters.clear()
        self.timers.clear()


# Global metrics instance
_metrics = PerformanceMetrics()


def get_metrics() -> PerformanceMetrics:
    """Get the global metrics instance."""
    return _metrics


def time_operation(operation_name: str) -> Callable[[Callable[P, R]], Callable[P, R]]:
    """Decorator to time function execution."""

    def decorator(func: Callable[P, R]) -> Callable[P, R]:
        @wraps(func)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            metrics = get_metrics()
            metrics.start_timer(operation_name)
            try:
                result = func(*args, **kwargs)
                return result
            finally:
                metrics.end_timer(operation_name)
                metrics.increment_counter(operation_name)

        return wrapper

    return decorator


def record_timing(operation: str, duration: float) -> None:
    """Record timing for an operation."""
    get_metrics().record_timing(operation, duration)


def increment_counter(operation: str) -> None:
    """Increment counter for an operation."""
    get_metrics().increment_counter(operation)


def get_performance_stats() -> dict[str, Any]:
    """Get current performance statistics."""
    return get_metrics().get_stats()


def reset_metrics() -> None:
    """Reset all performance metrics."""
    get_metrics().reset()
