""" test File Plugin
"""

from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import mock
import unittest
import tempfile
import os
import shutil
import errno

from DIRAC import S_OK
from DIRAC.Resources.Storage.StorageElement import StorageElementItem


def mock_StorageFactory_getConfigStorageName(storageName, referenceType, seConfigPath=None):
  resolvedName = storageName
  return S_OK(resolvedName)


def mock_StorageFactory_getConfigStorageOptions(storageName, derivedStorageName=None, seConfigPath=None):
  """ Get the options associated to the StorageElement as defined in the CS
  """
  optionsDict = {'BackendType': 'local',
                 'ReadAccess': 'Active',
                 'WriteAccess': 'Active',
                 'AccessProtocols': ['file'],
                 'WriteProtocols': ['file'], }
  return S_OK(optionsDict)


def mock_StorageFactory_getConfigStorageProtocols(storageName, derivedStorageName=None, seConfigPath=None):
  """ Protocol specific information is present as sections in the Storage configuration
  """
  protocolDetails = {'Section': {'Host': '',
                                 'Path': '/tmp/se',
                                 'PluginName': 'File',
                                 'Port': '',
                                 'Protocol': 'file',
                                 'SpaceToken': '',
                                 'WSUrl': ''}}

  return S_OK(protocolDetails)


class TestBase(unittest.TestCase):
  """ Base test class. Defines all the method to test
  """

  @mock.patch('DIRAC.Resources.Storage.StorageFactory.StorageFactory._getConfigStorageName',
              side_effect=mock_StorageFactory_getConfigStorageName)
  @mock.patch('DIRAC.Resources.Storage.StorageFactory.StorageFactory._getConfigStorageOptions',
              side_effect=mock_StorageFactory_getConfigStorageOptions)
  @mock.patch('DIRAC.Resources.Storage.StorageFactory.StorageFactory._getConfigStorageProtocols',
              side_effect=mock_StorageFactory_getConfigStorageProtocols)
  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem._StorageElementItem__isLocalSE',
              return_value=S_OK(True))  # Pretend it's local
  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem.addAccountingOperation',
              return_value=None)  # Don't send accounting
  def setUp(
          self,
          mk_getConfigStorageName,
          mk_getConfigStorageOptions,
          mk_getConfigStorageProtocols,
          mk_isLocalSE,
          mk_addAccountingOperation):
    self.se = StorageElementItem('FAKE')
    self.se.vo = 'test'

    self.basePath = tempfile.mkdtemp(dir='/tmp')
    # Update the basePath of the plugin
    self.se.storages[0].basePath = self.basePath

    self.srcPath = tempfile.mkdtemp(dir='/tmp')

    self.destPath = tempfile.mkdtemp(dir='/tmp')

    self.existingFile = '/test/file.txt'
    self.existingFileSize = 0

    self.nonExistingFile = '/test/nonExistingFile.txt'
    self.subDir = '/test/subDir'
    self.subFile = os.path.join(self.subDir, 'subFile.txt')
    self.subFileSize = 0

    self.FILES = [self.existingFile, self.nonExistingFile, self.subFile]
    self.DIRECTORIES = [self.subDir]
    self.ALL = self.FILES + self.DIRECTORIES

    with open(os.path.join(self.srcPath, self.existingFile.replace('/test/', '')), 'w') as f:
      f.write("I put something in the file so that it has a size\n")
    self.existingFileSize = os.path.getsize(os.path.join(self.srcPath, self.existingFile.replace('/test/', '')))

    assert self.existingFileSize

    os.mkdir(os.path.join(self.srcPath, os.path.basename(self.subDir)))

    with open(os.path.join(self.srcPath, self.subFile.replace('/test/', '')), 'w') as f:
      f.write("This one should have a size as well\n")
    self.subFileSize = os.path.getsize(os.path.join(self.srcPath, self.subFile.replace('/test/', '')))

    assert self.subFileSize

  def tearDown(self):
    shutil.rmtree(self.basePath)
    shutil.rmtree(self.srcPath)
    shutil.rmtree(self.destPath)
    pass

  def walkAll(self):
    for dirname in [self.basePath, self.destPath]:
      self.walkPath(dirname)

  def walkPath(self, path):
    for root, dirs, files in os.walk(path):
      print(root)
      print("  dirs")
      for d in dirs:
        print("    ", os.path.join(root, d))
      print("  files")
      for f in files:
        print("    ", os.path.join(root, f))

  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem._StorageElementItem__isLocalSE',
              return_value=S_OK(True))  # Pretend it's local
  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem.addAccountingOperation',
              return_value=None)  # Don't send accounting
  def test_01_getURL(self, mk_isLocalSE, mk_addAccounting):
    """Testing getURL"""
    # Testing the getURL
    res = self.se.getURL(self.ALL)
    self.assertTrue(res['OK'], res)
    self.assertTrue(not res['Value']['Failed'], res['Value']['Failed'])
    self.assertTrue(len(res['Value']['Successful']) == len(self.ALL))
    for lfn, url in res['Value']['Successful'].items():
      self.assertEqual(url, self.basePath.rstrip('/') + lfn)

  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem._StorageElementItem__isLocalSE',
              return_value=S_OK(True))  # Pretend it's local
  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem.addAccountingOperation',
              return_value=None)  # Don't send accounting
  def test_02_FileTest(self, mk_isLocalSE, mk_addAccounting):
    """Testing createDirectory"""
    # Putting the files

    def localPutFile(fn, size=0):
      """If fn is '/test/fn.txt', it calls
        { '/test/fn.txt' : /tmp/generatedPath/fn.txt}
      """
      transfDic = {fn: os.path.join(self.srcPath, fn.replace('/test/', ''))}
      return self.se.putFile(transfDic, sourceSize=size)

    # wrong size
    res = localPutFile(self.existingFile, size=-1)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.existingFile in res['Value']['Failed'], res)
    self.assertTrue('not match' in res['Value']['Failed'][self.existingFile], res)
    self.assertTrue(not os.path.exists(self.basePath + self.existingFile))

    # Correct size
    res = localPutFile(self.existingFile, size=self.existingFileSize)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.existingFile in res['Value']['Successful'], res)
    self.assertTrue(os.path.exists(self.basePath + self.existingFile))

    # No size
    res = localPutFile(self.existingFile)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.existingFile in res['Value']['Successful'], res)
    self.assertTrue(os.path.exists(self.basePath + self.existingFile))

    # No existing source file
    res = localPutFile(self.nonExistingFile)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.nonExistingFile in res['Value']['Failed'], res)
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)

    # sub file
    res = localPutFile(self.subFile)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.subFile in res['Value']['Successful'], res)
    self.assertTrue(os.path.exists(self.basePath + self.subFile))

    # Directory
    res = localPutFile(self.subDir)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.subDir in res['Value']['Failed'])
    self.assertTrue(os.strerror(errno.EISDIR) in res['Value']['Failed'][self.subDir], res)

    res = self.se.exists(self.FILES)
    self.assertTrue(res['OK'], res)
    self.assertTrue(not res['Value']['Failed'], res)
    self.assertTrue(res['Value']['Successful'][self.existingFile], res)
    self.assertTrue(not res['Value']['Successful'][self.nonExistingFile], res)

    res = self.se.getFileSize(self.ALL)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful'][self.existingFile], self.existingFileSize)
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)
    self.assertTrue(os.strerror(errno.EISDIR) in res['Value']['Failed'][self.subDir], res)

    res = self.se.getFileMetadata(self.ALL)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.existingFile in res['Value']['Successful'])
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)
    self.assertTrue(os.strerror(errno.EISDIR) in res['Value']['Failed'][self.subDir], res)

    res = self.se.isFile(self.ALL)
    self.assertTrue(res['OK'], res)
    self.assertTrue(res['Value']['Successful'][self.existingFile], res)
    self.assertTrue(not res['Value']['Successful'][self.subDir], res)
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)

    res = self.se.getFile(self.ALL, localPath=self.destPath)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful'][self.existingFile], self.existingFileSize)
    self.assertTrue(os.path.exists(os.path.join(self.destPath, os.path.basename(self.existingFile))))
    self.assertEqual(res['Value']['Successful'][self.subFile], self.subFileSize)
    self.assertTrue(os.path.exists(os.path.join(self.destPath, os.path.basename(self.subFile))))
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)
    self.assertTrue(os.strerror(errno.EISDIR) in res['Value']['Failed'][self.subDir], res)

    res = self.se.removeFile(self.ALL)
    self.assertTrue(res['OK'], res)
    self.assertTrue(res['Value']['Successful'][self.existingFile])
    self.assertTrue(not os.path.exists(self.basePath + self.existingFile))
    self.assertTrue(res['Value']['Successful'][self.subFile])
    self.assertTrue(not os.path.exists(self.basePath + self.subFile))
    self.assertTrue(res['Value']['Successful'][self.nonExistingFile])
    self.assertTrue(os.strerror(errno.EISDIR) in res['Value']['Failed'][self.subDir])

  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem._StorageElementItem__isLocalSE',
              return_value=S_OK(True))  # Pretend it's local
  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem.addAccountingOperation',
              return_value=None)  # Don't send accounting
  def test_03_createDirectory(self, mk_isLocalSE, mk_addAccounting):
    """Testing creating directories"""

    res = self.se.createDirectory(self.subDir)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.subDir in res['Value']['Successful'])
    self.assertTrue(os.path.exists(self.basePath + self.subDir))

  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem._StorageElementItem__isLocalSE',
              return_value=S_OK(True))  # Pretend it's local
  @mock.patch('DIRAC.Resources.Storage.StorageElement.StorageElementItem.addAccountingOperation',
              return_value=None)  # Don't send accounting
  def test_04_putDirectory(self, mk_isLocalSE, mk_addAccounting):
    """Testing putDirectory"""

    nonExistingDir = '/test/forsuredoesnotexist'
    localdirs = ['/test', nonExistingDir]

    # Correct size
    res = self.se.putDirectory({'/test': self.srcPath})
    self.assertTrue(res['OK'], res)
    self.assertTrue('/test' in res['Value']['Successful'], res)
    self.assertEqual(res['Value']['Successful']['/test'],
                     {'Files': 2, 'Size': self.existingFileSize + self.subFileSize})
    self.assertTrue(os.path.exists(self.basePath + '/test'))
    self.assertTrue(os.path.exists(self.basePath + self.existingFile))
    self.assertTrue(os.path.exists(self.basePath + self.subFile))

    # No existing source directory
    res = self.se.putDirectory({'/test': nonExistingDir})
    self.assertTrue(res['OK'], res)
    self.assertTrue('/test' in res['Value']['Failed'], res)
    self.assertEqual(res['Value']['Failed']['/test'], {'Files': 0, 'Size': 0})

    # sub file
    res = self.se.putDirectory({'/test': self.existingFile})
    self.assertTrue(res['OK'], res)
    self.assertTrue('/test' in res['Value']['Failed'], res)
    self.assertEqual(res['Value']['Failed']['/test'], {'Files': 0, 'Size': 0})

    res = self.se.exists(self.DIRECTORIES + localdirs)
    self.assertTrue(res['OK'], res)
    self.assertTrue(not res['Value']['Failed'], res)
    self.assertTrue(res['Value']['Successful'][self.subDir], res)
    self.assertTrue(not res['Value']['Successful'][nonExistingDir], res)

    res = self.se.getDirectorySize(self.ALL + localdirs)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful'][self.subDir], {'Files': 1, 'Size': self.subFileSize, 'SubDirs': 0})
    self.assertEqual(res['Value']['Successful']['/test'], {'Files': 1, 'Size': self.existingFileSize, 'SubDirs': 1})
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)
    self.assertTrue(os.strerror(errno.ENOTDIR) in res['Value']['Failed'][self.existingFile], res)
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][nonExistingDir], res)

    res = self.se.getDirectoryMetadata(self.ALL + localdirs)
    self.assertTrue(res['OK'], res)
    self.assertTrue(self.subDir in res['Value']['Successful'])
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][nonExistingDir], res)
    self.assertTrue(os.strerror(errno.ENOTDIR) in res['Value']['Failed'][self.existingFile], res)

    res = self.se.isDirectory(self.ALL + localdirs)
    self.assertTrue(res['OK'], res)
    self.assertTrue(not res['Value']['Successful'][self.existingFile])
    self.assertTrue(res['Value']['Successful'][self.subDir], res)
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][nonExistingDir], res)

    res = self.se.listDirectory(self.ALL + localdirs)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful'][self.subDir], {'Files': [self.subFile], 'SubDirs': []})
    self.assertEqual(res['Value']['Successful']['/test'], {'Files': [self.existingFile], 'SubDirs': [self.subDir]})
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][self.nonExistingFile], res)
    self.assertTrue(os.strerror(errno.ENOTDIR) in res['Value']['Failed'][self.existingFile], res)
    self.assertTrue(os.strerror(errno.ENOENT) in res['Value']['Failed'][nonExistingDir], res)

    res = self.se.getDirectory(self.ALL + localdirs, localPath=self.destPath)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful']['/test'],
                     {'Files': 2, 'Size': self.existingFileSize + self.subFileSize})
    self.assertTrue(os.path.exists(self.destPath + self.existingFile))
    self.assertTrue(os.path.exists(self.destPath + self.subFile))
    self.assertEqual(res['Value']['Successful'][self.subDir], {'Files': 1, 'Size': self.subFileSize})
    self.assertTrue(os.path.exists(self.destPath + self.subFile.replace('/test', '')))
    self.assertEqual(res['Value']['Failed'][self.nonExistingFile], {'Files': 0, 'Size': 0})
    self.assertEqual(res['Value']['Failed'][self.existingFile], {'Files': 0, 'Size': 0})
    self.assertEqual(res['Value']['Failed'][nonExistingDir], {'Files': 0, 'Size': 0})

    res = self.se.removeDirectory(nonExistingDir, recursive=False)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful'][nonExistingDir], True)

    res = self.se.removeDirectory(nonExistingDir, recursive=True)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Failed'][nonExistingDir], {'FilesRemoved': 0, 'SizeRemoved': 0})

    res = self.se.removeDirectory(self.nonExistingFile, recursive=False)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful'][self.nonExistingFile], True)

    res = self.se.removeDirectory(self.nonExistingFile, recursive=True)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Failed'][self.nonExistingFile], {'FilesRemoved': 0, 'SizeRemoved': 0})

    res = self.se.removeDirectory(self.existingFile, recursive=False)
    self.assertTrue(res['OK'], res)
    self.assertTrue(os.strerror(errno.ENOTDIR) in res['Value']['Failed'][self.existingFile], res)

    res = self.se.removeDirectory(self.existingFile, recursive=True)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Failed'][self.existingFile], {'FilesRemoved': 0, 'SizeRemoved': 0})

    res = self.se.removeDirectory('/test', recursive=False)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful']['/test'], True)
    self.assertTrue(not os.path.exists(self.basePath + self.existingFile))
    self.assertTrue(os.path.exists(self.basePath + self.subFile))

    res = self.se.removeDirectory('/test', recursive=True)
    self.assertTrue(res['OK'], res)
    self.assertEqual(res['Value']['Successful']['/test'], {'FilesRemoved': 1, 'SizeRemoved': self.subFileSize})
    self.assertTrue(not os.path.exists(self.basePath + '/test'))


if __name__ == '__main__':
  suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestBase)

  unittest.TextTestRunner(verbosity=2).run(suite)
