#!/usr/bin/env python3
"""

"""
# Import:
from __future__ import annotations

# ##-- stdlib imports
import datetime
import enum
import functools as ftz
import itertools as itz
import logging as logmod
import pathlib as pl
import re
import time
import weakref
from uuid import UUID, uuid1
# ##-- end stdlib imports

from ._interface import LogLevel_e

# ##-- types
# isort: off
# General
import abc
import collections.abc
import typing
import types
from typing import cast, assert_type, assert_never
from typing import Generic, NewType, Never
from typing import no_type_check, final, override, overload
# Protocols and Interfaces:
from typing import Protocol, runtime_checkable
# isort: on
# ##-- end types

# ##-- type checking
# isort: off
if typing.TYPE_CHECKING:
    from typing import Final, ClassVar, Any, Self
    from typing import Literal, LiteralString
    from typing import TypeGuard
    from collections.abc import Iterable, Iterator, Callable, Generator
    from collections.abc import Sequence, Mapping, MutableMapping, Hashable

    from jgdv import Maybe
## isort: on
# ##-- end type checking

##-- logging
logging = logmod.getLogger(__name__)
##-- end logging

# Vars:
LoggerClass : Final[type] = logmod.getLoggerClass()
# Body:

class JGDVLogger(LoggerClass):
    """ Basic extension of the logger class

    checks the classvar _levels (intEnum) for additional log levels
    which can be accessed as attributes and items.
    eg: logger.trace(...)
    and: logger['trace'](...)


    A Logger can add prefixes to a logged messages.
    eg:_

    ..code: python

        logger.set_prefixes('[Test]')
        logger.info('this is a test message')
        # Result :  '[Test] this is a test message'

    """
    _prefixes  : list[str|Callable]
    _colour    : Maybe[str]

    @classmethod
    def install(cls) -> None:
        logmod.setLoggerClass(cls)

    def __init__(self, *args:Any, **kwargs:Any) -> None:  # noqa: ANN401
        super().__init__(*args, **kwargs)
        self._prefixes = []
        self._colour   = None

    def __getattr__(self, attr:str) -> Callable:
        try:
            return ftz.partial(self.log, LogLevel_e[attr])
        except KeyError:
            msg = "Invalid Extension Log Level"
            raise AttributeError(msg, attr) from None

    def __getitem__(self, key:str) -> Callable:
        return self.__getattr__(key)

    ##--| public methods
    def set_colour(self, colour:Maybe[str]) -> None:
        self._colour = self._colour or colour

    def set_prefixes(self, *prefixes:str|Callable) -> None:
        """
        Set prefixes for the logger to add to logged messages
        """
        match prefixes:
            case None | ():
                pass
            case [*xs]:
                self._prefixes =  list(xs)

    def prefix(self, prefix:str|Callable) -> Self:
        """ Create a new logger, with a prefix """
        match prefix:
            case str():
                child = self.getChild(prefix)
            case x if callable(x):
                child = self.getChild(prefix.__name__)
            case _:
                raise TypeError(prefix)

        child.set_prefixes(*self._prefixes, prefix)
        return child

    def getChild(self, name:str) -> JGDVLogger:  # noqa: N802
        """
        Create a child logger, copying the colour of this logger
        """
        child = cast("JGDVLogger", super().getChild(name))
        child.set_colour(self._colour)
        return child

    def makeRecord(self, *args:Any, **kwargs:Any) -> logmod.LogRecord:  # noqa: ANN401, N802
        """
        A factory method which can be overridden in subclasses to create
        specialized LogRecords.
        args: name, level, fn, lno, msg, args, exc_info,
        kwargs: func=None, extra=None, sinfo=None
        """
        rv        : logmod.LogRecord
        modified  : list  = list(args)
        msg_total         = []
        for pre in self._prefixes:
            match pre:
                case None:
                    pass
                case str():
                    msg_total.append(pre)
                case x if callable(x):
                    msg_total.append(x())
        else:
            match args[4]:
                case str():
                    msg_total.append(args[4])
                case x:
                    msg_total.append("%s")
                    modified[5] = [args[4],  *args[5]]
            modified[4] = "".join(msg_total)

        rv = super().makeRecord(*modified, **kwargs)
        if self._colour and "colour" not in rv.__dict__:
            rv.__dict__["colour"] = self._colour
        return rv
