# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2017
# Authors: Amador Pahim <apahim@redhat.com>

"""
Plugin to run Golang tests in Avocado
"""

import fnmatch
import glob
import os
import re

from avocado.core.nrunner.runnable import Runnable
from avocado.core.plugin_interfaces import Resolver
from avocado.core.resolver import ReferenceResolution, ReferenceResolutionResult
from avocado.utils import path as utils_path

try:
    GO_BIN = utils_path.find_command("go")
except utils_path.CmdNotFoundError:
    GO_BIN = None


TEST_RE = re.compile(r"^func\s(Test|Example)[A-Z]")


def find_tests(test_path):
    test_suite = []
    with open(test_path, "r", encoding="utf-8") as test_file_fd:
        for line in test_file_fd.readlines():
            if TEST_RE.match(line):
                test_suite.append(line.split()[1].split("(")[0])

    return test_suite


def find_files(path, recursive=True):
    pattern = "*_test.go"
    if recursive:
        matches = []
        for root, _, filenames in os.walk(path):
            for filename in fnmatch.filter(filenames, pattern):
                matches.append(os.path.join(root, filename))
        return matches

    if path != os.path.curdir:
        pattern = os.path.join(path, pattern)
    return glob.iglob(pattern)


class GolangResolver(Resolver):

    name = "golang"
    description = "Test resolver for Go language tests"

    @staticmethod
    def resolve(reference):  # pylint: disable=W0221

        if GO_BIN is None:
            return ReferenceResolution(
                reference,
                ReferenceResolutionResult.NOTFOUND,
                info="go binary not found",
            )

        package_paths = []
        test_files = []
        go_path = os.environ.get("GOPATH")
        if go_path is not None:
            for directory in go_path.split(os.pathsep):
                pkg_path = os.path.join(os.path.expanduser(directory), "src")
                package_paths.append(pkg_path)

        for package_path in package_paths:
            url_path = os.path.join(package_path, reference)
            files = find_files(url_path)
            if files:
                test_files.append((package_path, files))
                break

        runnables = []
        for package_path, test_files_list in test_files:
            for test_file in test_files_list:
                for item in find_tests(test_file):
                    common_prefix = os.path.commonprefix([package_path, test_file])
                    match_package = os.path.relpath(test_file, common_prefix)
                    test_name = f"{os.path.dirname(match_package)}:{item}"
                    runnables.append(Runnable("golang", uri=test_name))

        if runnables:
            return ReferenceResolution(
                reference, ReferenceResolutionResult.SUCCESS, runnables
            )

        return ReferenceResolution(reference, ReferenceResolutionResult.NOTFOUND)
