import logging

import pytest
from full_match import match
from loguru import logger as loguru_logger

from emptylog import EmptyLogger, LoggersGroup, MemoryLogger, PrintingLogger
from emptylog.abstract_logger import AbstractLogger


@pytest.mark.parametrize(
    'first_logger',
    [
        EmptyLogger(),
        MemoryLogger(),
        PrintingLogger(),
    ],
)
@pytest.mark.parametrize(
    'second_logger',
    [
        EmptyLogger(),
        MemoryLogger(),
        PrintingLogger(),
        logging,
        logging.getLogger('kek'),
        loguru_logger,
    ],
)
def test_sum_of_inner_loggers(first_logger, second_logger):
    result = first_logger + second_logger

    assert isinstance(result, LoggersGroup)

    assert result is not first_logger
    assert result is not second_logger

    assert result.loggers[0] is first_logger
    assert result.loggers[1] is second_logger


@pytest.mark.parametrize(
    'first_logger',
    [
        logging,
        logging.getLogger('kek'),
        loguru_logger,
    ],
)
@pytest.mark.parametrize(
    'second_logger',
    [
        EmptyLogger(),
        MemoryLogger(),
        PrintingLogger(),
    ],
)
def test_sum_with_another_loggers_as_first_operand(first_logger, second_logger):
    result = first_logger + second_logger

    assert isinstance(result, LoggersGroup)

    assert result is not first_logger
    assert result is not second_logger

    assert result.loggers[0] is first_logger
    assert result.loggers[1] is second_logger


@pytest.mark.parametrize(
    'logger',
    [
        EmptyLogger(),
        LoggersGroup(),
        MemoryLogger(),
        PrintingLogger(),
    ],
)
def test_all_loggers_are_instances_of_abstract_logger(logger):
    assert isinstance(logger, AbstractLogger)


@pytest.mark.parametrize(
    'logger',
    [
        EmptyLogger(),
        LoggersGroup(),
        MemoryLogger(),
        PrintingLogger(),
    ],
)
@pytest.mark.parametrize(
    'wrong_operand',
    [
        1,
        'kek',
        None,
    ],
)
def test_sum_with_wrong_first_operand(logger, wrong_operand):
    with pytest.raises(NotImplementedError, match=match('The addition operation is defined only for loggers.')):
        wrong_operand + logger


@pytest.mark.parametrize(
    'logger',
    [
        EmptyLogger(),
        LoggersGroup(),
        MemoryLogger(),
        PrintingLogger(),
    ],
)
@pytest.mark.parametrize(
    'wrong_operand',
    [
        1,
        'kek',
        None,
    ],
)
def test_sum_with_wrong_second_operand(logger, wrong_operand):
    with pytest.raises(NotImplementedError, match=match('The addition operation is defined only for loggers.')):
        logger + wrong_operand


def test_sum_of_three_loggers():
    first_logger = EmptyLogger()
    second_logger = MemoryLogger()
    third_logger = PrintingLogger()

    result = first_logger + second_logger + third_logger

    assert isinstance(result, LoggersGroup)

    assert len(result) == 3
    assert len(result.loggers) == 3

    assert result.loggers[0] is first_logger
    assert result.loggers[1] is second_logger
    assert result.loggers[2] is third_logger


def test_sum_right_associative_is_flat():
    first_logger = EmptyLogger()
    second_logger = MemoryLogger()
    third_logger = PrintingLogger()

    inner = second_logger + third_logger
    result = first_logger + inner

    assert type(result) is LoggersGroup
    assert len(result) == 3
    assert result.loggers[0] is first_logger
    assert result.loggers[1] is second_logger
    assert result.loggers[2] is third_logger


@pytest.mark.parametrize(
    'logger',
    [
        EmptyLogger(),
        LoggersGroup(),
        MemoryLogger(),
        PrintingLogger(),
    ],
)
@pytest.mark.parametrize(
    'wrong_operand',
    [
        1.5,
        [],
    ],
)
def test_sum_with_additional_wrong_operands(logger, wrong_operand):
    with pytest.raises(NotImplementedError, match=match('The addition operation is defined only for loggers.')):
        wrong_operand + logger

    with pytest.raises(NotImplementedError, match=match('The addition operation is defined only for loggers.')):
        logger + wrong_operand
