package eu.stork.documentservice;

import java.util.Date;
import java.util.Properties;
import java.util.UUID;

import javax.jws.WebService;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.MTOM;

import org.apache.log4j.Logger;

import com.sun.tools.ws.processor.model.ModelException;

import eu.stork.documentservice.data.DatabaseConnector;
import eu.stork.documentservice.data.DatabaseConnectorMySQLImpl;
import eu.stork.documentservice.model.DocumentModel;
import eu.stork.documentservice.model.RequestModel;
import eu.stork.documentservice.model.TempDocumentModel;
import eu.stork.documentservice.utils.ExternalDocservice;
import eu.stork.documentservice.utils.XmlHelper;

@MTOM(threshold=500)
@WebService(endpointInterface = "eu.stork.documentservice.DocumentService", targetNamespace = "http://stork.eu", portName = "DocumentServicePort", serviceName = "DocumentService")
public class DocumentServiceImpl implements DocumentService
{
	//final static String PATH = "C:/Temp/upload/";
	static String COUNTRY;
	private DatabaseConnector conn;
	private Properties props = new Properties();
	private static final Logger LOG = Logger.getLogger(DocumentServiceImpl.class.getName());

	@Override	
	public String addDocument(byte[] document, String xmlRequest, String destinationCountry, String SpId, String mimeType, String receiverCert) {
		String returnMessage = "";
		if (document != null)
		{			
			try 
			{
				LOG.trace("Adding document starting, document size: " + Integer.toString(document.length)
						+ ", destination country: " + destinationCountry + ", SP Id: " + SpId
						+ ", mime type: " + mimeType + ", receiver cert: " + receiverCert);
				props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
				COUNTRY = props.getProperty("peps.country");
				RequestModel request = new RequestModel();			
				request.setDestcountry(destinationCountry);
				request.setSpcountry(COUNTRY);
				request.setSpid(SpId);
				request.setRequestid(XmlHelper.getRequestId(xmlRequest));
				request.setXmlrequest(xmlRequest);
				
				conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
						props.getProperty("sql.server"), props.getProperty("sql.database"));
				boolean ok = conn.addRequest(request);
				if (ok)
				{
					LOG.trace("Request added.");
					DocumentModel doc = new DocumentModel();
					doc.setDocid(UUID.randomUUID().toString());
					doc.setDocument(document);
					doc.setMimetype(mimeType);
					doc.setReicevercert(receiverCert);
					ok = conn.addDocument(doc);
					if (ok)
					{
						LOG.trace("Document added.");
						RequestModel req = conn.getRequest(request.getRequestid());
						if (req != null)
						{
							LOG.trace("Request found.");
							req.setDocid(doc.getDocid());
							if (conn.updateRequest(req))
								returnMessage = req.getFullDocID();
							else
							{
								LOG.warn("Could not update request.");
								throw new WebServiceException("Could not update request.");
							}
						}
					}
					else
					{
						LOG.warn("Could not add document.");
						throw new WebServiceException("Could not add document.");
					}
				}
				else
				{
					LOG.warn("Could not add request.");
					throw new WebServiceException("Could not add request.");
				}
			}
			catch (ModelException e)
			{
				LOG.error("Invalid model in input", e);
				e.printStackTrace();
				throw new WebServiceException("Invalid input.", e);
			}
			catch (Exception e)
			{
				LOG.error("Exception in addDocument.", e);
				e.printStackTrace();
				throw new WebServiceException("Upload Failed");
			}
			LOG.trace("Add document ending, return message: " + returnMessage);
			return returnMessage;
		}
		else
			throw new WebServiceException("No document to upload.");
	}

	@Override
	public byte[] getDocument(String documentTransferRequest, String dtlUrl) 
	{		
		try
		{
			String docId = XmlHelper.verifyRequest(documentTransferRequest);
			if (docId != null && !docId.isEmpty())
			{			
				props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
				String ownDtlUrl = props.getProperty("docservice.url");
				LOG.trace("Getting document starting for docId: " + docId);
				if (dtlUrl != null && !dtlUrl.isEmpty() && !ownDtlUrl.equalsIgnoreCase(dtlUrl))
				{
					LOG.trace("Getting document from external DTL for docId: " + docId);
					byte[] documentData = ExternalDocservice.getDocument(documentTransferRequest, dtlUrl);
					if (documentData != null)
					{
						props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
						conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
								props.getProperty("sql.server"), props.getProperty("sql.database"));
						DocumentModel doc = conn.getDocument(docId);
						if (doc != null)
						{
							doc.setDocument(documentData);
							conn.updateDocument(doc);
						}
						else
						{
							doc = new DocumentModel();
							doc.setDocid(docId);
							doc.setDocument(documentData);							
							doc.setMimetype(ExternalDocservice.getDocumentMime(docId, dtlUrl));
							//TODO handle cert
							doc.setReicevercert("");
							conn.addDocument(doc);
						}
					}
					
					LOG.trace("Getting document from external DTL ending.");
					return documentData;
				}	
				else
				{
					LOG.trace("Getting document from database for docId: " + docId);
					props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
					conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
							props.getProperty("sql.server"), props.getProperty("sql.database"));
					DocumentModel doc = conn.getDocument(docId);
					LOG.trace("Getting document from database ending.");
					return doc.getDocument();
				}
			}
			else
			{
				LOG.warn("Document id is null");
				throw new WebServiceException("Document Id is null");
			}			
		}
		catch (Exception e)
		{
			LOG.error("Exception in getDocument.", e);
			e.printStackTrace();
			throw new WebServiceException("Download Failed", e);
		}
	}
	
	@Override
	/**
	 * Get document mime type
	 */
	public String getDocumentMime(String docId, String dtlUrl)
	{
		try
		{
			if (docId != null && !docId.isEmpty())
			{	
				props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
				String ownDtlUrl = props.getProperty("docservice.url");
				docId = XmlHelper.StripDocId(docId);
				LOG.trace("Getting mime starting for docId: " + docId);
				if (dtlUrl != null && !dtlUrl.isEmpty() && !ownDtlUrl.equalsIgnoreCase(dtlUrl))
				{					
					LOG.trace("Getting mime from external DTL for docId: " + docId);
					String documentMime = ExternalDocservice.getDocumentMime(docId, dtlUrl);
					if (documentMime != null && !documentMime.isEmpty())
					{
						conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
								props.getProperty("sql.server"), props.getProperty("sql.database"));
						DocumentModel doc = conn.getDocument(docId);
						if (doc != null)
						{
							doc.setMimetype(documentMime);
							conn.updateDocument(doc);
						}
					}
					LOG.trace("Getting mime from external DTL ending, docId: " + docId + ", mime " + documentMime);
					return documentMime;
				}	
				else
				{
					LOG.trace("Getting mime from dabase for docId: " + docId);
					props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
					conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
							props.getProperty("sql.server"), props.getProperty("sql.database"));
					DocumentModel doc = conn.getDocument(docId);
					LOG.trace("Getting mime from database ending, docId: " + docId + ", mime " + doc.getMimetype());
					return doc.getMimetype();
				}
			}
			else
			{
				LOG.warn("Document id is null");
				throw new WebServiceException("Document Id is null");
			}			
		}
		catch (Exception e)
		{
			LOG.error("Exception in getDocumentMime.", e);
			e.printStackTrace();
			throw new WebServiceException("Download Failed", e);
		}
	}
	
	@Override
	/**
	 * Update document in document service
	 */
	public boolean updateDocument(String docId, String xmlResponse, byte[] document)
	{
		boolean success = false;
		if (docId != null && !docId.isEmpty())
		{			
			if (document != null)
			{
				try
				{
					docId = XmlHelper.StripDocId(docId);
					LOG.trace("Starting document update for docId: " + docId);
					props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
					conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
							props.getProperty("sql.server"), props.getProperty("sql.database"));
					DocumentModel doc = conn.getDocument(docId);
					if(doc != null)
					{
						doc.setDocument(document);
						success = conn.updateDocument(doc);
						if (success && xmlResponse != null && !xmlResponse.isEmpty())
						{
							RequestModel request = conn.getRequestByDocId(docId);
							if (request != null)
							{
								request.setXmlresponse(xmlResponse);
								request.setRestimestamp(new Date());
								success = conn.updateRequest(request);
							}
						}
						LOG.trace("Document " + docId + " updated successfully: " + Boolean.toString(success));
					}
					else
					{
						LOG.warn("No document found for docId: " + docId);
						throw new WebServiceException("Document is null");
					}
				}
				catch (Exception e)
				{
					LOG.error("Exception in updateDocument.", e);
					e.printStackTrace();
					throw new WebServiceException("Update Failed", e);
				}
			}
		}
		else
		{
			LOG.warn("Document id is null");
			throw new WebServiceException("Document Id is null");
		}
		
		return success;
	}
	
	@Override
	public String addSPDocument(String docId, String xmlRequest, String destinationCountry, String SpId, String receiverCert) 
	{
		String returnMessage = "";
		if (docId != null && !docId.isEmpty())
		{
			try 
			{
				LOG.trace("Adding document starting, document ID: " + docId
						+ ", destination country: " + destinationCountry + ", SP Id: " + SpId
						+ ", receiver cert: " + receiverCert);
				props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
				COUNTRY = props.getProperty("peps.country");
				RequestModel request = new RequestModel();			
				request.setDestcountry(destinationCountry);
				request.setSpcountry(COUNTRY);
				request.setSpid(SpId);
				request.setRequestid(XmlHelper.getRequestId(xmlRequest));
				request.setXmlrequest(xmlRequest);
				
				conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
						props.getProperty("sql.server"), props.getProperty("sql.database"));
				boolean ok = conn.addRequest(request);
				if (ok)
				{
					LOG.trace("Request added.");
					LOG.trace("Getting temp document.");
					TempDocumentModel tempDoc = conn.getTempDocument(docId);
					if (tempDoc != null)
					{
						LOG.trace("Got temp document.");
						conn.updateTempDocument(tempDoc);
						LOG.trace("Temp document updated.");
						DocumentModel doc = new DocumentModel();
						doc.setDocid(tempDoc.getDocid());
						doc.setDocument(tempDoc.getDocument());
						doc.setMimetype(tempDoc.getMimetype());
						doc.setReicevercert(receiverCert);
						ok = conn.addDocument(doc);
						if (ok)
						{
							LOG.trace("Document added.");							
							RequestModel req = conn.getRequest(request.getRequestid());
							if (req != null)
							{
								LOG.trace("Request found.");
								req.setDocid(doc.getDocid());
								if (conn.updateRequest(req))
									returnMessage = req.getFullDocID();
								else
								{
									LOG.warn("Could not update request.");
									throw new WebServiceException("Could not update request.");
								}
							}
						}
						else
						{
							LOG.warn("Could not add document.");
							throw new WebServiceException("Could not add document.");
						}
					}
					else
					{
						LOG.warn("No document found with id " + docId);
						throw new WebServiceException("No document found.");
					}
				}
				else
				{
					LOG.warn("Could not add request.");
					throw new WebServiceException("Could not add request.");
				}
			}
			catch (ModelException e)
			{
				LOG.error("Invalid model in input", e);
				e.printStackTrace();
				throw new WebServiceException("Invalid input.", e);
			}
			catch (Exception e)
			{
				LOG.error("Exception in addSPDocument.", e);
				e.printStackTrace();
				throw new WebServiceException("Upload Failed");
			}
			LOG.trace("Add SP document ending, return message: " + returnMessage);
		}
		else
		{
			LOG.warn("Document id is null");
			throw new WebServiceException("Document Id is null");
		}
		
		return returnMessage;				
	}
	
	@Override
	public boolean updateSPDocument(String documentTransferRequest, String dtlUrl, String xmlResponse)
	{
		boolean success = false;
		if (documentTransferRequest != null && !documentTransferRequest.isEmpty())
		{			
			if (xmlResponse != null && !xmlResponse.isEmpty())
			{
				try
				{
					String docId = XmlHelper.verifyRequest(documentTransferRequest);
					LOG.trace("Document transfer requst ok. Doc ID: " + docId);
					if (dtlUrl == null || dtlUrl.isEmpty())
					{
						LOG.trace("Starting SP document update from database for docId: " + docId);
						props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
						conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
							props.getProperty("sql.server"), props.getProperty("sql.database"));
						DocumentModel doc = conn.getDocument(docId);
						if(doc != null)
						{
							TempDocumentModel tempDoc = conn.getTempDocument(docId);
							tempDoc.setDocument(doc.getDocument());
							success = conn.updateTempDocument(tempDoc);
							if (success && xmlResponse != null && !xmlResponse.isEmpty())
							{
								RequestModel request = conn.getRequestByDocId(docId);
								if (request != null)
								{
									request.setXmlresponse(xmlResponse);
									request.setRestimestamp(new Date());
									if (conn.updateRequest(request))
									{
										LOG.trace("Request updated.");
										success = true;
									}
									else
									{
										LOG.warn("Could not update request with id " + request.getRequestid());
										throw new WebServiceException("Update SP document failed");
									}
								}
							}
							LOG.trace("Document " + docId + " updated successfully: " + Boolean.toString(success));
						}
						else
						{
							LOG.warn("No document found for docId: " + docId);
							throw new WebServiceException("Document is null");
						}
					}
					else
					{
						LOG.trace("Starting SP document update from external DTL for docId: " + docId);
						byte[] documentData = ExternalDocservice.getDocument(documentTransferRequest, dtlUrl);
						if (documentData != null)
						{
							props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
							conn = new DatabaseConnectorMySQLImpl(props.getProperty("sql.user"), props.getProperty("sql.password"), 
									props.getProperty("sql.server"), props.getProperty("sql.database"));
							DocumentModel doc = conn.getDocument(docId);
							if (doc != null)
							{
								LOG.trace("Document found");
								doc.setDocument(documentData);
								if (conn.updateDocument(doc))
								{
									LOG.trace("Document updated");
									TempDocumentModel tempDoc = conn.getTempDocument(docId);
									if (tempDoc != null)
									{
										LOG.trace("Temp document found");
										tempDoc.setDocument(documentData);
										if (conn.updateTempDocument(tempDoc))
										{
											LOG.trace("Temp document updated");
											RequestModel request = conn.getRequestByDocId(docId);
											request.setXmlresponse(xmlResponse);
											request.setRestimestamp(new Date());
											if (conn.updateRequest(request))
											{
												LOG.trace("Request updated");
												success = true;
											}
											else
											{
												LOG.warn("Could not update request with doc id " + docId);
												throw new WebServiceException("SP update failed");
											}
										}
										else
										{
											LOG.warn("Could not update temp document with id " + docId);
											throw new WebServiceException("SP update failed");
										}
									}
									else
									{
										LOG.warn("Could not find temp document with id " + docId);
										throw new WebServiceException("SP update failed");
									}
								}
								else
								{
									LOG.warn("Could not update document with id " + docId);
									throw new WebServiceException("SP update failed");
								}
							}
						}
					}
				}
				catch (Exception e)
				{
					LOG.error("Exception in updateSPDocument.", e);
					e.printStackTrace();
					throw new WebServiceException("Update Failed", e);
				}
			}
			else
			{
				LOG.warn("XML signing response is null or empty");
				throw new WebServiceException("XML signing response is null");
			}
		}
		else
		{
			LOG.warn("Document transfer request is null or empty");
			throw new WebServiceException("Document transfer request is null");
		}
		
		return success;
	}
}