# -*- coding: utf-8 -*-
# skeleton-python-binary -  Command-line tool that generates project templates based on predefined Python project template
# SPDX-License-Identifier: Apache-2.0
# Auto-generated by skeleton-python-binary
"""Some Utilities."""
import os
import time
import tracemalloc
from functools import partial
from functools import wraps

from loguru import logger


class UtilsMonitoring:  # noqa: R0205
    """Some Utilities."""

    # pylint: disable:invalid_name
    @staticmethod
    def io_display(
        func=None, input=True, output=True, level="TRACE"
    ):  # pylint: disable=W0622
        """
        Decorator to log the inputs and/or outputs of a function, with a configurable log level.

        Args:
            func (Callable, optional): Function to decorate. Defaults to `None`, which allows the decorator to be used with parameters.
            input (bool, optional): Indicates whether to log the inputs of the function. Defaults to `True`.
            output (bool, optional): Indicates whether to log the outputs of the function. Defaults to `True`.
            level (str, optional): The log level used for logging messages. Defaults to "TRACE". Can be any of the log levels supported by `loguru` ("TRACE", "DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL").

        Returns:
            Callable: The decorated function that logs its inputs and/or outputs.

        Usage:
            This decorator allows you to log the inputs and/or outputs of a function with a configurable log level.
            It can be used for debugging or monitoring function execution by tracking the values of arguments and results.

        Example usage:

            @UtilsMonitoring.io_display(input=True, output=False, level="DEBUG")
            def add(a, b):
                return a + b

            add(3, 4)

        In this example, only the inputs will be logged at the `DEBUG` level, and the output will not be logged.

        Notes:
            - If `func` is `None`, the decorator is configured with the given parameters.
            - The `input` and `output` parameters allow fine-grained control over whether the function's arguments or results are logged.
            - The `level` parameter must be a valid log level in `loguru` and is converted to uppercase before use.

        """

        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                # Obtenir le logger avec le niveau configuré
                log_level = level.upper()

                # Logger les entrées si configuré
                if input:
                    logger.log(
                        log_level,
                        f"Input in the function '{func.__name__}' with args={args}, kwargs={kwargs}",
                    )

                # Execute the function
                result = func(*args, **kwargs)

                # Log the outputs if configured
                if output:
                    logger.log(
                        log_level,
                        f"Output in the function '{func.__name__}' with result={result}",
                    )

                return result

            return wrapper

        return decorator

    @staticmethod
    def time_spend(func=None, level="DEBUG", threshold_in_ms=1000):
        """Monitor the performances of a function.

        Parameters
        ----------
        func: func
            Function to monitor (default: {None})
        level: int
            Level from which the monitoring starts (default: {logging.DEBUG})
        threshold_in_ms: int
            an alert is sent at any level when the function duration >
            threshold_in_ms (default: {1000})

        Returns
        -------
        object : the result of the function
        """
        if func is None:
            return partial(UtilsMonitoring.time_spend, level=level)

        @wraps(func)
        def newfunc(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            elapsed_time = time.time() - start_time
            logger.log(
                level,
                "function [{}] finished in {:.2f} ms".format(
                    func.__qualname__, elapsed_time * 1000
                ),
            )
            return result

        return newfunc

    @staticmethod
    def measure_memory(func=None, level="DEBUG"):
        """Measure the memory of the function

        Args:
            func (func, optional): Function to measure. Defaults to None.
            level (int, optional): Level of the log. Defaults to DEBUG.

        Returns:
            object : the result of the function
        """
        if func is None:
            return partial(UtilsMonitoring.measure_memory, level=level)

        @wraps(func)
        def newfunc(*args, **kwargs):
            tracemalloc.start()
            result = func(*args, **kwargs)
            current, peak = tracemalloc.get_traced_memory()
            msg = f"""
            \033[37mFunction Name       :\033[35;1m {func.__name__}\033[0m
            \033[37mCurrent memory usage:\033[36m {current / 10 ** 6}MB\033[0m
            \033[37mPeak                :\033[36m {peak / 10 ** 6}MB\033[0m
            """
            logger.log(level, msg)
            tracemalloc.stop()
            return result

        return newfunc
