""" This agent syncs CS and pilot files to a web server of your choice

.. literalinclude:: ../ConfigTemplate.cfg
  :start-after: ##BEGIN PilotSyncAgent
  :end-before: ##END
  :dedent: 2
  :caption: PilotsSyncAgent options

"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

__RCSID__ = "$Id$"

import os
import json
import shutil
import hashlib
import requests
import six

from DIRAC import S_OK
from DIRAC.Core.Base.AgentModule import AgentModule
from DIRAC.Core.Security.Locations import getHostCertificateAndKeyLocation, getCAsLocation
from DIRAC.DataManagementSystem.Client.DataManager import DataManager
from DIRAC.WorkloadManagementSystem.Utilities.PilotCStoJSONSynchronizer import PilotCStoJSONSynchronizer


class PilotSyncAgent(AgentModule):
  """ Syncs CS and pilot files to a web server of your choice
  """

  def __init__(self, *args, **kwargs):
    """ c'tor
    """
    super(PilotSyncAgent, self).__init__(*args, **kwargs)

    # This location would be enough if we are running this agent on the DIRAC web server
    # '/opt/dirac/webRoot/www/pilot'
    self.saveDir = ''
    self.uploadLocations = []
    self.includeMasterCS = True

  def initialize(self):
    """ Initial settings
    """
    self.workingDirectory = self.am_getOption('WorkDirectory')
    self.saveDir = self.am_getOption('SaveDirectory', self.saveDir)
    self.uploadLocations = self.am_getOption('UploadLocations', self.uploadLocations)
    includeMasterCS = self.am_getOption('IncludeMasterCS', self.includeMasterCS)
    if isinstance(includeMasterCS, six.string_types) and includeMasterCS.lower() in ['n', 'no', 'false']:
      self.includeMasterCS = False

    self.certAndKeyLocation = getHostCertificateAndKeyLocation()
    self.casLocation = getCAsLocation()

    return S_OK()

  def execute(self):
    """ cycle
    """

    ps = PilotCStoJSONSynchronizer()
    ps.workDir = self.workingDirectory

    self.log.verbose("Parameters for this sync:")
    self.log.verbose("repo=" + ps.pilotRepo)
    self.log.verbose("VO repo=" + ps.pilotVORepo)
    self.log.verbose("projectDir=" + ps.projectDir)
    self.log.verbose("pilotScriptsPath=" + ps.pilotScriptPath)
    self.log.verbose("pilotVOScriptsPath=" + ps.pilotVOScriptPath)
    self.log.verbose("pilotRepoBranch=" + ps.pilotRepoBranch)
    self.log.verbose("pilotVORepoBranch=" + ps.pilotVORepoBranch)

    # pilot.json
    res = ps.getCSDict(includeMasterCS=self.includeMasterCS)
    if not res['OK']:
      return res
    pilotDict = res['Value']
    print(json.dumps(pilotDict, indent=4, sort_keys=True))  # just print here as formatting is important
    with open(os.path.join(self.workingDirectory, 'pilot.json'), 'w') as jf:
      json.dump(pilotDict, jf)

    # pilot files
    res = ps.syncScripts()
    if not res['OK']:
      return res
    tarPath, tarFiles = res['Value']

    allFiles = [tarPath] + tarFiles + [os.path.join(self.workingDirectory, 'pilot.json')]

    # checksums
    checksumDict = {}
    for pFile in allFiles:
      filename = os.path.basename(pFile)
      with open(pFile, 'rb') as fp:
        checksumDict[filename] = hashlib.sha512(fp.read()).hexdigest()
      cksPath = os.path.join(self.workingDirectory, 'checksums.sha512')
    with open(cksPath, 'wt') as chksums:
      for filename, chksum in sorted(checksumDict.items()):
        # same as the output from sha512sum commands
        chksums.write('%s  %s\n' % (chksum, filename))

    allFiles = list(set(allFiles + [cksPath]))

    if self.saveDir:
      # Moving files to the correct location
      self.log.info("Moving pilot files", "to %s" % self.saveDir)
      for tf in allFiles:
        # this overrides the destinations
        shutil.move(tf, os.path.join(self.saveDir, os.path.basename(tf)))

    # Here, attempting upload somewhere, and somehow
    for server in self.uploadLocations:
      self.log.info("Attempting to upload", "to %s" % server)
      if server.startswith('https://'):
        for tf in allFiles:
          res = requests.put(
              server, data=tf, verify=self.casLocation, cert=self.certAndKeyLocation)
          if res.status_code not in (200, 202):
            self.log.error("Could not upload",
                           "to %s: status %s" % (server, res.status_code))
      else:  # Assumes this is a DIRAC SE
        for tf in allFiles:
          res = DataManager().put(lfn=tf, fileName=tf, diracSE=server)
          if not res['OK']:
            self.log.error("Could not upload", "to %s: %s" % (server, res['Message']))

    return S_OK()
