# pylint: disable=global-statement
"""Logging utility with per-run file logging and granular processor output."""

import logging
import os
import sys
from datetime import datetime
from pathlib import Path
from typing import Optional, Dict, Any

_LOGGING_CONFIGURED = False


class ThirdPartyFilter(logging.Filter):
    """Filter to suppress logs from third-party libraries based on level."""

    NOISY_LOGGERS = {
        "httpcore",
        "httpx",
        "openai",
        "urllib3",
        "asyncio",
        "multipart",
        "charset_normalizer",
        "moviepy",
        "imageio",
        "imageio_ffmpeg",
        "PIL",
        "matplotlib",
        "easyocr",
        "ultralytics",
        "torch",
        "tensorflow",
        "llama_index",
        "litellm",
    }

    def __init__(self, min_level: int = logging.WARNING):
        """
        Initialize filter with minimum log level for third-party libraries.

        Args:
            min_level: Minimum log level to allow (default: WARNING)
        """
        super().__init__()
        self.min_level = min_level

    def filter(self, record: logging.LogRecord) -> bool:
        """Filter out logs from noisy third-party libraries below min_level."""
        logger_name = record.name
        is_noisy = any(logger_name.startswith(lib) for lib in self.NOISY_LOGGERS)

        if is_noisy and record.levelno < self.min_level:
            return False

        return True


class TierLevelFilter(logging.Filter):
    """Filter to enforce tier-specific log levels on console output."""

    def __init__(self, tier_levels: Dict[str, str]):
        """
        Initialize filter with tier-specific log levels.

        Args:
            tier_levels: Dict mapping tier names to log level strings (e.g., {"utils": "INFO"})
        """
        super().__init__()
        self.module_levels = {}
        tier_modules = {
            "processors": "mini_videorag.processors",
            "llm": "mini_videorag.llm",
            "core": "mini_videorag.core",
            "storage": "mini_videorag.storage",
            "api": "mini_videorag.api",
            "temporal": "mini_videorag.temporal",
            "utils": "mini_videorag.utils",
        }

        for tier_name, level_str in tier_levels.items():
            if tier_name in tier_modules:
                module_path = tier_modules[tier_name]
                level = getattr(logging, level_str.upper(), logging.INFO)
                self.module_levels[module_path] = level

    def filter(self, record: logging.LogRecord) -> bool:
        """Filter logs based on tier-specific levels."""
        for module_path, min_level in self.module_levels.items():
            if record.name.startswith(module_path):
                # Suppress if below the tier's minimum level
                return record.levelno >= min_level

        return True


def setup_logger(
    run_id: str = None,
    log_dir: str = "logs",
    log_level: Optional[str] = None,
    third_party_level: Optional[str] = None,
    config: Optional[Dict[str, Any]] = None,
) -> None:
    """
    Setup logging with console + per-run file output.

    Args:
        run_id: Unique run identifier (default: current timestamp YYYYMMDD_HHMMSS)
        log_dir: Directory for log files
        log_level: Console log level (default: INFO)
        third_party_level: Third-party library log level (default: WARNING)
        config: Optional config dict from video_rag_config.yml
    """
    global _LOGGING_CONFIGURED

    if _LOGGING_CONFIGURED:
        return

    _LOGGING_CONFIGURED = True

    tier_levels = {}
    if config and "logging" in config:
        log_config = config["logging"]
        log_level = log_config.get("console_level", log_level or "INFO")
        third_party_level = log_config.get("third_party_level", third_party_level or "WARNING")
        log_dir = log_config.get("log_dir", log_dir)

        if "tiers" in log_config:
            tier_levels = log_config["tiers"]
    else:
        log_level = log_level or os.getenv("LOG_LEVEL", "INFO")
        third_party_level = third_party_level or os.getenv("THIRD_PARTY_LOG_LEVEL", "WARNING")

    run_id = run_id or datetime.now().strftime("%Y%m%d_%H%M%S")

    console_level = getattr(logging, log_level.upper())
    third_party_lvl = getattr(logging, third_party_level.upper())

    log_path = Path(log_dir)
    log_path.mkdir(parents=True, exist_ok=True)

    log_file = log_path / f"run_{run_id}.log"

    formatter = logging.Formatter(
        "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)d] %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
    )

    root_logger = logging.getLogger()
    root_logger.setLevel(logging.DEBUG)
    root_logger.handlers.clear()

    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(console_level)
    console_handler.setFormatter(formatter)
    console_handler.addFilter(ThirdPartyFilter(min_level=third_party_lvl))
    if tier_levels:
        console_handler.addFilter(TierLevelFilter(tier_levels))
    root_logger.addHandler(console_handler)

    file_handler = logging.FileHandler(log_file)
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(formatter)
    root_logger.addHandler(file_handler)

    _configure_tier_logging(tier_levels)

    _configure_third_party_logging(third_party_lvl, config)

    root_logger.info(f"Logging initialized - Run ID: {run_id}")
    root_logger.info(f"Log file: {log_file}")

    if tier_levels:
        tier_info = ", ".join([f"{k}={v}" for k, v in tier_levels.items()])
        root_logger.info(f"Tier levels: {tier_info}")
    root_logger.info(
        f"Console level: {logging.getLevelName(console_level)}, "
        f"Third-party level: {logging.getLevelName(third_party_lvl)}"
    )


def _configure_tier_logging(tier_levels: Dict[str, str]) -> None:
    """
    Configure logging for different tiers (modules).

    Args:
        tier_levels: Dict mapping tier names to log levels
    """
    tier_modules = {
        "processors": "mini_videorag.processors",
        "llm": "mini_videorag.llm",
        "core": "mini_videorag.core",
        "storage": "mini_videorag.storage",
        "api": "mini_videorag.api",
        "temporal": "mini_videorag.temporal",
        "utils": "mini_videorag.utils",
    }

    for tier_name, module_path in tier_modules.items():
        if tier_name in tier_levels:
            level_str = tier_levels[tier_name]
            level = getattr(logging, level_str.upper(), logging.INFO)
            module_logger = logging.getLogger(module_path)
            module_logger.setLevel(level)
            module_logger.propagate = True


def _configure_third_party_logging(level: int, config: Optional[Dict[str, Any]] = None) -> None:
    """
    Configure logging for third-party libraries.

    Args:
        level: Log level to set for third-party libraries
        config: Optional config dict with suppression settings
    """
    suppress_moviepy = True
    suppress_ultralytics = True

    if config and "logging" in config:
        log_config = config["logging"]
        suppress_moviepy = log_config.get("suppress_moviepy", True)
        suppress_ultralytics = log_config.get("suppress_ultralytics", True)

    if suppress_moviepy:
        os.environ["IMAGEIO_FFMPEG_EXE"] = os.environ.get("IMAGEIO_FFMPEG_EXE", "ffmpeg")
        logging.getLogger("moviepy").setLevel(logging.ERROR)
        logging.getLogger("imageio").setLevel(logging.ERROR)
        logging.getLogger("imageio_ffmpeg").setLevel(logging.ERROR)

    if suppress_ultralytics:
        logging.getLogger("ultralytics").setLevel(logging.ERROR)
        os.environ["YOLO_VERBOSE"] = "False"

    for lib in [
        "httpx",
        "httpcore",
        "openai",
        "urllib3",
        "PIL",
        "matplotlib",
        "easyocr",
        "torch",
        "tensorflow",
        "LiteLLM",
        "litellm",
    ]:
        logging.getLogger(lib).setLevel(level)


def log_processor_result(logger: logging.Logger, result) -> None:
    """
    Log detailed processor result.

    Args:
        logger: Logger instance
        result: ProcessorResult object
    """
    if result.success:
        logger.info(f"✓ {result.processor_name} completed successfully")
        logger.debug(f"Data: {result.data}")
        if result.segments:
            logger.debug(f"Segments: {len(result.segments)} time segments")
        if result.metadata:
            logger.debug(f"Metadata: {result.metadata}")
    else:
        logger.error(f"✗ {result.processor_name} failed: {result.error}")
