#
# Copyright (c) 2021-2026 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import logging

from logging.config import fileConfig
from pathlib import Path
from typing import ClassVar, Literal

from ahriman.core.configuration import Configuration
from ahriman.core.log.http_log_handler import HttpLogHandler
from ahriman.core.log.log_context import LogContext
from ahriman.core.module_loader import optional_module
from ahriman.models.log_handler import LogHandler
from ahriman.models.repository_id import RepositoryId


class LogLoader:
    """
    simple static method class which setups application loggers

    Attributes:
        DEFAULT_LOG_FORMAT(str): (class attribute) default log format (in case of fallback)
        DEFAULT_LOG_LEVEL(int): (class attribute) default log level (in case of fallback)
        DEFAULT_LOG_STYLE(str): (class attribute) default log style (in case of fallback)
        DEFAULT_SYSLOG_DEVICE(Path): (class attribute) default path to syslog device
    """

    DEFAULT_LOG_FORMAT: ClassVar[str] = "[{levelname} {asctime}] [{name}]: {message}"
    DEFAULT_LOG_LEVEL: ClassVar[int] = logging.DEBUG
    DEFAULT_LOG_STYLE: ClassVar[Literal["%", "{", "$"]] = "{"
    DEFAULT_SYSLOG_DEVICE: ClassVar[Path] = Path("/") / "dev" / "log"

    @staticmethod
    def handler(selected: LogHandler | None) -> LogHandler:
        """
        try to guess default log handler. In case if ``selected`` is set, it will return specified value with appended
        _handler suffix. Otherwise, it will try to import journald handler and returns
        :attr:`ahriman.models.log_handler.LogHandler.Journald` if library is available. Otherwise, it will check if
        there is ``/dev/log`` device and returns :attr:`ahriman.models.log_handler.LogHandler.Syslog` in this
        case. And, finally, it will fall back to :attr:`ahriman.models.log_handler.LogHandler.Console`
        if none were found

        Args:
            selected(LogHandler | None): user specified handler if any

        Returns:
            LogHandler: selected log handler
        """
        if selected is not None:
            return selected

        if optional_module("systemd.journal"):
            return LogHandler.Journald  # journald import was found
        if LogLoader.DEFAULT_SYSLOG_DEVICE.exists():
            return LogHandler.Syslog
        return LogHandler.Console

    @staticmethod
    def load(repository_id: RepositoryId, configuration: Configuration, handler: LogHandler, *,
             quiet: bool, report: bool) -> None:
        """
        setup logging settings from configuration

        Args:
            repository_id(RepositoryId): repository unique identifier
            configuration(Configuration): configuration instance
            handler(LogHandler): selected default log handler, which will be used if no handlers were set
            quiet(bool): force disable any log messages
            report(bool): force enable or disable reporting
        """
        default_handler = f"{handler.value}_handler"

        try:
            log_configuration = Configuration()
            log_configuration.read(configuration.logging_path)

            # set handlers if they are not set
            for section in filter(lambda s: s.startswith("logger_"), log_configuration.sections()):
                if "handlers" in log_configuration[section]:
                    continue
                log_configuration.set_option(section, "handlers", default_handler)

            # load logging configuration
            fileConfig(log_configuration, disable_existing_loggers=True)
            logging.debug("using %s logger", default_handler)
        except Exception:
            logging.basicConfig(filename=None, format=LogLoader.DEFAULT_LOG_FORMAT,
                                style=LogLoader.DEFAULT_LOG_STYLE, level=LogLoader.DEFAULT_LOG_LEVEL)
            logging.exception("could not load logging from configuration, fallback to stderr")

        HttpLogHandler.load(repository_id, configuration, report=report)
        LogLoader.register_context()

        if quiet:
            logging.disable(logging.WARNING)  # only print errors here

    @staticmethod
    def register_context() -> None:
        """
        register logging context
        """
        # predefined context variables
        for variable in ("package_id", "request_id"):
            LogContext.register(variable)
        logging.setLogRecordFactory(LogContext.log_record_factory)
