package org.iri_research.renkan.test.rest;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import java.util.UUID;

import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
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.ProjectRevisionsRepository;
import org.iri_research.renkan.repositories.ProjectsRepository;
import org.iri_research.renkan.repositories.SpacesRepository;
import org.iri_research.renkan.rest.RestApplication;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
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.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

//import com.sun.jersey.spi.spring.container.servlet.SpringServlet;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("rest-context.xml")
public class ProjectRestTest extends JerseyTest {

    private Logger logger = LoggerFactory.getLogger(ProjectRestTest.class);

    @Autowired
    private ProjectsRepository projectsRepository;

    @Autowired
    private SpacesRepository spacesRepository;

    @Autowired
    private NodesRepository nodesRepository;

    @Autowired
    private EdgesRepository edgesRepository;

    @Autowired
    private ProjectRevisionsRepository projectRevisionsRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    private Project testProject;
    private List<Node> testNodes = new ArrayList<Node>();
    private List<Edge> testEdges = new ArrayList<>();

    private DateTime creationDate = new DateTime();

    private String spaceId = UUID.randomUUID().toString();
    private String projectId = UUID.randomUUID().toString();
    
    private ObjectMapper getObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JodaModule());
        return mapper;
    }

    public ProjectRestTest() {
    }

    // @Override
    // protected AppDescriptor configure() {
    // return new WebAppDescriptor.Builder("org.iri_research.renkan.rest")
    // .contextPath("rest")
    // .contextParam("contextConfigLocation",
    // "classpath:/org/iri_research/renkan/test/rest/rest-context.xml")
    // .initParam("com.sun.jersey.config.property.packages",
    // "org.iri_research.renkan.rest")
    // .servletClass(SpringServlet.class)
    // .contextListenerClass(ContextLoaderListener.class)
    // .build();
    // };

    @Override
    protected Application configure() {
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
        ResourceConfig rc = new RestApplication()
                .property("contextConfigLocation",
                        "classpath:/org/iri_research/renkan/test/rest/rest-context.xml");
        rc.setApplicationName("rest");
        return rc;
    };

    @Before
    public void setup() {

        logger.debug("Setup");
        Space testSpace = new Space(this.spaceId, "test space", "Test space",
                null, null, null, "test_user", null, this.creationDate);
        testSpace = spacesRepository.save(testSpace);
        testProject = new Project(testSpace.getId(), this.projectId, "test", "desc",
                "http://localhost:8080/rest/projects/id", this.creationDate);

        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, testProject.getId());
            node = this.nodesRepository.save(node);
            testProject.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", testProject.getId());
            edge = this.edgesRepository.save(edge);
            testProject.getEdges().add(edge);
            this.testEdges.add(edge);
        }

        this.projectsRepository.save(testProject);

    }

    @After
    public void teardown() {
        logger.debug("Teardown");
        edgesRepository.deleteAll();
        nodesRepository.deleteAll();
        projectRevisionsRepository.deleteAll();
        projectsRepository.deleteAll();
        spacesRepository.deleteAll();
    }
    
    @Test
    public void testGetProject() throws JsonProcessingException, IOException {
        WebTarget webResource = this.target();
        String respString = webResource.path("projects")
                .path(testProject.getId()).request().acceptEncoding("UTF-8").accept(MediaType.APPLICATION_JSON_TYPE).get(String.class);
        Assert.assertNotNull("get resp String not empty", respString);
        Assert.assertFalse("get resp String non empty", respString.isEmpty());
        
        logger.debug("Test get Project : respString : " + respString);
        
        ObjectMapper mapper = this.getObjectMapper();
        
        JsonNode projectNode = mapper.readTree(respString);
        
        Assert.assertNotNull("project node not null", projectNode);
        
        Assert.assertNotNull("Must have an id", projectNode.get("id"));
        Assert.assertEquals("id must match", this.projectId, projectNode.get("id").asText());
        Assert.assertNotNull("Must have a created date", projectNode.get("created"));
        Assert.assertEquals("creation date must match", this.creationDate.toString(ISODateTimeFormat.dateTime()), projectNode.get("created").asText());
    }

    @Test
    public void testDeleteProject() {

        List<Node> nodes = testProject.getNodes();
        List<Edge> edges = testProject.getEdges();

        WebTarget webResource = this.target();
        String respString = webResource.path("projects")
                .path(testProject.getId()).request().delete(String.class);

        Assert.assertTrue("Response text must contain deleted",
                respString.contains("deleted"));
        Assert.assertTrue("Response text must contain id",
                respString.contains(this.testProject.getId()));

        DBObject filter = new BasicDBObject();
        filter.put("_id", testProject.getId());

        DBCollection coll = mongoTemplate.getCollection(mongoTemplate
                .getCollectionName(Project.class));
        DBCursor resFind = coll.find(filter);

        Assert.assertEquals("The project must not be found", 0, resFind.count());

        filter = new BasicDBObject();
        filter.put("project_id", testProject.getId());

        DBCollection nodesColl = mongoTemplate.getCollection(mongoTemplate
                .getCollectionName(Node.class));
        resFind = nodesColl.find(filter);

        Assert.assertEquals("no nodes left", 0, resFind.count());

        for (Node n : nodes) {
            DBObject nobj = nodesColl.findOne(n.getId());
            Assert.assertNull("node should be deleted", nobj);
        }

        DBCollection edgesColl = mongoTemplate.getCollection(mongoTemplate
                .getCollectionName(Edge.class));
        resFind = edgesColl.find(filter);

        Assert.assertEquals("no edges left", 0, resFind.count());

        for (Edge e : edges) {
            DBObject eobj = edgesColl.findOne(e.getId());
            Assert.assertNull("edge should be deleted", eobj);
        }

    }

}
