package eu.dnetlib.ariadneplus.workflows.nodes;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import eu.dnetlib.clients.enabling.ISLookUpClient;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.miscutils.datetime.DateUtils;
import eu.dnetlib.rmi.datasource.DatasourceManagerService;
import eu.dnetlib.rmi.datasource.DatasourceManagerServiceException;
import eu.dnetlib.rmi.datasource.IfaceDesc;
import eu.dnetlib.rmi.enabling.ISLookUpException;
import eu.dnetlib.rmi.enabling.ISRegistryException;
import eu.dnetlib.rmi.enabling.ISRegistryService;
import org.antlr.stringtemplate.StringTemplate;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import static java.nio.file.FileVisitResult.CONTINUE;

/**
 * Created by Alessia Bardi on 12/01/2018.
 *
 * @author Alessia Bardi
 */
public class ClarinFileVisitor extends SimpleFileVisitor<Path> {

	private static final Log log = LogFactory.getLog(ClarinFileVisitor.class);
	private static final String API_PREFIX = "api_________::";
	private static final String TDS_TEMPLATE ="/eu/dnetlib/ariadneplus/workflows/nodes/clarin_tds.xml.st";

	private List<String> interfaces = Lists.newArrayList();
	private String clarinDatasourceProfileID;
	private String clarinDatasourceOriginalId;
	private DatasourceManagerService dsMan = null;
	private String inputBaseUrlPrefix;
	private String metadataIdentifierPath;
	private ISLookUpClient lookupClient;
	private ISRegistryService registryService;

	private int countVisitedFiles = 0;
	private int countCreatedTDS = 0;
	private int countUpdatedTDS = 0;
	private int countVisitedFolders = 0;
	private int countCreatedInterfaces = 0;


	@Override
	public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
		log.info("Processing " + dir.toString());
		countVisitedFolders++;
		String dirName = dir.getFileName().toString();
		if(dirName.equalsIgnoreCase("x3ml-mappings")) return CONTINUE;
		String apiId = API_PREFIX + getClarinDatasourceOriginalId() + "::" + dirName;
		if (!interfaces.contains(apiId)) {
			//API TO BE CREATED
			IfaceDesc iface = new IfaceDesc();
			iface.setActive(false);
			iface.setCompliance("metadata");
			iface.setContentDescription("metadata");
			iface.setId(apiId);
			iface.setRemovable(true);
			iface.setTypology("dnet:repository::clarin");
			iface.setAccessProtocol("filesystem");
			Map<String, String> accessParams = Maps.newHashMap();
			accessParams.put("extensions", "xml");
			iface.setAccessParams(accessParams);
			iface.setBaseUrl(StringUtils.appendIfMissing(getInputBaseUrlPrefix(), "/") + dirName);
			Map<String, String> extraFields = Maps.newHashMap();
			extraFields.put("metadata_identifier_path", getMetadataIdentifierPath());
			iface.setExtraFields(extraFields);
			try {
				dsMan.addInterface(getClarinDatasourceProfileID(), iface);
				countCreatedInterfaces++;
				log.info("CREATED NEW INTERFACE " + iface.getId() + " for " + getClarinDatasourceOriginalId() + "(" + getClarinDatasourceProfileID() + ")");
			} catch (DatasourceManagerServiceException e) {
				log.error("Can't add interface " + iface.getId() + " to " + getClarinDatasourceOriginalId() + "(" + getClarinDatasourceProfileID() + ")", e);
				return CONTINUE;
			}
		} else {
			if (log.isDebugEnabled()) {
				log.debug("Interface " + apiId + " already exists");
			}
		}
		return CONTINUE;
	}

	@Override
	public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
		String filename = file.getFileName().toString();
		log.info("Processing " + file.toString());
		countVisitedFiles++;
		String tdsTitle = filename;
		//call Files.lines which will use a stream to iterate over each line of the file.
		//Next we will convert the stream to a string by calling Collectors.joining() which will join all the strings together.
		String updatedCode =  Files.lines(file).collect(Collectors.joining()).replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>", "");
		try {
			List<String> res = this.lookupClient.search("//RESOURCE_PROFILE[.//RESOURCE_TYPE/@value=\"TransformationRuleDSResourceType\" and .//CONFIGURATION/SCRIPT/TITLE/string()=\""+tdsTitle+"\"]/HEADER/RESOURCE_IDENTIFIER/@value/string()");
			if(res == null || res.isEmpty()){
				log.debug("Creating new TDS profile for "+filename);
				final String template = IOUtils.toString(getClass().getResourceAsStream(TDS_TEMPLATE), Charset.forName("UTF-8"));
				final StringTemplate st = new StringTemplate(template);
				st.setAttribute("date", DateUtils.calculate_ISO8601(DateUtils.now()));
				st.setAttribute("title", tdsTitle);
				st.setAttribute("mapping", "<![CDATA["+updatedCode+"]]>");
				String profId = this.registryService.registerProfile(st.toString());
				countCreatedTDS++;
				log.info("REGISTERED NEW TDS FOR "+filename+": "+profId);
			}
			else{
				String tdsProfileId = res.get(0);
				log.debug("Updating TDS profile "+tdsProfileId+"for "+filename);

				boolean done = this.registryService.updateProfileNode(tdsProfileId, "//CONFIGURATION/SCRIPT/CODE", "<CODE><![CDATA["+updatedCode+"]]></CODE>");
				if(done){
					log.info("TDS PROFILE "+tdsProfileId+" UPDATED with contents from "+filename);
					countUpdatedTDS++;
				}
				if(!done){
					log.error("!!! TDS PROFILE "+tdsProfileId+" COULD NOT BE UPDATED with contents from "+filename);
				}
			}
		} catch (ISLookUpException | ISRegistryException e) {
			log.error("CANNOT UPDATE/CREATE TDS PROFILE FOR "+filename, e);
		}

		return CONTINUE;
	}

	protected ClarinFileVisitor() {
		super();
	}

	public ClarinFileVisitor(final String clarinDatasourceProfileID, final String clarinDatasourceOriginalId,
			final String inputBaseUrlPrefix, final String metadataIdentifierPath, final List<String> interfaces,
			final UniqueServiceLocator locator, final ISLookUpClient lookupClient) {
		super();
		this.clarinDatasourceOriginalId = clarinDatasourceOriginalId;
		this.clarinDatasourceProfileID = clarinDatasourceProfileID;
		this.inputBaseUrlPrefix = inputBaseUrlPrefix;
		this.metadataIdentifierPath = metadataIdentifierPath;
		this.interfaces = interfaces;
		this.dsMan = locator.getService(DatasourceManagerService.class);
		this.registryService = locator.getService(ISRegistryService.class);
		this.lookupClient = lookupClient;
	}

	public List<String> getInterfaces() {
		return interfaces;
	}

	public void setInterfaces(final List<String> interfaces) {
		this.interfaces = interfaces;
	}

	public String getClarinDatasourceOriginalId() {
		return clarinDatasourceOriginalId;
	}

	public void setClarinDatasourceOriginalId(final String clarinDatasourceOriginalId) {
		this.clarinDatasourceOriginalId = clarinDatasourceOriginalId;
	}

	public DatasourceManagerService getDsMan() {
		return dsMan;
	}

	public void setDsMan(final DatasourceManagerService dsMan) {
		this.dsMan = dsMan;
	}

	public String getInputBaseUrlPrefix() {
		return inputBaseUrlPrefix;
	}

	public void setInputBaseUrlPrefix(final String inputBaseUrlPrefix) {
		this.inputBaseUrlPrefix = inputBaseUrlPrefix;
	}

	public String getMetadataIdentifierPath() {
		return metadataIdentifierPath;
	}

	public void setMetadataIdentifierPath(final String metadataIdentifierPath) {
		this.metadataIdentifierPath = metadataIdentifierPath;
	}

	public String getClarinDatasourceProfileID() {
		return clarinDatasourceProfileID;
	}

	public void setClarinDatasourceProfileID(final String clarinDatasourceProfileID) {
		this.clarinDatasourceProfileID = clarinDatasourceProfileID;
	}

	public ISLookUpClient getLookupClient() {
		return lookupClient;
	}

	public void setLookupClient(final ISLookUpClient lookupClient) {
		this.lookupClient = lookupClient;
	}

	public ISRegistryService getRegistryService() {
		return registryService;
	}

	public void setRegistryService(final ISRegistryService registryService) {
		this.registryService = registryService;
	}

	public int getCountVisitedFiles() {
		return countVisitedFiles;
	}

	public void setCountVisitedFiles(final int countVisitedFiles) {
		this.countVisitedFiles = countVisitedFiles;
	}

	public int getCountCreatedTDS() {
		return countCreatedTDS;
	}

	public void setCountCreatedTDS(final int countCreatedTDS) {
		this.countCreatedTDS = countCreatedTDS;
	}

	public int getCountUpdatedTDS() {
		return countUpdatedTDS;
	}

	public void setCountUpdatedTDS(final int countUpdatedTDS) {
		this.countUpdatedTDS = countUpdatedTDS;
	}

	public int getCountVisitedFolders() {
		return countVisitedFolders;
	}

	public void setCountVisitedFolders(final int countVisitedFolders) {
		this.countVisitedFolders = countVisitedFolders;
	}

	public int getCountCreatedInterfaces() {
		return countCreatedInterfaces;
	}

	public void setCountCreatedInterfaces(final int countCreatedInterfaces) {
		this.countCreatedInterfaces = countCreatedInterfaces;
	}
}
