""" DIRAC FileCatalog component representing a flat directory tree
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

# pylint: disable=protected-access

__RCSID__ = "$Id$"

import six
import os
import stat

from DIRAC import S_OK, S_ERROR
from DIRAC.Core.Utilities.List import stringListToString, intListToString
from DIRAC.DataManagementSystem.DB.FileCatalogComponents.DirectoryManager.DirectoryTreeBase import DirectoryTreeBase


class DirectoryFlatTree(DirectoryTreeBase):

  def __init__(self, database=None):
    DirectoryTreeBase.__init__(self, database)
    self.treeTable = 'DirectoryInfo'

  def getDirectoryCounters(self):
    req = "SELECT COUNT(*) FROM DirectoryInfo"
    res = self.db._query(req)
    if not res['OK']:
      return res
    return S_OK({'DirectoryInfo': res['Value'][0][0]})

  def _findDirectories(self, paths, metadata=[]):
    """ Find file ID if it exists for the given list of LFNs """
    successful = {}
    failed = {}
    req = "SELECT DirName,DirID"
    if metadata:
      req = "%s,%s" % (req, intListToString(metadata))
    req = "%s FROM DirectoryInfo WHERE DirName IN (%s)" % (req, stringListToString(paths))
    res = self.db._query(req)
    if not res['OK']:
      return res
    for tup in res['Value']:
      dirName = tup[0]
      dirID = tup[1]
      metaDict = {'DirID': dirID}
      metaDict.update(dict(zip(metadata, tup[2:])))
      successful[dirName] = metaDict
    for path in paths:
      if path not in successful:
        failed[path] = 'No such file or directory'
    return S_OK({"Successful": successful, "Failed": failed})

  def __findDirs(self, paths, metadata=['DirName']):
    dirs = {}
    req = "SELECT DirID,%s FROM DirectoryInfo WHERE DirName IN (%s)" % (
        intListToString(metadata), stringListToString(paths))
    res = self.db._query(req)
    if not res['OK']:
      return res
    if not res['Value']:
      return S_OK(dirs)
    for tup in res['Value']:
      dirID = tup[0]
      dirs[dirID] = dict(zip(metadata, tup[1:]))
    return S_OK(dirs)

  def getPathPermissions(self, paths, credDict):
    """ Get the permissions for the supplied paths """
    res = self.db.ugManager.getUserAndGroupID(credDict)
    if not res['OK']:
      return res
    uid, gid = res['Value']
    res = self._findDirectories(paths, metadata=['Mode', 'UID', 'GID'])
    if not res['OK']:
      return res
    successful = {}
    for dirName, dirDict in res['Value']['Successful'].items():
      mode = dirDict['Mode']
      p_uid = dirDict['UID']
      p_gid = dirDict['GID']
      successful[dirName] = {}
      if p_uid == uid:
        successful[dirName]['Read'] = mode & stat.S_IRUSR
        successful[dirName]['Write'] = mode & stat.S_IWUSR
        successful[dirName]['Execute'] = mode & stat.S_IXUSR
      elif p_gid == gid:
        successful[dirName]['Read'] = mode & stat.S_IRGRP
        successful[dirName]['Write'] = mode & stat.S_IWGRP
        successful[dirName]['Execute'] = mode & stat.S_IXGRP
      else:
        successful[dirName]['Read'] = mode & stat.S_IROTH
        successful[dirName]['Write'] = mode & stat.S_IWOTH
        successful[dirName]['Execute'] = mode & stat.S_IXOTH
    return S_OK({'Successful': successful, 'Failed': res['Value']['Failed']})

  def findDir(self, path):
    res = self.__findDirs([path])
    if not res['OK']:
      return res
    if not res['Value']:
      return S_OK(0)
    return S_OK(list(res['Value'])[0])

  def removeDir(self, path):
    """ Remove directory """
    res = self.findDir(path)
    if not res['OK']:
      return res
    if not res['Value']:
      return S_OK()
    dirID = res['Value']
    req = "DELETE FROM DirectoryInfo WHERE DirID=%d" % dirID
    return self.db._update(req)

  def makeDirectory(self, path, credDict, status=0):
    """Create a new directory.
       The return value is the dictionary containing all the parameters of the newly created directory
    """

    if path[0] != '/':
      return S_ERROR('Not an absolute path')

    result = self.findDir(path)
    if not result['OK']:
      return result
    if result['Value']:
      return S_OK(result['Value'])

    result = self.db.ugManager.getUserAndGroupID(credDict)
    if not result['OK']:
      return result
    uid, gid = result['Value']

    res = self.getParent(path)
    if not res['OK']:
      return res
    parentID = res['Value']

    req = "INSERT INTO DirectoryInfo (Parent,Status,DirName,UID,GID,Mode,CreationDate,ModificationDate)\
    VALUES (%d,%d,'%s',%d,%d,%d,UTC_TIMESTAMP(),UTC_TIMESTAMP());" % (parentID, status, path, uid, gid, self.db.umask)
    result = self.db._update(req)
    if not result['OK']:
      self.removeDir(path)
      return S_ERROR('Failed to create directory %s' % path)
    return S_OK(result['lastRowId'])

  def makeDir(self, path):
    result = self.findDir(path)
    if not result['OK']:
      return result
    dirID = result['Value']
    if dirID:
      return S_OK(dirID)
    names = ['DirName']
    values = [path]
    result = self.db.insertFields('DirectoryInfo', names, values)
    if not result['OK']:
      return result
    return S_OK(result['lastRowId'])

  def existsDir(self, path):
    """ Check the existence of a directory at the specified path
    """
    result = self.findDir(path)
    if not result['OK']:
      return result
    if not result['Value']:
      return S_OK({"Exists": False})
    return S_OK({"Exists": True, "DirID": result['Value']})

  def getParent(self, path):
    """ Get the parent ID of the given directory """
    return self.findDir(os.path.dirname(path))

  def getParentID(self, dirID):
    """ Get the ID of the parent of a directory specified by ID
    """
    if dirID == 0:
      return S_ERROR('Root directory ID given')
    req = "SELECT Parent FROM DirectoryInfo WHERE DirID=%d" % dirID
    result = self.db._query(req)
    if not result['OK']:
      return result
    if not result['Value']:
      return S_ERROR('No parent found')
    return S_OK(result['Value'][0][0])

  def getDirectoryPath(self, dirID):
    """ Get directory name by directory ID """
    req = "SELECT DirName FROM DirectoryInfo WHERE DirID=%d" % int(dirID)
    result = self.db._query(req)
    if not result['OK']:
      return result
    if not result['Value']:
      return S_ERROR('Directory with id %d not found' % int(dirID))
    return S_OK(result['Value'][0][0])

  def getDirectoryName(self, dirID):
    """ Get directory name by directory ID """
    result = self.getDirectoryPath(dirID)
    if not result['OK']:
      return result
    return S_OK(os.path.basename(result['Value']))

  def getPathIDs(self, path):
    """ Get IDs of all the directories in the parent hierarchy """
    elements = path.split('/')
    pelements = []
    dPath = ''
    for el in elements[1:]:
      dPath += '/' + el
      pelements.append(dPath)

    pathString = ["'" + p + "'" for p in pelements]
    req = "SELECT DirID FROM DirectoryInfo WHERE DirName in (%s) ORDER BY DirID" % ','.join(pathString)
    result = self.db._query(req)
    if not result['OK']:
      return result
    if not result['Value']:
      return S_ERROR('Directory %s not found' % path)
    return S_OK([x[0] for x in result['Value']])

  def getChildren(self, path):
    """ Get child directory IDs for the given directory  """
    if isinstance(path, six.string_types):
      result = self.findDir(path)
      if not result['OK']:
        return result
      dirID = result['Value']
    else:
      dirID = path
    req = "SELECT DirID FROM DirectoryInfo WHERE Parent=%d" % dirID
    result = self.db._query(req)
    if not result['OK']:
      return result
    if not result['Value']:
      return S_OK([])
    return S_OK([x[0] for x in result['Value']])
