"""
It is a helper module which contains the available reports
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import hashlib
import re

from DIRAC import S_OK, S_ERROR, gConfig
from DIRAC.ConfigurationSystem.Client.PathFinder import getServiceSection
from DIRAC.MonitoringSystem.private.Plotters.BasePlotter import BasePlotter as myBasePlotter
from DIRAC.Core.Utilities.ObjectLoader import loadObjects

__RCSID__ = "$Id$"


class PlottersList(object):

    """
    Used to determine all available plotters used to create the plots

    :param dict __plotters: stores the available plotters
    """

    def __init__(self):
        """c'tor

        :param self: self reference
        """

        objectsLoaded = loadObjects(
            "MonitoringSystem/private/Plotters", re.compile(r".*[a-z1-9]Plotter\.py$"), myBasePlotter
        )
        self.__plotters = {}
        for objName in objectsLoaded:
            self.__plotters[objName[:-7]] = objectsLoaded[objName]

    def getPlotterClass(self, typeName):
        """
        It returns the plotter class for a given monitoring type
        """
        try:
            return S_OK(self.__plotters[typeName])
        except KeyError:
            return S_ERROR()


class MainReporter(object):
    """
    :param object __db: database object
    :param str __setup: DIRAC setup
    :param str __csSection: CS section used to configure some parameters.
    :param list __plotterList: available plotters
    """

    def __init__(self, db, setup):
        """c'tor

        :param self: self reference
        :param object: the database module
        :param str setup: DIRAC setup
        """
        self.__db = db
        self.__setup = setup
        self.__csSection = getServiceSection("Monitoring/Monitoring", setup=setup)
        self.__plotterList = PlottersList()

    def __calculateReportHash(self, reportRequest):
        """
        It creates an unique identifier

        :param dict reportRequest: plot attributes used to create the plot
        :return: an unique value
        :rtype: str
        """
        requestToHash = dict(reportRequest)
        granularity = gConfig.getValue("%s/CacheTimeGranularity" % self.__csSection, 300)
        for key in ("startTime", "endTime"):
            epoch = requestToHash[key]
            requestToHash[key] = epoch - epoch % granularity
        md5Hash = hashlib.md5()
        md5Hash.update(repr(requestToHash).encode())
        md5Hash.update(self.__setup.encode())
        return md5Hash.hexdigest()

    def generate(self, reportRequest, credDict):
        """
        It is used to create a plot.

        :param dict reportRequest: plot attributes used to create the plot

        .. note:: I know credDict is not used, but if we plan to add some policy, we need to use it!

        :return: dict S_OK/S_ERROR the values used to create the plot
        """
        typeName = reportRequest["typeName"]
        plotterClass = self.__plotterList.getPlotterClass(typeName)
        if not plotterClass["OK"]:
            return S_ERROR("There's no reporter registered for type %s" % typeName)

        reportRequest["hash"] = self.__calculateReportHash(reportRequest)
        plotter = plotterClass["Value"](self.__db, self.__setup, reportRequest["extraArgs"])
        return plotter.generate(reportRequest)

    def list(self, typeName):
        """
        It returns the available plots

        :param str typeName: monitoring type
        :return: dict S_OK/S_ERROR list of available reports (plots)
        """
        plotterClass = self.__plotterList.getPlotterClass(typeName)
        if not plotterClass["OK"]:
            return S_ERROR("There's no plotter registered for type %s" % typeName)
        plotter = plotterClass["Value"](self.__db, self.__setup)
        return S_OK(plotter.plotsList())
