import sys
import inspect
import logging

from textwrap import dedent

from .utils import _get_variable
from .exceptions import HelpfulError

class Yikes:
    def find_module(self, fullname, path=None):
        if fullname == 'requests':
            return self
        return None

    def _get_import_chain(self, *, until=None):
        stack = inspect.stack()[2:]
        try:
            for frameinfo in stack:
                try:
                    if not frameinfo.code_context:
                        continue

                    data = dedent(''.join(frameinfo.code_context))
                    if data.strip() == until:
                        raise StopIteration

                    yield frameinfo.filename, frameinfo.lineno, data.strip()
                    del data
                finally:
                    del frameinfo
        finally:
            del stack

    def _format_import_chain(self, chain, *, message=None):
        lines = []
        for line in chain:
            lines.append("In %s, line %s:\n    %s" % line)

        if message:
            lines.append(message)

        return '\n'.join(lines)

    def load_module(self, name):
        if _get_variable('allow_requests'):
            sys.meta_path.pop(0)
            return __import__('requests')

        import_chain = tuple(self._get_import_chain(until='from .bot import MusicBot'))
        import_tb = self._format_import_chain(import_chain)

        raise HelpfulError(
            "You are attempting to import requests, or import a module that uses requests.  "
            "Requests (or any module that uses requests) should not be used in this code.  "
            "See %s for why requests is not suitable for this code."
            % "[https://discordpy.readthedocs.io/en/latest/faq.html#what-does-blocking-mean]",

            "Don't use requests, use aiohttp instead.  The api is very similar to requests "
            "when using session objects. [http://aiohttp.readthedocs.io/en/stable/]  If "
            "a module you're trying to use depends on requests, see if you can find a similar "
            "module compatable with asyncio.  If you can't find one, learn how to avoid blocking "
            "in coroutines.  If you're new to programming, consider learning more about how "
            "asynchronous code and coroutines work.  Blocking calls (notably HTTP requests) can take "
            "a long time, during which the bot is unable to do anything but wait for it.  "
            "If you're sure you know what you're doing, simply add `allow_requests = True` above your "
            "import statement, that being `import requests` or whatever requests dependent module.",

            footnote="Import traceback (most recent call last):\n" + import_tb
        )

sys.meta_path.insert(0, Yikes())

from .bot import MusicBot
from .constructs import BetterLogRecord

__all__ = ['MusicBot']

logging.setLogRecordFactory(BetterLogRecord)

_func_prototype = "def {logger_func_name}(self, message, *args, **kwargs):\n" \
                  "    if self.isEnabledFor({levelname}):\n" \
                  "        self._log({levelname}, message, args, **kwargs)"

def _add_logger_level(levelname, level, *, func_name = None):
    """

    :type levelname: str
        The reference name of the level, e.g. DEBUG, WARNING, etc
    :type level: int
        Numeric logging level
    :type func_name: str
        The name of the logger function to log to a level, e.g. "info" for log.info(...)
    """

    func_name = func_name or levelname.lower()

    setattr(logging, levelname, level)
    logging.addLevelName(level, levelname)

    exec(_func_prototype.format(logger_func_name=func_name, levelname=levelname), logging.__dict__, locals())
    setattr(logging.Logger, func_name, eval(func_name))


_add_logger_level('EVERYTHING', 1)
_add_logger_level('NOISY', 4, func_name='noise')
_add_logger_level('FFMPEG', 5)
_add_logger_level('VOICEDEBUG', 6)

log = logging.getLogger(__name__)
log.setLevel(logging.EVERYTHING)

fhandler = logging.FileHandler(filename='logs/musicbot.log', encoding='utf-8', mode='a')
fhandler.setFormatter(logging.Formatter(
    "[{relativeCreated:.16f}] {asctime} - {levelname} - {name} | "
    "In {filename}::{threadName}({thread}), line {lineno} in {funcName}: {message}",
    style='{'
))
log.addHandler(fhandler)

del _func_prototype
del _add_logger_level
del fhandler