""" Class that contains client access to the production DB handler. """
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.Core.Base.Client import Client, createClient
from DIRAC.ProductionSystem.Utilities.StateMachine import ProductionsStateMachine


@createClient('Production/ProductionManager')
class ProductionClient(Client):
  """ Exposes the functionality available in the ProductionSystem/ProductionManagerHandler
  """

  def __init__(self, **kwargs):
    """ Simple constructor
    """

    super(ProductionClient, self).__init__(**kwargs)
    self.setServer('Production/ProductionManager')
    self.prodDescription = {}
    self.stepCounter = 1

  # Method applying the Production System State machine

  def _applyProductionStatusStateMachine(self, prodID, status, force=False):
    """ Performs a state machine check for productions when asked to change the status

    :param int prodID: the ProductionID on which the state machine check is performed
    :param str status: the proposed status which is checked to be valid
    :param bool force: a boolean. When force=True the proposed status is forced to pass the state machine check

    :return: S_OK with the new status or S_ERROR
    """
    res = self.getProductionParameters(prodID, 'Status')
    if not res['OK']:
      return res
    originalStatus = res['Value']
    proposedStatus = status
    if force:
      return S_OK(proposedStatus)
    else:
      stateChange = ProductionsStateMachine(originalStatus).setState(proposedStatus)
      if not stateChange['OK']:
        return S_OK(originalStatus)
      else:
        return S_OK(stateChange['Value'])

  # Methods contacting the ProductionManager Service

  def setProductionStatus(self, prodID, status):
    """ Sets the status of a production

    :param int prodID: the ProductionID
    :param str status: the production status to be set to the prodID
    """
    rpcClient = self._getRPC()
    # Apply the production state machine
    res = self._applyProductionStatusStateMachine(prodID, status, force=False)
    if not res['OK']:
      return res
    newStatus = res['Value']
    if newStatus != status:
      return S_ERROR('Cannot change status')
    return rpcClient.setProductionStatus(prodID, status)

  def getProductions(self, condDict=None, older=None, newer=None, timeStamp=None,
                     orderAttribute=None, limit=100):
    """ Gets all the productions in the system, incrementally. "limit" here is just used to determine the offset.
    """

    rpcClient = self._getRPC()
    productions = []
    if condDict is None:
      condDict = {}
    if timeStamp is None:
      timeStamp = 'CreationDate'
    # getting transformations - incrementally
    offsetToApply = 0
    while True:
      res = rpcClient.getProductions(condDict, older, newer, timeStamp, orderAttribute, limit,
                                     offsetToApply)
      if not res['OK']:
        return res
      else:
        gLogger.verbose("Result for limit %d, offset %d: %d" % (limit, offsetToApply, len(res['Value'])))
        if res['Value']:
          productions = productions + res['Value']
          offsetToApply += limit
        if len(res['Value']) < limit:
          break
    return S_OK(productions)

  def getProductionTransformations(self, prodName, condDict=None, older=None, newer=None, timeStamp=None,
                                   orderAttribute=None, limit=10000):
    """ Gets all the production transformations for a production, incrementally.
        "limit" here is just used to determine the offset.

    :param str prodName: the production name
    :return: the list of the transformations associated to the production
    """

    rpcClient = self._getRPC()
    productionTransformations = []

    if condDict is None:
      condDict = {}
    if timeStamp is None:
      timeStamp = 'CreationTime'
    # getting productionTransformations - incrementally
    offsetToApply = 0
    while True:
      res = rpcClient.getProductionTransformations(prodName, condDict, older, newer, timeStamp, orderAttribute, limit,
                                                   offsetToApply)
      if not res['OK']:
        return res
      else:
        gLogger.verbose("Result for limit %d, offset %d: %d" % (limit, offsetToApply, len(res['Value'])))
        if res['Value']:
          productionTransformations = productionTransformations + res['Value']
          offsetToApply += limit
        if len(res['Value']) < limit:
          break
    return S_OK(productionTransformations)

  def addProductionStep(self, prodStep):
    """ Add a production step and update the production description

    :param object prodStep: the production step, i.e. a ProductionStep object describing the transformation
    """
    stepName = 'Step' + str(self.stepCounter) + '_' + prodStep.Name
    self.stepCounter += 1
    prodStep.Name = stepName

    res = prodStep.getAsDict()
    if not res['OK']:
      return res
    prodStepDict = res['Value']
    rpcClient = self._getRPC()
    res = rpcClient.addProductionStep(prodStepDict)
    if not res['OK']:
      return res
    stepID = res['Value']
    self.prodDescription[prodStep.Name] = {'stepID': stepID, 'parentStep': prodStepDict['parentStep']}
    return S_OK()
