import sys
import logging
import pytest
from pyppl.logger import Theme, THEMES, StreamFormatter, StreamHandler, StreamFilter, \
 FileFilter, FileFormatter, Logger, LEVEL_GROUPS, default_config, get_group, init_levels, get_value


def test_theme_init():
    theme = Theme(True)
    assert theme.theme == THEMES['green_on_black']

    theme = Theme(False)
    assert theme.theme == {}

    theme = Theme({'a': 1})
    assert theme.theme == {'a': 1}

    with pytest.raises(ValueError):
        Theme('nosuchtheme')


def test_get_value():
    assert get_value('_123') == 80
    assert get_value('PROCESS') == 80


def test_theme_get_color():
    THEMES['test'] = THEMES['green_on_black'].copy()
    theme = Theme('test')
    assert theme.get_color('DONE') == THEMES['test'][get_group('DONE')].format(
        **Theme.COLORS)
    assert theme.get_color('INFO') == THEMES['test']['CRITICAL'].format(
        **Theme.COLORS)
    assert theme.get_color('ABCDEF') == ''
    assert theme.get_color(
        'ERROR') == THEMES['green_on_black']['ERROR'].format(**Theme.COLORS)
    assert theme.get_color('NOTEXIST') == ''


def test_stream_formatter():
    sfmt = StreamFormatter(Theme())
    record = logging.makeLogRecord(dict())
    record.formatted = 'This is a logging record.'
    assert sfmt.format(record) == record.formatted

    record = logging.makeLogRecord(
        dict(msg="This is logging record.",
             mylevel="INFO",
             plugin="MAIN",
             proc='',
             jobidx=None))
    assert sfmt.format(record).endswith(
        '\x1b[32m   MAIN.   INFO\x1b[0m] \x1b[32mThis is logging record.\x1b[0m')

    record = logging.makeLogRecord(
        dict(
            msg="This is logging record1.\nThis is logging record2.",
            mylevel="INFO",
            proc='pProc',
            plugin="MAIN",
            jobidx=0,
            joblen=10,
        ))
    assert sfmt.format(record).endswith(
        '\x1b[32m   MAIN.   INFO\x1b[0m] \x1b[32mpProc: [01/10] This is logging record1.\x1b[0m'
    )
    assert len(record.tails) == 1
    assert record.tails[0].formatted.endswith(
        '\x1b[32m   MAIN.   INFO\x1b[0m] \x1b[32mpProc: [01/10] This is logging record2.\x1b[0m'
    )


def test_stream_handler(capsys):
    StreamHandler.DATA = {'prevbar': None, 'done': {}}
    shandler = StreamHandler()
    record1 = logging.makeLogRecord(
        dict(msg="This is logging record1.",
             mylevel="INFO",
             proc='',
             ispbar=False,
             jobidx=None))
    shandler.emit(record1)
    assert 'This is logging record1.' in capsys.readouterr().err

    record2 = logging.makeLogRecord(
        dict(msg="This is logging record2.",
             mylevel="INFO",
             proc='pProc',
             ispbar=True,
             done=True,
             jobidx=None))

    shandler.emit(record2)
    assert 'This is logging record2.\n' in capsys.readouterr().err
    assert StreamHandler.DATA['prevbar'] is None
    assert StreamHandler.DATA['done']['pProc']

    record3 = logging.makeLogRecord(
        dict(msg="This is logging record3.",
             formatted="This is logging record3 formatted.",
             mylevel="INFO",
             proc='pProc',
             ispbar=True,
             done=False,
             jobidx=None))
    shandler.emit(record3)
    assert 'This is logging record3.\r' in capsys.readouterr().err
    assert StreamHandler.DATA['prevbar'] is record3

    record4 = logging.makeLogRecord(
        dict(msg="This is logging record4.",
             mylevel="INFO",
             proc='pProc',
             ispbar=False,
             tails=[record3]))
    shandler.emit(record4)
    err = capsys.readouterr().err
    assert 'This is logging record4.\n' in err
    assert 'This is logging record3.\r' in err


def test_stream_filter():
    sfilter = StreamFilter('pyppl', [])
    record = logging.makeLogRecord(
        dict(
            msg="This is logging record1.",
            mylevel="INFO",
        ))
    assert not sfilter.filter(record)

    sfilter = StreamFilter('pyppl', ['INFO'])
    assert sfilter.filter(record)

    record = logging.makeLogRecord(
        dict(
            msg="This is logging record1.",
            mylevel="LOG",
        ))
    assert not sfilter.filter(record)

    record = logging.makeLogRecord(
        dict(
            msg="This is logging record1.",
            mylevel="_LOG",
        ))
    assert sfilter.filter(record)

    record = logging.makeLogRecord(
        dict(msg="This is logging record1.",
             mylevel="INFO",
             proc='pProc',
             slevel='CACHE_FAILED'))
    assert sfilter.filter(record)
    assert sfilter.subs['pProc']['CACHE_FAILED'] == 1
    assert not sfilter.filter(record)

    record = logging.makeLogRecord(
        dict(msg="This is logging record1.",
             mylevel="INFO",
             proc='pProc',
             slevel='SCRIPT_UPDATED'))
    assert sfilter.filter(record)
    assert sfilter.filter(record)
    assert not sfilter.filter(record)
    assert 'further information will be ignored.' in record.msg

    record = logging.makeLogRecord(
        dict(msg="This is logging record1.",
             mylevel="INFO",
             slevel='SCRIPT_UPDATED'))
    assert not sfilter.filter(record)

    # sub sublevel
    record = logging.makeLogRecord(
        dict(msg="This is logging record1.",
             mylevel="INFO",
             proc='SomeProc',
             slevel='NEW_SUBLEVEL'))
    assert sfilter.filter(record)
    assert sfilter.filter(record)
    logger = Logger()
    logger.add_sublevel('NEW_SUBLEVEL', 1)
    assert sfilter.filter(record)
    assert not sfilter.filter(record)


def test_file_filter():
    ffilter = FileFilter('pyppl', ['INFO'])
    record = logging.makeLogRecord(
        dict(
            msg="This is logging record1.",
            mylevel="INFO",
            ispbar=False,
        ))
    assert not ffilter.filter(record)

    record = logging.makeLogRecord(
        dict(
            msg="This is logging record1.",
            formatted="This is logging record1.",
            mylevel="INFO",
            ispbar=False,
        ))
    assert ffilter.filter(record)


def test_file_formatter():
    ffmt = FileFormatter()
    record = logging.makeLogRecord(
        dict(
            msg="This is logging record1.",
            #formatted = "\x1b[31mThis is logging record2.\x1b[0m",
            mylevel="INFO",
            ispbar=False,
        ))
    assert 'This is logging record1.' in ffmt.format(record)

    record = logging.makeLogRecord(
        dict(
            msg="This is logging record1.",
            formatted="\x1b[31mThis is logging record2.\x1b[0m",
            mylevel="INFO",
            ispbar=False,
        ))
    assert ffmt.format(record) == 'This is logging record2.'


def test_logger_init_levels():
    # using issuperset instead of ==, because plugins can add levels
    assert init_levels('TITLE', []).issuperset({'PROCESS'})
    assert init_levels(True, []).issuperset({
        'PROCESS', 'DONE', 'DEPENDS', 'WORKDIR', 'CACHED', 'P_DONE', 'INFO',
        'CONFIG', 'PLUGIN', 'PYPPL', 'TIPS', 'BLDING', 'SBMTING', 'RUNNING',
        'JOBDONE', 'KILLING', 'RTRYING', 'ERROR', 'WARNING'
    })
    assert init_levels('DEBUG', []).issuperset({
        'BLDING', 'CACHED', 'DEBUG', 'DEPENDS', 'ERROR', 'INFO', 'JOBDONE',
        'KILLING', 'PROCESS', 'P_DONE', 'RTRYING', 'RUNNING', 'SBMTING',
        'WARNING', 'WORKDIR', 'DONE', 'CONFIG', 'PLUGIN', 'PYPPL', 'TIPS'
    })
    assert init_levels('CRITICAL', ['+DEBUG', '-WORKDIR']).issuperset({
        'BLDING', 'CACHED', 'DEBUG', 'DEPENDS', 'INFO', 'JOBDONE', 'KILLING',
        'PROCESS', 'P_DONE', 'RUNNING', 'SBMTING', 'DONE'
    })
    assert init_levels('TITLE', 'DEBUG').issuperset({'PROCESS', 'DEBUG'})


def test_logger_init(tmpdir):
    logger = Logger()
    default_config.logger.file = tmpdir / 'logger.txt'
    logger.initialize()
    assert len(logger._logger.handlers) == 2

    logger.initialize({'file': False})
    assert len(logger._logger.handlers) == 1


# def test_logger_bake():
#     logger = Logger()
#     logger2 = logger.bake(a=1, b=2)
#     assert logger2.baked == {'a': 1, 'b': 2}


def test_logger_pbar():
    logger = Logger()
    assert logger.pbar is logger
    assert logger._ispbar


def test_logger_emit(caplog):
    logger = Logger()
    logger._emit('This is a message.', _level='INFO', _extra={'ispbar': False})
    assert 'This is a message.' in caplog.text


def test_logger_getattr(caplog):
    logger = Logger()
    logger.info('Hello world!')
    assert 'Hello world!' in caplog.text
    caplog.clear()
    logger['info']('Hello world2!')
    assert 'Hello world2!' in caplog.text


def test_logger_add_level():
    logger = Logger()
    assert init_levels('TITLE', []) == {'PROCESS', 'DONE'}
    logger.add_level('NOLEVEL', 'TITLE')
    assert init_levels('TITLE', []) == {'PROCESS', 'DONE', 'NOLEVEL'}

    with pytest.raises(ValueError):
        logger.add_level('NOLEVEL', 'nosuchgroup')