import os
import logging
from logging.handlers import RotatingFileHandler
from functools import wraps
import uuid
import pandas as pd
from variables import DEBUG_LEVEL, PATH_TO_LOGS
import inspect
#TODO Decorator for remote functions defined in utils.
class Typography:
    """
    Base class for logging and harmonization. Automatically applies the `base_function` decorator
    to all methods and provides dynamic logging setup.
    """

    def __init__(
        self, log_file: str = None, max_bytes: int = 1_000_000, backup_count: int = 3, debug_level = DEBUG_LEVEL
    ):
        """
        Initializes logging dynamically, using a log file named after the calling class if not provided.

        Args:
            log_file (str): Path to the log file (default: calling class (command)).
            max_bytes (int): Maximum size of log file before rotation (default: 1MB).
            backup_count (int): Number of backup log files to keep (default: 3).
            debug_level (str): Level of verbose -> 'INFO','DEBUG','WARNING','ERROR'
        """

        # If no log_file is provided, use the name of the calling class
        if log_file is None:
            # Get the name of the class calling the Logger
            caller_class = inspect.stack()[1].frame.f_globals["__name__"]
            log_file = f"{caller_class}.log"

        self.log_file = log_file
        self.log_dir = PATH_TO_LOGS

        os.makedirs(self.log_dir, exist_ok=True)  # Create log directory if not exists
        self.path_to_log_file = os.path.join(self.log_dir, self.log_file)
        self.instance_id = str(uuid.uuid4())  # Unique ID for each instance

        # Configure logging level dynamically
        log_level = getattr(logging, debug_level, logging.INFO)

        # Initialize general logger
        self.logger = logging.getLogger("general")
        general_file_handler = RotatingFileHandler(os.path.join(self.log_dir, "general.log"), maxBytes=max_bytes, backupCount=backup_count)
        general_file_handler.setFormatter(logging.Formatter('%(asctime)s, %(name)s, %(levelname)s, %(message)s'))
        self.logger.setLevel(log_level)
        self.logger.addHandler(general_file_handler)

        # Initialize class-specific logger
        self.class_logger = logging.getLogger(log_file)
        class_file_handler = RotatingFileHandler(self.path_to_log_file, maxBytes=max_bytes, backupCount=backup_count)
        class_file_handler.setFormatter(logging.Formatter('%(asctime)s, %(name)s, %(levelname)s, %(message)s'))
        self.class_logger.setLevel(log_level)
        self.class_logger.addHandler(class_file_handler)

        # Console handler for process-level logging
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(logging.Formatter('%(asctime)s, %(name)s, %(levelname)s, %(message)s'))
        self.logger.addHandler(console_handler)

        self.logger.info(f"Logger initialized for {log_file} with log level {debug_level}.")

    @staticmethod
    def _log_error(self, error, func_name, error_id):
        """
        Logs errors with additional metadata, including error ID and traceback.

        Args:
            error: Exception object.
            func_name: Name of the function where the error occurred.
            error_id: Unique error ID for traceability.
        """
        error_message = f"Error ID: {error_id}, Function: {func_name}, Error: {error} in class {self.log_file}"
        self.class_logger.error(error_message, exc_info=True)

        # Log a summary error in the general log
        self.logger.error(
            f"Error ID: {error_id}, Function: {func_name}, Class: {self.log_file}"
        )

    @staticmethod
    def base_function(func):
        """
        Decorator to log function entry, exit, and handle errors with error IDs.
        """
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            func_name = func.__name__
            error_id = hash(func_name) & 0xFFFFFF  # Simple unique ID from function name
            #self.logger.debug(f"Entering function: {func_name} with args: {args} and kwargs: {kwargs}")
            #self.logger.debug(f'Current directory: {os.getcwd()}')

            try:
                result = func(self, *args, **kwargs)
                #self.logger.info(f"Function {func_name} completed successfully.")
                return result
            except Exception as error:
                # Log detailed error in the class-specific log
                Typography._log_error(self, error, func_name, error_id)
                raise
        return wrapper

    def __init_subclass__(cls, **kwargs):
        """
        Automatically applies `base_function` to all public methods of subclasses.
        """
        super().__init_subclass__(**kwargs)
        for name, method in cls.__dict__.items():
            if callable(method) and not name.startswith("_"):  # Avoid private methods
                setattr(cls, name, Typography.base_function(method))

    def to_dataframe(self, log_file_path: str) -> pd.DataFrame:
        """
        Converts a log file to a DataFrame for further analysis.

        Args:
            log_file_path (str): Path to the log file to be converted.

        Returns:
            pd.DataFrame: DataFrame containing the log data.
        """
        with open(log_file_path, 'r') as file:
            lines = file.readlines()

        log_data = []
        for line in lines:
            parts = line.strip().split(', ')
            if len(parts) == 4:
                timestamp, name, level, message = parts
                log_data.append({
                    'timestamp': timestamp,
                    'name': name,
                    'level': level,
                    'message': message
                })

        return pd.DataFrame(log_data)
