package org.gcube.portlets.admin.resourcemanagement.server;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.ServletContext;

import org.gcube.common.authorization.library.provider.AccessTokenProvider;
import org.gcube.common.keycloak.KeycloakClient;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.keycloak.model.TokenResponse;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.informationsystem.model.reference.entities.Resource;
import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache;
import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClient;
import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClientFactory;
import org.gcube.informationsystem.types.reference.Type;
import org.gcube.portlets.admin.resourcemanagement.shared.resource.CacheList;
import org.gcube.portlets.admin.resourcemanagement.shared.resource.UtilityResource;
import org.gcube.resourcemanagement.support.client.views.ResourceTypeDecorator;
import org.gcube.resourcemanagement.support.shared.types.datamodel.CompleteResourceProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author pieve mail:alessandro.pieve@isti.cnr.it
 *
 */
public class RegistryClientRequester {

	private static final String CLIENT_PROPERTIES = "/WEB-INF/client.properties";

	private static final String ENDPOINT = "endpoint";
	private static final String CLIENT_ID = "clientID";
	private static final String SECRET = "secret";
	private static final String ROOT_SCOPE = "rootScope";
	
	private static final boolean IncludeContextInHeader = true;

	private static Logger logger = LoggerFactory.getLogger(RegistryClientRequester.class);

	private static ResourceRegistryClient resourceRegistryClient;

	private static KeycloakClient client = KeycloakClientFactory.newInstance();

	// private static final String endpoint =
	// "https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
	// private static final String clientID =
	// "resource-registry-portlet-expieve";
	// private static final String secret =
	// "179bd3bc-5cc4-11ec-bf63-0242ac130001";
	// private static final String rootScope = "/gcube";

	public static final Set<String> getAvailableContexts(ServletContext servletContext) throws Exception {
		Properties clientProperties=getClientProperties(servletContext);
		String rootScope=clientProperties.getProperty(ROOT_SCOPE);
		logger.info("GetAvailableContexts: [Scope: {}]", rootScope);
		Set<String> contextList = new HashSet<String>();
		ScopeProvider.instance.set(rootScope);
		AccessTokenProvider.instance.set(getTokenForContext(rootScope,clientProperties));

		try {
			resourceRegistryClient = ResourceRegistryClientFactory.create();
			//resourceRegistryClient.setIncludeContextsInHeader(IncludeContextInHeader);
			
			
			ContextCache contextCache = ContextCache.getInstance();

			contextList = contextCache.getContextFullNameToUUIDAssociation().keySet();

		} catch (Exception e) {
			logger.error("Error retrieving available contexts: {}",e.getLocalizedMessage(),e);
			throw new Exception("Error retrieving available contexts: "+e.getLocalizedMessage(),e);
		} finally {
			AccessTokenProvider.instance.reset();
			ScopeProvider.instance.reset();
		}
		logger.info("Available contexts: {}",contextList);
		return contextList;
	}

	private static Properties getClientProperties(ServletContext servletContext) throws Exception {
		Properties properties = new Properties();
		properties.load(servletContext.getResourceAsStream(CLIENT_PROPERTIES));
		return properties;
	}

	private static String getTokenForContext(String context, Properties clientProperties) {
		logger.info("GetTokenForContext: {}",context);
		String endpoint=clientProperties.getProperty(ENDPOINT);
		String clientID=clientProperties.getProperty(CLIENT_ID);
		String secret=clientProperties.getProperty(SECRET);
		
		try {
			TokenResponse response = client.queryUMAToken(new URL(endpoint), clientID, secret, context, null);
			return response.getAccessToken();

		} catch (Exception e) {
			throw new RuntimeException("error getting access token for context " + context, e);
		}

	}

	/**
	 * For all the resource in the scope retrieves their (type, subtype) values.
	 * The result is a list of couples of that form.
	 * 
	 * @return a list of string tuples (type, subtype)
	 * @throws Exception
	 */

	// list a resource with a sub category
	public static final HashMap<String, ArrayList<String>> getResourcesTree(ServletContext servletContext,String scope) throws Exception {
		logger.info("GetResourcesTree: [Scope: {}]", scope);
		Properties clientProperties=getClientProperties(servletContext);
		HashMap<String, ArrayList<String>> retval = new HashMap<String, ArrayList<String>>();
		ScopeProvider.instance.set(scope);
		AccessTokenProvider.instance.set(getTokenForContext(scope,clientProperties));
		try {
			

			resourceRegistryClient = ResourceRegistryClientFactory.create();
			List<Type> types = resourceRegistryClient.getType(Resource.class, true);

			logger.info("Resources Types: {}", types.size());

			for (Type t : types) {
				logger.info("Resource Type: {} in {} ", t.getName(), scope);
				if (t.getName() == null) {
					continue;
				}
				String val = null;
				switch (t.getName()) {
				case "Actor":
					val = "Actor";
					break;
				case "LegalBody":
					val = "LegalBody";
					break;
				case "Person":
					val = "Person";
					break;
				case "ConfigurationTemplate":
					val = "ConfigurationTemplate";
					break;
				case "Configuration":
					val = "GenericResource";
					break;
				case "Dataset":
					val = "Dataset";
					break;
				case "ConcreteDataset":
					val = "ConcreteDataset";
					break;
				case "Schema":
					val = "Schema";
					break;
				case "EService":
					val = "RunningInstance";
					break;
				case "RunningPlugin":
					val = "RunningPlugin";
					break;
				case "HostingNode":
					val = "GHN";
					break;
				case "VirtualService":
					val = "VirtualService";
					break;
				case "Site":
					val = "Site";
					break;
				case "Software":
					val = "Software";
					break;
				case "Plugin":
					val = "Plugin";
					break;
				default:
					break;

				}

				if (val != null) {
					ArrayList<String> subtrees = new ArrayList<String>();
					try {
						subtrees = UtilityResource.getSubResourcesTreeQuery(resourceRegistryClient, t.getName());
					} catch (Exception e) {
						logger.error(e.getLocalizedMessage(), e);
					}
					retval.put(val, subtrees);
				}

			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			AccessTokenProvider.instance.reset();
			ScopeProvider.instance.reset();
		}

		// Create a list from elements of HashMap
		LinkedList<String> list = new LinkedList<String>(retval.keySet());

		// Sort the list using lambda expression
		Collections.sort(list, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		});

		logger.info("Sorted Key: {}", list);
		// put data from sorted list to hashmap
		HashMap<String, ArrayList<String>> sortedretval = new LinkedHashMap<String, ArrayList<String>>();
		for (String k : list) {
			sortedretval.put(k, retval.get(k));
		}

		return sortedretval;
	}

	// list string (xml formatted) for table
	public static final List<String> getResourcesByTypeSubType(ServletContext servletContext,String scope, String type, String subType)
			throws Exception {
		logger.info("GetResourcesByTypeSubType: [Scope: {}]", scope);
		Properties clientProperties=getClientProperties(servletContext);
		// CacheList.resourceid.clear();
		ScopeProvider.instance.set(scope);
		AccessTokenProvider.instance.set(getTokenForContext(scope,clientProperties));
		try {
			logger.info("GetResourcesByTypeSubType: [Scope: {}, Type: {}, SubType: {}]",
					new String[] { scope, type, subType });

			resourceRegistryClient = ResourceRegistryClientFactory.create();
			// resourceRegistryClient.setIncludeContextsInHeader(IncludeContextInHeader);
			List<String> resources = new ArrayList<String>();

			switch (type) {
			case "Actor":
				logger.info("---------------->Actor");
				resources = UtilityResource.createListActor(resourceRegistryClient, type, subType);
				break;
			case "LegalBody":
				logger.info("---------------->LegalBody");
				resources = UtilityResource.createListLegalBody(resourceRegistryClient, type, subType);
				break;
			case "Person":
				logger.info("---------------->Person");
				resources = UtilityResource.createListPerson(resourceRegistryClient, type, subType);
				break;
			case "ConfigurationTemplate":
				logger.info("---------------->ConfigurationTemplate");
				resources = UtilityResource.createListConfigurationTemplate(resourceRegistryClient, type, subType);
				break;
			case "GenericResource":
				logger.info("---------------->Configuration");
				resources = UtilityResource.createListConfiguration(resourceRegistryClient, type, subType);
				break;
			case "Dataset":
				logger.info("---------------->Dataset");
				resources = UtilityResource.createListDataset(resourceRegistryClient, type, subType);
				break;
			case "ConcreteDataset":
				logger.info("---------------->ConcreteDataset");
				resources = UtilityResource.createListConcreteDataset(resourceRegistryClient, type, subType);
				break;
			case "Schema":
				logger.info("---------------->Schema");
				resources = UtilityResource.createListSchema(resourceRegistryClient, type, subType);
				break;
			case "RunningInstance":
				logger.info("---------------->EService");
				resources = UtilityResource.createListEservice(resourceRegistryClient, type, subType);
				break;
			case "RunningPlugin":
				logger.info("---------------->RunningPlugin");
				resources = UtilityResource.createListRunningPlugin(resourceRegistryClient, type, subType);
				break;
			case "GHN":
				logger.info("---------------->HostingNode");
				resources = UtilityResource.createListHostingNode(resourceRegistryClient, type, subType);
				break;
			case "VirtualService":
				logger.info("---------------->VirtualService");
				resources = UtilityResource.createListVirtualService(resourceRegistryClient, type, subType);
				break;
			case "Site":
				logger.info("---------------->Site");
				resources = UtilityResource.createListSite(resourceRegistryClient, type, subType);
				break;
			case "Software":
				logger.info("---------------->Software");
				resources = UtilityResource.createListSoftware(resourceRegistryClient, type, subType);
				break;
			case "Plugin":
				logger.info("---------------->Plugin");
				resources = UtilityResource.createListPlugin(resourceRegistryClient, type, subType);
				break;
			default:
				break;
			}

			return resources;
		} finally {
			AccessTokenProvider.instance.reset();
			ScopeProvider.instance.reset();
		}

	}

	public static CompleteResourceProfile getResourceByID(String xml2htmlMapping, String scope, String type,
			String resID) {
		logger.info("GetResourceByID: [Scope: {}]", scope);
		String representation = CacheList.resourceid.get(resID).getBody();
		// get resource by id
		String title = CacheList.resourceid.get(resID).getTitle();
		String xmlRepresentation = "<Resource>" + "<ID>" + resID + "</ID>" + "<Type>" + type + "</Type>" + "<Scope>"
				+ scope + "</Scope>" + representation + "</Resource>";
		String htmlRepresentation = representation;
		ScopeProvider.instance.set(scope);
		return new CompleteResourceProfile(resID, ResourceTypeDecorator.valueOf(type), title, xmlRepresentation,
				htmlRepresentation);
	}

}
