package de.ids_mannheim.korap.service;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

import de.ids_mannheim.korap.cache.VirtualCorpusCache;
import de.ids_mannheim.korap.config.FullConfiguration;
import de.ids_mannheim.korap.constant.GroupMemberStatus;
import de.ids_mannheim.korap.constant.QueryAccessStatus;
import de.ids_mannheim.korap.constant.QueryType;
import de.ids_mannheim.korap.constant.ResourceType;
import de.ids_mannheim.korap.dao.AdminDao;
import de.ids_mannheim.korap.dao.QueryAccessDao;
import de.ids_mannheim.korap.dao.QueryDao;
import de.ids_mannheim.korap.dto.QueryAccessDto;
import de.ids_mannheim.korap.dto.QueryDto;
import de.ids_mannheim.korap.dto.converter.QueryAccessConverter;
import de.ids_mannheim.korap.dto.converter.QueryConverter;
import de.ids_mannheim.korap.entity.QueryAccess;
import de.ids_mannheim.korap.entity.QueryDO;
import de.ids_mannheim.korap.entity.UserGroup;
import de.ids_mannheim.korap.entity.UserGroupMember;
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.query.serialize.QuerySerializer;
import de.ids_mannheim.korap.user.User.CorpusAccess;
import de.ids_mannheim.korap.utils.JsonUtils;
import de.ids_mannheim.korap.utils.KoralCollectionQueryBuilder;
import de.ids_mannheim.korap.utils.ParameterChecker;
import de.ids_mannheim.korap.web.SearchKrill;
import de.ids_mannheim.korap.web.controller.QueryReferenceController;
import de.ids_mannheim.korap.web.controller.VirtualCorpusController;
import de.ids_mannheim.korap.web.input.QueryJson;
import jakarta.ws.rs.core.Response.Status;

/**
 * QueryService handles the logic behind
 * {@link VirtualCorpusController} and
 * {@link QueryReferenceController}. Virtual corpora and
 * stored-queries are both treated as queries of different types.
 * Thus, they are handled logically similarly.
 * 
 * QueryService communicates with {@link QueryDao}, handles
 * {@link QueryDO} and
 * returns
 * {@link QueryDto} to {@link VirtualCorpusController} and
 * {@link QueryReferenceController}.
 * 
 * @author margaretha
 *
 */
@Service
public class QueryService {

    public static Logger jlog = LogManager.getLogger(QueryService.class);

    public static boolean DEBUG = false;

    public static Pattern queryNamePattern = Pattern
            .compile("[a-zA-Z0-9]+[a-zA-Z_0-9-.]+");

    @Autowired
    private QueryDao queryDao;
    @Autowired
    private QueryAccessDao accessDao;
    @Autowired
    private AdminDao adminDao;
    @Autowired
    private UserGroupService userGroupService;
    @Autowired
    private SearchKrill krill;
    @Autowired
    private FullConfiguration config;
    @Autowired
    private QueryConverter converter;
    @Autowired
    private QueryAccessConverter accessConverter;

    private void verifyUsername (String contextUsername, String pathUsername)
            throws KustvaktException {
        if (!contextUsername.equals(pathUsername)
                && !adminDao.isAdmin(contextUsername)) {
            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                    "Unauthorized operation for user: " + contextUsername,
                    contextUsername);
        }
    }

    public List<QueryDto> listOwnerQuery (String username,
            QueryType queryType) throws KustvaktException {
        List<QueryDO> list = queryDao.retrieveOwnerQuery(username, queryType);
        return createQueryDtos(list, queryType);
    }

    public List<QueryDto> listSystemQuery (QueryType queryType)
            throws KustvaktException {
        List<QueryDO> list = queryDao.retrieveQueryByType(ResourceType.SYSTEM,
                null, queryType);
        return createQueryDtos(list, queryType);
    }

    public List<QueryDto> listAvailableQueryForUser (String username,
            QueryType queryType) throws KustvaktException {
        List<QueryDO> list = queryDao.retrieveQueryByUser(username, queryType);
        return createQueryDtos(list, queryType);
    }

    public List<QueryDto> listQueryByType (String createdBy, ResourceType type,
            QueryType queryType) throws KustvaktException {

        List<QueryDO> virtualCorpora =
                queryDao.retrieveQueryByType(type, createdBy, queryType);
        Collections.sort(virtualCorpora);
        return createQueryDtos(virtualCorpora, queryType);

    }

    private ArrayList<QueryDto> createQueryDtos (List<QueryDO> queryList,
            QueryType queryType) throws KustvaktException {
        ArrayList<QueryDto> dtos = new ArrayList<>(queryList.size());
        QueryDO query;
        Iterator<QueryDO> i = queryList.iterator();
        while (i.hasNext()) {
            query = i.next();
            // String json = query.getKoralQuery();
            String statistics = null;
            // if (queryType.equals(QueryType.VIRTUAL_CORPUS)) {
            // statistics = krill.getStatistics(json);
            // }
            QueryDto dto = converter.createQueryDto(query, statistics);
            dtos.add(dto);
        }
        return dtos;
    }

    public void deleteQueryByName (String username, String queryName,
            String createdBy, QueryType type) throws KustvaktException {

        QueryDO query = queryDao.retrieveQueryByName(queryName, createdBy);

        if (query == null) {
            String code = createdBy + "/" + queryName;
            throw new KustvaktException(StatusCodes.NO_RESOURCE_FOUND,
                    "Query " + code + " is not found.", String.valueOf(code));
        }
        else if (query.getCreatedBy().equals(username)
                || adminDao.isAdmin(username)) {

            if (query.getType().equals(ResourceType.PUBLISHED)) {
                QueryAccess access =
                        accessDao.retrieveHiddenAccess(query.getId());
                accessDao.deleteAccess(access, "system");
                userGroupService.deleteAutoHiddenGroup(
                        access.getUserGroup().getId(), "system");
            }
            if (type.equals(QueryType.VIRTUAL_CORPUS)
                    && VirtualCorpusCache.contains(queryName)) {
                VirtualCorpusCache.delete(queryName);
            }
            queryDao.deleteQuery(query);
        }
        else {
            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                    "Unauthorized operation for user: " + username, username);
        }
    }

    public Status handlePutRequest (String username, String queryCreator,
            String queryName, QueryJson queryJson) throws KustvaktException {

        verifyUsername(username, queryCreator);
        QueryDO query = queryDao.retrieveQueryByName(queryName, queryCreator);

        if (query == null) {
            storeQuery(queryJson, queryName, queryCreator, username);
            return Status.CREATED;
        }
        else {
            editQuery(query, queryJson, queryName, username);
            return Status.NO_CONTENT;
        }
    }

    public void editQuery (QueryDO existingQuery, QueryJson newQuery,
            String queryName, String username) throws KustvaktException {

        if (!username.equals(existingQuery.getCreatedBy())
                && !adminDao.isAdmin(username)) {
            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                    "Unauthorized operation for user: " + username, username);
        }

        String koralQuery = null;
        CorpusAccess requiredAccess = null;
        String corpusQuery = newQuery.getCorpusQuery();
        String query = newQuery.getQuery();
        String queryLanguage = newQuery.getQueryLanguage();
        if (corpusQuery != null && !corpusQuery.isEmpty()) {
            koralQuery = serializeCorpusQuery(corpusQuery);
            requiredAccess = determineRequiredAccess(newQuery.isCached(),
                    queryName, koralQuery);
        }
        else if (query != null && !query.isEmpty() && queryLanguage != null
                && !queryLanguage.isEmpty()) {
            koralQuery = serializeQuery(query, queryLanguage);
        }

        ResourceType type = newQuery.getType();
        if (type != null) {
            if (existingQuery.getType().equals(ResourceType.PUBLISHED)) {
                // withdraw from publication
                if (!type.equals(ResourceType.PUBLISHED)) {
                    QueryAccess hiddenAccess = accessDao
                            .retrieveHiddenAccess(existingQuery.getId());
                    deleteQueryAccess(hiddenAccess.getId(), "system");
                    int groupId = hiddenAccess.getUserGroup().getId();
                    userGroupService.deleteAutoHiddenGroup(groupId, "system");
                    // EM: should the users within the hidden group
                    // receive
                    // notifications?
                }
                // else remains the same
            }
            else if (type.equals(ResourceType.PUBLISHED)) {
                publishQuery(existingQuery.getId());
            }
        }

        queryDao.editQuery(existingQuery, queryName, type, requiredAccess,
                koralQuery, newQuery.getDefinition(), newQuery.getDescription(),
                newQuery.getStatus(), newQuery.isCached(), query,
                queryLanguage);
    }

    private void publishQuery (int queryId) throws KustvaktException {

        QueryAccess access = accessDao.retrieveHiddenAccess(queryId);
        // check if hidden access exists
        if (access == null) {
            QueryDO query = queryDao.retrieveQueryById(queryId);
            // create and assign a new hidden group
            int groupId = userGroupService.createAutoHiddenGroup();
            UserGroup autoHidden =
                    userGroupService.retrieveUserGroupById(groupId);
            accessDao.createAccessToQuery(query, autoHidden, "system",
                    QueryAccessStatus.HIDDEN);
        }
        else {
            // should not happened
            jlog.error("Cannot publish query with id: " + queryId
                    + ". Hidden access exists! Access id: " + access.getId());
        }
    }

    public void storeQuery (QueryJson query, String queryName,
            String queryCreator, String username) throws KustvaktException {
        QueryType queryType = query.getQueryType();
        if (!checkNumberOfQueryLimit(username, queryType)) {
            String type = queryType.displayName().toLowerCase();
            throw new KustvaktException(StatusCodes.NOT_ALLOWED, 
                    "Cannot create "+type+". The maximum number "
                            + "of "+type+" has been reached.");
        }
        
        String koralQuery = computeKoralQuery(query);
        storeQuery(username, queryName, query.getType(), query.getQueryType(),
                koralQuery, query.getDefinition(), query.getDescription(),
                query.getStatus(), query.isCached(), queryCreator,
                query.getQuery(), query.getQueryLanguage());
    }
    
    private boolean checkNumberOfQueryLimit (String username,
            QueryType queryType) throws KustvaktException {
        Long num = queryDao.countNumberOfQuery(username, queryType);
        if (num < config.getMaxNumberOfUserQueries()) return true;
        else return false;
    }
    
    private String computeKoralQuery (QueryJson query) throws KustvaktException {
        if (query.getQueryType().equals(QueryType.VIRTUAL_CORPUS)) {
            ParameterChecker.checkStringValue(query.getCorpusQuery(),
                    "corpusQuery");
            return serializeCorpusQuery(query.getCorpusQuery());
        }
        
        if (query.getQueryType().equals(QueryType.QUERY)) {
            ParameterChecker.checkStringValue(query.getQuery(), "query");
            ParameterChecker.checkStringValue(query.getQueryLanguage(),
                    "queryLanguage");
            return
                    serializeQuery(query.getQuery(), query.getQueryLanguage());
        }
        
        return null;
    }

    public void storeQuery (String username, String queryName,
            ResourceType type, QueryType queryType, String koralQuery,
            String definition, String description, String status,
            boolean isCached, String queryCreator, String query,
            String queryLanguage) throws KustvaktException {
        ParameterChecker.checkNameValue(queryName, "queryName");
        ParameterChecker.checkObjectValue(type, "type");

        if (!queryNamePattern.matcher(queryName).matches()) {
            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT, queryType
                    .displayName() + " must consists of alphanumerical characters "
                            + "(limited to ASCII), underscores, dashes and periods. "
                            + "The name has to start with an alphanumerical character.",
                    queryName);
        }

        if (type.equals(ResourceType.SYSTEM)) {
            if (adminDao.isAdmin(username)) {
                queryCreator = "system";
            }
            else if (!username.equals("system")) {
                throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                        "Unauthorized operation for user: " + username,
                        username);
            }
        }

        CorpusAccess requiredAccess = CorpusAccess.PUB;
        if (queryType.equals(QueryType.VIRTUAL_CORPUS)) {
            requiredAccess =
                    determineRequiredAccess(isCached, queryName, koralQuery);
        }

        if (DEBUG) {
            jlog.debug("Storing query: " + queryName + "in the database ");
        }

        int queryId = 0;
        try {
            queryId = queryDao.createQuery(queryName, type, queryType,
                    requiredAccess, koralQuery, definition, description, status,
                    isCached, queryCreator, query, queryLanguage);

        }
        catch (Exception e) {
            Throwable cause = e;
            Throwable lastCause = null;
            while ((cause = cause.getCause()) != null
                    && !cause.equals(lastCause)) {
                if (cause instanceof SQLException) {
                    break;
                }
                lastCause = cause;
            }
            throw new KustvaktException(StatusCodes.DB_INSERT_FAILED,
                    cause.getMessage());
        }
        if (type.equals(ResourceType.PUBLISHED)) {
            publishQuery(queryId);
        }
    }

    private String serializeCorpusQuery (String corpusQuery)
            throws KustvaktException {
        QuerySerializer serializer = new QuerySerializer();
        serializer.setCollection(corpusQuery);
        String koralQuery;
        try {
            koralQuery = serializer.convertCollectionToJson();
        }
        catch (JsonProcessingException e) {
            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
                    "Invalid argument: " + corpusQuery, corpusQuery);
        }
        if (DEBUG) {
            jlog.debug(koralQuery);
        }
        return koralQuery;
    }

    private String serializeQuery (String query, String queryLanguage)
            throws KustvaktException {
        QuerySerializer serializer = new QuerySerializer();
        String koralQuery;
        koralQuery = serializer.setQuery(query, queryLanguage).toJSON();
        if (DEBUG) {
            jlog.debug(koralQuery);
        }
        return koralQuery;
    }

    public CorpusAccess determineRequiredAccess (boolean isCached, String name,
            String koralQuery) throws KustvaktException {

        if (isCached) {
            KoralCollectionQueryBuilder koral =
                    new KoralCollectionQueryBuilder();
            koral.with("referTo " + name);
            koralQuery = koral.toJSON();
            if (DEBUG) {
                jlog.debug("Determine vc access with vc ref: " + koralQuery);
            }

        }

        if (findDocWithLicense(koralQuery, config.getAllOnlyRegex())) {
            return CorpusAccess.ALL;
        }
        else if (findDocWithLicense(koralQuery, config.getPublicOnlyRegex())) {
            return CorpusAccess.PUB;
        }
        else {
            return CorpusAccess.FREE;
        }
    }

    private boolean findDocWithLicense (String koralQuery, String license)
            throws KustvaktException {
        KoralCollectionQueryBuilder koral = new KoralCollectionQueryBuilder();
        koral.setBaseQuery(koralQuery);
        koral.with("availability=/" + license + "/");
        String json = koral.toJSON();

        String statistics = krill.getStatistics(json);
        JsonNode node = JsonUtils.readTree(statistics);
        int numberOfDoc = node.at("/documents").asInt();
        if (DEBUG) {
            jlog.debug(
                    "License: " + license + ", number of docs: " + numberOfDoc);
        }
        return (numberOfDoc > 0) ? true : false;
    }

    public void shareQuery (String username, String createdBy, String queryName,
            String groupName) throws KustvaktException {

        QueryDO query = queryDao.retrieveQueryByName(queryName, createdBy);
        if (query == null) {
            String code = createdBy + "/" + queryName;
            throw new KustvaktException(StatusCodes.NO_RESOURCE_FOUND,
                    "Query " + code + " is not found.", String.valueOf(code));
        }
        if (!username.equals(query.getCreatedBy())
                && !adminDao.isAdmin(username)) {
            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                    "Unauthorized operation for user: " + username, username);
        }

        UserGroup userGroup =
                userGroupService.retrieveUserGroupByName(groupName);

        if (!isQueryAccessAdmin(userGroup, username)
                && !adminDao.isAdmin(username)) {
            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                    "Unauthorized operation for user: " + username, username);
        }
        else {
            try {
                accessDao.createAccessToQuery(query, userGroup, username,
                        QueryAccessStatus.ACTIVE);
            }
            catch (Exception e) {
                Throwable cause = e;
                Throwable lastCause = null;
                while ((cause = cause.getCause()) != null
                        && !cause.equals(lastCause)) {
                    if (cause instanceof SQLException) {
                        break;
                    }
                    lastCause = cause;
                }
                throw new KustvaktException(StatusCodes.DB_INSERT_FAILED,
                        cause.getMessage());
            }

            queryDao.editQuery(query, null, ResourceType.PROJECT, null, null,
                    null, null, null, query.isCached(), null, null);
        }
    }

    private boolean isQueryAccessAdmin (UserGroup userGroup, String username)
            throws KustvaktException {
        List<UserGroupMember> accessAdmins =
                userGroupService.retrieveQueryAccessAdmins(userGroup);
        for (UserGroupMember m : accessAdmins) {
            if (username.equals(m.getUserId())) {
                return true;
            }
        }
        return false;
    }

    // public void editVCAccess (VirtualCorpusAccess access, String
    // username)
    // throws KustvaktException {
    //
    // // get all the VCA admins
    // UserGroup userGroup = access.getUserGroup();
    // List<UserGroupMember> accessAdmins =
    // userGroupService.retrieveVCAccessAdmins(userGroup);
    //
    // User user = authManager.getUser(username);
    // if (!user.isSystemAdmin()) {
    // throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
    // "Unauthorized operation for user: " + username, username);
    // }
    // }

    public List<QueryAccessDto> listQueryAccessByUsername (String username)
            throws KustvaktException {
        List<QueryAccess> accessList = new ArrayList<>();
        if (adminDao.isAdmin(username)) {
            accessList = accessDao.retrieveAllAccess();
        }
        else {
            List<UserGroup> groups =
                    userGroupService.retrieveUserGroup(username);
            for (UserGroup g : groups) {
                if (isQueryAccessAdmin(g, username)) {
                    accessList.addAll(
                            accessDao.retrieveActiveAccessByGroup(g.getId()));
                }
            }
        }
        return accessConverter.createQueryAccessDto(accessList);
    }

    public List<QueryAccessDto> listQueryAccessByQuery (String username,
            String queryCreator, String queryName) throws KustvaktException {

        List<QueryAccess> accessList;
        if (adminDao.isAdmin(username)) {
            accessList =
                    accessDao.retrieveAllAccessByQuery(queryCreator, queryName);
        }
        else {
            accessList = accessDao.retrieveActiveAccessByQuery(queryCreator,
                    queryName);
            List<QueryAccess> filteredAccessList = new ArrayList<>();
            for (QueryAccess access : accessList) {
                UserGroup userGroup = access.getUserGroup();
                if (isQueryAccessAdmin(userGroup, username)) {
                    filteredAccessList.add(access);
                }
            }
            accessList = filteredAccessList;
        }
        return accessConverter.createQueryAccessDto(accessList);
    }

    @Deprecated
    public List<QueryAccessDto> listVCAccessByGroup (String username,
            int groupId) throws KustvaktException {
        UserGroup userGroup = userGroupService.retrieveUserGroupById(groupId);

        List<QueryAccess> accessList;
        if (adminDao.isAdmin(username)) {
            accessList = accessDao.retrieveAllAccessByGroup(groupId);
        }
        else if (isQueryAccessAdmin(userGroup, username)) {
            accessList = accessDao.retrieveActiveAccessByGroup(groupId);
        }
        else {
            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                    "Unauthorized operation for user: " + username, username);
        }

        return accessConverter.createQueryAccessDto(accessList);
    }

    public List<QueryAccessDto> listQueryAccessByGroup (String username,
            String groupName) throws KustvaktException {
        UserGroup userGroup =
                userGroupService.retrieveUserGroupByName(groupName);

        List<QueryAccess> accessList;
        if (adminDao.isAdmin(username)) {
            accessList = accessDao.retrieveAllAccessByGroup(userGroup.getId());
        }
        else if (isQueryAccessAdmin(userGroup, username)) {
            accessList =
                    accessDao.retrieveActiveAccessByGroup(userGroup.getId());
        }
        else {
            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                    "Unauthorized operation for user: " + username, username);
        }
        return accessConverter.createQueryAccessDto(accessList);
    }

    public void deleteQueryAccess (int accessId, String username)
            throws KustvaktException {

        QueryAccess access = accessDao.retrieveAccessById(accessId);
        UserGroup userGroup = access.getUserGroup();
        if (isQueryAccessAdmin(userGroup, username)
                || adminDao.isAdmin(username)) {
            accessDao.deleteAccess(access, username);
        }
        else {
            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                    "Unauthorized operation for user: " + username, username);
        }

    }

    public JsonNode retrieveKoralQuery (String username, String queryName,
            String createdBy, QueryType queryType) throws KustvaktException {
        QueryDO query =
                searchQueryByName(username, queryName, createdBy, queryType);
        String koralQuery = query.getKoralQuery();
        JsonNode kq = JsonUtils.readTree(koralQuery);
        return kq;
    }

    public JsonNode retrieveFieldValues (String username, String queryName,
            String createdBy, QueryType queryType, String fieldName)
            throws KustvaktException {
        
        ParameterChecker.checkStringValue(fieldName, "fieldName");
        
//        if (!adminDao.isAdmin(username)) {
//            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
//                    "Unauthorized operation for user: " + username, username);
//        }
        
        if (fieldName.equals("tokens") || fieldName.equals("base")) {
            throw new KustvaktException(StatusCodes.NOT_ALLOWED,
                    "Retrieving values of field "+fieldName+" is not allowed.");
        }
        else {
            QueryDO query = searchQueryByName(username, queryName, createdBy,
                    queryType);
            String koralQuery = query.getKoralQuery();
            return krill.getFieldValuesForVC(koralQuery, fieldName);
        }
    }

    public QueryDO searchQueryByName (String username, String queryName,
            String createdBy, QueryType queryType) throws KustvaktException {
        QueryDO query = queryDao.retrieveQueryByName(queryName, createdBy);
        if (query == null) {
            String code = createdBy + "/" + queryName;
            throw new KustvaktException(StatusCodes.NO_RESOURCE_FOUND,
                    queryType.displayName() + " " + code + " is not found.",
                    String.valueOf(code));
        }
        checkQueryAccess(query, username);
        return query;
    }

    public QueryDto retrieveQueryByName (String username, String queryName,
            String createdBy, QueryType queryType) throws KustvaktException {
        QueryDO query =
                searchQueryByName(username, queryName, createdBy, queryType);
        // String json = query.getKoralQuery();
        String statistics = null;
        // long start,end;
        // start = System.currentTimeMillis();
        // if (query.getQueryType().equals(QueryType.VIRTUAL_CORPUS))
        // {
        // statistics = krill.getStatistics(json);
        // }
        // end = System.currentTimeMillis();
        // jlog.debug("{} statistics duration: {}", queryName, (end -
        // start));
        return converter.createQueryDto(query, statistics);
    }

    public QueryDto searchQueryById (String username, int queryId)
            throws KustvaktException {

        QueryDO query = queryDao.retrieveQueryById(queryId);
        checkQueryAccess(query, username);
        // String json = query.getKoralQuery();
        // String statistics = krill.getStatistics(json);
        return converter.createQueryDto(query, null);
    }

    private void checkQueryAccess (QueryDO query, String username)
            throws KustvaktException {
        ResourceType type = query.getType();

        if (!adminDao.isAdmin(username)
                && !username.equals(query.getCreatedBy())) {
            if (type.equals(ResourceType.PRIVATE)
                    || (type.equals(ResourceType.PROJECT)
                            && !hasAccess(username, query.getId()))) {
                throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                        "Unauthorized operation for user: " + username,
                        username);
            }

            else if (ResourceType.PUBLISHED.equals(type)
                    && !username.equals("guest")) {
                // add user in the query's auto group
                UserGroup userGroup = userGroupService
                        .retrieveHiddenUserGroupByQuery(query.getId());
                try {
                    userGroupService.addGroupMember(username, userGroup,
                            "system", GroupMemberStatus.ACTIVE);
                    // member roles are not set (not necessary)
                }
                catch (KustvaktException e) {
                    // member exists
                    // skip adding user to hidden group
                }
            }
            // else VirtualCorpusType.SYSTEM
        }
    }

    private boolean hasAccess (String username, int queryId)
            throws KustvaktException {
        UserGroup userGroup;
        List<QueryAccess> accessList =
                accessDao.retrieveActiveAccessByQuery(queryId);
        for (QueryAccess access : accessList) {
            userGroup = access.getUserGroup();
            if (userGroupService.isMember(username, userGroup)) {
                return true;
            }
        }
        return false;
    }
}
