import json
import sys
import os
import pytest
import importlib
from oaa.app_runners.custom_app import CustomAppRunner
from oaa.settings_config import DBConnection

# test that a configuration can identify multiple DB sources (queries), pull
# that data into streams (petl.fromdb) and feed into OAA

SKIP_DB_TESTS = not bool(os.environ.get("DB_PASSWORD"))


def test_specify_configuration_as_yaml(config):
    """
    if configuration is specified in .yaml format, we can more easily specify
    lists of items, specifically queries in this case.
    """
    config.update(config_file="./tests/config-v2.yaml", config_format="yaml")

    assert config.RUNNER_TYPE == "CUSTOM_APP"
    assert config.LOG_LEVEL == "ERROR"
    assert isinstance(config.sources, dict)


@pytest.fixture
def multi_db_stream_config(config):
    config.update(config_file="./tests/config-v2.yaml", config_format="yaml")

    return config


# @pytest.fixture(scope="session")
# def db_available():
#     # TODO do something to test if DB is available
#     print('evaluating if DB is available')

#     return not SKIP_DB_TESTS


@pytest.mark.skipif(SKIP_DB_TESTS, reason="Requires PG DB")
def test_get_multiple_streams(multi_db_stream_config):

    config = multi_db_stream_config

    runner = CustomAppRunner()

    assert runner.oaa
    assert config.RUNNER_TYPE == "CUSTOM_APP"

    # import bpdb; bpdb.set_trace()  # noqa: E702
    assert len(config.sources.keys()) == 4

    # streams = runner.streams()
    # assert len(streams) == 2

    assert "connection_for_users" in config.connections

    for label, s in config.sources.items():
        if s.connection:
            assert config.connections[s.connection]
            assert isinstance(config.connections[s.connection], DBConnection)
            assert s.stream
            assert s.stream.nrows() > 1


# @pytest.mark.skipif(not db_available, reason="Requires PG DB")
def test_module_directory(multi_db_stream_config):

    config = multi_db_stream_config

    assert config.module_directory

    assert os.path.exists(config.module_directory)
    assert str(config.module_directory) in sys.path

    # module_path = str(config.MODULE_DIRECTORY / 'my_custom_module.py')
    module_path = "role_assignments"
    # my_custom_module = import_from_path('my_custom_module', module_dir)

    # my_custom_module = import_from_path('my_custom_module', module_path)
    my_custom_module = importlib.import_module(module_path)

    assert callable(my_custom_module.run)
    from oaa.utils import validate_logic_module

    assert validate_logic_module(my_custom_module)


def test_get_logic_module(multi_db_stream_config):

    config = multi_db_stream_config

    runner = CustomAppRunner()

    assert runner.oaa
    assert config.RUNNER_TYPE == "CUSTOM_APP"

    assert len(config.sources.keys()) == 4

    mods = runner._get_logic_modules()

    assert mods

    my_custom_module = importlib.import_module("role_assignments")

    assert mods[0].__name__ == my_custom_module.__name__

    for module in mods:
        assert callable(module.run)


@pytest.mark.skipif(SKIP_DB_TESTS, reason="Requires PG DB")
def test_run_custom_logic(multi_db_stream_config):
    """TODO: Docstring for test_read_config_process_logic.

    :multi_db_stream_config: TODO
    :returns: TODO

    """
    config = multi_db_stream_config

    runner = CustomAppRunner()

    runner.run(push_to_oaa=False)
    # TODO what is the state after this runs?
    # should still be the same numberof streams
    assert len(config.sources.keys()) == 4
    # the runner.app objecs should now have data
    oaa_app = runner._provider_object
    assert oaa_app
    assert len(oaa_app.local_users) == 1000
    assert len(oaa_app.local_roles) == 2


@pytest.mark.skipif(SKIP_DB_TESTS, reason="Requires PG DB")
def test_roles(multi_db_stream_config):

    # config = multi_db_stream_config

    runner = CustomAppRunner()

    runner.run(push_to_oaa=False)
    oaa_app = runner._provider_object
    assert len(oaa_app.local_users) == 1000
    assert len(oaa_app.local_roles) == 2
    user = oaa_app.local_users["khansen+lauren-robertson-okta@veza.com"]
    assert "admin" in user.role_assignments


@pytest.mark.skipif(SKIP_DB_TESTS, reason="Requires PG DB")
def test_groups(multi_db_stream_config):

    runner = CustomAppRunner()

    runner.run(push_to_oaa=False)
    oaa_app = runner._provider_object
    assert len(oaa_app.local_users) == 1000
    assert len(oaa_app.local_roles) == 2
    assert len(oaa_app.local_groups) == 2

    should_be_ingroup = [
        "khansen+lauren-robertson-okta@veza.comkhansen+gonca-alpuan-okta@veza.com",
        "khansen+adrin-cabrera-okta@veza.com",
    ]

    for username in should_be_ingroup:
        user = oaa_app.local_users["khansen+lauren-robertson-okta@veza.com"]
        assert user.groups
        local_group = oaa_app.local_groups["1"]
        assert local_group.unique_id in user.groups


@pytest.mark.skipif(SKIP_DB_TESTS, reason="Requires PG DB")
def test_resources(multi_db_stream_config):

    runner = CustomAppRunner()

    runner.run(push_to_oaa=True)
    oaa_app = runner._provider_object
    assert len(oaa_app.local_users) == 1000
    assert len(oaa_app.local_roles) == 2
    assert len(oaa_app.local_groups) == 2
    assert len(oaa_app.resources) == 1000

    resource = oaa_app.resources["1002"]
    assert resource
    with open("./output.json", "w+") as _f:
        json.dump(oaa_app.get_payload(), _f, indent=2)
    assert resource.properties["parent_id"] == "1001"
    # TODO assert that some resources have sub-resources
    app_with_subs = oaa_app.resources["1002"]
    assert app_with_subs.sub_resources
    assert len(app_with_subs.sub_resources) == 8

    # TODO assert there are roles that have permissions to this resource
    assert oaa_app.local_roles
