""" :mod: RegisterReplica

    ==================

    .. module: RegisterReplica

    :synopsis: register replica handler

    RegisterReplica operation handler
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

__RCSID__ = "$Id $"

from DIRAC import S_OK, S_ERROR
from DIRAC.FrameworkSystem.Client.MonitoringClient import gMonitor
from DIRAC.DataManagementSystem.Agent.RequestOperations.DMSRequestOperationsBase import DMSRequestOperationsBase

from DIRAC.MonitoringSystem.Client.MonitoringReporter import MonitoringReporter

########################################################################


class RegisterReplica(DMSRequestOperationsBase):
  """
  .. class:: RegisterReplica

  RegisterReplica operation handler

  :param self: self reference
  :param ~DIRAC.RequestManagementSystem.Client.Operation.Operation operation: Operation instance
  :param str csPath: CS path for this handler
  """

  def __init__(self, operation=None, csPath=None):
    """c'tor

    """
    DMSRequestOperationsBase.__init__(self, operation, csPath)

  def __call__(self):
    """ call me maybe """

    # The flag  'rmsMonitoring' is set by the RequestTask and is False by default.
    # Here we use 'createRMSRecord' to create the ES record which is defined inside OperationHandlerBase.
    if self.rmsMonitoring:
      self.rmsMonitoringReporter = MonitoringReporter(monitoringType="RMSMonitoring")
    else:
      # # RegisterReplica specific monitor info
      gMonitor.registerActivity("RegisterReplicaAtt", "Attempted replicas registrations",
                                "RequestExecutingAgent", "Replicas/min", gMonitor.OP_SUM)
      gMonitor.registerActivity("RegisterReplicaOK", "Successful replicas registrations",
                                "RequestExecutingAgent", "Replicas/min", gMonitor.OP_SUM)
      gMonitor.registerActivity("RegisterReplicaFail", "Failed replicas registrations",
                                "RequestExecutingAgent", "Replicas/min", gMonitor.OP_SUM)

    # # counter for failed replicas

    failedReplicas = 0
    # # catalog to use
    catalogs = self.operation.Catalog
    if catalogs:
      catalogs = [cat.strip() for cat in catalogs.split(',')]
    # # get waiting files
    waitingFiles = self.getWaitingFilesList()

    if self.rmsMonitoring:
      self.rmsMonitoringReporter.addRecord(
          self.createRMSRecord("Attempted", len(waitingFiles))
      )

    # # loop over files
    registerOperations = {}
    successReplicas = 0
    for opFile in waitingFiles:

      if not self.rmsMonitoring:
        gMonitor.addMark("RegisterReplicaAtt", 1)

      # # get LFN
      lfn = opFile.LFN
      # # and others
      targetSE = self.operation.targetSEList[0]
      replicaTuple = (lfn, opFile.PFN, targetSE)
      # # call ReplicaManager
      registerReplica = self.dm.registerReplica(replicaTuple, catalogs)
      # # check results
      if not registerReplica["OK"] or lfn in registerReplica["Value"]["Failed"]:
        # There have been some errors

        if self.rmsMonitoring:
          self.rmsMonitoringReporter.addRecord(
              self.createRMSRecord("Failed", 1)
          )
        else:
          gMonitor.addMark("RegisterReplicaFail", 1)
#        self.dataLoggingClient().addFileRecord( lfn, "RegisterReplicaFail", ','.join( catalogs ) if catalogs else "all catalogs", "", "RegisterReplica" )

        reason = registerReplica.get("Message", registerReplica.get("Value", {}).get("Failed", {}).get(lfn, 'Unknown'))
        errorStr = "failed to register LFN %s: %s" % (lfn, str(reason))
        # FIXME: this is incompatible with the change made in the DM that we
        # ignore failures if successful in at least one catalog
        if lfn in registerReplica.get("Value", {}).get("Successful", {}) and isinstance(reason, dict):
          # As we managed, let's create a new operation for just the remaining registration
          errorStr += ' - adding registerReplica operations to request'
          for failedCatalog in reason:
            key = '%s/%s' % (targetSE, failedCatalog)
            newOperation = self.getRegisterOperation(opFile, targetSE, type='RegisterReplica', catalog=failedCatalog)
            if key not in registerOperations:
              registerOperations[key] = newOperation
            else:
              registerOperations[key].addFile(newOperation[0])
          opFile.Status = 'Done'
        else:
          opFile.Error = errorStr
          catMaster = True
          if isinstance(reason, dict):
            from DIRAC.Resources.Catalog.FileCatalog import FileCatalog
            for failedCatalog in reason:
              catMaster = (catMaster and
                           FileCatalog()._getCatalogConfigDetails(failedCatalog).get('Value', {}).get('Master', False))
          # If one targets explicitly a catalog and it fails or if it fails on the master catalog
          if (catalogs or catMaster) \
                  and ('file does not exist' in opFile.Error.lower()
                       or 'no such file' in opFile.Error.lower()):
            # Check if the file really exists in SE, if not, consider this file registration as Done
            res = self.dm.getReplicaMetadata(lfn, targetSE)
            notExist = bool('No such file' in res.get('Value', {}).get('Failed', {}).get(lfn, ''))
            if not notExist:
              opFile.Status = 'Failed'
            else:
              opFile.Status = 'Done'
          if opFile.Status != 'Done':
            failedReplicas += 1
        self.log.warn(errorStr)

      else:
        # All is OK
        if self.rmsMonitoring:
          self.rmsMonitoringReporter.addRecord(
              self.createRMSRecord("Successful", 1)
          )
        else:
          gMonitor.addMark("RegisterReplicaOK", 1)
          successReplicas += 1
          self.log.verbose("Replica %s has been registered at %s" %
                           (lfn, ','.join(catalogs) if catalogs else "all catalogs"))

        opFile.Status = "Done"

    # # if we have new replications to take place, put them at the end
    if registerOperations:
      self.log.info("adding %d operations to the request" % len(registerOperations))
    for operation in registerOperations.values():
      self.operation._parent.addOperation(operation)

    if self.rmsMonitoring:
      self.rmsMonitoringReporter.commit()

    # # final check
    infoStr = ''
    if successReplicas:
      infoStr = "%d replicas successfully registered" % successReplicas
    if failedReplicas:
      infoStr += ", %d replicas failed to register" % failedReplicas
    self.log.info("All replicas processed", infoStr)
    if failedReplicas:
      self.operation.Error = "some replicas failed to register"
      return S_ERROR(self.operation.Error)

    return S_OK()
