import importlib
import sys
import unittest

import pytest
import _pytest.unittest

from irctest.cases import _IrcTestCase, BaseClientTestCase, BaseServerTestCase
from irctest.basecontrollers import BaseClientController, BaseServerController

def pytest_addoption(parser):
    """Called by pytest, registers CLI options passed to the pytest command."""
    parser.addoption("--controller", help="Which module to use to run the tested software.")
    parser.addoption('--openssl-bin', type=str, default='openssl',
            help='The openssl binary to use')


def pytest_configure(config):
    """Called by pytest, after it parsed the command-line."""
    module_name = config.getoption("controller")

    if module_name is None:
        pytest.exit("--controller is required.", 1)

    try:
        module = importlib.import_module(module_name)
    except ImportError:
        pytest.exit('Cannot import module {}'.format(module_name), 1)

    controller_class = module.get_irctest_controller_class()
    if issubclass(controller_class, BaseClientController):
        from irctest import client_tests as module
    elif issubclass(controller_class, BaseServerController):
        from irctest import server_tests as module
    else:
        pytest.exit(
            r'{}.Controller should be a subclass of '
            r'irctest.basecontroller.Base{{Client,Server}}Controller'
            .format(module_name),
            1
        )
    _IrcTestCase.controllerClass = controller_class
    _IrcTestCase.controllerClass.openssl_bin = config.getoption("openssl_bin")
    _IrcTestCase.show_io = True  # TODO


def pytest_collection_modifyitems(session, config, items):
    """Called by pytest after finishing the test collection,
    and before actually running the tests.

    This function filters out client tests if running with a server controller,
    and vice versa.
    """

    # First, check if we should run server tests or client tests
    if issubclass(_IrcTestCase.controllerClass, BaseServerController):
        server_tests = True
    elif issubclass(_IrcTestCase.controllerClass, BaseClientController):
        server_tests = False
    else:
        assert False, (
            f"{_IrcTestCase.controllerClass} inherits neither "
            f"BaseClientController or BaseServerController"
        )

    filtered_items = []

    # Iterate over each of the test functions (they are pytest "Nodes")
    for item in items:
        # we only use unittest-style test function here
        assert isinstance(item, _pytest.unittest.TestCaseFunction)

        # unittest-style test functions have the node of UnitTest class as parent
        assert isinstance(item.parent, _pytest.unittest.UnitTestCase)

        # and that node references the UnitTest class
        assert issubclass(item.parent.cls, unittest.TestCase)

        # and in this project, TestCase classes all inherit either from BaseClientController
        # or BaseServerController.
        if issubclass(item.parent.cls, BaseServerTestCase):
            if server_tests:
                filtered_items.append(item)
        elif issubclass(item.parent.cls, BaseClientTestCase):
            if not server_tests:
                filtered_items.append(item)
        else:
            assert False, (
                f"{item}'s class inherits neither BaseServerTestCase "
                "or BaseClientTestCase"
            )

    # Finally, rewrite in-place the list of tests pytest will run
    items[:] = filtered_items