# -*- coding: utf8 -*-
import os
import inspect

from .interface import BUIaudit, BUIauditLogger as BUIauditLoggerInterface

from importlib import import_module
from collections import OrderedDict
from flask_login import current_user


class BUIauditLoader(BUIaudit):
    """See :class:`burpui.misc.audit.interface.BUIaudit`"""

    def __init__(self, app=None):
        """See :func:`burpui.misc.audit.interface.BUIaudit.__init__`

        :param app: Instance of the app we are running in
        :type app: :class:`burpui.engines.server.BUIServer`
        """
        self.app = app
        backends = []
        self.errors = {}
        if self.app.audit_backends and "none" not in self.app.audit_backends:
            me, _ = os.path.splitext(os.path.basename(__file__))
            back = self.app.audit_backends
            for au in back:
                if au == me:
                    self.app.logger.critical("Recursive import not permitted!")
                    continue
                try:
                    (modpath, _) = __name__.rsplit(".", 1)
                    mod = import_module("." + au, modpath)
                    obj = mod.BUIauditLoader(self.app)
                    backends.append(obj)
                except:
                    import traceback

                    self.errors[au] = traceback.format_exc()
        for name, plugin in self.app.plugin_manager.get_plugins_by_type(
            "audit"
        ).items():
            try:
                obj = plugin.BUIauditLoader(self.app)
                backends.append(obj)
            except:
                import traceback

                self.errors[name] = traceback.format_exc()
        backends.sort(key=lambda x: getattr(x, "priority", -1), reverse=True)
        if (
            not backends
            and self.app.audit_backends
            and "none" not in self.app.audit_backends
        ):
            raise ImportError(
                "No backend found for '{}':\n{}".format(
                    self.app.audit_backends, self.errors
                )
            )
        for name, err in self.errors.items():
            self.app.logger.error(
                "Unable to load module {}:\n{}".format(repr(name), err)
            )
        self.backends = OrderedDict()
        for obj in backends:
            self.backends[obj.name] = obj
        self._logger = BUIauditLogger(self)


class BUIauditLogger(BUIauditLoggerInterface):
    def __init__(self, loader):
        self.loader = loader

    def log(self, level, message, *args, **kwargs):
        server_log = ""
        if "server" in kwargs:
            server = kwargs["server"]
            del kwargs["server"]
            if server:
                server_log = f" on {server}"
        if current_user and not current_user.is_anonymous:
            msg = f"{current_user} -> {message}{server_log}"
        else:
            msg = f"{message}{server_log}"
        caller = ""
        stack = inspect.stack()
        exclude = [
            "audit/interface.py",
            "audit/handler.py",
        ]
        for frame in stack:
            if any(frame.filename.endswith(x) for x in exclude):
                continue
            caller = f"{frame.function} [{frame.filename}:{frame.lineno}]"
            break
        if "extra" in kwargs and isinstance(kwargs["extra"], dict):
            kwargs["extra"]["from"] = caller
        else:
            kwargs["extra"] = {"from": caller}
        for back in self.loader.backends.values():
            back.logger.log(level, msg, *args, **kwargs)
