package org.iri_research.renkan.test.repositories;

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.Space;
import org.iri_research.renkan.repositories.SpacesRepository;
import org.joda.time.DateTime;
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.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.mongodb.DBCollection;
import com.mongodb.DBObject;

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

    private final static int SPACE_NB = 3;

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

    @Autowired
    private SpacesRepository spacesRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

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

    @Before
    public void setup() {

        logger.debug("Setup");
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
        spacesRepository.deleteAll();
        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);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void testFieldMapping() {
        // query json directly with mongodb driver
        // check field values
        DBCollection coll = mongoTemplate.getCollection(mongoTemplate
                .getCollectionName(Space.class));

        for (DBObject obj : coll.find()) {
            Assert.assertTrue("mongo object must have _id field",
                    obj.containsField("_id"));

            String id = obj.get("_id").toString();

            Space sp = this.spacesList.get(id);

            Assert.assertTrue("mongo object must have title field",
                    obj.containsField("title"));
            Assert.assertEquals("Titles must be the same", obj.get("title"),
                    sp.getTitle());

            Assert.assertTrue("mongo object must have description field",
                    obj.containsField("description"));
            Assert.assertEquals("description must be the same",
                    obj.get("description"), sp.getDescription());

            Assert.assertTrue("mongo object must have color field",
                    obj.containsField("color"));
            Assert.assertEquals("Color must be the same", obj.get("color"),
                    sp.getColor());

            Assert.assertTrue("mongo object must have uri field",
                    obj.containsField("uri"));
            Assert.assertEquals("Uri must be the same", obj.get("uri"),
                    sp.getUri());

            Assert.assertTrue("mongo object must have image field",
                    obj.containsField("image"));
            Assert.assertEquals("Image must be the same", obj.get("image"),
                    sp.getImage());

            Assert.assertTrue("mongo object must have created field",
                    obj.containsField("created"));
            Assert.assertEquals("Created must be the same", new DateTime(obj.get("created")),
                    sp.getCreated());

            Assert.assertTrue("mongo object must have bin_config field",
                    obj.containsField("bin_config"));
            Assert.assertEquals("Uri must be the same", obj.get("bin_config"),
                    sp.getBinConfig());

            Assert.assertTrue("mongo object must have created_by field",
                    obj.containsField("created_by"));
            Assert.assertEquals("created by must be the same",
                    obj.get("created_by"), sp.getCreatedBy());

        }
    }

    @Test
    public void testPagination() {

        Sort s = new Sort(Direction.DESC, "created");
        Pageable p = new PageRequest(0, 2, s);

        Page<Space> page = this.spacesRepository.findAll(p);

        Assert.assertEquals("page content length must be two", 2,
                page.getNumberOfElements());
        Assert.assertEquals("id must be id of last created space",
                this.spacesUuids.get(SPACE_NB - 1), page.getContent().get(0)
                        .getId());
        Assert.assertEquals("id must be id of last created space",
                this.spacesUuids.get(SPACE_NB - 2), page.getContent().get(1)
                        .getId());

        p = new PageRequest(1, 2, s);
        page = this.spacesRepository.findAll(p);
        Assert.assertEquals("page content length must be one", 1,
                page.getNumberOfElements());
        Assert.assertEquals("id must be id of first created space",
                this.spacesUuids.get(0), page.getContent().get(0).getId());

    }
    
    @Test
    public void testFindByTitleRegex() {

        List<Space> res = this.spacesRepository.findByTitleRegex(".*test.*");

        Assert.assertEquals("res must have length " + SPACE_NB ,
                SPACE_NB, res.size());
        for (Space sp : res) {
            Assert.assertNotNull("space title must not be null", sp.getTitle());
            Assert.assertTrue("space title must contains title", sp.getTitle()
                    .matches(".*test.*"));
        }
    }

    @Test
    public void testFindByTitleRegexBad() {

        List<Space> res = this.spacesRepository.findByTitleRegex(".*foo.*");

        Assert.assertEquals("res must have zero length", 0, res.size());
    }

    @Test
    public void testFindByTitleRegexPageable() {

        PageRequest pr = new PageRequest(0, 1);

        Page<Space> res = this.spacesRepository.findByTitleRegex(".*test.*", pr);

        Assert.assertEquals("res must have length 1", 1,
                res.getNumberOfElements());
        for (Space p : res) {
            Assert.assertNotNull("project title must not be null", p.getTitle());
            Assert.assertTrue("project tilte must contains title", p.getTitle()
                    .matches(".*test.*"));
        }
    }

    @Test
    public void testFindByTitleRegexPageableBadSize() {

        PageRequest pr = new PageRequest(0, 3);

        Page<Space> res = this.spacesRepository.findByTitleRegex(".*test.*", pr);

        Assert.assertEquals("res must have length 3", 3,
                res.getNumberOfElements());
        for (Space sp : res) {
            Assert.assertNotNull("project title must not be null", sp.getTitle());
            Assert.assertTrue("project tilte must contains title", sp.getTitle()
                    .matches(".*test.*"));
        }
    }

    @Test
    public void testFindBySpaceIdAndTitleRegexPageableBadPage() {

        PageRequest pr = new PageRequest(1, 3);

        Page<Space> res = this.spacesRepository.findByTitleRegex(".*test.*", pr);

        Assert.assertEquals("res must have length 0", 0,
                res.getNumberOfElements());
    }

    @After
    public void teardown() {
        spacesRepository.deleteAll();
    }

}
