package eu.dnetlib.dli.resolver;

import java.io.ByteArrayOutputStream;
import java.net.URLEncoder;
import java.util.zip.Inflater;

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.QueryBuilder;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import eu.dnetlib.dli.resolver.model.DLIResolvedObject;
import eu.dnetlib.pid.resolver.AbstractPIDResolver;
import groovy.transform.Synchronized;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;

// TODO: Auto-generated Javadoc

/**
 * The Class CrossrefDOIResolver.
 */
public class CrossrefResolver extends AbstractPIDResolver {

	/**
	 * The Constant log.
	 */
	private static final Log log = LogFactory.getLog(CrossrefResolver.class);

	/**
	 * The Constant baseUrlCrossref.
	 */
	private final static String baseUrlCrossref = "http://api.crossref.org/works/";


	/**
	 * The parser.
	 */
	@Autowired
	private CrossRefParserJSON parser;

	@Autowired
	private MongoClient mongoClient;

	private MongoDatabase db;

	private String dumpType;


	private static String decompressBlob(final String blob) {
		try {
			byte[] byteArray = Base64.decodeBase64(blob.getBytes());
			final Inflater decompresser = new Inflater();
			decompresser.setInput(byteArray);
			final ByteArrayOutputStream bos = new ByteArrayOutputStream(byteArray.length);
			byte[] buffer = new byte[8192];
			while (!decompresser.finished()) {
				int size = decompresser.inflate(buffer);
				bos.write(buffer, 0, size);
			}
			byte[] unzippeddata = bos.toByteArray();
			decompresser.end();
			return new String(unzippeddata);
		} catch (Throwable e) {
			throw new RuntimeException("Wrong record:" + blob,e);
		}
	}


	@Override
	protected boolean canResolvePid(final String pidType) {
		return (pidType != null) && ("doi".equals(pidType.toLowerCase().trim()) || "handle".equals(pidType.toLowerCase().trim()));
	}

	@Override
	@Synchronized
    public DLIResolvedObject resolve(final String pid, final String pidType) {
        String response;
		try {
//			response = requestURL(baseUrlCrossref + URLEncoder.encode(pid, "UTF-8"));
			response = retrieveCrossRefFromDump(pid);
		} catch (Exception e) {
			log.debug("unable to get response");
			return null;
		}
		try {

			log.debug("Obtained " + response);
			if (response == null) return null;
			if (response.contains("Resource not found")) return null;


			if (response.contains("blob")) {
				JsonParser p = new JsonParser();
				final JsonElement root = p.parse(response);
				response =decompressBlob(root.getAsJsonObject().get("_source").getAsJsonObject().get("blob").getAsString());
			}
            DLIResolvedObject record = parser.parseRecord(response);
            if (record.getPid() == null) return null;
			return record;
		} catch (Throwable e) {
			log.error("¯\\_(ツ)_/¯ Error on resolve pid  " + pid, e);
		}
		return null;
	}


	private String retrieveCrossRefFromDump(final String pid) {
		if (dumpType.equalsIgnoreCase("mongo")){
			return retrieveCrossRefFromDumpMongo(pid);
		}
		else if (dumpType.equalsIgnoreCase("ES")){
			return retrieveCrossRefFromDumpES(pid);
		}
		throw new  RuntimeException("incorrect dump Type expected [mongo, ES] found: "+dumpType );

	}

	private String retrieveCrossRefFromDumpES(final String pid) {
		return  requestURL("http://ip-90-147-167-25.ct1.garrservices.it:9200/crossref/item/" + pid.replaceAll("/","%2F"));
	}


	private String retrieveCrossRefFromDumpMongo(final String pid) {
		if (db == null) {
			db = mongoClient.getDatabase("crossRef");

		}
		final MongoCollection<Document> crossRef = db.getCollection("dump");

		DBObject query = QueryBuilder.start("_id").is(pid).get();
		FindIterable<Document> documents = crossRef.find((Bson) query).limit(1);
		MongoCursor<Document> iterator = documents.iterator();
		if (iterator.hasNext()){
			return iterator.next().toJson();
		}
		return null;
	}

	public String getDumpType() {
		return dumpType;
	}

	@Required
	public void setDumpType(String dumpType) {
		this.dumpType = dumpType;
	}

	public CrossRefParserJSON getParser() {
		return parser;
	}

	public void setParser(CrossRefParserJSON parser) {
		this.parser = parser;
	}
}
