# 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.

import contextlib
import re
import sys


def filter_tests(filters, test_ids):
    """Filter test_ids by the test_filters.

    :param list filters: A list of regex filters to apply to the test_ids. The
        output will contain any test_ids which have a re.search() match for any
        of the regexes in this list. If this is None all test_ids will be
        returned
    :param list test_ids: A list of test_ids that will be filtered
    :return: A list of test ids.
    """
    if filters is None:
        return test_ids

    _filters = []
    for f in filters:
        if isinstance(f, str):
            try:
                _filters.append(re.compile(f))
            except re.error:
                print("Invalid regex: %s provided in filters" % f, file=sys.stderr)
                sys.exit(5)
        else:
            _filters.append(f)

    def include(test_id):
        for pred in _filters:
            if pred.search(test_id):
                return True

    return list(filter(include, test_ids))


def exclusion_reader(exclude_list):
    with contextlib.closing(open(exclude_list)) as exclude_file:
        regex_comment_lst = []  # tuple of (regex_compiled, msg, skipped_lst)
        for line in exclude_file:
            raw_line = line.strip()
            split_line = raw_line.split("#")
            # Before the # is the regex
            line_regex = split_line[0].strip()
            if len(split_line) > 1:
                # After the # is a comment
                comment = "".join(split_line[1:]).strip()
            else:
                comment = "Skipped because of regex %s:" % line_regex
            if not line_regex:
                continue
            try:
                regex_comment_lst.append((re.compile(line_regex), comment, []))
            except re.error:
                print(
                    "Invalid regex: %s in provided exclusion list file" % line_regex,
                    file=sys.stderr,
                )
                sys.exit(5)
    return regex_comment_lst


def _get_regex_from_include_list(file_path):
    lines = []
    for line in open(file_path).read().splitlines():
        split_line = line.strip().split("#")
        # Before the # is the regex
        line_regex = split_line[0].strip()
        if line_regex:
            try:
                lines.append(re.compile(line_regex))
            except re.error:
                print(
                    "Invalid regex: %s in provided inclusion_list file" % line_regex,
                    file=sys.stderr,
                )
                sys.exit(5)
    return lines


def construct_list(
    test_ids, regexes=None, exclude_list=None, include_list=None, exclude_regex=None
):
    """Filters the discovered test cases

    :param list test_ids: The set of test_ids to be filtered
    :param list regexes: A list of regex filters to apply to the test_ids. The
        output will contain any test_ids which have a re.search() match for any
        of the regexes in this list. If this is None all test_ids will be
        returned
    :param str exclude_list: The path to an exclusion_list file
    :param str include_list: The path to an inclusion_list file
    :param str exclude_regex: regex pattern to exclude tests

    :return: iterable of strings. The strings are full
        test_ids
    :rtype: set
    """

    if not regexes:
        regexes = None  # handle the other false things

    safe_re = None
    if include_list:
        safe_re = _get_regex_from_include_list(include_list)

    if not regexes and safe_re:
        regexes = safe_re
    elif regexes and safe_re:
        regexes += safe_re

    if exclude_list:
        exclude_data = exclusion_reader(exclude_list)
    else:
        exclude_data = None

    if exclude_regex:
        msg = "Skipped because of regexp provided as a command line argument:"
        try:
            record = (re.compile(exclude_regex), msg, [])
        except re.error:
            print(
                "Invalid regex: %s used for exclude_regex" % exclude_regex,
                file=sys.stderr,
            )
            sys.exit(5)
        if exclude_data:
            exclude_data.append(record)
        else:
            exclude_data = [record]

    list_of_test_cases = filter_tests(regexes, test_ids)
    set_of_test_cases = set(list_of_test_cases)

    if not exclude_data:
        return set_of_test_cases

    # NOTE(afazekas): We might use a faster logic when the
    # print option is not requested
    for (rex, msg, s_list) in exclude_data:
        for test_case in list_of_test_cases:
            if rex.search(test_case):
                # NOTE(mtreinish): In the case of overlapping regex the test
                # case might have already been removed from the set of tests
                if test_case in set_of_test_cases:
                    set_of_test_cases.remove(test_case)
                    s_list.append(test_case)

    return set_of_test_cases
