package org.springframework.multitenancy.mongo.mapping;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.bson.Document;
import org.json.JSONException;
import org.skyscreamer.jsonassert.JSONCompare;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.skyscreamer.jsonassert.JSONCompareResult;
import org.springframework.multitenancy.mongo.mapping.annotation.CompoundIndex;
import org.springframework.multitenancy.mongo.mapping.annotation.CompoundIndexes;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.IndexOptions;

/**
 * Class responsible for managing the creation of indexes
 * 
 * @author WRP
 * */
public class MongoPersistentEntityIndexCreator implements MongoPersistentEntityIndex {
	
	private final Logger logger = Logger.getLogger(MongoPersistentEntityIndexCreator.class);
	
	@Override
	public <T> List<MongoIndexChange> getChanges(MongoCollection<Document> collection, Class<T> type) {
		
		List<MongoIndexChange> changes = new ArrayList<>();
		
		if(type.isAnnotationPresent(CompoundIndexes.class)){
			
			Map<String, String> currentIndex = getCurrentIndex(collection);
			
			CompoundIndexes indexes = type.getAnnotation(CompoundIndexes.class);
			
			for (CompoundIndex index : indexes.value()) {
				
				String currentDefinition = currentIndex.get(index.name());
				
				if(currentDefinition == null){
					changes.add(createChange(index, false));
				}else if(isDifferenceInDefinition(currentDefinition, index.def())){
					changes.add(createChange(index, true));
				}
			}
		}
		
		return changes;
	}
	
	private MongoIndexChange createChange (CompoundIndex index, boolean replace){
		
		Document definition = Document.parse(index.def());
		
		IndexOptions opts = new IndexOptions();
		opts.name(index.name());
		opts.unique(index.unique());
		opts.sparse(index.sparse());
		opts.background(index.background());
		
		return new MongoIndexChange(index.name(), definition, opts, replace);
	}

	private Map<String, String> getCurrentIndex(MongoCollection<Document> collection){
		
		Map<String, String> result = new  HashMap<>();
		
		MongoCursor<Document> indexes = collection.listIndexes().iterator();
		
		while(indexes.hasNext()){
			Document index = indexes.next();
			Document definition = (Document)index.get("key");
			result.put(index.getString("name"), definition.toJson());
		}
		
		return result;
	}
	
	
	private boolean isDifferenceInDefinition(String currentIndex, String definition){
			
		try {
			JSONCompareResult result = JSONCompare.compareJSON(definition, currentIndex, JSONCompareMode.STRICT);
			return result.passed();
			
		} catch (JSONException ex){
			logger.error("Failed while checking indexes", ex);
		}
		
		return false;
	}
}