package eu.dnetlib.data.utility.download;


import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import javax.xml.ws.Endpoint;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import eu.dnetlib.enabling.resultset.rmi.ResultSetService;

import pl.edu.icm.driver.factories.UniBiBeansFactory;
import pl.edu.icm.driver.index.DummyStaticResultSet;
import pl.edu.icm.driver.is.ISConstants;
import pl.edu.icm.driver.is.ISUtils;
import pl.edu.icm.driver.tester.TestDescription;
import pl.edu.icm.driver.tester.runner.Parameterized4TestCase;
import pl.edu.icm.driver.tester.runner.TestJobContext;
import pl.edu.icm.driver.utils.PropertiesKeys;

/**
 * {@link IDownloadService} test class.
 * 
 * @author mhorst
 *
 */
@TestDescription(
		name="Download Service test suite",
		description="Performs all required functionality tests of Download service.\n" +
				"Expected properties:\n" +
				"unibi.download.service.location - localisation of Download service to be tested\n" +
				"Optional properties:\n" +
				"is.rs.dataprov.ip - data provider service ip\n" +
				"is.rs.dataprov.port - data provider service port\n" +
				"is.rs.dataprov.location - data provider service location (default: 'IDataProvider?wsdl')\n" +
				"text.pdf.location - remote pdf file location\n" +
				"text.txt.location - remote txt file location\n"
)
public class DownloadServiceTest extends Parameterized4TestCase {

	protected static final Logger log = Logger.getLogger(DownloadServiceTest.class);
	
	IDownloadService downloadService;
	
	String downloadServiceLocation;
	
	String textPdfLocation = null;
	String textTxtLocation = null;
	
	String dataProviderIP = null;
	String dataProviderPort = null;
	String dataProviderLocation = null;
	String dataProviderURL = null;
	
	/**
	 * DMF data provider.
	 */
	ResultSetService dataProvider;
	
	/**
	 * CXF bus containing HTTP server.
	 */
	Bus bus;
	
	/* (non-Javadoc)
	 * @see pl.edu.icm.driver.tester.ParameterizedTest#setParameters(java.util.Map)
	 */
	@Override
	public void setParameters(Map<Object, Object> parameters) {
		downloadServiceLocation = (String) parameters.get(
				PropertiesKeys.SERVICE_LOCATION_DOWNLOAD);
		log.info("TestCase downloadServiceLocation: " + 
				downloadServiceLocation);
		
		textPdfLocation = (String) parameters.get(PropertiesKeys.TEXT_PDF_LOCATION);
		textTxtLocation = (String) parameters.get(PropertiesKeys.TEXT_TXT_LOCATION);
		
		dataProviderIP = (String) parameters.get(PropertiesKeys.RS_DATA_PROVIDER_IP);
		dataProviderPort = (String) parameters.get(PropertiesKeys.RS_DATA_PROVIDER_PORT);
		dataProviderLocation = (String) parameters.get(PropertiesKeys.RS_DATA_PROVIDER_LOCATION);
	}

	@Before
	public void setUp() throws Exception {
		ResourceBundle bundle = ResourceBundle.getBundle("pl.edu.icm.driver.is.auxiliary");
		if (textPdfLocation==null)
			textPdfLocation = bundle.getString(PropertiesKeys.TEXT_PDF_LOCATION);
		if (textTxtLocation==null)
			textTxtLocation = bundle.getString(PropertiesKeys.TEXT_TXT_LOCATION);
		if (dataProviderIP==null)
			dataProviderIP = bundle.getString(PropertiesKeys.RS_DATA_PROVIDER_IP);
		if (dataProviderPort==null)
			dataProviderPort = bundle.getString(PropertiesKeys.RS_DATA_PROVIDER_PORT);
		if (dataProviderLocation==null)
			dataProviderLocation = bundle.getString(PropertiesKeys.RS_DATA_PROVIDER_LOCATION);
		StringBuffer strBuff = new StringBuffer();
		strBuff.append("http://");
		strBuff.append(dataProviderIP);
		strBuff.append(':');
		strBuff.append(dataProviderPort);
		strBuff.append('/');
		strBuff.append(dataProviderLocation);
		dataProviderURL = strBuff.toString();
		log.info("dataProviderURL: "+dataProviderURL);
		
		UniBiBeansFactory ubBeansFactory = new UniBiBeansFactory();
		downloadService = ubBeansFactory.getDownload(downloadServiceLocation);

	}
	
	@After
	public void tearDown() throws Exception {

	}
	
	@Test
	@TestDescription(
			planId="dwnl.functionality",
			description="Retrieves version number from identity call in Download service. " +
					"Stores it in the result with description ''"
			)
	public void testIdentify() throws Exception {
		String identify = downloadService.identify();
		assertNotNull(identify);
		assertTrue(identify.length()>0);
		System.out.println("service version: "+identify);
		TestJobContext.storeResult(identify, "service version");
	}

	@Test
	@TestDescription(
			planId="dwnl.functionality",
			description="Checks whether download service fails smoothly " +
					"when downloading invalid URL"
			)
	public void testDownloadForInvalidURL() throws Exception {
		String url = "invalidURL";
		try {
			downloadService.downloadURL(url);
			fail("exception should be thrown when downloading data for invalid URL");
		} catch (DownloadServiceException e) {
//			ok
		}
	}
	
	@Test
	@TestDescription(
			planId="dwnl.functionality",
			description="Checks whether download service is able to download single url"
			)
	public void testDownloadSingleURL() throws Exception {
		String result = downloadService.downloadURL(textPdfLocation);
		assertNotNull(result);
		assertTrue(result.length()>0);
		if (isLocalService(downloadServiceLocation)) {
			checkLocalFileExistance(result);
		} else {
			String message = "file existance cannot be checked, " +
					"testing non local downloadService instance!";
			log.warn(message);
			TestJobContext.storeResult(message, "status");
		}
	}
	
	@Test
	@TestDescription(
			planId="dwnl.functionality",
			description="Checks whether download service fails smoothly " +
					"when downloading invalid URLs"
			)
	public void testDownloadForInvalidURLs() throws Exception {
		try {
			List<String> urls = new ArrayList<String>();
			urls.add("invalidURL1");
			urls.add("invalidURL2");
			downloadService.downloadURLs(urls);
			fail("exception should be thrown when downloading data for invalid URLs");
		} catch (DownloadServiceException e) {
//			ok
		}
	}
	
	@Test
	@TestDescription(
			planId="dwnl.functionality",
			description="Checks whether download service is able to download multiple urls"
			)
	public void testDownloadMultipleURLs() throws Exception {
		List<String> urls = new ArrayList<String>();
		urls.add(textPdfLocation);
		urls.add(textTxtLocation);
		
		W3CEndpointReference epr = downloadService.downloadURLs(urls);
		assertNotNull(epr);
		ResultSetService resultSetService = epr.getPort(ResultSetService.class);
		assertNotNull(resultSetService);
		String rsId = ISUtils.extractResultSetId(epr);
		assertNotNull(rsId);

//		waiting until RS is closed
		int maxTriesCount = 5;
		int triesCount = 0;
		while(!ISConstants.RESULT_SET_STATUS_CLOSED.equals(
				resultSetService.getRSStatus(rsId))) {
			Thread.sleep(1000);
			triesCount++;
			if (triesCount>maxTriesCount) {
				fail("rs is not closed, maxTriesCount: " + maxTriesCount + 
						" was exceeded!");
			}
		}
		
		assertEquals(2, resultSetService.getNumberOfElements(rsId));
		List<String> results = resultSetService.getResult(rsId, 1, 2, 
				ISConstants.RESULT_SET_REQUEST_MODE_WAITING);
		assertNotNull(results);
		assertEquals(2, results.size());
		for (String currentResult : results) {
			assertNotNull(currentResult);
			assertTrue(currentResult.length()>0);
		}
		if (isLocalService(downloadServiceLocation)) {
			for (String currentResult : results) {
				checkLocalFileExistance(currentResult);
			}
		} else {
			String message = "file existance cannot be checked, " +
					"testing non local downloadService instance!";
			log.warn(message);
			TestJobContext.storeResult(message, "status");
		}
	}
	
	@Test
	@TestDescription(
			planId="dwnl.functionality",
			description="Checks whether download service fails smoothly " +
					"when downloading URLs for invalid RS"
			)
	public void testDownloadURLsFromInvalidRS() throws Exception {
		try {
			String dataProviderServiceAddress = "http://localhost:1234/nonExisting";
			downloadService.downloadURLsFromRS(
					ISUtils.buildEPR(dataProviderServiceAddress, 
					"unknownID", dataProviderServiceAddress+"?wsdl", "RSName"));
			fail("exception should be thrown when downloading data for invalid EPR");
		} catch (DownloadServiceException e) {
//			ok
		}
	}
	
	@Test
	@TestDescription(
			planId="dwnl.functionality",
			description="Checks whether download service is able to " +
					"download multiple urls from RS"
			)
	public void testDownloadMultipleURLsFromRS() throws Exception {
		try {
			serverStartup(new Integer(dataProviderPort).intValue(), 
					new String[] {textPdfLocation, textTxtLocation});
			int expiryTime = 10000;
			String bdId = String.valueOf(System.currentTimeMillis());
			int initialPageSize = 10;
			W3CEndpointReference epr = downloadService.downloadURLsFromRS(
					dataProvider.createPullRS(dataProviderURL, bdId, 
							initialPageSize, expiryTime, null, null, null));
			assertNotNull(epr);
			ResultSetService resultSetService = epr.getPort(ResultSetService.class);
			assertNotNull(resultSetService);
			String rsId = ISUtils.extractResultSetId(epr);
			assertNotNull(rsId);

//			waiting until RS is closed
			int maxTriesCount = 5;
			int triesCount = 0;
			while(!ISConstants.RESULT_SET_STATUS_CLOSED.equals(
					resultSetService.getRSStatus(rsId))) {
				Thread.sleep(1000);
				triesCount++;
				if (triesCount>maxTriesCount) {
					fail("rs is not closed, maxTriesCount: " + maxTriesCount + 
							" was exceeded!");
				}
			}
			
			assertEquals(2, resultSetService.getNumberOfElements(rsId));
			List<String> results = resultSetService.getResult(rsId, 1, 2, 
					ISConstants.RESULT_SET_REQUEST_MODE_WAITING);
			assertNotNull(results);
			assertEquals(2, results.size());
			for (String currentResult : results) {
				assertNotNull(currentResult);
				assertTrue(currentResult.length()>0);
			}
			if (isLocalService(downloadServiceLocation)) {
				for (String currentResult : results) {
					checkLocalFileExistance(currentResult);
				}
			} else {
				String message = "file existance cannot be checked, " +
						"testing non local downloadService instance!";
				log.warn(message);
				TestJobContext.storeResult(message, "status");
			}
			
		} finally {
			serverShutdown();
		}
	}
	
	protected boolean isLocalService(String downloadServiceLocation) {
		if (downloadServiceLocation==null) {
			downloadServiceLocation = UniBiBeansFactory.downloadLocation;
		}
		return (downloadServiceLocation.contains("localhost") ||
				downloadServiceLocation.contains("127.0.0.1"));
	}
	
	protected void checkLocalFileExistance(String path) {
		File file = new File(path);
		assertTrue(file.exists());
	}
	
	/**
	 * Starts up cxf jetty server.
	 * @param dataProviderPort
	 * @throws Exception
	 */
	void serverStartup(int dataProviderPort, String[] data) throws Exception {
//		 Create CXF Service
		bus = BusFactory.getDefaultBus();
		dataProvider = new DummyStaticResultSet(data);
		StringBuffer strBuff = new StringBuffer();
		strBuff.append("http://");
		strBuff.append(dataProviderIP);
		strBuff.append(':');
		strBuff.append(dataProviderPort);
		strBuff.append('/');
		strBuff.append(dataProviderLocation);
		Endpoint.publish(strBuff.toString(), dataProvider);
		log.info("cxf service started...");
	}
	
	/**
	 * Shuts down cxf jetty server.
	 * @throws Exception
	 */
	void serverShutdown() throws Exception {
		if (bus!=null) {
			log.info("shutting down cxf service...");
			bus.shutdown(true);
		}
	}
}
