package org.iri_research.renkan.test.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
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.joda.time.DateTime;
import org.junit.After;
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.geo.Point;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "controller-context.xml",
        "file:src/main/webapp/WEB-INF/spring-servlet.xml" })
public class RenkanControllerTest {

    private final static int SPACE_NB = 3;

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

    @Autowired
    private SpacesRepository spacesRepository;
    @Autowired
    private ProjectsRepository projectsRepository;
    @Autowired
    private NodesRepository nodesRepository;
    @Autowired
    private EdgesRepository edgesRepository;

    private Map<String, Space> spacesList = new HashMap<String, Space>(SPACE_NB);
    private List<String> spacesUuids = new ArrayList<>(SPACE_NB);

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

    @Autowired
    private WebApplicationContext context;
    private MockMvc mvc;

    private void clean() {
        edgesRepository.deleteAll();
        nodesRepository.deleteAll();
        projectsRepository.deleteAll();
        spacesRepository.deleteAll();
    }
    
    @Before
    public void setup() {

        logger.debug("Setup");
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
        this.clean();

        ArrayList<Project> pl = new ArrayList<Project>();
        for (int i = 0; i < SPACE_NB; i++) {
            DateTime creationDate = new DateTime();
            String uuid = UUID.randomUUID().toString();
            spacesUuids.add(uuid);
            Space testSpace = new Space(uuid, "test " + i, "Test space " + 1,
                    "{}", "http://ldt.iri.centrepompidou.fr", "#ababab",
                    "test_user", "http://ldt.iri.centrepompidou.fr",
                    creationDate);
            testSpace = spacesRepository.save(testSpace);
            this.spacesList.put(uuid, 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), creationDate));
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        Project testProject = pl.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, 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);
        }


        for (Project p : projectsRepository.save(pl)) {
            this.testProjects.add(p);
        }

        this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
    }

    
    @After
    public void teardown() {
        this.clean();
    }
    
    @Test
    public void testExportProject() throws Exception {
        MockHttpServletRequestBuilder get = MockMvcRequestBuilders.get("/p/exp/"+this.testProjects.get(0).getId());
        MvcResult res = this.mvc.perform(get)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                .andReturn();
        
        logger.debug("testExportProject resp : "
                + res.getResponse().getContentAsString());
    }
    
    @Test
    public void testExportProjectContent() throws Exception {
        MockHttpServletRequestBuilder get = MockMvcRequestBuilders.get("/p/exp/"+this.testProjects.get(0).getId());
        MvcResult res = this.mvc.perform(get)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.jsonPath("title").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("title").value("test1"))
                .andExpect(MockMvcResultMatchers.jsonPath("description").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("description").value("desc1"))
                .andExpect(MockMvcResultMatchers.jsonPath("nodes").isArray())
                .andExpect(MockMvcResultMatchers.jsonPath("edges").isArray())
                .andReturn();
        
        logger.debug("testExportProjectContent resp : "
                + res.getResponse().getContentAsString());
    }
    
    
    @Test
    public void testExportProjectExclude() throws Exception {
        MockHttpServletRequestBuilder get = MockMvcRequestBuilders.get("/p/exp/"+this.testProjects.get(0).getId());
        MvcResult res = this.mvc.perform(get)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.jsonPath("id").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("nodes[*].id").doesNotExist())
                .andExpect(MockMvcResultMatchers.jsonPath("nodes[*].@id").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("edges[*].id").doesNotExist())
                .andReturn();
        
        logger.debug("testExportProjectContentExclude resp : "
                + res.getResponse().getContentAsString());
    }
    
}
