package org.gcube.application.geoportal;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.gcube.application.geoportal.model.Configuration;
import org.gcube.application.geoportal.model.db.DBConstants;
import org.gcube.application.geoportal.model.db.PostgisTable;
import org.gcube.application.geoportal.model.fault.ConfigurationException;
import org.gcube.application.geoportal.model.fault.InvalidRecordException;
import org.gcube.application.geoportal.storage.PostgisDBManager;
import org.gcube.application.geoportal.storage.PostgisDBManagerI;
import org.gcube.application.geoportal.utils.GpkgUtils;
import org.gcube.spatial.data.geonetwork.GeoNetworkPublisher;
import org.gcube.spatial.data.gis.GISInterface;

import lombok.extern.slf4j.Slf4j;
import mil.nga.geopackage.GeoPackage;
import mil.nga.geopackage.features.user.FeatureDao;
import mil.nga.geopackage.features.user.FeatureResultSet;

@Slf4j
public class Uploader {

	//	private DatabaseConnection postgisConnection;
	private GISInterface gis;
	private GeoNetworkPublisher geonetwork;
//	private ArchiveManager archiveManager;


	private Configuration config;


//	public Uploader(
//			GISInterface gis, 
//			GeoNetworkPublisher geonetwork, 
//			ArchiveManager manager,Configuration config) throws SQLException, IOException {
//		super();
//		//		this.postgisConnection = postgisConnection;
//		this.gis = gis;
//		this.geonetwork = geonetwork;
//		this.archiveManager=manager;
//		this.config=config;
//
//
//
//	}

//	public ArchiveDescriptor readArchive(File theArchive) {
//		// Unpack
//		// check file presence
//		// read xls
//		return null;
//	}

	// Expectcs CSV nome,anno,regione,xcentroid,ycentroid,csv,shp,nome_csv,poligono,punti,linee
	// I.E {nome=Acquacadda Nuxis, anno=2019, regione=Sardegna, xcentroid=8.751, ycentroid=39.179, csv=TRUE, shp=TRUE, nome_csv=AcquacaddaNuxis_poligoni, poligono=TRUE, punti=FALSE, linee=FALSE}
	public void insertConcessioni(File CSVFile,Boolean centroidsOnly) throws FileNotFoundException, IOException, SQLException, ConfigurationException {
		log.info("Connecting to postgresDB.. ");
		log.info("Parsing file "+CSVFile.getAbsolutePath());
		File exportedFolder=new File(CSVFile.getParent()+File.separator+"esportazioni");
		CSVParser parser=null;
		try {

			parser=CSVParser.parse(new FileReader(CSVFile), CSVFormat.DEFAULT.withFirstRecordAsHeader()) ;

			Map<String,Integer> headerMap=parser.getHeaderMap();

			log.debug("Headers are : "+(headerMap==null?null:headerMap.keySet()));




			PostgisDBManagerI db=PostgisDBManager.get();

			PostgisTableFactory tableFactory=new PostgisTableFactory(db);





			PostgisTable centroids=DBConstants.Concessioni.CENTROIDS;
			if(config.getUseTestSuffixes())
				centroids.setTablename(centroids.getTablename()+"_test");



			PreparedStatement psCentroids=db.prepareInsertStatement(centroids, true, true);

			if(config.getCleanUpBeforeUpdate()) {
				log.info("Cleaning up Centroids.. "+centroids);
				db.truncate(centroids.getTablename());				
			}

			for(CSVRecord r:parser.getRecords()) {				
				String nomeConcessione=r.get("nome");
				try {
//					ArchiveDescriptor toRegister=new ArchiveDescriptor(nomeConcessione,ArchiveType.CONCESSIONI);


					//Each CSV Record is a Concessione
					log.info("Importing "+nomeConcessione+" ["+r.getRecordNumber()+"] ");
//					ArrayList<LayerDescriptor> layers=new ArrayList<LayerDescriptor>();


					//publish layer(s)
					if(!centroidsOnly) {
						//if csv -> db table else shp
						if(Boolean.parseBoolean(r.get("csv"))) {

							// PUBLISH CSV LAYERS

							String[] csvNames=r.get("nome_csv").split(",");
							for(String csv:csvNames) {
								String csvPath=exportedFolder.getAbsolutePath()+File.separator+csv+".csv";
								log.debug("loading CSV : "+csvPath);
								PostgisTable table=tableFactory.fromCSV(csvPath);
								log.debug("Committing table..");
								db.commit();

								String layerName=nomeConcessione+" "+csv;
								log.debug("Creating layer "+layerName);
//								layers.add(Layers.publishPostgisTable(table, gis, geonetwork, layerName, config.getWorkspaceConcessioni(), config.getPostgisStore()));
							}
						}{
							// TODO PUBLISH SHP LAYERS
						}
					}
					//publish centroid	


					Map<String,String> centroidsRow=new HashMap<String, String>();
//					centroidsRow.put(DBConstants.Concessioni.UUID, toRegister.getId()+"");
					centroidsRow.put(DBConstants.Concessioni.ANNO, r.get("anno"));
					centroidsRow.put(DBConstants.Concessioni.NOME, r.get("nome"));
					centroidsRow.put(DBConstants.Concessioni.REGIONE, r.get("regione"));
					centroidsRow.put(DBConstants.Defaults.XCOORD_FIELD, r.get("xcentroid"));
					centroidsRow.put(DBConstants.Defaults.YCOORD_FIELD, r.get("ycentroid"));

					//Updated Schema 
					centroidsRow.put(DBConstants.Concessioni.DESCRIZIONE,r.get("descrizione_progetto"));
					centroidsRow.put(DBConstants.Concessioni.CONTENUTO,r.get("descrizione_contenuto"));
					centroidsRow.put(DBConstants.Concessioni.AUTORE,r.get("autore"));
					centroidsRow.put(DBConstants.Concessioni.CONTRIBUTORE,r.get("contributore"));
					centroidsRow.put(DBConstants.Concessioni.TITOLARE,r.get("titolare_dati"));
					centroidsRow.put(DBConstants.Concessioni.RESPONSABILE,r.get("responsabile"));
					centroidsRow.put(DBConstants.Concessioni.EDITORE,r.get("editore"));
					centroidsRow.put(DBConstants.Concessioni.FINANZIAMENTO,r.get("finanziamento"));
					centroidsRow.put(DBConstants.Concessioni.SOGGETTO,r.get("soggetto"));
					centroidsRow.put(DBConstants.Concessioni.RISORSE,r.get("risorse_correlate"));
					centroidsRow.put(DBConstants.Concessioni.DATE_SCAVO,r.get("date_scavo"));
					centroidsRow.put(DBConstants.Concessioni.DATA_ARCHIVIAZIONE,r.get("data_archiviazione"));
					centroidsRow.put(DBConstants.Concessioni.VERSIONE,r.get("versione"));
					centroidsRow.put(DBConstants.Concessioni.LICENZA,r.get("licenza"));
					centroidsRow.put(DBConstants.Concessioni.TITOLARE_LICENZA,r.get("titolare_licenza_copyright"));
					centroidsRow.put(DBConstants.Concessioni.ACCESSO,r.get("accesso_dati"));
					centroidsRow.put(DBConstants.Concessioni.PAROLE_CHIAVE,r.get("parole_chiave"));

					centroids.fillCSVPreparedStatament(centroidsRow, psCentroids, false);
					psCentroids.executeUpdate();
					//transfer to WS

					//Fill archive details
//					toRegister.setVersion("1.0.0");
//					for(LayerDescriptor layer:layers)
//						toRegister.addLayer(layer);

					//store archive info into app DB
//					archiveManager.store(toRegister);
//				}catch(InvalidRecordException e) {
//					log.warn("Skipping record : ",e);
				}catch(SQLException e) {
					throw new SQLException("Unable to insert record ",e);
				}catch(Throwable t) {
					throw new RuntimeException("Unexpected error while handling "+r.toMap(),t);
				}
			}
			db.commit();
		}finally {
			parser.close();
		}
	}



	public void insertMOSIFromTemplate(String gpkgFile, String requestedTable, Boolean centroidsOnly) throws Exception {

		//for each record in mosi (by accc)

		// publish centroid
		// publish mosi layer

		GeoPackage gpkg=GpkgUtils.open(new File(gpkgFile));


		PostgisDBManagerI db=PostgisDBManager.get();

		PostgisTableFactory tableFactory=new PostgisTableFactory(db);


		PostgisTable centroids=DBConstants.MOSI.CENTROID_MOSI;
		if(config.getUseTestSuffixes())
			centroids.setTablename(centroids.getTablename()+"_test");


		PreparedStatement psCentroids=db.prepareInsertStatement(centroids, true, true);

		if(config.getCleanUpBeforeUpdate())
			db.truncate(centroids.getTablename());


		List<String> tablenames=new ArrayList<String>();

		if(requestedTable==null) {
			log.debug("Scanning GPKG fro MOSIs");
			for(String s: gpkg.getFeatureTables()) {
				if(s.startsWith("MOSI")) tablenames.add(s);
			}

		}else tablenames.add(requestedTable);





		for(String tablename:tablenames) {


			FeatureDao featureDao = gpkg.getFeatureDao(tablename);
			PostgisTable mosiSchema=tableFactory.fromGPKGFeatureTable(gpkg, tablename, false);


			FeatureResultSet rs=featureDao.rawQuery("SELECT distinct(accc) from "+tablename);




			log.debug("Found "+rs.getCount()+" codes");
			while(rs.moveToNext()) {
				//			codes.add(rs.getString(0));
				String code=rs.getString(0);
				// Create Site Table			
				PostgisTable siteTable=tableFactory.createAs(mosiSchema, code);

				PreparedStatement psInsert= db.prepareInsertStatement(siteTable, true, false);

				FeatureResultSet rsByCode=featureDao.query("accc='"+code+"'");
				log.debug("Found "+rsByCode.getCount()+" entries for site "+code);

				int insertedCount=0;

				while(rsByCode.moveToNext()) {
					//insert into site table
					try{
						Map<String,Object> row=GpkgUtils.rowAsMap(rsByCode, siteTable.getFields());
						siteTable.fillObjectsPreparedStatement(row, psInsert);
						insertedCount+=psInsert.executeUpdate();
					}catch(InvalidRecordException e) {
						log.debug("Skipping Invalid row, cause : ",e.getMessage());
					}
				}
				if(insertedCount!=0) {

					// Get centroid of site table
					siteTable.setBoundingBox(db.evaluateBoundingBox(siteTable));
					siteTable.setCentroid(db.evaluateCentroid(siteTable));

					if(centroidsOnly) {
						db.deleteTable(siteTable.getTablename());
					}



					// Insert centroid into mosi_centroids		

					Map<String,String> centroidsRow=new HashMap<String, String>();
					centroidsRow.put(DBConstants.MOSI.CODE, code);
					centroidsRow.put(DBConstants.Defaults.XCOORD_FIELD, siteTable.getCentroid().getX()+"");
					centroidsRow.put(DBConstants.Defaults.YCOORD_FIELD, siteTable.getCentroid().getY()+"");



					centroids.fillCSVPreparedStatament(centroidsRow, psCentroids, false);
					psCentroids.executeUpdate();
				}else {
					log.debug("No actual row registered for site : "+code);
					db.deleteTable(siteTable.getTablename());
				}
			}
		}
		db.commit();



	}



//	public LayerDescriptor publishGpkgFeature(String gpkgFile, String tableName) throws Exception {
//		GeoPackage gpkg=GpkgUtils.open(new File(gpkgFile));
//
//
//		PostgisDBManagerI db=PostgisDBManager.get();
//
//		PostgisTableFactory tableFactory=new PostgisTableFactory(db);
//
//		PostgisTable table=tableFactory.fromGPKGFeatureTable(gpkg, tableName);
//		log.debug("Created table "+table);
//		db.commit();
//		LayerDescriptor desc=Layers.publishPostgisTable(table, gis, geonetwork, tableName, config.getWorkspacePreventiva(), config.getPostgisStore());
//		log.debug("Created layer "+desc);
//		return desc;
//	}

	public void cleanUp() {
		// list existing archives
		// for each		 	
		// delete layers
		// delete metadata
		// delete shp/table
		// delete centroid
		// delete from WS
	}
}
