"""Logging decorators for automatic logging."""

import functools
import logging
import time
from typing import Any, Callable, TypeVar

from src.logging.logger import get_logger

F = TypeVar("F", bound=Callable[..., Any])


def log_execution(logger: logging.Logger = None) -> Callable[[F], F]:
    """
    Decorator to log function entry, exit, and execution time.

    Args:
        logger: Logger instance (if None, creates one from function module)

    Returns:
        Decorated function
    """

    def decorator(func: F) -> F:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            # Get logger
            if logger is None:
                func_logger = get_logger(func.__module__)
            else:
                func_logger = logger

            # Log entry
            func_logger.debug(
                f"Entering {func.__name__}",
                extra={"function": func.__name__, "args": str(args), "kwargs": str(kwargs)},
            )

            # Execute function and measure time
            start_time = time.time()
            try:
                result = func(*args, **kwargs)
                execution_time = time.time() - start_time

                # Log exit
                func_logger.debug(
                    f"Exiting {func.__name__}",
                    extra={
                        "function": func.__name__,
                        "execution_time_seconds": execution_time,
                    },
                )

                return result

            except Exception as e:
                execution_time = time.time() - start_time
                func_logger.error(
                    f"Error in {func.__name__}: {e}",
                    exc_info=True,
                    extra={
                        "function": func.__name__,
                        "execution_time_seconds": execution_time,
                        "error": str(e),
                    },
                )
                raise

        return wrapper  # type: ignore

    return decorator


def log_exceptions(logger: logging.Logger = None) -> Callable[[F], F]:
    """
    Decorator to catch and log exceptions.

    Args:
        logger: Logger instance (if None, creates one from function module)

    Returns:
        Decorated function
    """

    def decorator(func: F) -> F:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            # Get logger
            if logger is None:
                func_logger = get_logger(func.__module__)
            else:
                func_logger = logger

            try:
                return func(*args, **kwargs)
            except Exception as e:
                func_logger.error(
                    f"Exception in {func.__name__}: {e}",
                    exc_info=True,
                    extra={
                        "function": func.__name__,
                        "error": str(e),
                        "error_type": type(e).__name__,
                    },
                )
                raise

        return wrapper  # type: ignore

    return decorator


def log_performance(logger: logging.Logger = None, min_time_seconds: float = 1.0) -> Callable[[F], F]:
    """
    Decorator to log performance metrics for slow functions.

    Args:
        logger: Logger instance (if None, creates one from function module)
        min_time_seconds: Minimum execution time to log (default: 1 second)

    Returns:
        Decorated function
    """

    def decorator(func: F) -> F:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            # Get logger
            if logger is None:
                func_logger = get_logger(func.__module__)
            else:
                func_logger = logger

            start_time = time.time()
            result = func(*args, **kwargs)
            execution_time = time.time() - start_time

            if execution_time >= min_time_seconds:
                func_logger.warning(
                    f"Slow execution: {func.__name__} took {execution_time:.2f}s",
                    extra={
                        "function": func.__name__,
                        "execution_time_seconds": execution_time,
                    },
                )

            return result

        return wrapper  # type: ignore

    return decorator

