package org.iri_research.renkan.test.repositories;


import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.iri_research.renkan.models.Edge;
import org.iri_research.renkan.models.Node;
import org.iri_research.renkan.models.Project;
import org.iri_research.renkan.models.Space;
import org.iri_research.renkan.repositories.EdgesRepository;
import org.iri_research.renkan.repositories.NodesRepository;
import org.iri_research.renkan.repositories.ProjectsRepository;
import org.iri_research.renkan.repositories.SpacesRepository;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("repositories-context.xml")
public class ProjectsRepositoryTest {

	private final static int SPACE_NB = 3;
	
	private Logger logger = LoggerFactory.getLogger(ProjectsRepositoryTest.class);
	
	@Autowired
	private ProjectsRepository projectsRepository;
	
	@Autowired
	private SpacesRepository spacesRepository;
		
	@Autowired
	private NodesRepository nodesRepository;
	
	@Autowired
	private EdgesRepository edgesRepository;
	
	@Autowired
	private MongoTemplate mongoTemplate;
	
	private ArrayList<Project> testProjects = new ArrayList<Project>();
	private List<Node> testNodes = new ArrayList<Node>();
	private List<Edge> testEdges = new ArrayList<>();
	
	private Date creationDate = new Date();
	
	private List<String> spaceIds = new ArrayList<>(); 
	
	private Project copyProject;
	
	public ProjectsRepositoryTest() {
	}
	
	@Before
	public void setup() {

		logger.debug("Setup");
		ArrayList<Project> pl = new ArrayList<Project>();
		Space testSpace = null;
		for(int i=0; i<SPACE_NB; i++) {
			spaceIds.add(UUID.randomUUID().toString());
			testSpace = new Space(this.spaceIds.get(i), "test space " + i, "Test space " + i, null, null, null, "test_user", null, this.creationDate);
			testSpace = spacesRepository.save(testSpace);
			for(int j=0; j<SPACE_NB-1-i; j++) {
				pl.add(new Project(testSpace.getId(), UUID.randomUUID().toString(), "test"+((SPACE_NB-1)*i+j+1), "desc"+((SPACE_NB-1)*i+j+1), "http://localhost:8080/rest/projects/id"+((SPACE_NB-1)*i+j+1), this.creationDate));
			}
		}
		for(Project p: projectsRepository.save(pl)) {
			this.testProjects.add(p);
		}
		copyProject = this.testProjects.get(0);
		
		for(int i=0;i<3;i++) {
			Node node = new Node("Node"+i, "Node"+i, "Node "+i, "http://renkan.org/nodes/node"+i, "#ffff0"+i, "test_user", new Point(0, i), "http://renkan.org/images/node"+i, i, copyProject.getId());
			node = this.nodesRepository.save(node);
			copyProject.getNodes().add(node);
			this.testNodes.add(node);
		}
		
		for(int i=0;i<3;i++) {
			Edge edge = new Edge("Node"+i, "Node"+i, "Node "+i, "http://renkan.org/edges/edge"+i, "#ffff0"+i, this.testNodes.get((i+2)%3), this.testNodes.get(i), "test_user", copyProject.getId());
			edge = this.edgesRepository.save(edge);
			copyProject.getEdges().add(edge);
			this.testEdges.add(edge);
		}
		
		this.projectsRepository.save(copyProject);

	}
	
	@After
	public void teardown() {
		logger.debug("Teardown");
		edgesRepository.deleteAll();
		nodesRepository.deleteAll();
		projectsRepository.deleteAll();
		spacesRepository.deleteAll();
	}
	
	@Test
	public void testInitialRevCounter() {
		
		Project p = projectsRepository.findOne(this.testProjects.get(0).getId());
		
		Assert.assertEquals("Initial rev counter should be 1", 1, p.getRevCounter());
	}
	
	@Test
	public void testIncrementRevCounter() {
		
		int revCounter = projectsRepository.getRevCounter(this.testProjects.get(0).getId());
		Assert.assertEquals("After 1 call rev counter should be 1", 1, revCounter);
		revCounter = projectsRepository.getRevCounter(this.testProjects.get(0).getId());
		Assert.assertEquals("After 2 calls rev counter should be 2", 2, revCounter);

		for(int i=0; i<10; i++) {
			revCounter = projectsRepository.getRevCounter(this.testProjects.get(0).getId());
		}
		
		Assert.assertEquals("After 10 more calls rev counter should be 12", 12, revCounter);
		
		Project p = projectsRepository.findOne(this.testProjects.get(0).getId());
		
		Assert.assertEquals("next rev counter should be 13", 13, p.getRevCounter());
		
		p = projectsRepository.findOne(this.testProjects.get(1).getId());
		
		Assert.assertEquals("other project next rev counter should be 1", 1, p.getRevCounter());
		
	}
	
	
	@Test
	public void testIncrementNonExisting() {
		
		int revCounter = projectsRepository.getRevCounter("aaaa");
		Assert.assertEquals("Rev counter non existing == -1", -1, revCounter);
		
	}
	
	@Test
	public void testIncrementNull() {
		
		int revCounter = projectsRepository.getRevCounter(null);
		Assert.assertEquals("Rev counter null == -1", -1, revCounter);
		
	}

	@Test
	public void testIncrementEmpty() {
		
		int revCounter = projectsRepository.getRevCounter("");
		Assert.assertEquals("Rev counter empty == -1", -1, revCounter);
		
	}
	
	@Test
	public void testDateCreation() {
		
		for (Project p : projectsRepository.findAll()) {
			Assert.assertEquals(this.creationDate, p.getCreated());
		} 
	}
	
	@Test
	public void testGetCountBySpace() {
		
		Map<String, Integer> groupRes = projectsRepository.getCountBySpace();
		
		Assert.assertNotNull("GroupRes not null", groupRes);
		Assert.assertEquals("Group res size", SPACE_NB-1, groupRes.size());
		
		for(int i=0;i<(SPACE_NB-1);i++) {
			Integer count = groupRes.get(this.spaceIds.get(i));
			Assert.assertNotNull("count not null", count);
			Assert.assertEquals("Nb of project/space", 2-i, count.intValue());
		}
		
		Assert.assertNull("Last space id has no project i.e count is null", groupRes.get(this.spaceIds.get(SPACE_NB-1)));		
	}
	
	@Test
	public void testCopyProjectCreation() {
		DBCollection coll = mongoTemplate.getCollection(mongoTemplate.getCollectionName(Project.class));
		
		DBObject filter = new BasicDBObject();
		filter.put("_id", copyProject.getId());
		
		DBCursor resFind = coll.find(filter);
		
		Assert.assertEquals("The project must be found", 1, resFind.count());
		
		for(DBObject obj: coll.find(filter)) {
			Assert.assertEquals("id must be equal", copyProject.getId(), obj.get("_id"));
		}
	}
	
	@Test
	public void testCopyProjectCopy() {
		
		Project newProject = this.projectsRepository.copy(this.copyProject, "copy_project");
		
		DBCollection coll = mongoTemplate.getCollection(mongoTemplate.getCollectionName(Project.class));
		
		DBObject filter = new BasicDBObject();
		filter.put("_id", newProject.getId());
		
		DBCursor resFind = coll.find(filter);
		
		Assert.assertEquals("The project must be found", 1, resFind.count());
		
		for(DBObject obj: coll.find(filter)) {
			Assert.assertEquals("id must be equal", newProject.getId(), obj.get("_id"));
			Assert.assertEquals("title must be must be copy_project", "copy_project", obj.get("title"));
			Assert.assertEquals("space_id must be must be same than copyProject", this.copyProject.getSpaceId(), obj.get("space_id"));
		}
	}
	
}
