import logging
import os
import random
import shutil
import sys
from datetime import datetime, timedelta
from pathlib import Path

from utils.constants import LOG_FOLDER
from utils.signal_utils import signal_manager

Path(LOG_FOLDER).mkdir(parents=True, exist_ok=True)

# Add a random number to the filename to avoid conflicts
rand_num = random.randint(0, 9999)
now = datetime.now()
ms = f"{int(now.microsecond / 1000):03d}"  # milliseconds, always 3 digits
file_path = f"{LOG_FOLDER}/log_{now.strftime('%Y-%m-%d_%H-%M-%S')}-{ms}_{rand_num}.log"

LOG_FILE_PATH = file_path


def setup_logger(name: str = "logger", log_file_path: str = LOG_FILE_PATH) -> logging.Logger:
    logger = logging.getLogger(name)
    # Only configure if not already configured
    if not logger.handlers:
        logger.setLevel(logging.DEBUG)

        # Console handler and logging format
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)
        console_formatter = logging.Formatter("[%(levelname)s] [%(filename)s:%(lineno)d]: %(message)s")
        console_handler.setFormatter(console_formatter)

        # File handler and logging format
        file_handler = logging.FileHandler(log_file_path)
        file_handler.setLevel(logging.DEBUG)
        file_formatter = logging.Formatter("%(asctime)s - [%(levelname)s] [%(filename)s:%(lineno)d]: %(message)s")
        file_handler.setFormatter(file_formatter)

        logger.addHandler(console_handler)
        logger.addHandler(file_handler)
        print(f"LOGGER INITIALIZED. Saving logs to: {log_file_path}")
    return logger


def save_log_file_path(save_dir: str) -> None:
    with open(os.path.join(save_dir, "log_files.txt"), "a+") as f:
        f.write(f"'{LOG_FILE_PATH}'\n")


def save_log_file(save_dir: str, log_filename: str = "log.txt") -> None:
    try:
        shutil.copy(LOG_FILE_PATH, os.path.join(save_dir, log_filename))
    except Exception as e:
        logger.error(f"Error saving log file: {e}")


def cleanup_logs(log_dir: str = LOG_FOLDER, old_threshold: int = 5) -> None:
    """
    Deletes log files in the specified directory that are empty or whitespace-only,
    but only if they are older than `old_threshold` days.
    """
    try:
        log_directory = Path(log_dir)
        cutoff_time = datetime.now() - timedelta(days=old_threshold)
        for log_file in log_directory.glob("*.log"):
            if not log_file.is_file():
                continue

            # Only consider files older than the cutoff
            file_mtime = datetime.fromtimestamp(log_file.stat().st_mtime)
            if file_mtime > cutoff_time:
                continue

            # If the file is completely empty (0 bytes), delete it immediately.
            if log_file.stat().st_size == 0:
                # logger.info(f"Deleting empty log file: {log_file.name}")
                log_file.unlink()
                continue

            # Otherwise, read the file contents.
            try:
                with log_file.open("r", encoding="utf-8") as f:
                    content = f.read()
            except UnicodeDecodeError:
                # Fallback in case of decoding issues
                with log_file.open("r", encoding="latin-1") as f:
                    content = f.read()

            # Delete the file if it contains only whitespace.
            if content.strip() == "":
                # logger.info(f"Deleting whitespace-only log file: {log_file.name}")
                log_file.unlink()
    except Exception as e:
        logger.error(f"Error cleaning up logs: {e}")


# Create a default logger when module is imported
signal_manager.add_cleanup_function(cleanup_logs)
logger = setup_logger()


def handle_uncaught_exception(exc_type, exc_value, exc_traceback):
    """
    This function logs all uncaught exceptions other than KeyboardInterrupt.
    By setting sys.excepthook, any uncaught exceptions in the script
    importing this logger will be logged.
    """
    # Do not log KeyboardInterrupt as an error.
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))


# Set the custom exception handler to log unhandled exceptions.
sys.excepthook = handle_uncaught_exception
