"""
Define facilities for automatically applying rules to data.
"""
import os
import re

from typing import Callable, Dict, List

from lnt.util import logger


def load_rules():
    """
    Load available rules scripts from a directory.

    Rules are organized as:

    <current dir>/rules/
    <current dir>/rules/rule_.*.py
    ...
    """

    rule_script_rex = re.compile(
        r'^rule_(.*)\.py$')
    rule_scripts = {}

    rules_path = os.path.join(os.path.dirname(__file__), 'rules')
    for item in os.listdir(rules_path):
        # Ignore certain known non-scripts.
        if item in ('README.txt', '__init__.py',
                    '__pycache__') or item.endswith('.pyc'):
            continue

        # Ignore non-matching files.
        m = rule_script_rex.match(item)
        if m is None:
            logger.warning("ignoring item {} in rule  directory: {}"
                           .format(item, rules_path))
            continue

        name = m.groups()[0]
        # Allow rules to be disabled by name
        if name.endswith("disabled"):
            continue

        rule_scripts[name] = os.path.join(rules_path, item)

    return rule_scripts


# Places our rules can hook to.
HOOKS: Dict['str', List[Callable]] = {
    'post_test_hook': [],
    'post_submission_hook': [],
    'post_regression_create_hook': [],
    'is_useful_change': [],
}

DESCRIPTIONS = {}
HOOKS_LOADED = False


def register_hooks():
    """Exec all the rules files.  Gather the hooks from them
    and load them into the hook dict for later use.
    """
    global HOOKS_LOADED
    for name, path in load_rules().items():
        globals = {}
        with open(path) as f:
            exec(compile(f.read(), path, 'exec'), globals)
        DESCRIPTIONS[name] = globals['__doc__']
        for hook_name in HOOKS.keys():
            if hook_name in globals:
                HOOKS[hook_name].append(globals[hook_name])
    HOOKS_LOADED = True
    return HOOKS


def post_submission_hooks(session, ts, run_id):
    """Run all the post submission hooks on the submitted run."""
    if not HOOKS_LOADED:
        logger.error("Running Hooks without loading them first.")
    for func in HOOKS['post_submission_hook']:
        func(session, ts, run_id)


def is_useful_change(session, ts, field_change):
    """Run all the change filters. If any are false, drop this change."""
    all_filters = []
    for func in HOOKS['is_useful_change']:
        decision = func(session, ts, field_change)
        all_filters.append(decision)
    if len(all_filters) == 0:
        return True
    else:
        #  If any filter ignores, we ignore.
        return all(all_filters)
