import logging
import sys
import unittest

try:
    from unittest import mock
except ImportError:
    import mock

import testlib
import mitogen.core
from mitogen.core import b

PY2 = sys.version_info[0] == 2
if PY2:

    def logging_getLogRecordFactory():
        return logging.LogRecord

    def logging_setLogRecordFactory(factory):
        logging.LogRecord = factory

else:
    logging_getLogRecordFactory = logging.getLogRecordFactory
    logging_setLogRecordFactory = logging.setLogRecordFactory


def ping():
    pass


def log_test():
    logging.getLogger(__name__).info("This is a test")


class BufferingTest(testlib.TestCase):
    klass = mitogen.core.LogHandler

    def record(self):
        return logging.LogRecord(
            name='name',
            level=99,
            pathname='pathname',
            lineno=123,
            msg='msg',
            args=(),
            exc_info=None,
        )

    def build(self):
        context = mock.Mock()
        return context, self.klass(context)

    def test_initially_buffered(self):
        context, handler = self.build()
        rec = self.record()
        handler.emit(rec)
        self.assertEqual(0, context.send.call_count)
        self.assertEqual(1, len(handler._buffer))

    def test_uncork(self):
        context, handler = self.build()
        rec = self.record()
        handler.emit(rec)
        handler.uncork()

        self.assertEqual(1, context.send.call_count)
        self.assertEqual(None, handler._buffer)

        _, args, _ = context.send.mock_calls[0]
        msg, = args

        self.assertEqual(mitogen.core.FORWARD_LOG, msg.handle)
        self.assertEqual(b('name\x0099\x00msg'), msg.data)


class StartupTest(testlib.RouterMixin, testlib.TestCase):
    def test_earliest_messages_logged(self):
        log = testlib.LogCapturer()
        log.start()

        c1 = self.router.local()
        c1.shutdown(wait=True)

        logs = log.stop()
        self.assertIn('Python version is', logs)
        self.assertIn('Parent is context 0 (master)', logs)

    def test_earliest_messages_logged_via(self):
        c1 = self.router.local(name='c1')
        # ensure any c1-related msgs are processed before beginning capture
        c1.call(ping)

        log = testlib.LogCapturer()
        log.start()

        c2 = self.router.local(via=c1, name='c2', debug=True)
        c2.shutdown(wait=True)

        logs = log.stop()
        self.assertIn('Python version is', logs)

        expect = 'Parent is context %s (%s)' % (c1.context_id, 'parent')
        self.assertIn(expect, logs)


class LogRecordFactoryTest(testlib.RouterMixin, testlib.TestCase):
    def setUp(self):
        super(LogRecordFactoryTest, self).setUp()
        self.original_factory = logging_getLogRecordFactory()

    def tearDown(self):
        logging_setLogRecordFactory(self.original_factory)
        super(LogRecordFactoryTest, self).tearDown()

    def test_logrecordfactory(self):
        # Change logging factory and add a custom attribute
        old_factory = logging_getLogRecordFactory()

        def record_factory(*args, **kwargs):
            record = old_factory(*args, **kwargs)
            record.custom_attribute = 0xDEADBEEF
            return record

        logging_setLogRecordFactory(record_factory)
        log = testlib.LogCapturer(
            formatter=logging.Formatter("%(custom_attribute)x - %(message)s"),
        )
        log.start()
        c1 = self.router.local(name="c1")
        c1.call(log_test)
        logs = log.stop()
        self.assertIn("deadbeef - This is a test", logs)


StartupTest = unittest.skipIf(
    condition=sys.version_info < (2, 7) or sys.version_info >= (3, 6),
    reason="Message log flaky on Python < 2.7 or >= 3.6"
)(StartupTest)
