"""
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
"""

import logging
import re


class OptionalFieldFormatter(logging.Formatter):
    """
    Logging formatter that supports optional fields marked by `?`.

    Optional fields are denoted by placing a `?` after the field name inside
    the parentheses, e.g., `%(role?)s`. If the field is not provided in the
    log call's `extra` dict, it will use the default value from `defaults`
    or `None` if no default is specified.

    :param fmt: Format string with optional `%(name?)s` style fields.
    :type fmt: str or None
    :param datefmt: Date format string, passed to parent :class:`logging.Formatter`.
    :type datefmt: str or None
    :param style: Formatting style, must be '%'. Passed to parent.
    :type style: str
    :param defaults: Default values for optional fields when not provided.
    :type defaults: dict or None

    :example:

        >>> formatter = OptionalFieldFormatter(
        ...     fmt="%(asctime)s %(levelname)s %(role?)s %(message)s",
        ...     defaults={"role": ""-""}
        ... )
        >>> handler = logging.StreamHandler()
        >>> handler.setFormatter(formatter)
        >>> logger = logging.getLogger(__name__)
        >>> logger.addHandler(handler)
        >>>
        >>> logger.chat("Hello there!", extra={"role": "USER"})
        2025-01-15 10:30:00 CHAT USER Hello there!
        >>>
        >>> logger.info("A logging message")
        2025-01-15 10:30:01 INFO - A logging message

    .. note::
        Only `%`-style formatting is supported. The `{` and `$` styles are not
        compatible with this formatter.

    .. seealso::
        :class:`logging.Formatter` for base formatter documentation.
    """

    # Match %(name?)s or %(name?)d etc.
    OPTIONAL_PATTERN = re.compile(r"%\((\w+)\?\)([sdifFeEgGxXocrba%])")

    def __init__(self, fmt=None, datefmt=None, style="%", defaults=None):
        self.defaults = defaults or {}

        self.optional_fields = set(self.OPTIONAL_PATTERN.findall(fmt or ""))

        # Convert %(name?)s to %(name)s for standard formatting
        normalized_fmt = self.OPTIONAL_PATTERN.sub(r"%(\1)\2", fmt or "")

        super().__init__(normalized_fmt, datefmt, style)

    def format(self, record):
        for field, _ in self.optional_fields:
            if not hasattr(record, field):
                default = self.defaults.get(field, None)
                setattr(record, field, default)

        return super().format(record)
