""" Agent to extend the number of tasks given the Transformation definition

The following options can be set for the MCExtensionAgent.

.. literalinclude:: ../ConfigTemplate.cfg
  :start-after: ##BEGIN MCExtensionAgent
  :end-before: ##END
  :dedent: 2
  :caption: MCExtensionAgent options
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from DIRAC import S_OK, gLogger
from DIRAC.Core.Base.AgentModule import AgentModule
from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient

__RCSID__ = "$Id$"

AGENT_NAME = "Transformation/MCExtensionAgent"


class MCExtensionAgent(AgentModule):
    def __init__(self, *args, **kwargs):
        """c'tor"""
        AgentModule.__init__(self, *args, **kwargs)

        self.transClient = TransformationClient()
        agentTSTypes = self.am_getOption("TransformationTypes", [])
        if agentTSTypes:
            self.transformationTypes = sorted(agentTSTypes)
        else:
            self.transformationTypes = sorted(
                Operations().getValue("Transformations/ExtendableTransfTypes", ["MCSimulation", "Simulation"])
            )
        self.maxIterationTasks = self.am_getOption("TasksPerIteration", 50)
        self.maxFailRate = self.am_getOption("MaxFailureRate", 30)
        self.maxWaitingJobs = self.am_getOption("MaxWaitingJobs", 1000)

    #############################################################################
    def initialize(self):
        """Sets defaults"""

        gLogger.info("Will consider the following transformation types: %s" % str(self.transformationTypes))
        gLogger.info("Will create a maximum of %s tasks per iteration" % self.maxIterationTasks)
        gLogger.info(
            "Will not submit tasks for transformations with failure rate greater than %s%%" % (self.maxFailRate)
        )
        gLogger.info("Will not submit tasks for transformations with more than %d waiting jobs" % self.maxWaitingJobs)

        return S_OK()

    #############################################################################
    def execute(self):
        """The MCExtensionAgent execution method."""

        self.enableFlag = self.am_getOption("EnableFlag", "True")
        if not self.enableFlag == "True":
            self.log.info("MCExtensionAgent is disabled by configuration option EnableFlag")
            return S_OK("Disabled via CS flag")

        # Obtain the transformations in Cleaning status and remove any mention of the jobs/files
        res = self.transClient.getTransformations({"Status": "Active", "Type": self.transformationTypes})
        if res["OK"]:
            for transDict in res["Value"]:
                transID = transDict["TransformationID"]
                maxTasks = transDict["MaxNumberOfTasks"]
                self.extendTransformation(transID, maxTasks)
        return S_OK()

    def extendTransformation(self, transID, maxTasks):
        gLogger.info("Considering extension of transformation %d" % transID)
        # Get the current count of tasks submitted for this transformation
        res = self.transClient.getTransformationTaskStats(transID)
        if not res["OK"]:
            if res["Message"] != "No records found":
                gLogger.error("Failed to get task statistics", "%s %s" % (transID, res["Message"]))
                return res
            else:
                statusDict = {}
        else:
            statusDict = res["Value"]
        gLogger.verbose("Current task count for transformation %d" % transID)
        for status in sorted(statusDict):
            statusCount = statusDict[status]
            gLogger.verbose("%s : %s" % (status.ljust(20), str(statusCount).rjust(8)))
        # Determine the number of tasks to be created
        numberOfTasks = self._calculateTaskNumber(maxTasks, statusDict)
        if not numberOfTasks:
            gLogger.info("No tasks required for transformation %d" % transID)
            return S_OK()
        # Extend the transformation by the determined number of tasks
        res = self.transClient.extendTransformation(transID, numberOfTasks)
        if not res["OK"]:
            gLogger.error("Failed to extend transformation", "%s %s" % (transID, res["Message"]))
            return res
        gLogger.info("Successfully extended transformation %d by %d tasks" % (transID, numberOfTasks))
        return S_OK()

    def _calculateTaskNumber(self, maxTasks, statusDict):
        """Utility function"""
        done = statusDict.get("Done", 0)
        failed = statusDict.get("Failed", 0)
        waiting = statusDict.get("Waiting", 0)
        total = statusDict.get("TotalCreated", 0)
        # If the failure rate is higher than acceptable
        if (total != 0) and ((100.0 * float(failed) / float(total)) > self.maxFailRate):
            return 0
        # If we already have enough completed jobs
        if done >= maxTasks:
            return 0
        if waiting > self.maxWaitingJobs:
            return 0
        numberOfTasks = maxTasks - (total - failed)
        if numberOfTasks < 0:
            # this happens when people extend the transformation manually instead of increasing MaxNumberOfTasks
            return 0
        if numberOfTasks > self.maxIterationTasks:
            numberOfTasks = self.maxIterationTasks
        return numberOfTasks
