package org.gcube.application.geoportal.common.model.useCaseDescriptor;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.vdurmont.semver4j.Semver;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.gcube.application.geoportal.common.model.document.accounting.AccountingInfo;
import org.gcube.application.geoportal.common.model.document.accounting.User;

import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Data
@NoArgsConstructor
@XmlRootElement
@Slf4j
public class UseCaseDescriptor implements Serializable {

	public static final String MONGO_ID="_mongoId";
	public static final String ID="_id";
	public static final String VERSION="_version";
	public static final String NAME="_name";
	public static final String DESCRIPTION="_description";
	public static final String CREATION_INFO="_creationInfo";
	public static final String SCHEMA="_schema";
	public static final String HANDLERS="_handlers";
	public static final String DATA_ACCESS_POLICIES="_dataAccessPolicies";


	@JsonProperty(MONGO_ID)
	private ObjectId mongoId;

	@JsonProperty(ID)
	private String id;
	@JsonProperty(VERSION)
	private Semver version;

	@JsonProperty(NAME)
	private String name;
	@JsonProperty(DESCRIPTION)
	private String description;
	@JsonProperty(CREATION_INFO)
	private AccountingInfo creationInfo;

	@JsonProperty(SCHEMA)
	private Field schema;

	@JsonProperty(HANDLERS)
	private List<HandlerDeclaration> handlers;


	@JsonProperty(DATA_ACCESS_POLICIES)
	private List<DataAccessPolicy> dataAccessPolicies;

	/**
	 * Returns map Type -> Handler Declaration
	 * @return
	 */
	@JsonIgnore
	public Map<String,List<HandlerDeclaration>> getHandlersMapByType(){
		HashMap<String,List<HandlerDeclaration>> toReturn=new HashMap<>();
		handlers.forEach(h->{
			try {
				if(!toReturn.containsKey(h.getType()))
					toReturn.put(h.getType(),new ArrayList<>());
				toReturn.get(h.getType()).add(h);
			}catch (Throwable t){
				log.error("Invalid useCaseDescriptor : unable to get Handler Map by Type with {} in useCaseDescriptor [ID {}]",h,this.getId(),t);}
		});
		if(log.isTraceEnabled()) {
			toReturn.forEach((s, handlerDeclarations) -> log.trace("UCD {} : Found N {} handlers of type {}",this.getId(),handlerDeclarations.size(),s));
		}
		return toReturn;
	}

	/**
	 * Returns List of Handler Declaration by type
	 *
	 * @param type
	 * @return
	 */
	@JsonIgnore
	public List<HandlerDeclaration> getHandlersByType(String type){
		List<HandlerDeclaration> toReturn=
		handlers.stream().sequential().
				filter(handlerDeclaration -> handlerDeclaration.getType().equals(type)).collect(Collectors.toList());
		log.debug("UCD {} : Found {} Handlers for {}",getId(),toReturn.size(),type);
		return toReturn;
	}

	/**
	 * Returns map ID -> Handler Declaration
	 * @return
	 */
	@JsonIgnore
	public Map<String,List<HandlerDeclaration>> getHandlersMapByID(){
		HashMap<String,List<HandlerDeclaration>> toReturn=new HashMap<>();
		handlers.forEach(h->{
			try {
				if (!toReturn.containsKey(h.getId()))
					toReturn.put(h.getId(), new ArrayList<>());
				toReturn.get(h.getId()).add(h);
			}catch (Throwable t){
				log.error("Invalid useCaseDescriptor : unable to get Handler Map by ID with {} in useCaseDescriptor [ID {}]",h,this.getId(),t);}
		});
		return toReturn;
	}

	@JsonIgnore
	public DataAccessPolicy getMatching(User u){
		DataAccessPolicy defaultPolicy = null;
		if(dataAccessPolicies!=null)
			for (DataAccessPolicy dataAccessPolicy : dataAccessPolicies) {
				if(dataAccessPolicy.getRoles()==null||dataAccessPolicy.getRoles().isEmpty())
					defaultPolicy= dataAccessPolicy;
				for (String r : dataAccessPolicy.getRoles())
					if (u.getRoles().contains(r))
						return dataAccessPolicy;
			}
		return defaultPolicy;
	}



}
