"""
  Defines the plugin to take storage space information given by WLCG Accounting Json
  https://twiki.cern.ch/twiki/bin/view/LCG/AccountingTaskForce#Storage_Space_Accounting
  https://twiki.cern.ch/twiki/pub/LCG/AccountingTaskForce/storage_service_v4.txt
  https://docs.google.com/document/d/1yzCvKpxsbcQC5K9MyvXc-vBF1HGPBk4vhjw3MEXoXf8

  When this is used, the OccupancyLFN has to be the full path on the storage, and not just the LFN
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import json
import os
import tempfile
import shutil
import errno
import gfal2  # pylint: disable=import-error

from DIRAC import S_OK, S_ERROR


class WLCGAccountingJson(object):
  """ .. class:: WLCGAccountingJson

  Occupancy plugin to return the space information given by WLCG Accouting Json
  """

  def __init__(self, se):
    self.se = se
    self.log = se.log.getSubLogger('WLCGAccountingJson')
    self.name = self.se.name

  def _downloadJsonFile(self, occupancyLFN, filePath):
    """ Download the json file at the location

        :param occupancyLFN: lfn for the file
        :param filePath: destination path for the file

    """
    for storage in self.se.storages:
      try:
        ctx = gfal2.creat_context()
        params = ctx.transfer_parameters()
        params.overwrite = True
        res = storage.updateURL(occupancyLFN)
        if not res['OK']:
          continue
        occupancyURL = res['Value']
        ctx.filecopy(params, occupancyURL, 'file://' + filePath)
        return
      except gfal2.GError as e:
        detailMsg = "Failed to copy file %s to destination url %s: [%d] %s" % (
            occupancyURL, filePath, e.code, e.message)
        self.log.debug("Exception while copying", detailMsg)
        continue

  def getOccupancy(self, **kwargs):
    """ Returns the space information given by WLCG Accouting Json

        :returns: S_OK with dict (keys: SpaceReservation, Total, Free)
    """
    occupancyLFN = kwargs['occupancyLFN']

    if not occupancyLFN:
      return S_ERROR("Failed to get occupancyLFN")

    tmpDirName = tempfile.mkdtemp()
    filePath = os.path.join(tmpDirName, os.path.basename(occupancyLFN))

    self._downloadJsonFile(occupancyLFN, filePath)

    if not os.path.isfile(filePath):
      return S_ERROR('No WLCGAccountingJson file of %s is downloaded.' % (self.name))

    with open(filePath, 'r') as path:
      occupancyDict = json.load(path)

    # delete temp dir
    shutil.rmtree(tmpDirName)

    try:
      storageShares = occupancyDict['storageservice']['storageshares']
    except KeyError as e:
      return S_ERROR(errno.ENOMSG, 'Issue finding storage shares. %s in %s at %s.' % (repr(e), occupancyLFN, self.name))

    spaceReservation = self.se.options.get('SpaceReservation')

    # get storageshares in WLCGAccountingJson file
    storageSharesSR = None
    if spaceReservation:
      for storageshare in storageShares:
        if storageshare.get('name') == spaceReservation:
          storageSharesSR = storageshare
          break
    else:
      self.log.debug(
          'Could not find SpaceReservation in CS, and get storageShares and spaceReservation from WLCGAccoutingJson.')
      shareLen = []
      for storage in self.se.storages:
        basePath = storage.getParameters()['Path']
        for share in storageShares:
          shareLen.append((share, len(os.path.commonprefix([share['path'][0], basePath]))))
      storageSharesSR = max(shareLen, key=lambda x: x[1])[0]
      spaceReservation = storageSharesSR.get('name')

    sTokenDict = {}
    sTokenDict['SpaceReservation'] = spaceReservation
    try:
      sTokenDict['Total'] = storageSharesSR['totalsize']
      sTokenDict['Free'] = sTokenDict['Total'] - storageSharesSR['usedsize']
    except KeyError as e:
      return S_ERROR(
          errno.ENOMSG, 'Issue finding Total or Free space left. %s in %s storageshares.' % (repr(e), spaceReservation))

    return S_OK(sTokenDict)
