""" ResourceManagementHandler

  Module that allows users to access the ResourceManagementDB remotely.
"""
from DIRAC import gConfig, S_OK
from DIRAC.Core.DISET.RequestHandler import RequestHandler, getServiceOption
from DIRAC.ResourceStatusSystem.Utilities import Synchronizer
from DIRAC.ResourceStatusSystem.Service.ResourceStatusHandler import loadResourceStatusComponent


class ResourceManagementHandlerMixin:
    """
  The ResourceManagementHandler exposes the DB front-end functions through a
  XML-RPC server, functionalities inherited from :class:`DIRAC.Core.DISET.Reques\
  tHandler.RequestHandler`

  According to the ResourceManagementDB philosophy, only functions of the type:
  - insert
  - select
  - delete
  - addOrModify

  are exposed. If you need anything more complicated, either look for it on the
  :class:`ResourceManagementClient`, or code it yourself. This way the DB and the
  Service are kept clean and tidied.
  """

    @classmethod
    def initializeHandler(cls, serviceInfoDict):
        """
        Dynamically loads ResourceManagement database plugin module, as advised by the config,
        (assumes that the module name and a class name are the same)

        :param serviceInfoDict: service info dictionary
        :return: standard Dirac return object
        """
        defaultOption, defaultClass = "ResourceManagementDB", "ResourceManagementDB"
        configValue = getServiceOption(serviceInfoDict, defaultOption, defaultClass)
        result = loadResourceStatusComponent(configValue, configValue, parentLogger=cls.log)

        if not result["OK"]:
            return result

        cls.db = result["Value"]
        syncObject = Synchronizer.Synchronizer()
        gConfig.addListenerToNewVersionEvent(syncObject.sync)

        return S_OK()

    def __logResult(self, methodName, result):
        """
        Method that writes to log error messages
        """

        if not result["OK"]:
            self.log.error(f"{methodName} : {result['Message']}")

    types_insert = [str, dict]

    def export_insert(self, table, params):
        """
        This method is a bridge to access :class:`ResourceManagementDB` remotely. It
        does not add neither processing nor validation. If you need to know more
        about this method, you must keep reading on the database documentation.

        :param table: string or dict - should contain the table from which querying
        :paract dict params: arguments for the mysql query. Currently it is being used only for column selection.

        For example: meta = {'columns': ['Name']} will return only the 'Name' column.

        :return: S_OK() || S_ERROR()
        """

        self.log.info(f"insert: {table} {params}")

        # remove unnecessary key generated by locals()
        del params["self"]

        res = self.db.insert(table, params)
        self.__logResult("insert", res)

        return res

    types_select = [[str, dict], dict]

    def export_select(self, table, params):
        """
        This method is a bridge to access :class:`ResourceManagementDB` remotely.
        It does not add neither processing nor validation. If you need to know more\
        about this method, you must keep reading on the database documentation.

        :param table: string or dict - should contain the table from which querying
        :paract dict params: arguments for the mysql query. Currently it is being used only for column selection.

        For example: meta = {'columns': ['Name']} will return only the 'Name' column.

        :return: S_OK() || S_ERROR()
        """

        params = {k: list(set(v)) if isinstance(v, list) else v for k, v in params.items()}
        self.log.info(f"select: {table} {params}")

        res = self.db.select(table, params)
        self.__logResult("select", res)

        return res

    types_delete = [[str, dict], dict]

    def export_delete(self, table, params):
        """
        This method is a bridge to access :class:`ResourceManagementDB` remotely.\
        It does not add neither processing nor validation. If you need to know more \
        about this method, you must keep reading on the database documentation.

        :param table: string or dict - should contain the table from which querying
        :paract dict params: arguments for the mysql query. Currently it is being used only for column selection.

        For example: meta = {'columns': ['Name']} will return only the 'Name' column.

        :return: S_OK() || S_ERROR()
        """

        self.log.info(f"delete: {table} {params}")

        res = self.db.delete(table, params)
        self.__logResult("delete", res)

        return res

    types_addOrModify = [[str, dict], dict]

    def export_addOrModify(self, table, params):
        """
        This method is a bridge to access :class:`ResourceManagementDB` remotely. It does
        not add neither processing nor validation. If you need to know more about
        this method, you must keep reading on the database documentation.

        :param table: string or dict - should contain the table from which querying
        :paract dict params: arguments for the mysql query. Currently it is being used only for column selection.

        For example: meta = {'columns': ['Name']} will return only the 'Name' column.

        :return: S_OK() || S_ERROR()
        """

        self.log.info(f"addOrModify: {table} {params}")

        res = self.db.addOrModify(table, params)
        self.__logResult("addOrModify", res)

        return res


class ResourceManagementHandler(ResourceManagementHandlerMixin, RequestHandler):
    pass
