"""
Stegasoo Debugging Utilities

Debugging, logging, and performance monitoring tools.

Configuration:
    STEGASOO_LOG_LEVEL env var controls log level:
        - Not set or empty: logging disabled (production default)
        - DEBUG: verbose debug output (encode/decode flow, crypto params, etc.)
        - INFO: operational messages (format detection, mode selection)
        - WARNING: potential issues (fallback KDF, format transcoding)
        - ERROR: operation failures

    STEGASOO_DEBUG=1 is a shorthand for STEGASOO_LOG_LEVEL=DEBUG

    CLI: stegasoo --debug encode ... (sets DEBUG level for that invocation)

All output goes to Python's logging module under the 'stegasoo' logger hierarchy.
The legacy debug.print() API is preserved for backward compatibility.
"""

import logging
import os
import sys
import time
import traceback
from collections.abc import Callable
from functools import wraps
from typing import Any

# Map string level names to logging levels
_LEVEL_MAP = {
    "DEBUG": logging.DEBUG,
    "INFO": logging.INFO,
    "WARNING": logging.WARNING,
    "ERROR": logging.ERROR,
    "CRITICAL": logging.CRITICAL,
}

# Root logger for the stegasoo package
logger = logging.getLogger("stegasoo")

# Global debug configuration
LOG_PERFORMANCE = True  # Log function timing
VALIDATION_ASSERTIONS = True  # Enable runtime validation assertions


def _configure_from_env() -> bool:
    """Configure logging from environment variables. Returns True if debug enabled."""
    # STEGASOO_DEBUG=1 is shorthand for DEBUG level
    if os.environ.get("STEGASOO_DEBUG", "").strip() in ("1", "true", "yes"):
        _setup_logging(logging.DEBUG)
        return True

    level_str = os.environ.get("STEGASOO_LOG_LEVEL", "").strip().upper()
    if level_str and level_str in _LEVEL_MAP:
        _setup_logging(_LEVEL_MAP[level_str])
        return level_str == "DEBUG"

    return False


def _setup_logging(level: int) -> None:
    """Configure the stegasoo logger with a stderr handler."""
    logger.setLevel(level)

    # Only add handler if none exist (avoid duplicates on re-init)
    if not logger.handlers:
        handler = logging.StreamHandler(sys.stderr)
        handler.setLevel(level)
        formatter = logging.Formatter(
            "[%(asctime)s.%(msecs)03d] [%(levelname)s] [%(name)s] %(message)s",
            datefmt="%H:%M:%S",
        )
        handler.setFormatter(formatter)
        logger.addHandler(handler)
    else:
        # Update existing handler level
        for handler in logger.handlers:
            handler.setLevel(level)


# Auto-configure on import
DEBUG_ENABLED = _configure_from_env()


def enable_debug(enable: bool = True) -> None:
    """Enable or disable debug mode globally."""
    global DEBUG_ENABLED
    DEBUG_ENABLED = enable
    if enable:
        _setup_logging(logging.DEBUG)
    else:
        logger.setLevel(logging.WARNING)


def enable_performance_logging(enable: bool = True) -> None:
    """Enable or disable performance timing."""
    global LOG_PERFORMANCE
    LOG_PERFORMANCE = enable


def enable_assertions(enable: bool = True) -> None:
    """Enable or disable validation assertions."""
    global VALIDATION_ASSERTIONS
    VALIDATION_ASSERTIONS = enable


def debug_print(message: str, level: str = "INFO") -> None:
    """Log a message at the given level via the stegasoo logger."""
    log_level = _LEVEL_MAP.get(level.upper(), logging.DEBUG)
    logger.log(log_level, message)


def debug_data(data: bytes, label: str = "Data", max_bytes: int = 32) -> str:
    """Format bytes for debugging."""
    if not logger.isEnabledFor(logging.DEBUG):
        return ""

    if not data:
        return f"{label}: Empty"

    if len(data) <= max_bytes:
        return f"{label} ({len(data)} bytes): {data.hex()}"
    else:
        return (
            f"{label} ({len(data)} bytes): "
            f"{data[:max_bytes // 2].hex()}...{data[-max_bytes // 2:].hex()}"
        )


def debug_exception(e: Exception, context: str = "") -> None:
    """Log exception with context for debugging."""
    logger.error("Exception in %s: %s: %s", context, type(e).__name__, e)
    if logger.isEnabledFor(logging.DEBUG):
        logger.debug(traceback.format_exc())


def time_function(func: Callable) -> Callable:
    """Decorator to time function execution for performance debugging."""

    @wraps(func)
    def wrapper(*args, **kwargs) -> Any:
        if not (logger.isEnabledFor(logging.DEBUG) and LOG_PERFORMANCE):
            return func(*args, **kwargs)

        start = time.perf_counter()
        try:
            result = func(*args, **kwargs)
            return result
        finally:
            end = time.perf_counter()
            logger.debug("%s took %.6fs", func.__name__, end - start)

    return wrapper


def validate_assertion(condition: bool, message: str) -> None:
    """Runtime validation that can be disabled in production."""
    if VALIDATION_ASSERTIONS and not condition:
        raise AssertionError(f"Validation failed: {message}")


def memory_usage() -> dict[str, float | str]:
    """Get current memory usage (if psutil is available)."""
    try:
        import psutil

        process = psutil.Process(os.getpid())
        mem_info = process.memory_info()

        return {
            "rss_mb": mem_info.rss / 1024 / 1024,
            "vms_mb": mem_info.vms / 1024 / 1024,
            "percent": process.memory_percent(),
        }
    except ImportError:
        return {"error": "psutil not installed"}


def hexdump(data: bytes, offset: int = 0, length: int = 64) -> str:
    """Create hexdump string for debugging binary data."""
    if not data:
        return "Empty"

    result = []
    data_to_dump = data[:length]

    for i in range(0, len(data_to_dump), 16):
        chunk = data_to_dump[i : i + 16]
        hex_str = " ".join(f"{b:02x}" for b in chunk)
        hex_str = hex_str.ljust(47)
        ascii_str = "".join(chr(b) if 32 <= b < 127 else "." for b in chunk)
        result.append(f"{offset + i:08x}: {hex_str}  {ascii_str}")

    if len(data) > length:
        result.append(f"... ({len(data) - length} more bytes)")

    return "\n".join(result)


def get_logger(name: str) -> logging.Logger:
    """Get a child logger under the stegasoo namespace.

    Usage in modules:
        from .debug import get_logger
        logger = get_logger(__name__)
        logger.debug("message")
    """
    return logging.getLogger(name)


class Debug:
    """Debugging utility class (backward-compatible API)."""

    def __init__(self):
        self.enabled = DEBUG_ENABLED

    def print(self, message: str, level: str = "INFO") -> None:
        """Print debug message."""
        debug_print(message, level)

    def data(self, data: bytes, label: str = "Data", max_bytes: int = 32) -> str:
        """Format bytes for debugging."""
        return debug_data(data, label, max_bytes)

    def exception(self, e: Exception, context: str = "") -> None:
        """Log exception with context."""
        debug_exception(e, context)

    def time(self, func: Callable) -> Callable:
        """Decorator to time function execution."""
        return time_function(func)

    def validate(self, condition: bool, message: str) -> None:
        """Runtime validation assertion."""
        validate_assertion(condition, message)

    def memory(self) -> dict[str, float | str]:
        """Get current memory usage."""
        return memory_usage()

    def hexdump(self, data: bytes, offset: int = 0, length: int = 64) -> str:
        """Create hexdump string."""
        return hexdump(data, offset, length)

    def enable(self, enable: bool = True) -> None:
        """Enable or disable debug mode."""
        enable_debug(enable)
        self.enabled = enable

    def enable_performance(self, enable: bool = True) -> None:
        """Enable or disable performance logging."""
        enable_performance_logging(enable)

    def enable_assertions(self, enable: bool = True) -> None:
        """Enable or disable validation assertions."""
        enable_assertions(enable)


# Create singleton instance
debug = Debug()
