package gr.uoa.di.web.utils.ep;


import edu.emory.mathcs.backport.java.util.Collections;
import eu.dnetlib.api.data.PublisherService;
import eu.dnetlib.api.data.PublisherServiceException;
import eu.dnetlib.domain.data.Document;
import eu.dnetlib.domain.functionality.UserProfile;
import gr.uoa.di.driver.util.ServiceLocator;
import gr.uoa.di.web.utils.ep.domain.Edge;
import gr.uoa.di.web.utils.ep.domain.Entity;
import gr.uoa.di.web.utils.ep.domain.Field;
import gr.uoa.di.web.utils.ep.domain.Graph;
import gr.uoa.di.web.utils.ep.domain.Node;
import gr.uoa.di.web.utils.ep.domain.RelatedNode;
import gr.uoa.di.web.utils.ep.domain.Relation;
import gr.uoa.di.web.utils.search.DocumentPage;
import gr.uoa.di.web.utils.search.DocumentReader;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import org.apache.log4j.Logger;

public class EPManagerMock implements EPManager {
	private static final String FORMAT = "DMF";
	private static final String COMMAND = "command";
	private static final String CREATE = "create";
	private static final String IMPORT = "import";
	private static final String EDIT = "edit";
	private static final String DELETE ="delete";
	private static final String ADD = "add";
	private static final String REMOVE = "remove";
	private static final String ID = "id";
	private static final String ENTITY = "entity";
	private static final String RELATION = "relation";
	private static final String SOURCE = "source";
	private static final String TARGET = "target";
	private static final String DOCUMENT_PREFIX = "document.";
	private static final String PARAMETER_DELIMITER = "&";
	private static final String UTF8 = "utf8";
	private static final String VALUE_DELIMITER = "=";
	private static final String USER_PLACEHOLDER = "$user";
	private static final String TEMP_ID_PREFIX = "temporaryId";
	
	private static Logger logger = Logger.getLogger(EPManagerMock.class);
	
	private ServiceLocator<PublisherService> publisherServiceLocator;
	private DocumentReader documentReader;
	private Field description;
	private Field language;
	private Field subject;
	private Field title;
	private Entity aggregationObjects;
	private Entity ePrints;
	private Entity ePubs;
	private Entity nonEPrints;
	private Relation aggregates;
	private Relation composedBy;
	private Relation hasEPrint;
	private Set<Entity> entities;
	private Set<Relation> relations;
	private Graph graph;
	private Map<String, Map<String, String>> temporaryIds;
	
	@SuppressWarnings("unchecked")
	public EPManagerMock() {
		Field cObjCategory = new Field("CobjCategory", "Compound Object Category", false, false, null);
		Field cObjContentSynthesis = new Field("CobjContentSynthesis", "Compound Object Content Synthesis", false, false, null);
		Field cObjDescriptionSynthesis = new Field("CobjDescriptionSynthesis", "Compound Object Description Synthesis", false, false, null);
		Field cObjIdentifier = new Field("id", "Compound Object Identifier", false, false, null);
		Field cObjMDFormats = new Field("CobjMDFormats", "Compound Object Metadata Formats", false, false, null);
		Field cObjModel = new Field("CobjModel", "Compound Object Model", false, false, null);
		Field cObjTypologyCompoundObject = new Field("CobjTypology", "Compound Object Typology", false, false, "Compound Object");
		Field cObjTypologyTextual = new Field("CobjTypology", "Compound Object Typology", false, false, "Textual");
		Field cObjTypologyDataSet = new Field("CobjTypology", "Compound Object Typology", false, false, "Data Set");
		Field contributor = new Field("contributor", "Contributor", true, false, null);
		Field creator = new Field("creator", "Creator", true, false, "$user");
		Field dateAccepted = new Field("dateAccepted", "Date Accepted", false, false, null);
		Field dateOfCollection = new Field("dateOfCollection", "Date Of Collection", false, false, null);
		description = new Field("description", "Description", false, true, null);
		Field identifier = new Field("identifier", "Identifier", false, false, null);
		Field itemIdentifier = new Field("itemIdentifier", "Item Identifier", false, false, null);
		language = new Field("language", "Language", false, true, null);
		Field mdFormat = new Field("mdFormat", "Metadata Format", false, false, "DMF");
		Field mdFormatInterpretation = new Field("mdFormatInterpretation", "Metadata Format Interpretation", false, false, null);
		Field objIdentifier = new Field("objIdentifier", "Object Identifier", false, false, null);
		Field objectIdentifier = new Field("objectIdentifier", "Object Identifier", false, false, null);
		Field publisher = new Field("publisher", "Publisher", false, false, null);
		Field recordIdentifier = new Field("recordIdentifier", "Record Identifier", false, false, null);
		Field relation = new Field("relation", "Relation", true, false, null);
		Field repositoryCountry = new Field("repositoryCountry", "Repository Country", false, false, null);
		Field repositoryId = new Field("repositoryId", "Repository Identifier", false, false, null);
		Field repositoryInstitution = new Field("repositoryInstitution", "RepositoryInstitution", false, false, "Driver");
		Field repositoryLink = new Field("repositoryLink", "Repository Link", false, false, "http://search.driver.research-infrastructures.eu/");
		Field repositoryName = new Field("repositoryName", "Repository Name", false, false, "Driver");
		Field source = new Field("source", "Source", false, false, null);
		subject = new Field("subject", "Subject", true, true, null);
		title = new Field("title", "Title", true, true, null);
		Field sequenceNo = new Field("sequenceNo", "Sequence Number", false, true, null);
		
		Set<Field> commonFields = new HashSet<Field>();
		commonFields.add(cObjCategory);
		commonFields.add(cObjContentSynthesis);
		commonFields.add(cObjDescriptionSynthesis);
		commonFields.add(cObjIdentifier);
		commonFields.add(cObjMDFormats);
		commonFields.add(cObjModel);
		commonFields.add(contributor);
		commonFields.add(creator);
		commonFields.add(dateAccepted);
		commonFields.add(dateOfCollection);
		commonFields.add(description);
		commonFields.add(identifier);
		commonFields.add(itemIdentifier);
		commonFields.add(language);
		commonFields.add(mdFormat);
		commonFields.add(mdFormatInterpretation);
		commonFields.add(objIdentifier);
		commonFields.add(objectIdentifier);
		commonFields.add(publisher);
		commonFields.add(recordIdentifier);
		commonFields.add(relation);
		commonFields.add(repositoryCountry);
		commonFields.add(repositoryId);
		commonFields.add(repositoryInstitution);
		commonFields.add(repositoryLink);
		commonFields.add(repositoryName);
		commonFields.add(source);
		commonFields.add(subject);
		commonFields.add(title);
		
		Set<Field> compoundObjectFields = new HashSet<Field>(commonFields);
		compoundObjectFields.add(cObjTypologyCompoundObject);
		Set<Field> textualFields = new HashSet<Field>(commonFields);
		textualFields.add(cObjTypologyTextual);
		Set<Field> dataSetFields = new HashSet<Field>(commonFields);
		dataSetFields.add(cObjTypologyDataSet);
		
		aggregationObjects = new Entity("AggregationObjects", compoundObjectFields, cObjIdentifier.getName(), cObjTypologyCompoundObject.getName(), title.getName(), true, "Aggregation Object", 0xffa9a9ff, "diamond");
		ePrints = new Entity("ePrints", textualFields, cObjIdentifier.getName(), cObjTypologyTextual.getName(), title.getName(), false, "E-Print", 0xffdd7a00, "circle");
		ePubs = new Entity("ePubs", compoundObjectFields, cObjIdentifier.getName(), cObjTypologyCompoundObject.getName(), title.getName(), true, "E-Pub", 0xffa13ba4, "square");
		nonEPrints = new Entity("nonEPrints", textualFields, cObjIdentifier.getName(), cObjTypologyTextual.getName(), title.getName(), false, "Non-E-Print", 0xff3a9db8, "circle");
		Entity researchData = new Entity("ResearchData", dataSetFields, cObjIdentifier.getName(), cObjTypologyDataSet.getName(), title.getName(), false, "Research Data", 0xff8aac46, "circle");
		Entity sequence = new Entity("Sequence", Collections.singleton(sequenceNo), null, null, sequenceNo.getName(), true, "Sequence", 0xff7f7f7f, "circle"); 
		
		entities = new HashSet<Entity>();
		entities.add(aggregationObjects);
		entities.add(ePrints);
		entities.add(ePubs);
		entities.add(nonEPrints);
		entities.add(researchData);
		entities.add(sequence);

		Set<String> components = new HashSet<String>();
		components.add(aggregationObjects.getName());
		components.add(ePrints.getName());
		components.add(nonEPrints.getName());
		components.add(researchData.getName());
		
		aggregates = new Relation("aggregates", Collections.singleton(aggregationObjects.getName()), components, "many_to_many", "part_part", "aggregates", 2, 0xff7f7f7f); 
		Relation cites = new Relation("cites", Collections.singleton(ePrints.getName()), Collections.singleton(ePrints.getName()), "many_to_many", "part_part", "cites", 2, 0xff7f7f7f);
		composedBy = new Relation("composedBy", Collections.singleton(ePubs.getName()), components, "many_to_many", "part_part", "composed by", 2, 0xff7f7f7f);
		Relation generatedBy = new Relation("generatedBy", Collections.singleton(researchData.getName()), Collections.singleton(researchData.getName()), "many_to_many", "part_part", "generated by", 2, 0xff7f7f7f);
		hasEPrint = new Relation("hasEPrint", Collections.singleton(ePubs.getName()), Collections.singleton(ePrints.getName()), "many_to_one", "tot_part", "has e-print", 3, 0xffa13ba4);
		Relation order = new Relation("order", Collections.singleton(aggregates.getName()), Collections.singleton(sequence.getName()), "one_to_one", "part_tot", "order", 2, 0xff7f7f7f);
		Relation relatedWith = new Relation("relatedWith", Collections.singleton(ePubs.getName()), Collections.singleton(ePubs.getName()), "many_to_many", "part_part", "related with", 2, 0xff7f7f7f);
		
		
		relations = new HashSet<Relation>();
		relations.add(aggregates);
		relations.add(cites);
		relations.add(composedBy);
		relations.add(generatedBy);
		relations.add(hasEPrint);
		relations.add(order);
		relations.add(relatedWith);

		graph = new Graph();
		temporaryIds = new HashMap<String, Map<String, String>>();
		logger.debug("EPManagerMock initialized successfully");
	}

	public void setPublisherServiceLocator(ServiceLocator<PublisherService> publisherServiceLocator) {
		this.publisherServiceLocator = publisherServiceLocator;
	}
	
	
	public void setDocumentReader(DocumentReader documentReader) {
		this.documentReader = documentReader;
	}
	
	
	@Override
	public Set<Entity> getEntities() {
		logger.debug("Retrieved entities");
		return entities;
	}
	
	
	@Override
	public Set<Relation> getRelations() {
		logger.debug("Retrieved relations");
		return relations;
	}
	

	@Override
	public Set<Map<String, List<String>>> getDropboxContents(UserProfile userProfile) throws EPManagerException {
		Set<Map<String, List<String>>> dropboxContents = new HashSet<Map<String, List<String>>>();
		try {
			for (String documentId : userProfile.getDocumentIds()) {
				String xml = publisherServiceLocator.getService().getResourceById(documentId, FORMAT);
				Map<String, List<String>> document = null;
				if (xml != null)
					document = documentReader.read(xml).getMap();
				else
					document = getNode(documentId).getDocumentMap();
				if (document != null)
					dropboxContents.add(document);
			}
		} catch (PublisherServiceException e) {
			logger.error("Error retrieving dropbox contents of user " + userProfile, e);
			throw new EPManagerException("Error retrieving dropbox contents of user " + userProfile, e);
		}
		logger.debug("Retrieved dropbox contents of user " + userProfile);
		return dropboxContents;
	}

	@Override
	public Graph getGraph(String rootId) {
		Graph graph = new Graph();
		Node root = getNode(rootId);
		if (root == null) {
			logger.debug("No graph with root " + rootId + " exists");
			return null;
		}
		graph.getNodes().add(root);
		for (RelatedNode parent : getParents(root)) {
			Node node = parent.getNode();
			graph.getNodes().add(node);
			graph.getEdges().add(new Edge(parent.getRelation(), node.getDocumentMap().get(getEntity(node.getEntity()).getIdField()).get(0), root.getDocumentMap().get(getEntity(root.getEntity()).getIdField()).get(0)));
		}
		getDescendants(root, graph);
		logger.debug("Retrieved graph with root " + rootId);
		return graph;
	}
	
	
	@Override
	public void saveGraph(UserProfile userProfile, String [] commands) throws EPManagerException {
		for (String command : commands)
			executeCommand(userProfile, command);
		temporaryIds.put(userProfile.getResourceId(), null);
		logger.debug("User " + userProfile + " saved graph");
	}
	
	
	@Override
	public String saveEnhancedPublication(UserProfile userProfile, String description, String language, String subject, String title, String ePrintId, Set<String> nonEPrintIds) throws EPManagerException {
		Map<String, List<String>> document = new HashMap<String, List<String>>();
		document.put(DOCUMENT_PREFIX + this.description.getName(), new ArrayList<String>());
		document.get(DOCUMENT_PREFIX + this.description.getName()).add(description);
		document.put(DOCUMENT_PREFIX + this.language.getName(), new ArrayList<String>());
		document.get(DOCUMENT_PREFIX + this.language.getName()).add(language);
		document.put(DOCUMENT_PREFIX + this.subject.getName(), new ArrayList<String>());
		document.get(DOCUMENT_PREFIX + this.subject.getName()).add(subject);
		document.put(DOCUMENT_PREFIX + this.title.getName(), new ArrayList<String>());
		document.get(DOCUMENT_PREFIX + this.title.getName()).add(title);
		String id = createNode(userProfile, TEMP_ID_PREFIX + Math.round(Math.random() * Long.MAX_VALUE), ePubs.getName());
		editNode(userProfile, id, document);
		importNode(userProfile, ePrintId, ePrints.getName());
		addEdge(userProfile, hasEPrint.getName(), id, ePrintId);
		for (String nonEPrintId : nonEPrintIds) {
			importNode(userProfile, nonEPrintId, nonEPrints.getName());
			addEdge(userProfile, composedBy.getName(), id, nonEPrintId);
		}
		logger.debug("User " + userProfile + " saved enhanced publication " + id);
		return id;
	}

	@Override
	public String saveAggregationObject(UserProfile userProfile, String description, String language, String subject, String title, Set<String> contentIds) throws EPManagerException {
		Map<String, List<String>> document = new HashMap<String, List<String>>();
		document.put(DOCUMENT_PREFIX + this.description.getName(), new ArrayList<String>());
		document.get(DOCUMENT_PREFIX + this.description.getName()).add(description);
		document.put(DOCUMENT_PREFIX + this.language.getName(), new ArrayList<String>());
		document.get(DOCUMENT_PREFIX + this.language.getName()).add(language);
		document.put(DOCUMENT_PREFIX + this.subject.getName(), new ArrayList<String>());
		document.get(DOCUMENT_PREFIX + this.subject.getName()).add(subject);
		document.put(DOCUMENT_PREFIX + this.title.getName(), new ArrayList<String>());
		document.get(DOCUMENT_PREFIX + this.title.getName()).add(title);		
		String id = createNode(userProfile, TEMP_ID_PREFIX + Math.round(Math.random() * Long.MAX_VALUE), aggregationObjects.getName());
		editNode(userProfile, id, document);
		for (String contentId : contentIds) {
			importNode(userProfile, contentId, nonEPrints.getName());
			addEdge(userProfile, aggregates.getName(), id, contentId);
		}
		logger.debug("User " + userProfile + " saved aggregation object " + id);
		return id;
	}
	
	@Override
	public DocumentPage getEnhancedPublications(int pageSize, int pageNumber) {
		List<Document> documents = new ArrayList<Document>();
		for (Node node : graph.getNodes()) {
			if (node.getEntity().equals(ePubs.getName()))
				documents.add(new Document(node.getDocumentMap()));
		} 
		int numberOfDocuments = documents.size();
		int numberOfPages = numberOfDocuments / pageSize;
		if ((numberOfDocuments % pageSize) > 0)
			numberOfPages++;
		int start = (pageNumber - 1) * pageSize;
		if (start < 0)
			start = 0;
		int end = pageNumber * pageSize;
		if (end > numberOfDocuments)
			end = numberOfDocuments;
		documents = documents.subList(start, end);
		logger.debug("Retrieved enhanced publications");
		return new DocumentPage(documents, pageSize, pageNumber, numberOfDocuments, numberOfPages);
	}
	
	
	@Override
	public Document getEnhancedPublication(String id) {
		Node node = getNode(id);
		if ((node == null) || (!node.getEntity().equals(ePubs.getName()))) {
			logger.debug("Enhanced publication " + id + " not found");
			return null;
		} else {
			logger.debug("Retrieved enhanced publication " + id);
			return new Document(node.getDocumentMap());
		}
	}
	

	@Override
	public Node getNode(String id) {
		for (Node node : graph.getNodes()) {
			if (node.getDocumentMap().get(getEntity(node.getEntity()).getIdField()).get(0).equals(id)) {
				logger.debug("Retrieved node " + id);
				return node;
			}
		}
		logger.debug("Node " + id + " not found");
		return null;
	}

	
	@Override
	public Set<RelatedNode> getParents(Node node) {
		Set<RelatedNode> parents = new HashSet<RelatedNode>();
		String id = node.getDocumentMap().get(getEntity(node.getEntity()).getIdField()).get(0);
		for (Edge edge : graph.getEdges()) {
			if (edge.getTarget().equals(id))
				parents.add(new RelatedNode(edge.getRelation(), getNode(edge.getSource())));
		}
		logger.debug("Retrieved parents of node " + id);
		return parents;
	}
	

	@Override
	public Set<RelatedNode> getChildren(Node node) {
		Set<RelatedNode> children = new HashSet<RelatedNode>();
		String id = node.getDocumentMap().get(getEntity(node.getEntity()).getIdField()).get(0);
		for (Edge edge : graph.getEdges()) {
			if (edge.getSource().equals(id))
				children.add(new RelatedNode(edge.getRelation(), getNode(edge.getTarget())));
		}
		logger.debug("Retrieved children of node " + id);
		return children;
	}
	
		
	private void getDescendants(Node root, Graph graph){
		String rootId = root.getDocumentMap().get(getEntity(root.getEntity()).getIdField()).get(0);
		for (RelatedNode child : getChildren(root)) {
			Node node = child.getNode();
			graph.getNodes().add(node);
			graph.getEdges().add(new Edge(child.getRelation(), rootId, node.getDocumentMap().get(getEntity(node.getEntity()).getIdField()).get(0)));
			getDescendants(node, graph);
		}
		logger.debug("Retrieved descendants of node " + rootId);
	}
	
	
	private void executeCommand(UserProfile userProfile, String command) throws EPManagerException {
		Map<String, List<String>> arguments = parseCommand(command);
		if (arguments.get(COMMAND).get(0).equals(CREATE)) {
			createNode(userProfile, arguments.get(ID).get(0), arguments.get(ENTITY).get(0));
		} else if (arguments.get(COMMAND).get(0).equals(IMPORT)) {
			importNode(userProfile, arguments.get(ID).get(0), arguments.get(ENTITY).get(0));
		} else if (arguments.get(COMMAND).get(0).equals(EDIT)) {
			editNode(userProfile, arguments.get(ID).get(0), arguments);
		} else if (arguments.get(COMMAND).get(0).equals(DELETE)) {
			deleteNode(userProfile, arguments.get(ID).get(0));
		} else if (arguments.get(COMMAND).get(0).equals(ADD)) {
			addEdge(userProfile, arguments.get(RELATION).get(0), arguments.get(SOURCE).get(0), arguments.get(TARGET).get(0));
		} else if (arguments.get(COMMAND).get(0).equals(REMOVE)) {
			removeEdge(userProfile, arguments.get(RELATION).get(0), arguments.get(SOURCE).get(0), arguments.get(TARGET).get(0));
		}
		logger.debug("User " + userProfile + " executed command " + command);
	}
	
	private Map<String, List<String>> parseCommand(String command) throws EPManagerException {
		Map<String, List<String>> arguments = new HashMap<String, List<String>>();
		try {
			for (String parameter: command.split(PARAMETER_DELIMITER)) {
				String [] pair = parameter.split(VALUE_DELIMITER);
				String key = URLDecoder.decode(pair[0], UTF8);
				String value = (pair.length > 1) ? URLDecoder.decode(pair[1], UTF8) : "";
				if (arguments.get(key) == null)
					arguments.put(key, new ArrayList<String>());
				arguments.get(key).add(value);
			}
		} catch (UnsupportedEncodingException e) {
			logger.error("Error parsing command " + command);
			throw new EPManagerException("Error parsing command " + command, e);
		}
		return arguments;
	}
	
	
	private String createNode(UserProfile userProfile, String id, String entity) {
		String userId = userProfile.getResourceId();
		String uuid = UUID.randomUUID().toString();
		if (temporaryIds.get(userId) == null)
			temporaryIds.put(userId, new HashMap<String, String>());
		temporaryIds.get(userId).put(id, uuid);
		Entity ent = getEntity(entity);
		Map<String, List<String>> document = new HashMap<String, List<String>>();
		
		for (Field field : ent.getFields()) {
			String name = field.getName();
			String defaultValue = field.getDefaultValue();
			document.put(name, new ArrayList<String>());
			if (name.equals(ent.getIdField()))
				document.get(name).add(uuid);
			if (defaultValue == null)
				continue;
			else if(defaultValue.equals(USER_PLACEHOLDER)) {
				String user = userProfile.getFirstname() + " " + userProfile.getLastname();
				if (user.trim().isEmpty())
					user = userProfile.getEmail();
				document.get(name).add(user);
			} else
				document.get(name).add(defaultValue);
		}
		graph.getNodes().add(new Node(entity, document));
		logger.debug("User " + userProfile + " created node " + id + " as " + entity + " (new id " + uuid + ")");
		return uuid;
	}
	
	private void importNode(UserProfile userProfile, String id, String entity) throws EPManagerException {
		Entity ent = getEntity(entity);
		for (Map<String, List<String>> document: getDropboxContents(userProfile)) {
			if (document.get(ent.getIdField()).get(0).equals(id)) {
				graph.getNodes().add(new Node(entity, document));
				logger.debug("User " + userProfile + " imported node " + id + " as " + entity);
				return;
			}
		}
		logger.debug("Document " + id + " not found in dropbox of user " + userProfile);
	}
	
	private void editNode(UserProfile userProfile, String id, Map<String, List<String>> document) {
		id = resolveId(userProfile.getResourceId(), id);
		for (Node node : graph.getNodes()) {
			Entity entity = getEntity(node.getEntity());
			if (node.getDocumentMap().get(entity.getIdField()).get(0).equals(id)) {
				for (Field field : entity.getFields()) {
					String name = field.getName();
					List<String> values = document.get(DOCUMENT_PREFIX + name);
					if (values != null)
							node.getDocumentMap().put(name, values);
				}
				logger.debug("User " + userProfile + " edited node " + id);
				return;
			}
		}
	}
	
	private void deleteNode(UserProfile userProfile, String id) {
		id = resolveId(userProfile.getResourceId(), id);
		for (Iterator<Node> i = graph.getNodes().iterator(); i.hasNext();) {
			Node node = i.next();
			if (node.getDocumentMap().get(getEntity(node.getEntity()).getIdField()).get(0).equals(id)) {
				for (RelatedNode parent : getParents(node)) // break relations with parents
					removeEdge(userProfile, parent.getRelation(), parent.getNode().getDocumentMap().get(getEntity(parent.getNode().getEntity()).getIdField()).get(0), id);
				for (RelatedNode child: getChildren(node)) // break relations with children
					removeEdge(userProfile, child.getRelation(), id, child.getNode().getDocumentMap().get(getEntity(child.getNode().getEntity()).getIdField()).get(0));
				i.remove(); // remove node
				break;
			}
		}		
		logger.debug("User " + userProfile + " deleted node " + id);
	}
	
	private void addEdge(UserProfile userProfile, String relation, String source, String target) {
		source = resolveId(userProfile.getResourceId(), source);
		target = resolveId(userProfile.getResourceId(), target);
		graph.getEdges().add(new Edge(relation, source, target));
		logger.debug("User " + userProfile + " added edge " + relation + " from node " + source + " to node " + target);
	}
	
	private void removeEdge(UserProfile userProfile, String relation, String source, String target) {
		source = resolveId(userProfile.getResourceId(), source);
		target = resolveId(userProfile.getResourceId(), target);
		for (Iterator<Edge> i = graph.getEdges().iterator(); i.hasNext();) {
			Edge edge = i.next();
			if (edge.getRelation().equals(relation) && edge.getSource().equals(source) && edge.getTarget().equals(target)) {
				i.remove();
				break;
			}
		}
		logger.debug("User " + userProfile + " removed edge " + relation + " from node " + source + " to node " + target);
	}
	
	private Entity getEntity(String name) {
		for (Entity entity : entities) {
			if (entity.getName().equals(name))
				return entity;
		}
		return null;
	}
	
	private String resolveId(String userId, String id) {
		return id.startsWith(TEMP_ID_PREFIX) ? temporaryIds.get(userId).get(id) : id;
	}
}
