import asyncio
import json
import logging.handlers
import traceback

import typing
from aiohttp.client_exceptions import ClientError
import aiohttp
from lbry import utils, __version__
if typing.TYPE_CHECKING:
    from lbry.conf import Config

LOGGLY_TOKEN = 'BQEzZmMzLJHgAGxkBF00LGD0YGuyATVgAmqxAQEuAQZ2BQH4'


class JsonFormatter(logging.Formatter):
    """Format log records using json serialization"""

    def __init__(self, **kwargs):
        super().__init__()
        self.attributes = kwargs

    def format(self, record):
        data = {
            'loggerName': record.name,
            'asciTime': self.formatTime(record),
            'fileName': record.filename,
            'functionName': record.funcName,
            'levelNo': record.levelno,
            'lineNo': record.lineno,
            'levelName': record.levelname,
            'message': record.getMessage(),
        }
        data.update(self.attributes)
        if record.exc_info:
            data['exc_info'] = self.formatException(record.exc_info)
        return json.dumps(data)


class HTTPSLogglyHandler(logging.Handler):
    def __init__(self, loggly_token: str, config: 'Config'):
        super().__init__()
        self.cookies = {}
        self.url = "https://logs-01.loggly.com/inputs/{token}/tag/{tag}".format(
            token=utils.deobfuscate(loggly_token), tag='lbrynet-' + __version__
        )
        self._loop = asyncio.get_event_loop()
        self._session = aiohttp.ClientSession()
        self._config = config

    @property
    def enabled(self):
        return self._config.share_usage_data

    @staticmethod
    def get_full_message(record):
        if record.exc_info:
            return '\n'.join(traceback.format_exception(*record.exc_info))
        else:
            return record.getMessage()

    async def _emit(self, record, retry=True):
        data = self.format(record).encode()
        try:
            async with self._session.post(self.url, data=data,
                                          cookies=self.cookies) as response:
                self.cookies.update(response.cookies)
        except ClientError:
            if self._loop.is_running() and retry and self.enabled:
                await self._session.close()
                self._session = aiohttp.ClientSession()
                return await self._emit(record, retry=False)

    def emit(self, record):
        if not self.enabled:
            return
        try:
            asyncio.ensure_future(self._emit(record), loop=self._loop)
        except RuntimeError:  # TODO: use a second loop
            print(f"\nfailed to send traceback to loggly, please file an issue with the following traceback:\n"
                  f"{self.format(record)}")

    def close(self):
        super().close()
        try:
            loop = asyncio.get_event_loop()
            loop.run_until_complete(self._session.close())
        except RuntimeError:
            pass


def get_loggly_handler(config):
    handler = HTTPSLogglyHandler(LOGGLY_TOKEN, config=config)
    handler.setFormatter(JsonFormatter())
    return handler