import os
import subprocess
import abc
from typing import List

import path


class PathFinder(metaclass=abc.ABCMeta):
    """
    A base class for locating an executable or executables.
    """

    candidate_paths = ['']
    "Potential roots to search for self.exe"

    @abc.abstractproperty
    def exe(self):
        "The target executable"

    args: List[str] = []
    "Additional args to pass to the exe when testing for its suitability"

    @classmethod
    def resolve(cls):
        """
        Resolve an executable or raise RuntimeError if one
        cannot be found.
        """
        return cls.find_root() / cls.exe

    @classmethod
    def find_root(cls):
        try:
            result = next(cls.find_valid_roots())
        except StopIteration:
            raise RuntimeError(f"{cls.__name__} unable to find executables")
        return path.Path(result)

    @classmethod
    def find_valid_roots(cls):
        """
        Generate valid roots for the target executable based on the
        candidate paths.
        """
        return filter(cls.is_valid_root, cls.candidate_paths)

    @classmethod
    def is_valid_root(cls, root):
        try:
            cmd = [os.path.join(root, cls.exe)] + cls.args
            subprocess.check_call(cmd, stdout=subprocess.DEVNULL)
        except OSError:
            return False
        return True
