package org.gcube.application.cms.sdi.engine;


import it.geosolutions.geoserver.rest.encoder.datastore.GSPostGISDatastoreEncoder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.gcube.application.cms.sdi.faults.SDIInteractionException;
import org.gcube.application.geoportal.common.model.rest.DatabaseConnection;
import org.gcube.data.transfer.library.DataTransferClient;
import org.gcube.spatial.data.gis.GISInterface;
import org.gcube.spatial.data.gis.is.AbstractGeoServerDescriptor;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class SDIManager {



	public static final Pattern HOSTNAME_PATTERN=Pattern.compile("(?<=\\:\\/\\/)[^\\:]*");
	public static final Pattern PORT_PATTERN=Pattern.compile("(?<=\\:)[\\d]+");
	public static final Pattern DB_NAME_PATTERN=Pattern.compile("(?<=\\/)[^\\/]*(?=$)");


	private final GISInterface gis;
	@Getter
	private final DataTransferClient dtGeoServer;
	@Getter
	private final String geoserverHostName;
	@Getter
	private final AbstractGeoServerDescriptor currentGeoserver;


	public SDIManager() throws SDIInteractionException {
		try{
			log.debug("Initializing GIS Interface..");
			gis=GISInterface.get();
			currentGeoserver=gis.getCurrentGeoServer();
			if(currentGeoserver==null)
				throw new Exception("Unable to contact data transfer for geoserver ");

			log.debug("Found geoserver descriptor "+currentGeoserver);
			geoserverHostName=new URL(currentGeoserver.getUrl()).getHost();

			log.debug("Contacting Data Transfer from geoserver {} ",geoserverHostName);
			dtGeoServer=DataTransferClient.getInstanceByEndpoint("https://"+geoserverHostName);
			if(!currentGeoserver.getReader().existGeoserver())
				throw new Exception("Geoserver not reachable");
		}catch(Exception e) {
			throw new SDIInteractionException("Unable to initialize SDI Manager",e);
		}
	}




	public String createWorkspace(String toCreate) throws SDIInteractionException {
		try {
			if(!currentGeoserver.getReader().getWorkspaceNames().contains(toCreate)) {
				log.debug("Creating workspace : "+toCreate);
				if(!currentGeoserver.getPublisher().createWorkspace(toCreate))
					throw new SDIInteractionException("Unable to create workspace "+toCreate);
			}else log.debug("Workspace "+toCreate+" exists.");
			return toCreate;
		} catch (IllegalArgumentException | MalformedURLException e) {
			throw new SDIInteractionException("Unable to create workspace "+toCreate,e);
		}
	}


//	protected static final String getToUseBaseLayerName(RegisteredFileSet fileset){
//		// ******** IDENTIFY LAYER NAME correct layer name
//		// Must be unique under same WS
//		// equal to shp base name
//
//		String baseName= "";
//
//		// Chose layer name first identifying filename
//		for(Object p:fileset.getPayloads()){
//				RegisteredFile f = Serialization.convert(p,RegisteredFile.class);
//				String name=f.getName();
//				if(name.endsWith(".shp")||name.endsWith(".tif")) {
//					log.debug("File for layer is {}",name);
//					baseName= Files.fixFilename(name.substring(0,name.lastIndexOf('.')));
//					break;
//				}
//			}
//		return baseName;
//	}

	protected String createStore(GSPostGISDatastoreEncoder encoder, String workspace) throws SDIInteractionException {
		String storeName=encoder.getName();
		try {
			log.debug("Looking for datastore "+storeName+" under "+workspace);

			if(currentGeoserver.getReader().getDatastore(workspace,storeName)==null)

				if(!currentGeoserver.getDataStoreManager().create(workspace, encoder))
					throw new SDIInteractionException("Unable to create store "+storeName+" in "+workspace);
			log.debug("Store "+storeName+" exists under "+workspace);
			return storeName;
		} catch (IllegalArgumentException | MalformedURLException e) {
			throw new SDIInteractionException("Unable to create store "+storeName,e);
		}
	}


	protected String createStoreFromPostgisDB(String workspace, String storeName, DatabaseConnection connection) throws SDIInteractionException{
		String connectionUrl=connection.getUrl();

		Matcher hostname=HOSTNAME_PATTERN.matcher(connectionUrl);
		if (!hostname.find()) throw new SDIInteractionException("Unable to get Hostname from "+connection);

		Matcher port = PORT_PATTERN.matcher(connectionUrl);
		if (!port.find()) throw new SDIInteractionException("Unable to get PORT from "+connection);

		Matcher db = DB_NAME_PATTERN.matcher(connectionUrl);
		if (!db.find()) throw new SDIInteractionException("Unable to get DB from "+connection);


		GSPostGISDatastoreEncoder encoder=new GSPostGISDatastoreEncoder(storeName);
		encoder.setHost(hostname.group());
		encoder.setPort(Integer.parseInt(port.group()));
		encoder.setDatabase(db.group());
		encoder.setSchema("public");
		encoder.setUser(connection.getUser());
		encoder.setPassword(connection.getPwd());
		encoder.setLooseBBox(true);
		encoder.setDatabaseType("postgis");
		encoder.setEnabled(true);
		encoder.setFetchSize(1000);
		encoder.setValidateConnections(true);

		return createStore(encoder,workspace);
	}

	protected String publishStyle(File sldFile, String name) throws SDIInteractionException {
		try {
			if(!currentGeoserver.getReader().existsStyle(name)) {
				log.debug("Registering style "+name);
				if(!currentGeoserver.getPublisher().publishStyle(sldFile, name))
					throw new SDIInteractionException("Unable to register style "+name);
			}else log.debug("Style "+name+" already existing");
			return name;
		} catch (IllegalArgumentException | MalformedURLException e) {
			throw new SDIInteractionException("Unable to create style "+name,e);
		}

	}

}
