package net.canadensys.dataportal.occurrence.search;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.sql.DataSource;

import net.canadensys.chart.ChartModel;
import net.canadensys.dataportal.occurrence.config.OccurrencePortalConfig;
import net.canadensys.dataportal.occurrence.search.OccurrenceSearchService.StatsPropertiesEnum;
import net.canadensys.dataportal.occurrence.search.config.SearchServiceConfig;
import net.canadensys.dataportal.occurrence.search.config.SearchServiceConfig.SearchableFieldEnum;
import net.canadensys.query.QueryOperatorEnum;
import net.canadensys.query.SearchQueryPart;
import net.canadensys.query.SearchableField;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * Unit and integration tests for the Search Service
 * 
 * @author canadensys
 * 
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-dispatcher-servlet.xml" })
public class OccurrenceSearchServiceTest extends
		AbstractTransactionalJUnit4SpringContextTests {

	@Autowired
	@Qualifier("occurrencePortalConfig")
	private OccurrencePortalConfig appConfig;

	@Autowired
	@Qualifier("searchServiceConfig")
	private SearchServiceConfig searchServiceConfig;

	@Autowired
	private RequestMappingHandlerAdapter handlerAdapter;

	@Autowired
	private RequestMappingHandlerMapping handlerMapping;

	@Autowired
	private ApplicationContext applicationContext;

	private JdbcTemplate jdbcTemplate;

	@Autowired
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	@Before
	public void setup() {
		// make sure the table is empty and reset the sequence
		jdbcTemplate.update("DELETE FROM occurrence");

		// WARNING : If you add a line here, you might need to update
		// testGetDistinctValuesCount and testGetValuesFrequencyDistribution
		// tests
		jdbcTemplate
				.update("INSERT INTO occurrence_raw (auto_id,country,locality,sourcefileid,eventdate) VALUES (1,'Mexico','Mexico','uom-occurrence','2001-03-21')");
		jdbcTemplate
				.update("INSERT INTO occurrence_raw (auto_id,country,locality,sourcefileid) VALUES (2,'Sweden','Stockholm','uos-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence_raw (auto_id,country,locality,sourcefileid) VALUES (3,'Sweden','Uppsala','uou-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence_raw (auto_id,country,locality,sourcefileid) VALUES (4,'United States','Mexico','uow-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence_raw (auto_id,country,locality,sourcefileid) VALUES (5,'United States','New-York','uow-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence_raw (auto_id,country,sourcefileid) VALUES (6,'United States','uow-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence_raw (auto_id,country,locality,sourcefileid) VALUES (7,'United States','','uow-occurrence')");

		jdbcTemplate
				.update("INSERT INTO occurrence (auto_id,country,locality,sourcefileid,syear,smonth,sday) VALUES (1,'Mexico','Mexico','uom-occurrence',2001,03,21)");
		jdbcTemplate
				.update("INSERT INTO occurrence (auto_id,country,locality,sourcefileid) VALUES (2,'Sweden','Stockholm','uos-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence (auto_id,country,locality,sourcefileid) VALUES (3,'Sweden','Uppsala','uou-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence (auto_id,country,locality,datasetname,sourcefileid) VALUES (4,'United States','Mexico','Test USA Herbarium','uow-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence (auto_id,country,locality,datasetname,sourcefileid) VALUES (5,'United States','New-York','Test USA Herbarium','uow-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence (auto_id,country,sourcefileid) VALUES (6,'United States','uow-occurrence')");
		jdbcTemplate
				.update("INSERT INTO occurrence (auto_id,country,locality,sourcefileid) VALUES (7,'United States','','uow-occurrence')");
	}

	@Test
	public void testGetSearchQueryPartFromSourceFileId() {
		OccurrenceSearchService searchService = (OccurrenceSearchService) applicationContext
				.getBean("occurrenceSearchService");
		SearchQueryPart qp = searchService.getSearchQueryPartFromFieldName(
				"sourcefileid", "uow-occurrence");

		// This must match the field in datasetname SearchServiceConfig
		assertEquals(new Integer(29), qp.getSearchableFieldId());
		assertEquals("uow-occurrence", qp.getSingleValue());
	}

	@Test
	public void testGetSearchQueryPartFromDatasetName() {
		OccurrenceSearchService searchService = (OccurrenceSearchService) applicationContext
				.getBean("occurrenceSearchService");
		SearchQueryPart qp = searchService.getSearchQueryPartFromFieldName(
				"datasetname", "Test USA Herbarium");

		// This must match the field in datasetname SearchServiceConfig
		assertEquals(new Integer(8), qp.getSearchableFieldId());
		assertEquals("Test USA Herbarium", qp.getSingleValue());
	}

	/**
	 * Make sure we do not count empty or null values
	 */
	@Test
	public void testGetDistinctValuesCount() {
		OccurrenceSearchService searchService = (OccurrenceSearchService) applicationContext
				.getBean("occurrenceSearchService");
		Map<String, List<SearchQueryPart>> searchCriteria = new HashMap<String, List<SearchQueryPart>>();
		List<SearchQueryPart> searchCriteriaList = new ArrayList<SearchQueryPart>();

		SearchableField countrySearchableField = searchServiceConfig
				.getSearchableField(SearchableFieldEnum.COUNTRY);
		SearchableField localitySearchableField = searchServiceConfig
				.getSearchableField(SearchableFieldEnum.LOCALITY);

		// Mock a search for all country = USA or Sweden
		SearchQueryPart sqp1 = new SearchQueryPart();
		sqp1.addValue("Sweden");
		sqp1.setSearchableField(countrySearchableField);
		sqp1.addParsedValue("Sweden", countrySearchableField.getRelatedField(),
				"Sweden");
		sqp1.setOp(QueryOperatorEnum.EQ);
		searchCriteriaList.add(sqp1);

		SearchQueryPart sqp2 = new SearchQueryPart();
		sqp2.addValue("United States");
		sqp2.setSearchableField(countrySearchableField);
		sqp2.addParsedValue("United States",
				countrySearchableField.getRelatedField(), "United States");
		sqp2.setOp(QueryOperatorEnum.EQ);
		searchCriteriaList.add(sqp2);

		searchCriteria.put("country", searchCriteriaList);

		Integer count = searchService.getDistinctValuesCount(searchCriteria,
				(OccurrenceSearchableField) localitySearchableField);
		assertEquals(new Integer(4), count);
	}

	@Test
	public void testGetValuesFrequencyDistribution() {
		OccurrenceSearchService searchService = (OccurrenceSearchService) applicationContext
				.getBean("occurrenceSearchService");
		SearchableField countrySearchableField = searchServiceConfig
				.getSearchableField(SearchableFieldEnum.COUNTRY);
		SearchableField localitySearchableField = searchServiceConfig
				.getSearchableField(SearchableFieldEnum.LOCALITY);
		Map<String, List<SearchQueryPart>> searchCriteria = new HashMap<String, List<SearchQueryPart>>();
		List<SearchQueryPart> searchCriteriaList = new ArrayList<SearchQueryPart>();

		// Mock a search for all country = USA or Sweden
		SearchQueryPart sqp1 = new SearchQueryPart();
		sqp1.addValue("Sweden");
		sqp1.setSearchableField(countrySearchableField);
		sqp1.addParsedValue("Sweden", countrySearchableField.getRelatedField(),
				"Sweden");
		sqp1.setOp(QueryOperatorEnum.EQ);
		searchCriteriaList.add(sqp1);

		SearchQueryPart sqp2 = new SearchQueryPart();
		sqp2.addValue("United States");
		sqp2.setSearchableField(countrySearchableField);
		sqp2.addParsedValue("United States",
				countrySearchableField.getRelatedField(), "United States");
		sqp2.setOp(QueryOperatorEnum.EQ);
		searchCriteriaList.add(sqp2);

		SearchQueryPart sqp3 = new SearchQueryPart();
		sqp3.addValue("Mexico");
		sqp3.setSearchableField(countrySearchableField);
		sqp3.addParsedValue("Mexico", countrySearchableField.getRelatedField(),
				"Mexico");
		sqp3.setOp(QueryOperatorEnum.EQ);
		searchCriteriaList.add(sqp3);

		searchCriteria.put("country", searchCriteriaList);

		Map<StatsPropertiesEnum, Object> extraProperties = new HashMap<OccurrenceSearchService.StatsPropertiesEnum, Object>();
		extraProperties.put(StatsPropertiesEnum.RESOURCE_BUNDLE,
				appConfig.getResourceBundle(Locale.FRENCH));
		extraProperties.put(StatsPropertiesEnum.MAX_RESULT, 10);

		ChartModel chartModel = searchService.getValuesFrequencyDistribution(
				searchCriteria,
				(OccurrenceSearchableField) localitySearchableField,
				extraProperties);
		List<Object[]> rows = chartModel.getRows();
		// test total rows
		assertEquals(5, rows.size());
		// test specific rows
		for (Object[] currRow : rows) {
			if ("Mexico".equals(currRow[0])) {
				assertEquals(2, currRow[1]);
			}
			// empty and null values should be regrouped
			else if (currRow[0].toString().startsWith("<")) {
				assertEquals(2, currRow[1]);
			}
		}
	}

	@Test
	// Put this test on the shelf until we find why the content is not always
	// deleted
	public void testDownloadResult() throws Exception {
		// MockHttpServletRequest request = new MockHttpServletRequest();
		// request.setMethod("GET");
		// request.setRequestURI("/downloadresult");
		//
		// List<String> countryList = new ArrayList<String>();
		// countryList.add("Mexico");
		// countryList.add("Sweden");
		// appendSearchParameters(request,1,countryList);
		//
		// MockHttpServletResponse response = new MockHttpServletResponse();
		// Object handler = handlerMapping.getHandler(request).getHandler();
		// handlerAdapter.handle(request, response, handler);
		//
		// JSONObject a = new JSONObject(response.getContentAsString());
		// String fileName = a.get("dwcaFileName").toString();
		//
		// File dwcaFile = new
		// File(FilenameUtils.concat(searchServiceConfig.getGeneratedContentDownloadFolder(),fileName));
		// assertTrue(dwcaFile.exists());
		//
		// //cleaning
		// FileUtils.cleanDirectory(new
		// File(searchServiceConfig.getGeneratedContentDownloadFolder()));
		// //TODO find why a meta.xml will appear after the delete???
		// //TODO validate the content of the archive
	}

	private MockHttpServletRequest appendSearchParameters(
			MockHttpServletRequest request, int fieldId, List<String> values) {
		String currValue;
		String currParamId;
		for (int i = 0; i < values.size(); i++) {
			currValue = values.get(i);
			currParamId = Integer.toString(i + 1);
			request.addParameter(currParamId + "_f", Integer.toString(fieldId));
			request.addParameter(currParamId + "_o", "EQ");
			request.addParameter(currParamId + "_v", currValue);
		}
		return request;
	}
}
