from __future__ import annotations

import logging
import pathlib
from collections.abc import Callable

from academy.logging.configs.base import LogConfig
from academy.logging.helpers import _Formatter
from academy.logging.helpers import _os_thread_filter

logger = logging.getLogger(__name__)


class FileLogging(LogConfig):
    """Configures logging to a file."""

    def __init__(
        self,
        *,
        logfile: str | pathlib.Path,
        level: int | str = logging.INFO,
        extra: int = False,
    ) -> None:
        super().__init__()
        self.logfile = logfile
        self.level = level
        self.extra = extra

    def init_logging(self) -> Callable[[], None]:
        """Initialize logging to file."""
        path = pathlib.Path(self.logfile)
        path.parent.mkdir(parents=True, exist_ok=True)
        file_handler = logging.FileHandler(path)
        file_handler.setFormatter(
            _Formatter(color=False, extra=self.extra),
        )
        file_handler.setLevel(self.level)
        if self.extra:
            file_handler.addFilter(_os_thread_filter)

        root_logger = logging.getLogger()
        root_logger.addHandler(file_handler)

        # if the root logger is not going to log at this level, reconfigure it
        # to do so. (smaller log levels = more logging)
        # for example, by default the root logger logs at level 30 WARNING,
        # which means INFO (20) logs will not be recorded. But implicitly
        # if the user is asking for INFO logs, we should provide INFO logs.
        root_logger.level = min(root_logger.level, file_handler.level)

        logging.captureWarnings(True)

        logger.info(
            'Configured logger (file-level=%s)',
            logging.getLevelName(self.level)
            if isinstance(self.level, int)
            else self.level,
        )
        file_handler.flush()

        def uninitialize_callback() -> None:
            root_logger.removeHandler(file_handler)
            file_handler.close()

        return uninitialize_callback

    def __repr__(self) -> str:
        return (
            f'<file config {self.logfile}, {self.level}, extra={self.extra}>'
        )
