"""
  This module contains methods for the validation of production definitions
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

__RCSID__ = "$Id $"

from DIRAC import gLogger, S_OK, S_ERROR
from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
from DIRAC.Resources.Catalog.FileCatalog import FileCatalog


class ProdValidator(object):
    def __init__(self):
        self.transClient = TransformationClient()

    def checkTransStatus(self, transID):
        """Check if the status of the transformation is valid for the transformation to be added to a production.
            New is the only valid status

        :param int transID: the TransformationID
        """
        res = self.transClient.getTransformationParameters(transID, "Status")
        if not res["OK"]:
            return res
        status = res["Value"]
        if status != "New":
            return S_ERROR("checkTransStatus failed : Invalid transformation status: %s" % status)

        return S_OK()

    def checkTransDependency(self, transID, parentTransID):
        """Check if the transformation and the parent transformation are linked

        :param int transID: the TransformationID
        :param int parentTransID: the parent TransformationID
        """
        res = self.transClient.getTransformationMetaQuery(transID, "Input")
        if not res["OK"]:
            return res
        inputquery = res["Value"]
        if not inputquery:
            return S_ERROR("No InputMetaQuery defined for transformation %s" % transID)

        res = self.transClient.getTransformationMetaQuery(parentTransID, "Output")
        if not res["OK"]:
            return res
        parentoutputquery = res["Value"]
        if not parentoutputquery:
            return S_ERROR("No OutputMetaQuery defined for parent transformation %s" % parentTransID)

        # Check the matching between inputquery and parent outputmeta query
        # Currently very simplistic: just support expression with "=" and "in" operators
        gLogger.notice("Applying checkMatchQuery")
        res = self.checkMatchQuery(inputquery, parentoutputquery)

        if not res["OK"]:
            gLogger.error("checkMatchQuery failed")
            return res
        if not res["Value"]:
            return S_ERROR("checkMatchQuery result is False")

        return S_OK()

    def checkMatchQuery(self, mq, mqParent):
        """Check the logical intersection between the two metaqueries

        :param dict mq: a dictionary of the MetaQuery to be checked against the mqParent
        :param dict mqParent: a dictionary of the parent MetaQuery to be checked against the mq
        """
        # Get the metadata types defined in the catalog
        catalog = FileCatalog()
        res = catalog.getMetadataFields()
        if not res["OK"]:
            gLogger.error("Error in getMetadataFields: %s" % res["Message"])
            return res
        if not res["Value"]:
            gLogger.error("Error: no metadata fields defined")
            return res

        MetaTypeDict = res["Value"]["FileMetaFields"]
        MetaTypeDict.update(res["Value"]["DirectoryMetaFields"])

        res = self.checkformatQuery(mq)
        if not res["OK"]:
            return res
        MetaQueryDict = res["Value"]

        res = self.checkformatQuery(mqParent)
        if not res["OK"]:
            return res
        ParentMetaQueryDict = res["Value"]

        for meta, value in MetaQueryDict.items():
            if meta not in MetaTypeDict:
                msg = "Metadata %s is not defined in the Catalog" % meta
                return S_ERROR(msg)
            mtype = MetaTypeDict[meta]
            if mtype.lower() not in ["varchar(128)", "int", "float"]:
                msg = "Metatype %s is not supported" % mtype.lower()
                return S_ERROR(msg)
            if meta not in ParentMetaQueryDict:
                msg = "Metadata %s is not in parent transformation query" % meta
                return S_ERROR(msg)
            if self.compareValues(value, ParentMetaQueryDict[meta]):
                continue
            else:
                msg = "Metadata values %s do not match with %s" % (value, ParentMetaQueryDict[meta])
                gLogger.error(msg)
                return S_OK(False)

        return S_OK(True)

    def checkformatQuery(self, MetaQueryDict):
        """Check the format query and transform all dict values in dict for uniform treatment

        :param dict MetaQueryDict: a dictionary of the MetaQuery
        """
        for meta, value in MetaQueryDict.items():
            values = []
            if isinstance(value, dict):
                operation = list(value)[0]
                if operation not in ["=", "in"]:
                    msg = "Operation %s is not supported" % operation
                    return S_ERROR(msg)
                else:
                    if not isinstance(list(value.values())[0], list):
                        MetaQueryDict[meta] = {"in": list(value.values())}
            else:
                values.append(value)
                MetaQueryDict[meta] = {"in": values}

        return S_OK(MetaQueryDict)

    def compareValues(self, value, parentValue):
        """Very simple comparison. To be improved

        :param dict value: a dictionary with meta data values to be compared with the parentValues
        :param dict parentValue: a dictionary with meta data parentValues be compared with values
        """
        return set(list(value.values())[0]).issubset(set(list(parentValue.values())[0])) or set(
            list(parentValue.values())[0]
        ).issubset(set(list(value.values())[0]))
