package eu.dnetlib.enabling.aas.utils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import eu.dnetlib.enabling.aas.service.A2Constants;


/**
 * Provides queries for data retrieving from IS.
 * Some changes will be made to queries when final IS version is hooked up.
 * TODO add some resourceId validation (if needed) to prevent xquery injection.
 * @author mhorst
 *
 */
public class QueryProvider {

	public static final String RESOURCE_PROFILE_PREFIX = "RESOURCE_PROFILE/";
	
	/**
	 * Finds SecurityProfile for given resourceId.
	 * @param resourceId
	 * @return query
	 */
	public static String findSecurityProfileForResourceQuery(String resourceId) {
		if (resourceId==null) return null;
		return "for $el in fn:collection(\"" +
		A2Constants.IS_COLLECTION_SECPROF_PATH.substring("db/".length()) +
		"\") where $el/RESOURCE_PROFILE/BODY/CONFIGURATION/resourceId = '" +
		resourceId+"' return $el";
	}
	
	/**
	 * Finds SecurityProfile for given resourceId.
	 * @param resourceId
	 * @return query
	 */
	public static String findSecurityProfileForResourceQuery_new(String resourceId) {
		if (resourceId==null) return null;
		return "for $el in fn:collection(\"" +
		A2Constants.IS_COLLECTION_SECPROF_PATH.substring("db/".length()) +
		"\")/RESOURCE_PROFILE[BODY/CONFIGURATION/resourceId = '" +
		resourceId+"'] return $el";
	}
	
	/**
	 * Finds SecurityContext for given resourceId.
	 * @param resourceId
	 * @return query
	 */
	public static String findSecurityContextForResourceQuery(String resourceId) {
		if (resourceId==null) return null;
		return "for $el in fn:collection(\"" +
		A2Constants.IS_COLLECTION_SECCTX_PATH.substring("db/".length()) +
		"\") where $el/RESOURCE_PROFILE/BODY/CONFIGURATION/resourceId = '" +
		resourceId+"' return $el";
	}
	
	/**
	 * Finds SecurityContext for given resourceId.
	 * @param resourceId
	 * @return query
	 */
	public static String findSecurityContextForResourceQuery_new(String resourceId) {
		if (resourceId==null) return null;
		return "for $el in fn:collection(\"" +
		A2Constants.IS_COLLECTION_SECCTX_PATH.substring("db/".length()) +
		"\")/RESOURCE_PROFILE[BODY/CONFIGURATION/resourceId = '" +
		resourceId+"'] return $el";
	}
	
	public static String findAllSecurityContextProfIds() {
		return "for $el in fn:collection(\"" +
		A2Constants.IS_COLLECTION_SECCTX_PATH.substring("db/".length()) +
		"\") return $el/RESOURCE_PROFILE/HEADER/RESOURCE_IDENTIFIER";
	}

	public static String findAllSecurityProfilesIds() {
		return "for $el in fn:collection(\"" +
		A2Constants.IS_COLLECTION_SECPROF_PATH.substring("db/".length()) +
		"\") return $el/RESOURCE_PROFILE/HEADER/RESOURCE_IDENTIFIER";
	}
	
	/**
	 * Builds collection name for given profileKind and profileType.
	 * @param profileKind
	 * @param profileType
	 * @return collection name
	 */
	public static String buildCollectionName(String profileKind, String profileType) {
		if (profileKind == null || profileType ==null)
			return null;
		StringBuffer strBuff = new StringBuffer(A2Constants.IS_COLLECTION_DRIVER_ROOT_PATH);
		strBuff.append(profileKind);
		strBuff.append('/');
		strBuff.append(profileType);
		return strBuff.toString();
	}
	

	/**
	 * Build query for given parameters. 
	 * If conditionField = null or conditionValue = null, returns all elements.
	 * If path = null returns complete element.
	 * ConditionField cannot contain white characters, conditionValue cannon contain "'" character.
	 * @param profileKind
	 * @param profileType
	 * @param xpath
	 * @return built query
	 */
	@Deprecated
	public static String buildQuery(String profileKind, String profileType, 
			String conditionField, String conditionValue, String xpath) {
		
		if (!validateConditionField(conditionField) 
				|| !validateConditionValue(conditionValue))
			return null;

		String collectionName = buildCollectionName(profileKind, profileType);
		if (collectionName==null)
			return null;
		
		StringBuffer strBuff = new StringBuffer("for $el in fn:collection(\"");
		strBuff.append(collectionName.substring("db/".length()));
		strBuff.append("\") ");
		if (conditionField!=null && conditionValue!=null) {
			strBuff.append("where $el/" + conditionField + " = '" + conditionValue + "' ");
		}
		strBuff.append("return $el");
		if (xpath!=null) {
			strBuff.append('/');
			strBuff.append(xpath);
		}
		return strBuff.toString();
	}
	
	/**
	 * Build query for given parameters. 
	 * If conditionField = null or conditionValue = null, returns all elements.
	 * If path = null returns complete element.
	 * ConditionField cannot contain white characters, conditionValue cannon contain "'" character.
	 * @param profileKind
	 * @param profileType
	 * @param xpath
	 * @return built query
	 */
	public static String buildQuery_new(String profileKind, String profileType, 
			String conditionFullPath, String conditionValue, String xpath) {
//		FIXME fix that method!
		
		if (conditionFullPath!=null && conditionFullPath.startsWith(RESOURCE_PROFILE_PREFIX)) {
			conditionFullPath = conditionFullPath.substring(RESOURCE_PROFILE_PREFIX.length());
		}
		
		if (!validateConditionField(conditionFullPath) 
				|| !validateConditionValue(conditionValue))
			return null;

		String collectionName = buildCollectionName(profileKind, profileType);
		if (collectionName==null)
			return null;
		
		StringBuffer strBuff = new StringBuffer("for $el in fn:collection(\"");
		strBuff.append(collectionName.substring("db/".length()));
		strBuff.append("\")");
		if (conditionFullPath!=null && conditionValue!=null) {
			strBuff.append("/RESOURCE_PROFILE[" + conditionFullPath + " = '" + conditionValue + "'] ");
			strBuff.append("return $el");
			if (xpath!=null) {
				strBuff.append('/');
				if (xpath.startsWith(RESOURCE_PROFILE_PREFIX)) {
					xpath = xpath.substring(RESOURCE_PROFILE_PREFIX.length());
					strBuff.append(xpath);
				}
			}
		} else {
			strBuff.append(" return $el");
			if (xpath!=null) {
				strBuff.append('/');
				 strBuff.append(xpath);
			}
		}
		
		return strBuff.toString();
	}
	
	/**
	 * Cannot contain white characters.
	 * Can be null.
	 * @param value
	 * @return true if valid
	 */
	public static boolean validateConditionField(String value) {
		if (value==null)
			return true;
		Pattern p = Pattern.compile("\\s");
		Matcher m = p.matcher(value);
		if (m.find())
			return false;
		return true;
	}
	
	
	/**
	 * Cannot contain "'" character.
	 * Can be null.
	 * @param value
	 * @return true if valid
	 */
	public static boolean validateConditionValue(String value) {
		if (value==null)
			return true;
		if (value.indexOf('\'')!=-1)
			return false;
		return true;
	}
	
	/**
     * Simple field validation preventing from xpath injection.
     * @param field
     * @return true if value is valid.
     */
    public static boolean validateQueryValue(String field) {
    	// e.g. cannot contain "'" character.
    	String regex = "[a-zA-Z_0-9]+";
    	Pattern pattern = Pattern.compile(regex);
    	Matcher matcher = pattern.matcher(field);
    	return matcher.matches();
    }
    
}
