import subprocess
import logging
from typing import Dict, Optional

from zc.buildout.buildout import Buildout, Options

logger = logging.getLogger("buildout/test_recipes")


def get_recipes() -> Dict[str, str]:
    """
    Get all currently installed buildout recipes (including this one!)
    :return: Dictionary with format {recipe_name: recipe_path_from_root,}
    """
    logger.debug("Getting list of recipes...")
    recipes = {}
    find = subprocess.run(['find', './build/recipes', '-name', 'setup.py'],
                           stdout=subprocess.PIPE)
    paths = find.stdout.decode('utf-8').split('\n')
    dirs = []
    names = []
    for p in paths:
        if len(p) > 1:
            # Exclude empty result from find
            dirs.append(p.replace('/setup.py', ''))
            names.append(p.split('/')[-2])
    for d, n in zip(dirs, names):
        recipes[n] = d
    logger.debug("Done getting recipes.")

    return recipes


class Recipe:
    """
    Buildout Recipe class.
    For more detailed information, see the link.
    http://www.buildout.org/en/latest/topics/writing-recipes.html
    """

    def run_test(self, recipe: str):
        """
        Run test for given recipe.
        :param recipe: Name of recipe to be run.
        """
        logger.debug(f"Running tests for recipe {recipe}...")
        subprocess.run(['pytest', '-vv', '--log-level=DEBUG', '--showlocals',
                        self.recipes[recipe]])

    def __init__(self, buildout: Optional[Buildout], name: str, options: Options):
        """
        Initializes fields needed for recipe.
        :param buildout: (Boilerplate) Dictionary of options from buildout section
        of buildout.cfg
        :param name: (Boilerplate) Name of section that uses this recipe.
        :param options: (Boilerplate) Options of section that uses this recipe.
        """
        self.name = name
        self.options = options
        self.recipes = get_recipes()
        self.choice = options['name']

    def install(self):
        """
        Install method that runs when recipe has components it needs to install.

        In this case, nothing is "installed" per se.
        :return: Paths to files, as strings, created by the recipe.
        """
        if self.choice == 'all':
            # Run tests for all recipes
            for recipe in self.recipes:
                self.run_test(recipe)
        else:
            if self.choice in self.recipes:
                self.run_test(self.choice)