package org.gcube.application.geoportal.storage;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.gcube.application.geoportal.model.InputStreamDescriptor;
import org.gcube.application.geoportal.model.Record;
import org.gcube.application.geoportal.model.concessioni.Concessione;
import org.gcube.application.geoportal.model.concessioni.LayerConcessione;
import org.gcube.application.geoportal.model.concessioni.RelazioneScavo;
import org.gcube.application.geoportal.model.content.AssociatedContent;
import org.gcube.application.geoportal.model.content.GeoServerContent;
import org.gcube.application.geoportal.model.content.OtherContent;
import org.gcube.application.geoportal.model.content.PersistedContent;
import org.gcube.application.geoportal.model.content.UploadedImage;
import org.gcube.application.geoportal.model.content.WorkspaceContent;
import org.gcube.application.geoportal.model.fault.PersistenceException;
import org.gcube.application.geoportal.model.fault.SDIInteractionException;
import org.gcube.application.geoportal.model.gis.SDILayerDescriptor;
import org.gcube.application.geoportal.model.report.PublicationReport;
import org.gcube.application.geoportal.model.report.ValidationReport;
import org.gcube.application.geoportal.model.report.ValidationReport.ValidationStatus;
import org.gcube.application.geoportal.utils.Files;
import org.gcube.common.storagehub.client.dsl.FolderContainer;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import org.gcube.data.transfer.library.faults.RemoteServiceException;

import lombok.extern.slf4j.Slf4j;


@Slf4j
public class ContentHandler<T extends Record> {

	private Map<AssociatedContent,ArrayList<TempFile>> uploadedResources=new HashMap<AssociatedContent, ArrayList<TempFile>>();

	private Map<AssociatedContent,ArrayList<TempFile>> toDeleteResources=new HashMap<AssociatedContent, ArrayList<TempFile>>();

	private ArrayList<TempFile> toDeleteTemps=new ArrayList<TempFile>();

	private T record;

	public ContentHandler(T record) {
		this.record=record;
	}


	/**
	 * Register files as to be associated with content 
	 * 
	 * @param content
	 * @param files
	 * @throws IOException 
	 */
	public void register(AssociatedContent content, InputStreamDescriptor...iss) throws IOException {
		ArrayList<TempFile> tempFiles=new ArrayList<TempFile>();
		for(InputStreamDescriptor is:iss)
			tempFiles.add(new TempFile(Files.copyToTemp(is.getStream()),is.getFilename()));

		toDeleteTemps.addAll(tempFiles);
		uploadedResources.put(content, tempFiles);
	}

	/**
	 * Unregisters files from current operation set	 * 
	 * 
	 */
	public void unregister(AssociatedContent content, InputStreamDescriptor...iss) {

	}

	public void dispose(AssociatedContent content) throws StorageHubException, SDIInteractionException, IllegalArgumentException, MalformedURLException, RemoteServiceException{
		log.debug("Deleting Content "+content);
		WorkspaceManager wsManager=null;

		SDIManager sdiManager=null;
		for(PersistedContent p:content.getActualContent()) {
			if(p instanceof WorkspaceContent) {
				if(wsManager==null) wsManager=new WorkspaceManager(record);
				wsManager.deleteFromWS((WorkspaceContent) p);
			}else if (p instanceof GeoServerContent) {
				if(sdiManager==null) sdiManager=new SDIManager();
				sdiManager.deleteContent((GeoServerContent) p);
			}

		}
	}

	public void disposeQuietly(AssociatedContent content) {
		try {
			dispose(content);
		} catch (IllegalArgumentException | MalformedURLException | RemoteServiceException | StorageHubException
				| SDIInteractionException e) {
			log.warn("Unable to delete content "+content,e);
		}
	}


	public PublicationReport publish() {
		log.debug("Publishing "+record.getNome());
		PublicationReport toReturn=new PublicationReport("Publish report");
		try {
			SDIManager sdiManager=new SDIManager();
			ArrayList<AssociatedContent> list=new ArrayList<AssociatedContent>(); 

			//Concessione
			Concessione conc=(Concessione) record;
			String workspace= sdiManager.createWorkspace("gna_conc_"+record.getId());
			list.add(conc.getPosizionamentoScavo());
			list.addAll(conc.getPianteFineScavo());
			
			for(AssociatedContent c:list) {
				if(c instanceof LayerConcessione) {
					try {
						List<PersistedContent> p=c.getActualContent();

						GeoServerContent geoserverPersisted=sdiManager.pushShapeLayerFileSet((SDILayerDescriptor)c, workspace);
						geoserverPersisted.setAssociated(c);
						
						
						p.add(geoserverPersisted);
						c.setActualContent(p);
					}catch(SDIInteractionException e) {
						log.warn("Unable to publish layers.",e);
						toReturn.addMessage(ValidationStatus.WARNING, "Layer "+c.getTitolo()+" non pubblicato.");
					}
					toReturn.addMessage(ValidationStatus.PASSED, "Pubblicato layer "+c.getTitolo());
				}
			}


		} catch (SDIInteractionException e1) {
			toReturn.addMessage(ValidationStatus.WARNING, "Unable to publish layers "+e1.getMessage());
		}
		return toReturn;
	}





	public PublicationReport storeChanges() throws PersistenceException {

		// 

		log.debug("Starting to persist "+uploadedResources.size()+" resources "+record.getNome());
		PublicationReport toReturn=new PublicationReport("Storage report");
		try {
			WorkspaceManager wsManager=new WorkspaceManager(record);

			for(Entry<AssociatedContent,ArrayList<TempFile>> entry:uploadedResources.entrySet()) {
				AssociatedContent content=entry.getKey();
				ArrayList<PersistedContent> persisted=new ArrayList<PersistedContent>();
				FolderContainer destination=null;
				String description=null;
				log.debug("Storing "+content);

				if(content instanceof RelazioneScavo) {
					destination= wsManager.getSubFolder("relazione");
					description="Relazione di scavo : "+content.getTitolo();

				}else if (content instanceof UploadedImage) {
					destination= wsManager.getSubFolder("imgs");
					description="Immagine significativa : "+content.getTitolo();
				}else if (content instanceof SDILayerDescriptor) {
					//SDI Section
					if(content instanceof LayerConcessione) {
						destination= wsManager.getSubFolder("layers/"+content.getTitolo());
						description="Layer concessione : "+content.getTitolo();

					}else throw new Exception("Invalid SDI Content "+content);


				}else if (content instanceof OtherContent ) {
					destination= wsManager.getSubFolder("other/"+content.getTitolo());
					description= "Other content : "+content.getTitolo();
				}else throw new Exception ("Invalid content "+content);

				log.debug("Actually Storing files to WS folder "+destination.getId());


				for(TempFile theFile : entry.getValue()) {
					WorkspaceContent wsContent=wsManager.storeToWS(theFile.getTheFile(), destination, theFile.getOriginalFileName(), description);
					wsContent.setAssociated(content);
					persisted.add(wsContent);
				}

				toReturn.addMessage(ValidationStatus.PASSED, "Registrati "+entry.getValue().size()+" elementi in archivio per : "+content.getTitolo());

				content.setActualContent(persisted);
			}
			return toReturn;
		}catch(StorageHubException e) {
			toReturn.addMessage(ValidationStatus.ERROR, "Impossibile archiviare.");
			log.error("Unexpected SGHUB Exception",e);
			throw new PersistenceException("Unexpected Exception",e,toReturn);
		}catch(Throwable t) {
			toReturn.addMessage(ValidationStatus.ERROR, "Errore inatteso.");
			throw new PersistenceException("Unexpected Exception",t,toReturn);
		}

	}


	@Override
	protected void finalize() throws Throwable {
		for(TempFile f:toDeleteTemps)
			java.nio.file.Files.deleteIfExists(f.getTheFile().toPath());
	}


	public ValidationReport validateContent() {
		return null;
	}

}
