package org.gcube.application.geoportal.service.engine.providers;

import java.util.ArrayList;
import java.util.List;

import org.bson.Document;
import org.gcube.application.cms.implementations.ProjectAccess;
import org.gcube.application.cms.implementations.faults.InvalidUserRoleException;
import org.gcube.application.cms.implementations.faults.ProjectNotFoundException;
import org.gcube.application.cms.implementations.faults.RegistrationException;
import org.gcube.application.cms.implementations.faults.UnauthorizedAccess;
import org.gcube.application.geoportal.common.model.document.Project;
import org.gcube.application.geoportal.common.model.document.relationships.Relationship;
import org.gcube.application.geoportal.common.model.document.relationships.RelationshipNavigationObject;
import org.gcube.application.geoportal.common.model.rest.ConfigurationException;
import org.gcube.application.geoportal.common.model.rest.QueryRequest;
import org.gcube.application.geoportal.service.engine.mongo.ProfiledMongoManager;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ProjectAccessImpl implements ProjectAccess {

    @Override
    public Project getById(String ucid, String id) throws RegistrationException, ConfigurationException, InvalidUserRoleException, ProjectNotFoundException, UnauthorizedAccess {
        return new ProfiledMongoManager(ucid).getByID(id);
    }

    @Override
    public Iterable<Document> query(String ucid, QueryRequest query) throws RegistrationException, ConfigurationException, InvalidUserRoleException {
        return new ProfiledMongoManager(ucid).query(query);
    }

    @Override
    public List<RelationshipNavigationObject> getRelations(String ucid, String id, String relation, Boolean deep) throws InvalidUserRoleException, RegistrationException, ProjectNotFoundException, ConfigurationException, UnauthorizedAccess {
        return getRelationshipChain(ucid,id,relation,deep);
    }


    public static List<RelationshipNavigationObject> getRelationshipChain(String ucid, String id, String relationshipId, Boolean deep) throws RegistrationException, ConfigurationException, InvalidUserRoleException, ProjectNotFoundException, UnauthorizedAccess {
        // recursive
        ProfiledMongoManager manager = new ProfiledMongoManager(ucid);
        log.info("UCD {} : Getting Relationships List for {} [rel : {}, recurse {}]",
                manager.getUseCaseDescriptor().getId(), id, relationshipId, deep);
        Project current = manager.getByID(id);
        long startTime = System.currentTimeMillis();
        List<RelationshipNavigationObject> toReturn = getLinked(current, relationshipId, deep);
        log.info("Got {} relationship elements in {}ms", toReturn.size(), (System.currentTimeMillis() - startTime));
        return toReturn;
    }

    private static List<RelationshipNavigationObject> getLinked(Project current, String relationName, Boolean recurse) {
        log.debug("Getting Relationships Lists for {} [rel : {}, recurse {}]", current.getId(), relationName, recurse);
        ArrayList<RelationshipNavigationObject> toReturn = new ArrayList<>();
        List<Relationship> existing = current.getRelationshipsByName(relationName);
        for (Relationship relationship : existing) {
            try {
                log.trace("Navigating from  {} : {} to[rel {} ] {} : {}", relationship.getTargetUCD(),
                        relationship.getTargetID(), relationship.getRelationshipName(), current.getProfileID(), current.getId());
                RelationshipNavigationObject linkedProject = new RelationshipNavigationObject();
                linkedProject.setTarget(new ProfiledMongoManager(relationship.getTargetUCD()).getByID(relationship.getTargetID()));
                if (recurse) linkedProject.setChildren(getLinked(linkedProject.getTarget(), relationName, recurse));
                toReturn.add(linkedProject);
            } catch (Exception e) {
                log.warn("Unable to navigate from  {} : {} to[rel {} ] {} : {}", relationship.getTargetUCD(),
                        relationship.getTargetID(), relationship.getRelationshipName(), current.getProfileID(), current.getId(), e);
            }
        }
        return toReturn;
    }
}
