import importlib.util
import inspect


class PythonPolicyEngine:
    def __init__(self, package_path):
        self.package_path = package_path
        self._load_module()
        self._build_policy_map()

    def _load_module(self):
        """Todo: Make this to work on different python versions"""
        spec = importlib.util.spec_from_file_location(
            "pypol",
            "{}/__init__.py".format(self.package_path)
        )
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)

        self.module = module

    def _build_policy_map(self):
        """Loop through all classes that are part of this module and build a map
        of policies by resource type
        """
        self.policy_map = {}
        for name, obj in inspect.getmembers(self.module):
            if inspect.isclass(obj):
                if hasattr(obj, 'applies_to'):
                    if isinstance(obj.applies_to, list):
                        for resource_type in obj.applies_to:
                            if resource_type not in self.policy_map:
                                self.policy_map[resource_type] = []
                            self.policy_map[resource_type].append(obj)

    def configured_policies(self):
        # adding for compatibility
        # todo: implement me
        return []

    def policies(self, resource):
        """
        Args:
            resource: The resource we'd like to evaluate

        Returns:
            A list of names of policies that apply to the provided resource

        """
        return self.policy_map[resource.type()]

    def violations(self, resource):
        """
        Args:
            resource: The resource we'd like to evaluate

        Returns:
            A list of names of policies this resource violates

        """
        violations = []
        for cls in self.policies(resource):
            policy = cls(resource)
            if not policy.evaluate():
                violations.append(policy)
        return violations

    def remediate(self, resource, violation):
        violation.remediate(resource)
