package eu.dnetlib.dli.resolver.model;

import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import eu.dnetlib.dli.DLIUtils;
import eu.dnetlib.pid.resolver.model.*;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * The Class DLIObject.
 */
public class DLIResolvedObject extends AbstractResolvedObject {
	private static final Log log = LogFactory.getLog(DLIResolvedObject.class);

	/**
	 * The titles.
	 */
	private List<String> titles = new ArrayList<>();

	/**
	 * The authors.
	 */
	private List<String> authors = new ArrayList<>();

	/**
	 * The type.
	 */
	private ObjectType type;

	/**
	 * Description of the resolved object
	 */
	private String description;


	private List<DLIObjectProvenance> datasourceProvenanceDLI;


	/**
	 * The completion status.
	 */
	private String completionStatus;

	/**
	 * The date.
	 */
	private String date;

	/**
	 * A list of subjects of type scheme term
	 */
	private List<SubjectType> subjects;

	/**
	 * The related objects.
	 */
	private List<ObjectRelation> relations;


    /**
     * The Date where the record was resolved
     */
    private String resolvedDate;


    private OAFHostedBy hostedBy;


	/**
	 * Instantiates a new DLI object.
	 */

	private static final DLIResolvedObjectDeserializer deserializer = new DLIResolvedObjectDeserializer();

	public static DLIResolvedObject fromJson(final String json) {
		try {
			return new GsonBuilder().registerTypeAdapter(DLIResolvedObject.class,deserializer ).create().fromJson(json, DLIResolvedObject.class);
		} catch (Throwable e) {
			log.error("ERROR on parsing "+json);
			return null;
		}
	}

	public DLIResolvedObject() {

	}

	/**
	 * Instantiates a new DLI object.
	 *
	 * @param pid                  the pid
	 * @param pidType              the pid type
	 * @param titles               the titles
	 * @param authors              the authors
	 * @param type                 the type
	 * @param datasourceProvenance the datasource provenance
	 * @param relations            the relations
	 * @param status               the status
	 */
	public DLIResolvedObject(final String pid, final String pidType, final List<String> titles, final List<String> authors, final ObjectType type,
							 final List<ObjectProvenance> datasourceProvenance, final List<ObjectRelation> relations, final String status) {
		super();
		if (pid != null) {
			this.pid = pid.replace("http://dx.doi.org/", "").replace("http://doi.org/", "").toLowerCase();
		}
		this.pidType = pidType;
		this.titles = titles;
		this.authors = authors;
		this.type = type;
		this.setDatasourceProvenance(datasourceProvenance);
		this.relations = relations;
		this.setCompletionStatus(status);
	}

	/**
	 * Escaped array.
	 *
	 * @param inputArray the input array
	 * @return the string[]
	 */
	public static List<String> escapedArray(final List<String> inputArray) {
		if (inputArray == null || inputArray.size() == 0) return null;
		return inputArray.stream().map(input -> input.replaceAll("&", "&amp;")
				.replaceAll("\"", "&quot;")
				.replaceAll("'", "&apos;")
				.replaceAll("<", "&lt;")
				.replaceAll(">", "&gt;"))
				.collect(Collectors.toList());
	}

	/**
	 * Gets the doi.
	 *
	 * @return the doi
	 */
	public String getPid() {
		if (pid != null)
			return pid.toLowerCase();
		return pid;
	}

	/**
	 * Sets the doi.
	 *
	 * @param pid the new pid
	 */
	@Override
	public DLIResolvedObject setPid(final String pid) {
		if (pid != null) {
			this.pid = pid.replace("http://dx.doi.org/", "").replace("http://doi.org/", "");
		}
		return this;
	}

	/**
	 * Check not null fields.
	 *
	 * @return the string
	 */
	private String checkNotNullFields() {

		final List<String> notNUllFields = Lists.newArrayList();
		if (!StringUtils.isEmpty(pid)) {
			notNUllFields.add("pid");
		}
		if (!StringUtils.isEmpty(pidType)) {
			notNUllFields.add("pidType");
		}
		if (titles == null || titles.size() == 0) {
			notNUllFields.add("titles");
		}
		if (authors == null || authors.size() == 0) {
			notNUllFields.add("authors");
		}
		if (!StringUtils.isEmpty(date)) {
			notNUllFields.add("date");
		}
		return new Gson().toJson(notNUllFields);
	}

	/**
	 * Fix contribution.
	 *
	 * @param provenance the provenance
	 */
	public void fixContribution(final DLIObjectProvenance provenance) {
		provenance.setDatasourceContribution(checkNotNullFields());
	}

	public String getResolvedPIDUrl() {
		if (pidType == null || StringUtils.isBlank(pidType) || pid == null || StringUtils.isBlank(pid))
			return "#";

		if (DLIUtils.resolvedTypes.containsKey(pidType.toLowerCase()))
			return String.format(DLIUtils.resolvedTypes.get(pidType.toLowerCase()), getEscapedXMLPid());

		return "#";
	}

	/**
	 * Gets the escaped xml pid.
	 *
	 * @return the escaped xml pid
	 */
	public String getEscapedXMLPid() {
		return StringEscapeUtils.escapeXml11(pid);
	}

	/**
	 * Gets the titles.
	 *
	 * @return the titles
	 */
	public List<String> getTitles() {
		return titles;
	}

	/**
	 * Sets the titles.
	 *
	 * @param titles the new titles
	 */
	public void setTitles(final List<String> titles) {
		this.titles = titles;
	}

	/**
	 * Gets the escaped xml titles.
	 *
	 * @return the escaped xml titles
	 */
	public List<String> getEscapedXMLTitles() {

		return escapedArray(titles);
	}

	/**
	 * Gets the authors.
	 *
	 * @return the authors
	 */
	public List<String> getAuthors() {
		return authors;
	}

	/**
	 * Sets the authors.
	 *
	 * @param authors the new authors
	 */
	public void setAuthors(final List<String> authors) {
		this.authors = authors;
	}

	/**
	 * Gets the escaped xml authors.
	 *
	 * @return the escaped xml authors
	 */
	public List<String> getEscapedXMLAuthors() {
		return escapedArray(authors);
	}

	/**
	 * get All the subjects
	 *
	 * @return a list of Subjects
	 */
	public List<SubjectType> getSubjects() {
		return subjects;
	}

	/**
	 * Set all the subjects
	 *
	 * @param subjects
	 */
	@Override
	public ResolvedObject setSubjects(final List<SubjectType> subjects) {
		this.subjects = subjects;
		return this;
	}

	/**
	 * Gets the type.
	 *
	 * @return the type
	 */
	public ObjectType getType() {
		return type == null ? ObjectType.unknown : type;
	}

	/**
	 * Sets the type.
	 *
	 * @param type the new type
	 */
	public DLIResolvedObject setType(final ObjectType type) {
		this.type = type;
		return this;
	}

	/**
	 * Gets the escaped xml type.
	 *
	 * @return the escaped xml type
	 */
	public String getEscapedXMLType() {
		if (type != null)
			return type.toString();
		else return null;
	}


	public OAFHostedBy getHostedBy() {
		return hostedBy;
	}

	public void setHostedBy(OAFHostedBy hostedBy) {
		this.hostedBy = hostedBy;
	}

	/**
	 * Gets the relations.
	 *
	 * @return the relations
	 */
	public List<ObjectRelation> getRelations() {
		return relations;
	}

	/**
	 * Sets the relations.
	 *
	 * @param relations the new relations
	 */
	public void setRelations(final List<ObjectRelation> relations) {
		this.relations = relations;
	}

	/**
	 * Gets the date.
	 *
	 * @return the date
	 */
	public String getDate() {
		return date;
	}

	/**
	 * Sets the date.
	 *
	 * @param date the date to set
	 */
	public void setDate(final String date) {
		this.date = date;
	}

	/**
	 * Gets the pid type.
	 *
	 * @return the pidType
	 */
	@Override
	public String getPidType() {
		return pidType;
	}

	/**
	 * Sets the pid type.
	 *
	 * @param pidType the pidType to set
	 */
	@Override
	public DLIResolvedObject setPidType(final String pidType) {
		this.pidType = pidType;
		return this;
	}

	/**
	 * Gets the completion status.
	 *
	 * @return the completion status
	 */
	public String getCompletionStatus() {
		return this.completionStatus;
	}

	/**
	 * Sets the completion status.
	 *
	 * @param completionStatus the new completion status
	 */
	public void setCompletionStatus(final String completionStatus) {
		this.completionStatus = completionStatus;
	}


	/**
	 * {@inheritDoc}
	 *
	 * @see Object#toString()
	 */
	@Override
    public String toString() {
		return new GsonBuilder().setPrettyPrinting().create().toJson(this);
	}

    public String getResolvedDate() {
        return resolvedDate;
    }

    public void setResolvedDate(String resolvedDate) {
        this.resolvedDate = resolvedDate;
    }

	public String getDescription() {
		return description;
	}

	public void setDescription(final String description) {
		if(description!= null && description.length()>1024)
			this.description = description.substring(0,1024) + "...";
		else
			this.description = description;
	}


	public String getEscapedDescription() {
		return StringEscapeUtils.escapeXml11(description);
	}

//	public List<DLIObjectProvenance> getDatasourceProvenanceDLI() {
//		return datasourceProvenance.stream().map(it-> (DLIObjectProvenance) it).collect(Collectors.toList());
//	}

	public List<DLIObjectProvenance> getDliDatasourceProvenance() {
		return datasourceProvenance.stream().map(it-> (DLIObjectProvenance) it).collect(Collectors.toList());
	}


}
