from __future__ import annotations

import logging
from logging.handlers import RotatingFileHandler
from typing import TYPE_CHECKING, cast

import structlog

from home_agent.core.config import LoggingConfig

if TYPE_CHECKING:
    from structlog.types import Processor

_active_logging_config = LoggingConfig()
_configured = False


def configure_logging(config: LoggingConfig) -> None:
    global _active_logging_config, _configured

    reset_logging()
    config.directory.mkdir(parents=True, exist_ok=True)

    shared_processors: list[Processor] = [
        structlog.contextvars.merge_contextvars,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.processors.TimeStamper(fmt="iso", utc=True),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
    ]
    renderer_pre_chain = list(shared_processors)
    processor_formatter = structlog.stdlib.ProcessorFormatter(
        foreign_pre_chain=renderer_pre_chain,
        processors=[
            structlog.stdlib.ProcessorFormatter.remove_processors_meta,
            structlog.processors.JSONRenderer(),
        ],
    )
    console_formatter = structlog.stdlib.ProcessorFormatter(
        foreign_pre_chain=renderer_pre_chain,
        processors=[
            structlog.stdlib.ProcessorFormatter.remove_processors_meta,
            structlog.dev.ConsoleRenderer(),
        ],
    )

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

    file_handler = RotatingFileHandler(
        config.directory / config.file_name,
        maxBytes=config.max_bytes,
        backupCount=config.backup_count,
        encoding="utf-8",
    )
    file_handler.setLevel(_coerce_level(config.file_level))
    file_handler.setFormatter(processor_formatter)
    root_logger.addHandler(file_handler)

    if config.enable_console:
        console_handler = logging.StreamHandler()
        console_handler.setLevel(_coerce_level(config.console_level))
        console_handler.setFormatter(console_formatter)
        root_logger.addHandler(console_handler)

    structlog.configure(
        processors=[
            *shared_processors,
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        wrapper_class=structlog.stdlib.BoundLogger,
        logger_factory=structlog.stdlib.LoggerFactory(),
        cache_logger_on_first_use=True,
    )

    _active_logging_config = config
    _configured = True


def reset_logging() -> None:
    global _active_logging_config, _configured

    root_logger = logging.getLogger()
    for handler in list(root_logger.handlers):
        root_logger.removeHandler(handler)
        handler.close()
    logging.shutdown()
    structlog.reset_defaults()
    _active_logging_config = LoggingConfig()
    _configured = False


def get_logger(name: str) -> structlog.stdlib.BoundLogger:
    if not _configured:
        configure_logging(_active_logging_config)
    return cast("structlog.stdlib.BoundLogger", structlog.get_logger(name))


def get_logging_config() -> LoggingConfig:
    return _active_logging_config


def _coerce_level(level_name: str) -> int:
    return logging.getLevelNamesMapping().get(level_name.upper(), logging.DEBUG)
