# src/blackant/utils/logging.py
"""BlackAnt SDK Logging module.

This module provides unified logging functionality for the entire BlackAnt SDK.
Supports colorized console output, file logging, and request tracking.
"""

import logging
import sys
import threading
import re
from datetime import datetime
from pathlib import Path
from typing import Optional, Dict, Union
from colorama import Fore, Style, init

init(autoreset=True)


class CustomFormatter(logging.Formatter):
    """Custom formatter with colorized log levels and abbreviated level names."""

    LEVEL_ABBREVIATIONS = {
        logging.DEBUG: "DBG",
        logging.INFO: "INF",
        logging.WARNING: "WAR",
        logging.ERROR: "ERR",
        logging.CRITICAL: "CRT",
    }

    LEVEL_COLORS = {
        logging.DEBUG: Fore.WHITE,
        logging.INFO: Fore.CYAN,
        logging.WARNING: Fore.YELLOW,
        logging.ERROR: Fore.RED,
        logging.CRITICAL: Fore.LIGHTRED_EX,
    }

    def __init__(self, use_colors: bool = True, include_request_id: bool = True):
        self.use_colors = use_colors
        self.include_request_id = include_request_id

        if include_request_id:
            template = (
                "[{asctime}].[{name}].[{levelname}].[{request_id}] -> "
                "{message} \t || {module}:{funcName}"
            )
        else:
            template = "[{asctime}].[{name}].[{levelname}] -> {message} \t || {module}:{funcName}"

        super().__init__(template, style="{", datefmt="%Y-%m-%d %H:%M:%S")

    def format(self, record: logging.LogRecord) -> str:
        if self.include_request_id:
            request_id = getattr(record, "request_id", "NO-REQ-ID")
            record.request_id = request_id

        level_abbr = self.LEVEL_ABBREVIATIONS.get(record.levelno, record.levelname[:3])

        if self.use_colors:
            color = self.LEVEL_COLORS.get(record.levelno, "")
            levelname_colored = f"{color}{level_abbr}{Style.RESET_ALL}"
            msg_colored = f"{color}{record.getMessage()}{Style.RESET_ALL}"
        else:
            levelname_colored = level_abbr
            msg_colored = record.getMessage()

        record_copy = logging.makeLogRecord(record.__dict__)
        record_copy.levelname = levelname_colored
        record_copy.msg = msg_colored

        return super().format(record_copy)


class SensitiveDataFilter(logging.Filter):  # pylint: disable=too-few-public-methods
    """Filter to scrub sensitive data from log messages."""

    DEFAULT_PATTERNS = [
        r"Bearer\s+[A-Za-z0-9\-_]+",
        r'token["\s:=]+[A-Za-z0-9\-_\.]+',
        r'password["\s:=]+\S+',
        r'api_key["\s:=]+[A-Za-z0-9\-_]+',
        r"Authorization:\s*[^\s]+",
        r"X-API-Key:\s*[^\s]+",
    ]

    def __init__(self, patterns: Optional[list] = None):
        super().__init__()
        self.patterns = patterns or self.DEFAULT_PATTERNS
        self._compiled_patterns = [re.compile(pattern, re.IGNORECASE) for pattern in self.patterns]

    def filter(self, record: logging.LogRecord) -> bool:
        message = record.getMessage()
        for pattern in self._compiled_patterns:
            message = pattern.sub("[REDACTED]", message)

        record.msg = message
        record.args = ()

        return True


class RequestIdFilter(logging.Filter):
    """Filter that adds request ID context to log records."""

    def __init__(self):
        super().__init__()
        self._local = threading.local()

    def set_request_id(self, request_id: str):
        self._local.request_id = request_id

    def clear_request_id(self):
        if hasattr(self._local, "request_id"):
            delattr(self._local, "request_id")

    def get_request_id(self) -> Optional[str]:
        return getattr(self._local, "request_id", None)

    def filter(self, record: logging.LogRecord) -> bool:
        request_id = self.get_request_id()
        record.request_id = request_id or "NO-REQ-ID"
        return True


class LoggerManager:  # pylint: disable=too-many-instance-attributes
    """Central logger manager for the BlackAnt SDK."""

    def __init__(  # pylint: disable=too-many-arguments
        self,
        app_name: str = "BlackAnt",
        default_log_file: Optional[str] = None,
        log_level: Union[str, int] = logging.INFO,
        enable_colors: bool = True,
        enable_request_tracking: bool = True,
        enable_sensitive_filter: bool = True,
    ):
        self.app_name = app_name
        self.enable_colors = enable_colors
        self.enable_request_tracking = enable_request_tracking
        self.enable_sensitive_filter = enable_sensitive_filter

        if default_log_file is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            default_log_file = f"{app_name.lower()}_{timestamp}.log"

        self.log_file = Path(default_log_file)

        self.request_id_filter = RequestIdFilter() if enable_request_tracking else None
        self.sensitive_filter = SensitiveDataFilter() if enable_sensitive_filter else None

        self._setup_root_logger(log_level)

        self._module_loggers: Dict[str, logging.Logger] = {}

    def _setup_root_logger(self, log_level: Union[str, int]):
        root_logger = logging.getLogger(self.app_name)
        root_logger.setLevel(log_level)
        root_logger.propagate = False

        for handler in root_logger.handlers[:]:
            root_logger.removeHandler(handler)

        console_handler = self._create_console_handler()
        root_logger.addHandler(console_handler)

        file_handler = self._create_file_handler(self.log_file)
        root_logger.addHandler(file_handler)

        self.root_logger = root_logger

    def _create_console_handler(self) -> logging.StreamHandler:
        handler = logging.StreamHandler(sys.stdout)
        handler.setLevel(logging.INFO)

        formatter = CustomFormatter(
            use_colors=self.enable_colors, include_request_id=self.enable_request_tracking
        )
        handler.setFormatter(formatter)

        if self.request_id_filter:
            handler.addFilter(self.request_id_filter)

        if self.sensitive_filter:
            handler.addFilter(self.sensitive_filter)

        return handler

    def _create_file_handler(self, log_file: Path) -> logging.FileHandler:
        log_file.parent.mkdir(parents=True, exist_ok=True)

        handler = logging.FileHandler(log_file, mode="w", encoding="utf-8")
        handler.setLevel(logging.DEBUG)

        formatter = CustomFormatter(
            use_colors=False, include_request_id=self.enable_request_tracking
        )
        handler.setFormatter(formatter)

        if self.request_id_filter:
            handler.addFilter(self.request_id_filter)

        if self.sensitive_filter:
            handler.addFilter(self.sensitive_filter)

        return handler

    def get_logger(self, name: str) -> logging.Logger:
        full_name = f"{self.app_name}.{name}" if name else self.app_name

        if full_name not in self._module_loggers:
            logger = logging.getLogger(full_name)
            logger.setLevel(self.root_logger.level)

            if not logger.handlers:
                logger.parent = self.root_logger

            self._module_loggers[full_name] = logger

        return self._module_loggers[full_name]

    def set_request_id(self, request_id: str):
        if self.request_id_filter:
            self.request_id_filter.set_request_id(request_id)

    def clear_request_id(self):
        if self.request_id_filter:
            self.request_id_filter.clear_request_id()

    def update_log_file(self, new_log_file: Union[str, Path]):
        new_path = Path(new_log_file)

        for handler in self.root_logger.handlers[:]:
            if isinstance(handler, logging.FileHandler):
                self.root_logger.removeHandler(handler)
                handler.close()

        new_file_handler = self._create_file_handler(new_path)
        self.root_logger.addHandler(new_file_handler)

        self.log_file = new_path
        self.root_logger.info("Log file updated to: %s", new_path)

    def set_level(self, level: Union[str, int]):
        self.root_logger.setLevel(level)
        for logger in self._module_loggers.values():
            logger.setLevel(level)

    def enable_debug_mode(self):
        self.set_level(logging.DEBUG)

        for handler in self.root_logger.handlers:
            if isinstance(handler, logging.StreamHandler) and handler.stream == sys.stdout:
                handler.setLevel(logging.DEBUG)


_logger_manager: Optional[LoggerManager] = None


def init_logging(  # pylint: disable=too-many-arguments
    app_name: str = "BlackAnt",
    log_file: Optional[str] = None,
    log_level: Union[str, int] = logging.INFO,
    enable_colors: bool = True,
    enable_request_tracking: bool = True,
    enable_sensitive_filter: bool = True,
) -> LoggerManager:
    global _logger_manager  # pylint: disable=global-statement,invalid-name

    _logger_manager = LoggerManager(
        app_name=app_name,
        default_log_file=log_file,
        log_level=log_level,
        enable_colors=enable_colors,
        enable_request_tracking=enable_request_tracking,
        enable_sensitive_filter=enable_sensitive_filter,
    )

    return _logger_manager


def get_logger(name: str = "") -> logging.Logger:
    global _logger_manager  # pylint: disable=global-statement,invalid-name

    if _logger_manager is None:
        _logger_manager = init_logging()

    return _logger_manager.get_logger(name)


def set_request_id(request_id: str):
    if _logger_manager:
        _logger_manager.set_request_id(request_id)


def clear_request_id():
    if _logger_manager:
        _logger_manager.clear_request_id()


def update_log_file(new_log_file: Union[str, Path]):
    if _logger_manager:
        _logger_manager.update_log_file(new_log_file)


def set_log_level(level: Union[str, int]):
    if _logger_manager:
        _logger_manager.set_level(level)


def enable_debug_mode():
    if _logger_manager:
        _logger_manager.enable_debug_mode()


def gb_to_mb(gigabytes: float) -> int:
    return int(gigabytes * 1024)


if __name__ == "__main__":
    init_logging(app_name="BlackAnt-Demo", log_level="DEBUG")

    main_logger = get_logger("main")
    http_logger = get_logger("http")

    main_logger.debug("Debug message")
    main_logger.info("Info message")
    main_logger.warning("Warning message")
    main_logger.error("Error message")

    set_request_id("req_demo_12345")
    http_logger.info("Message with request ID")

    clear_request_id()
    main_logger.info("Message without request ID")

    http_logger.info("Token test: Bearer secret_token_123")
