# -*- coding: utf-8 -*-
# vim: sw=4:ts=4:expandtab

"""
pygogo
~~~~~~

A Python logging library with super powers

Examples:
    basic usage::

        >>> logger = Gogo('basic').logger
        >>> logger.debug('hello world')
        hello world

    intermediate usage::

        >>> formatter = logging.Formatter('IP: %(ip)s - %(message)s')
        >>> kwargs = {'low_formatter': formatter}
        >>> logger = Gogo('intermediate', **kwargs).get_logger(ip='1.1.1.1')
        >>> logger.debug('hello world')
        IP: 1.1.1.1 - hello world

    advanced usage::

        >>> from io import StringIO
        >>> from json import loads
        >>>
        >>> high = StringIO()
        >>> low = StringIO()
        >>> kwargs = {
        ...     'monolog': True, 'high_hdlr': handlers.fileobj_hdlr(high),
        ...     'low_hdlr': handlers.fileobj_hdlr(low)}
        >>> logger = Gogo('adv', **kwargs).get_structured_logger(ip='1.1.1.1')
        >>> logger.debug('debug')
        >>> logger.error('error')
        >>> loads(low.getvalue()) == {'ip': '1.1.1.1', 'message': 'debug'}
        True
        >>> loads(high.getvalue()) == {'ip': '1.1.1.1', 'message': 'error'}
        True
"""

import logging
import hashlib
import sys

from copy import copy
from . import formatters, handlers, utils

__version__ = "1.2.0"

__all__ = ["formatters", "handlers", "utils"]
__title__ = "pygogo"
__author__ = "Reuben Cummings"
__description__ = "A Python logging library with super powers"
__email__ = "reubano@gmail.com"
__license__ = "MIT"
__copyright__ = "Copyright 2015 Reuben Cummings"

module_hdlr = logging.StreamHandler(sys.stdout)
module_fltr = logging.Filter(name="%s.init" % __name__)

# prevent handler from logging `pygogo.main` events
module_hdlr.addFilter(module_fltr)
module_logger = logging.getLogger(__name__)
module_logger.addHandler(module_hdlr)


class Gogo(object):
    """A logging class that logs events via different handlers depending on the
    severity

    http://stackoverflow.com/a/28743317/408556

    Attributes:
        loggers (set[str]): Set of existing loggers

        monolog (bool): Log high level events only to high pass handler.

        name (string): The logger name.

        levels (dict): The min levels to log to handlers.

        handlers (dict): The high/low pass log handlers.

        formatters (dict): The high/low pass log formatter.

        +------------------------------------+-----------------+
        | messages level                     | message handler |
        +====================================+=================+
        | < levels['low']                    | none            |
        +------------------------------------+-----------------+
        | >= levels['low'], < levels['high'] | low handler     |
        +------------------------------------+-----------------+
        | >= levels['high']                  | both handlers * |
        +------------------------------------+-----------------+

        * This is the case when :attr:`monolog` is `False`. If :attr:`monolog`
          is True, then :attr:`handlers['high']` will be the only
          message handler

    Args:
        name (string): The logger name.

        high_level (string): The min level to log to high_hdlr
            (default: warning).

        low_level (string): The min level to log to low_hdlr
            (default: debug).

        kwargs (dict): Keyword arguments.

        +---------------------+---------------+
        | messages level      | Numeric value |
        +=====================+===============+
        | critical            | 50            |
        +---------------------+---------------+
        | error               | 40            |
        +---------------------+---------------+
        | warning             | 30            |
        +---------------------+---------------+
        | info                | 20            |
        +---------------------+---------------+
        | debug               | 10            |
        +---------------------+---------------+

    Kwargs:
        high_hdlr (obj): The high pass log handler (a :class:`logging.handlers`
            instance, default: `stderr` StreamHandler).

        low_hdlr (obj): The low pass log handler (a :class:`logging.handlers`
            instance, default: `stdout` StreamHandler).

        high_formatter (obj): The high pass log formatter (a
            :class:`logging.Formatter` instance, default:
            :data:`pygogo.formatters.basic_formatter`).

        low_formatter (obj): The low pass log formatter (a
            :class:`logging.Formatter` instance, default:
            :data:`pygogo.formatters.basic_formatter`).

        verbose (bool): If False, set low level to `info`, if True, set low
            level to `debug`, overrides `levels['low']` if specified
            (default: None).

        monolog (bool): Log high level events only to high pass handler (
            default: False)

    Returns:
        New instance of :class:`pygogo.Gogo`

    Examples:
        >>> Gogo('name').logger.debug('message')
        message
    """

    def __init__(self, name="root", high_level=None, low_level=None, **kwargs):
        """Initialization method.

        Examples:
            >>> Gogo('name') # doctest: +ELLIPSIS
            <pygogo.Gogo object at 0x...>
        """
        verbose = kwargs.get("verbose")
        high_level = high_level or "warning"

        if verbose is None:
            low_level = low_level or "debug"
        elif verbose:
            low_level = "debug"
        else:
            low_level = "info"

        self.levels = {
            "high": getattr(logging, high_level.upper(), None),
            "low": getattr(logging, low_level.upper(), None),
        }

        if not isinstance(self.levels["high"], int):
            raise ValueError("Invalid high_level: %s" % self.levels["high"])
        elif not isinstance(self.levels["low"], int):
            raise ValueError("Invalid low_level: %s" % self.levels["low"])
        elif self.levels["high"] < self.levels["low"]:
            raise ValueError("high_level must be >= low_level")

        self.loggers = set()
        self.name = name
        self.handlers = {
            "high": kwargs.get("high_hdlr", handlers.stderr_hdlr()),
            "low": kwargs.get("low_hdlr", handlers.stdout_hdlr()),
        }

        self.formatters = {
            "high": kwargs.get("high_formatter", formatters.basic_formatter),
            "low": kwargs.get("low_formatter", formatters.basic_formatter),
        }

        self.monolog = kwargs.get("monolog")

    @property
    def logger(self):
        """The logger property.

        Returns:
            New instance of :class:`logging.Logger`

        Examples:
            >>> from io import StringIO

            >>> s = StringIO()
            >>> sys.stderr = s
            >>> logger = Gogo('default').logger
            >>> logger  # doctest: +ELLIPSIS
            <...Logger...>
            >>> logger.debug('stdout')
            stdout
            >>> kwargs = {'low_level': 'info', 'monolog': True}
            >>> logger = Gogo('ignore_if_lt_info', **kwargs).logger
            >>> logger.debug('ignored')
            >>> logger.info('stdout')
            stdout
            >>> logger.warning('stderr')
            >>> s.getvalue().strip() == 'stderr'
            True
        """
        return self.get_logger()

    def update_hdlr(self, hdlr, level, formatter=None, monolog=False, **kwargs):
        """Update a handler with a formatter, level, and filters.

        Args:
            hdlr (obj): A :class:`logging.handlers` instance.

            level (int): The min level to log to to `hdlr`.

            formatter (obj): The log formatter (a :class:`logging.Formatter`
                instance, default: :data:`pygogo.formatters.basic_formatter`)

            monolog (bool): Log high level events only to high pass handler (
                default: False)

            kwargs (dict): Keyword arguemnts passed to
                `pygogo.utils.get_structured_filter`

        See also:
            :meth:`pygogo.Gogo.get_logger`

            :meth:`pygogo.Gogo.get_structured_logger`

            :func:`pygogo.utils.LogFilter`

            :func:`pygogo.utils.get_structured_filter`

        Examples:
            >>> low_hdlr = logging.StreamHandler(sys.stdout)
            >>> going = Gogo(low_hdlr=low_hdlr, monolog=True)
            >>> hdlr = going.handlers['low']
            >>> [hdlr.formatter, hdlr.filters, hdlr.level]
            [None, [], 0]
            >>> fmtr = going.formatters['low']
            >>> kwargs = {'formatter': fmtr, 'monolog': going.monolog}