#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

"""
This package contains various utilities which make it easier to test plugins.
"""

import os
from collections import OrderedDict
from typing import Tuple
from buildstream.exceptions import ErrorDomain, LoadErrorReason
from ._yaml import generate_project, generate_element, load_yaml
from .repo import Repo
from .runcli import cli, cli_integration, cli_remote_execution, Cli
from .integration import integration_cache
from ._cachekeys import check_cache_key_stability

__all__ = [
    "check_cache_key_stability",
    "create_repo",
    "register_repo_kind",
    "sourcetests_collection_hook",
]

# To make use of these test utilities it is necessary to have pytest
# available. However, we don't want to have a hard dependency on
# pytest.
try:
    import pytest
except ImportError:
    module_name = globals()["__name__"]
    msg = "Could not import pytest:\n" "To use the {} module, you must have pytest installed.".format(module_name)
    raise ImportError(msg)


# Of the form plugin_name -> (repo_class, plugin_package)
ALL_REPO_KINDS = OrderedDict()  # type: OrderedDict[str, Tuple[Repo, str]]


def create_repo(kind, directory, subdir="repo"):
    """Convenience method for creating a Repo

    Args:
        kind (str): The kind of repo to create (a source plugin basename). This
                    must have previously been registered using
                    `register_repo_kind`
        directory (str): The path where the repo will keep a cache

    Returns:
        (Repo): A new Repo object
    """
    try:
        constructor = ALL_REPO_KINDS[kind]
    except KeyError as e:
        raise AssertionError("Unsupported repo kind {}".format(kind)) from e

    return constructor[0](directory, subdir=subdir)


def register_repo_kind(kind, cls, plugin_package):
    """Register a new repo kind.

    Registering a repo kind will allow the use of the `create_repo`
    method for that kind and include that repo kind in ALL_REPO_KINDS

    In addition, repo_kinds registred prior to
    `sourcetests_collection_hook` being called will be automatically
    used to test the basic behaviour of their associated source
    plugins using the tests in `testing._sourcetests`.

    Args:
       kind (str): The kind of repo to create (a source plugin basename)
       cls (cls) : A class derived from Repo.
       plugin_package (str): The name of the python package containing the plugin

    """
    ALL_REPO_KINDS[kind] = (cls, plugin_package)


def sourcetests_collection_hook(session):
    """Used to hook the templated source plugin tests into a pyest test suite.

    This should be called via the `pytest_sessionstart
    hook <https://docs.pytest.org/en/latest/reference.html#collection-hooks>`_.
    The tests in the _sourcetests package will be collected as part of
    whichever test package this hook is called from.

    Args:
        session (pytest.Session): The current pytest session
    """

    def should_collect_tests(config):
        # When no args are supplied, pytest defaults the arg list to the
        # value of the `testpaths` configuration option. We want to collect
        # tests as part of the default collection
        return config.args_source != pytest.Config.ArgsSource.ARGS

    from . import _sourcetests

    source_test_path = os.path.dirname(_sourcetests.__file__)
    # Add the location of the source tests to the session's
    # python_files config. Without this, pytest may filter out these
    # tests during collection.
    session.config.addinivalue_line("python_files", os.path.join(source_test_path, "*.py"))
    # If test invocation has specified specic tests, don't
    # automatically collect templated tests.
    if should_collect_tests(session.config):
        session.config.args.append(source_test_path)
