# piker: trading gear for hackers
# Copyright (C) 2018-present  Tyler Goodlet (in stewardship of piker0)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

"""
Log like a forester!
"""
import logging
import json
import reprlib
from typing import (
    Callable,
)

import tractor
from pygments import (
    highlight,
    lexers,
    formatters,
)

# Makes it so we only see the full module name when using ``__name__``
# without the extra "piker." prefix.
_proj_name: str = 'piker'


def get_logger(
    name: str|None = None,
    **tractor_log_kwargs,
) -> logging.Logger:
    '''
    Return the package log or a sub-logger if a `name=` is provided,
    which defaults to the calling module's pkg-namespace path.

    See `tractor.log.get_logger()` for details.

    '''
    pkg_name: str = _proj_name
    if (
        name
        and
        pkg_name in name
    ):
        name: str = name.lstrip(f'{_proj_name}.')

    return tractor.log.get_logger(
        name=name,
        pkg_name=pkg_name,
        **tractor_log_kwargs,
    )


def get_console_log(
    level: str|None = None,
    name: str|None = None,
    pkg_name: str|None = None,
    with_tractor_log: bool = False,
    # ?TODO, support a "log-spec" style `str|dict[str, str]` which
    # dictates both the sublogger-key and a level?
    # -> see similar idea in `modden`'s usage.
    **tractor_log_kwargs,

) -> logging.Logger:
    '''
    Get the package logger and enable a handler which writes to
    stderr.

    Yeah yeah, i know we can use `DictConfig`.
    You do it.. Bp

    '''
    pkg_name: str = _proj_name
    if (
        name
        and
        pkg_name in name
    ):
        name: str = name.lstrip(f'{_proj_name}.')

    tll: str|None = None
    if (
        with_tractor_log is not False
    ):
        tll = level

    elif maybe_actor := tractor.current_actor(
        err_on_no_runtime=False,
    ):
        tll = maybe_actor.loglevel

    if tll:
        t_log = tractor.log.get_console_log(
            level=tll,
            name='tractor',  # <- XXX, force root tractor log!
            **tractor_log_kwargs,
        )
        # TODO/ allow only enabling certain tractor sub-logs?
        assert t_log.name == 'tractor'

    return tractor.log.get_console_log(
        level=level,
        name=name,
        pkg_name=pkg_name,
        **tractor_log_kwargs,
    )


def colorize_json(
    data: dict,
    style='algol_nu',
):
    '''
    Colorize json output using ``pygments``.

    '''
    formatted_json = json.dumps(
        data,
        sort_keys=True,
        indent=4,
    )
    return highlight(
        formatted_json,
        lexers.JsonLexer(),

        # likeable styles: algol_nu, tango, monokai
        formatters.TerminalTrueColorFormatter(style=style)
    )


# TODO, eventually defer to the version in `modden` once
# it becomes a dep!
def mk_repr(
    **repr_kws,
) -> Callable[[str], str]:
    '''
    Allocate and deliver a `repr.Repr` instance with provided input
    settings using the std-lib's `reprlib` mod,
     * https://docs.python.org/3/library/reprlib.html

    ------ Ex. ------
    An up to 6-layer-nested `dict` as multi-line:
    - https://stackoverflow.com/a/79102479
    - https://docs.python.org/3/library/reprlib.html#reprlib.Repr.maxlevel

    '''
    def_kws: dict[str, int] = dict(
        indent=2,
        maxlevel=6,  # recursion levels
        maxstring=66,  # match editor line-len limit
    )
    def_kws |= repr_kws
    reprr = reprlib.Repr(**def_kws)
    return reprr.repr
