import pymel.core as pm

import pyblish.api
import pype.api


class ValidateAttributes(pyblish.api.ContextPlugin):
    """Ensure attributes are consistent.

    Attributes to validate and their values comes from the
    "maya/attributes.json" preset, which needs this structure:
        {
          "family": {
            "node_name.attribute_name": attribute_value
          }
        }
    """

    order = pype.api.ValidateContentsOrder
    label = "Attributes"
    hosts = ["maya"]
    actions = [pype.api.RepairContextAction]
    optional = True

    attributes = None

    def process(self, context):
        # Check for preset existence.

        if not self.attributes:
            return

        invalid = self.get_invalid(context, compute=True)
        if invalid:
            raise RuntimeError(
                "Found attributes with invalid values: {}".format(invalid)
            )

    @classmethod
    def get_invalid(cls, context, compute=False):
        invalid = context.data.get("invalid_attributes", [])
        if compute:
            invalid = cls.get_invalid_attributes(context)

        return invalid

    @classmethod
    def get_invalid_attributes(cls, context):
        invalid_attributes = []
        for instance in context:
            # Filter publisable instances.
            if not instance.data["publish"]:
                continue

            # Filter families.
            families = [instance.data["family"]]
            families += instance.data.get("families", [])
            families = list(set(families) & set(self.attributes.keys()))
            if not families:
                continue

            # Get all attributes to validate.
            attributes = {}
            for family in families:
                for preset in self.attributes[family]:
                    [node_name, attribute_name] = preset.split(".")
                    try:
                        attributes[node_name].update(
                            {attribute_name: self.attributes[family][preset]}
                        )
                    except KeyError:
                        attributes.update({
                            node_name: {
                                attribute_name: self.attributes[family][preset]
                            }
                        })

            # Get invalid attributes.
            nodes = pm.ls()
            for node in nodes:
                name = node.name(stripNamespace=True)
                if name not in attributes.keys():
                    continue

                presets_to_validate = attributes[name]
                for attribute in node.listAttr():
                    names = [attribute.shortName(), attribute.longName()]
                    attribute_name = list(
                        set(names) & set(presets_to_validate.keys())
                    )
                    if attribute_name:
                        expected = presets_to_validate[attribute_name[0]]
                        if attribute.get() != expected:
                            invalid_attributes.append(
                                {
                                    "attribute": attribute,
                                    "expected": expected,
                                    "current": attribute.get()
                                }
                            )

        context.data["invalid_attributes"] = invalid_attributes
        return invalid_attributes

    @classmethod
    def repair(cls, instance):
        invalid = cls.get_invalid(instance)
        for data in invalid:
            data["attribute"].set(data["expected"])
