package eu.dnetlib.goldoa.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import eu.dnetlib.goldoa.domain.*;
import eu.dnetlib.goldoa.service.dao.PublicationDAO;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.rmi.runtime.Log;

import java.io.InputStream;
import java.math.BigInteger;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.TimeZone;

//import eu.dnetlib.goldoa.domain.BankAccount;


/**
 * Created by antleb on 3/13/15.
 */
@Transactional
@Service("publicationManager")
public class PublicationManagerImpl implements PublicationManager {

	private static Logger logger = Logger.getLogger(PublicationManagerImpl.class);

	@Autowired
	private PublicationDAO publicationDAO;
	@Autowired
	private JournalManager journalManager;
	@Autowired
	private PublisherManager publisherManager;
	@Autowired
	private UserManager personManager;
	@Autowired
	private OrganizationManager organizationManager;

	@Override
	public Publication getPublication(String publicationId) {
	    return publicationDAO.getPublication(publicationId);
	}
	
	@Override
	public Publication savePublication(Publication publication) {

		if (publication.getId() == null) {
			publication.setSource("portal");
			publication.setId("portal::" + DigestUtils.md5Hex(publication.getTitle()));
		}

		if(publication.getIdentifiers() != null){
			for(Identifier id : publication.getIdentifiers())
				if(id.getId() == null)
					id.setId(this.generateId());
		}

		if(publication.getJournal()!=null)
			journalManager.saveJournal(publication.getJournal());

		return publicationDAO.savePublication(publication);
	}

	@Override
	public Publication resolveDOI(String doi) throws ManagerException {
		Publication publication = null;
		try {
			publication = resolveCrossrefDoi(doi);
		} catch (ManagerException e) {
			e.printStackTrace();
			return null;
		}

		/*if(publication == null) {
			try {
				return resolveDataCiteDoi(doi);
			} catch (ManagerException e) {
				e.printStackTrace();
			}
		}

		if(publication == null)
			throw new ManagerException(ManagerException.ErrorCause.UNKNOWN);*/

		return  publication;
	}

	private Publication resolveDataCiteDoi(String doi) throws ManagerException {
		Publication publication = null;
		InputStream is = null;
		try {

			System.out.println("https://api.datacite.org/works/" + doi);
			is = new URL("https://api.datacite.org/works/" + doi).openStream();
			ObjectMapper mapper = new ObjectMapper();
			mapper.enable(SerializationFeature.INDENT_OUTPUT);
			JsonNode root = mapper.readTree(is);

			System.out.println(mapper.writeValueAsString(root));
			System.out.println(mapper.writeValueAsString(root.get("data").get("attributes").get("title")));
			if(root.get("errors") == null){
				publication = new Publication();
				publication.setTitle(root.get("data").get("attributes").get("title").textValue());
				publication.setDoi(doi);
				//publication.setSubjects(root.get("data").get("attributes").);
				publication.setSource("datacite");
				publication.setId("datacite::" + DigestUtils.md5Hex(doi));
			}

		} catch (Exception e) {
			logger.error("Error resolving datacite doi", e);
			throw new ManagerException(ManagerException.ErrorCause.UNKNOWN);
		} finally {
			try {
				if (is != null)
					is.close();
			} catch (Exception e) {
			}
		}
		return publication;
	}

	private Publication resolveCrossrefDoi(String doi) throws ManagerException {
		Publication publication = null;
		InputStream is = null;
		try {
			is = new URL("http://api.crossref.org/works/" + doi).openStream();
			ObjectMapper mapper = new ObjectMapper();
			JsonNode root = mapper.readTree(is);

			if (root.get("status").textValue().equals("ok")) {
				publication = new Publication();
				publication.setTitle(root.path("message").path("title").path(0).textValue());
				publication.setDoi(doi);
				publication.setSubjects(root.path("message").path("subject").textValue());
				publication.setSource("crossref");
				publication.setId("crossref::" + DigestUtils.md5Hex(doi));

				if ("journal-article".equals(root.path("message").path("type").textValue()))
					publication.setType(PublicationType.ARTICLE);
				else if("book-chapter".equals(root.path("message").path("type").textValue()))
					publication.setType(PublicationType.BOOK_CHAPTER);
				else
					publication.setType(PublicationType.MONOGRAPH);

				publication.setDate(this.parsePublicationDate(root.path("message").path("issued")));

				if(publication.getType() == PublicationType.ARTICLE) {
					publication.setJournal(this.parseJournal(root.path("message").path("container-title"), root.path("message").path("ISSN")));
					publication.getJournal().setPublisher(this.parsePublisher(root.path("message").path("publisher")));
				}else
					publication.setPublisher(this.parsePublisher(root.path("message").path("publisher")));

				for (JsonNode author : root.path("message").path("author"))
					publication.getAuthors().add(this.parseAffiliation(author,publication));

				publication.setLicense(root.path("message").path("license").path(0).path("URL").textValue());
				publication.getIdentifiers().add(new Identifier("doi", doi));

				logger.debug(publication.getType());
			} else {
				throw new ManagerException(ManagerException.ErrorCause.NOT_EXISTS);
			}
		} catch (ManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new ManagerException(ManagerException.ErrorCause.UNKNOWN);
		} finally {
			try {
				if (is != null)
					is.close();
			} catch (Exception e) {
			}
		}

		return publication;
	}

	private Author parseAffiliation(JsonNode author,Publication publication) {
		Author a = new Author();

		a.setFirstname(author.path("given").textValue());
		a.setLastname(author.path("family").textValue());
		a.setId(DigestUtils.md5Hex(a.getFirstname() + a.getEmail()) + publication.getId());
		if(a.getFirstname() != null)
			return a;
		return null;
	}

	private Publisher parsePublisher(JsonNode publisherName) {

		Publisher publisher = publisherManager.getPublisherByName(publisherName.textValue());
		if(publisher == null){
			logger.debug("Publisher not found!");
			publisher = new Publisher();

			publisher.setName(publisherName.textValue());

			String value = publisherManager.getBankAddress().toString();
			BigInteger id = publisherManager.getBankId();
			publisher.setBankAccount(new BankAccount(id,null,value,
					null,null,null));

			publisher.setId("crossref::" + DigestUtils.md5Hex(publisher.getName()));
			publisher.setSource("crossref");
		}
		return publisher;
	}

	private Journal parseJournal(JsonNode name, JsonNode issn) {

		Journal journal = journalManager.getJournalByTitle(name.path(0).textValue());
		if (journal == null) {
			journal = new Journal();

			journal.setTitle(name.path(0).textValue());
			journal.setAlternativeTitle(name.path(1).textValue());

			StringBuilder sb = new StringBuilder();
			Iterator<JsonNode> ssn = issn.elements();
			while (ssn.hasNext()) {
				sb.append(ssn.next().textValue()).append(",");
			}

			if (sb.length() > 0)
				sb.deleteCharAt(sb.length() - 1);

			journal.setIssn(sb.toString());

			journal.setApccurrency(Currency.EUR);
			journal.setSource("crossref");
			journal.setId("crossref::" + DigestUtils.md5Hex(journal.getTitle()));
		}

		return journal;
	}

	private Date parsePublicationDate(JsonNode node) {
		Calendar c = Calendar.getInstance();

		c.clear();
		c.setTimeZone(TimeZone.getTimeZone("UTC"));

		c.set(Calendar.YEAR, node.path("date-parts").path(0).path(0).intValue());
		c.set(Calendar.MONTH, node.path("date-parts").path(0).path(1).intValue() - 1);
		c.set(Calendar.DAY_OF_MONTH, 1 + node.path("date-parts").path(0).path(2).intValue());
		c.set(Calendar.HOUR_OF_DAY, 0);


		return c.getTime();
	}

	private BigInteger generateId(){
		return publicationDAO.generateIdentifierID();
	}

}