""" DIRAC FileCatalog mix-in class to manage users and groups within the FC database
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

__RCSID__ = "$Id$"

import six
import time
import threading

from DIRAC import S_OK, S_ERROR, gConfig, gLogger
from DIRAC.DataManagementSystem.DB.FileCatalogComponents.UserGroupManager.UserAndGroupManagerBase import (
    UserAndGroupManagerBase,
)


class UserAndGroupManagerDB(UserAndGroupManagerBase):
    def getUserAndGroupID(self, credDict):
        """Get a uid, gid tuple for the given Credentials"""
        # Get the user
        s_uid = credDict.get("username", "anon")
        res = self.getUserID(s_uid)
        if not res["OK"]:
            return res
        uid = res["Value"]
        # Get the group (create it if it doesn't exist)
        s_gid = credDict.get("group", "anon")
        res = self.getGroupID(s_gid)
        if not res["OK"]:
            return res
        gid = res["Value"]
        return S_OK((uid, gid))

    #####################################################################
    #
    #  User related methods
    #
    #####################################################################

    def getUserID(self, user):
        """Get ID for a user specified by its name"""
        if isinstance(user, six.integer_types):
            return S_OK(user)
        if user in self.db.users.keys():
            return S_OK(self.db.users[user])
        return self.__addUser(user)

    def addUser(self, uname):
        """Add a new user with a name 'uname'"""
        return self.getUserID(uname)

    def getUsers(self):
        # self.__refreshUsers()
        return S_OK(self.db.users)

    def findUser(self, user):
        return self.getUserID(user)

    def getUserName(self, uid):
        """Get user name for the given id"""
        if uid in self.db.uids.keys():
            return S_OK(self.db.uids[uid])
        return S_ERROR("User id %d not found" % uid)

    def deleteUser(self, uname, force=True):
        """Delete a user specified by its name"""
        # ToDo: Check first if there are files belonging to the user
        if not force:
            pass
        return self.__removeUser(uname)

    def __addUser(self, uname):
        startTime = time.time()
        self.lock.acquire()
        waitTime = time.time()
        gLogger.debug("UserGroupManager AddUser lock created. Waited %.3f seconds. %s" % (waitTime - startTime, uname))
        if uname in self.db.users.keys():
            uid = self.db.users[uname]
            gLogger.debug(
                "UserGroupManager AddUser lock released. Used %.3f seconds. %s" % (time.time() - waitTime, uname)
            )
            self.lock.release()
            return S_OK(uid)
        res = self.db.insertFields("FC_Users", ["UserName"], [uname])
        if not res["OK"]:
            gLogger.debug(
                "UserGroupManager AddUser lock released. Used %.3f seconds. %s" % (time.time() - waitTime, uname)
            )
            self.lock.release()
            if "Duplicate entry" in res["Message"]:
                result = self._refreshUsers()
                if not result["OK"]:
                    return result
                if uname in self.db.users.keys():
                    uid = self.db.users[uname]
                    return S_OK(uid)
            return res
        uid = res["lastRowId"]
        self.db.uids[uid] = uname
        self.db.users[uname] = uid
        gLogger.debug("UserGroupManager AddUser lock released. Used %.3f seconds. %s" % (time.time() - waitTime, uname))
        self.lock.release()
        return S_OK(uid)

    def __removeUser(self, uname):
        startTime = time.time()
        self.lock.acquire()
        waitTime = time.time()
        gLogger.debug(
            "UserGroupManager RemoveUser lock created. Waited %.3f seconds. %s" % (waitTime - startTime, uname)
        )
        uid = self.db.users.get(uname, "Missing")
        req = "DELETE FROM FC_Users WHERE UserName='%s'" % uname
        res = self.db._update(req)
        if not res["OK"]:
            gLogger.debug(
                "UserGroupManager RemoveUser lock released. Used %.3f seconds. %s" % (time.time() - waitTime, uname)
            )
            self.lock.release()
            return res
        if uid != "Missing":
            self.db.users.pop(uname)
            self.db.uids.pop(uid)
        gLogger.debug(
            "UserGroupManager RemoveUser lock released. Used %.3f seconds. %s" % (time.time() - waitTime, uname)
        )
        self.lock.release()
        return S_OK()

    def _refreshUsers(self):
        """Get the current user IDs and names"""
        startTime = time.time()
        self.lock.acquire()
        waitTime = time.time()
        gLogger.debug("UserGroupManager RefreshUsers lock created. Waited %.3f seconds." % (waitTime - startTime))
        req = "SELECT UID,UserName from FC_Users"
        res = self.db._query(req)
        if not res["OK"]:
            gLogger.debug("UserGroupManager RefreshUsers lock released. Used %.3f seconds." % (time.time() - waitTime))
            self.lock.release()
            return res
        self.db.users = {}
        self.db.uids = {}
        for uid, uname in res["Value"]:
            self.db.users[uname] = uid
            self.db.uids[uid] = uname
        gLogger.debug("UserGroupManager RefreshUsers lock released. Used %.3f seconds." % (time.time() - waitTime))
        self.lock.release()
        return S_OK()

    #####################################################################
    #
    #  Group related methods
    #

    def getGroupID(self, group):
        """Get ID for a group specified by its name"""
        if isinstance(group, six.integer_types):
            return S_OK(group)
        if group in self.db.groups.keys():
            return S_OK(self.db.groups[group])
        return self.__addGroup(group)

    def addGroup(self, gname):
        """Add a new group with a name 'name'"""
        return self.getGroupID(gname)

    def getGroups(self):
        # self.__refreshGroups()
        return S_OK(self.db.groups)

    def findGroup(self, group):
        return self.getGroupID(group)

    def getGroupName(self, gid):
        """Get group name for the given id"""
        if gid in self.db.gids.keys():
            return S_OK(self.db.gids[gid])
        return S_ERROR("Group id %d not found" % gid)

    def deleteGroup(self, gname, force=True):
        """Delete a group specified by its name"""
        if not force:
            # ToDo: Check first if there are files belonging to the group
            pass
        return self.__removeGroup(gname)

    def __addGroup(self, group):
        startTime = time.time()
        self.lock.acquire()
        waitTime = time.time()
        gLogger.debug("UserGroupManager AddGroup lock created. Waited %.3f seconds. %s" % (waitTime - startTime, group))
        if group in self.db.groups.keys():
            gid = self.db.groups[group]
            gLogger.debug(
                "UserGroupManager AddGroup lock released. Used %.3f seconds. %s" % (time.time() - waitTime, group)
            )
            self.lock.release()
            return S_OK(gid)
        res = self.db.insertFields("FC_Groups", ["GroupName"], [group])
        if not res["OK"]:
            gLogger.debug(
                "UserGroupManager AddGroup lock released. Used %.3f seconds. %s" % (time.time() - waitTime, group)
            )
            self.lock.release()
            if "Duplicate entry" in res["Message"]:
                result = self._refreshGroups()
                if not result["OK"]:
                    return result
                if group in self.db.groups.keys():
                    gid = self.db.groups[group]
                    return S_OK(gid)
            return res
        gid = res["lastRowId"]
        self.db.gids[gid] = group
        self.db.groups[group] = gid
        gLogger.debug(
            "UserGroupManager AddGroup lock released. Used %.3f seconds. %s" % (time.time() - waitTime, group)
        )
        self.lock.release()
        return S_OK(gid)

    def __removeGroup(self, group):
        startTime = time.time()
        self.lock.acquire()
        waitTime = time.time()
        gLogger.debug(
            "UserGroupManager RemoveGroup lock created. Waited %.3f seconds. %s" % (waitTime - startTime, group)
        )
        gid = self.db.groups.get(group, "Missing")
        req = "DELETE FROM FC_Groups WHERE GroupName='%s'" % group
        res = self.db._update(req)
        if not res["OK"]:
            gLogger.debug(
                "UserGroupManager RemoveGroup lock released. Used %.3f seconds. %s" % (time.time() - waitTime, group)
            )
            self.lock.release()
            return res
        if gid != "Missing":
            self.db.groups.pop(group)
            self.db.gids.pop(gid)
        gLogger.debug(
            "UserGroupManager RemoveGroup lock released. Used %.3f seconds. %s" % (time.time() - waitTime, group)
        )
        self.lock.release()
        return S_OK()

    def _refreshGroups(self):
        """Get the current group IDs and names"""
        req = "SELECT GID,GroupName from FC_Groups"
        startTime = time.time()
        self.lock.acquire()
        waitTime = time.time()
        gLogger.debug("UserGroupManager RefreshGroups lock created. Waited %.3f seconds." % (waitTime - startTime))
        res = self.db._query(req)
        if not res["OK"]:
            gLogger.debug("UserGroupManager RefreshGroups lock released. Used %.3f seconds." % (time.time() - waitTime))
            self.lock.release()
            return res
        self.db.groups = {}
        self.db.gids = {}
        for gid, gname in res["Value"]:
            self.db.groups[gname] = gid
            self.db.gids[gid] = gname
        gLogger.debug("UserGroupManager RefreshGroups lock released. Used %.3f seconds." % (time.time() - waitTime))
        self.lock.release()
        return S_OK()
