package eu.eudat.elastic.entities;

import org.elasticsearch.common.xcontent.XContentBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

public class Dmp implements ElasticEntity<Dmp> {
	private static final Logger logger = LoggerFactory.getLogger(Dmp.class);

	public enum DMPStatus {
		ACTIVE((short) 0), FINALISED((short) 1),DELETED((short) 99);

		private short value;

		private DMPStatus(short value) {
			this.value = value;
		}

		public short getValue() {
			return value;
		}

		public static DMPStatus fromInteger(short value) {
			switch (value) {
				case 0:
					return ACTIVE;
				case 1:
					return FINALISED;
				case 99:
					return DELETED;
				default:
					throw new RuntimeException("Unsupported DMP Status");
			}
		}
	}

	private UUID id;
	private String label;
	private String description;
	private UUID groupId;
	private Short status;
	private List<DatasetTempalate> templates;
	private List<Collaborator> collaborators;
	private List<Organization> organizations;
	private Boolean lastVersion;
	private Boolean lastPublicVersion;
	private Boolean isPublic;
	private List<Dataset> datasets;
	private UUID grant;
	private Short grantStatus;
	private Date created;
	private Date modified;
	private Date finalizedAt;
	private Date publishedAt;
	private String doi;

	public UUID getId() {
		return id;
	}

	public void setId(UUID id) {
		this.id = id;
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public UUID getGroupId() {
		return groupId;
	}

	public void setGroupId(UUID groupId) {
		this.groupId = groupId;
	}

	public Short getStatus() {
		return status;
	}

	public void setStatus(Short status) {
		this.status = status;
	}

	public List<DatasetTempalate> getTemplates() {
		return templates;
	}

	public void setTemplates(List<DatasetTempalate> templates) {
		this.templates = templates;
	}

	public List<Collaborator> getCollaborators() {
		return collaborators;
	}

	public void setCollaborators(List<Collaborator> collaborators) {
		this.collaborators = collaborators;
	}

	public List<Organization> getOrganizations() {
		return organizations;
	}

	public void setOrganizations(List<Organization> organizations) {
		this.organizations = organizations;
	}

	public Boolean getLastVersion() {
		return lastVersion;
	}

	public void setLastVersion(Boolean lastVersion) {
		this.lastVersion = lastVersion;
	}

	public Boolean getLastPublicVersion() {
		return lastPublicVersion;
	}

	public void setLastPublicVersion(Boolean lastPublicVersion) {
		this.lastPublicVersion = lastPublicVersion;
	}

	public Boolean getPublic() {
		return isPublic;
	}

	public void setPublic(Boolean aPublic) {
		isPublic = aPublic;
	}

	public List<Dataset> getDatasets() {
		return datasets;
	}

	public void setDatasets(List<Dataset> datasets) {
		this.datasets = datasets;
	}

	public UUID getGrant() {
		return grant;
	}

	public void setGrant(UUID grant) {
		this.grant = grant;
	}

	public Short getGrantStatus() {
		return grantStatus;
	}

	public void setGrantStatus(Short grantStatus) {
		this.grantStatus = grantStatus;
	}

	public Date getCreated() {
		return created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	public Date getModified() {
		return modified;
	}

	public void setModified(Date modified) {
		this.modified = modified;
	}

	public Date getFinalizedAt() {
		return finalizedAt;
	}

	public void setFinalizedAt(Date finalizedAt) {
		this.finalizedAt = finalizedAt;
	}

	public Date getPublishedAt() {
		return publishedAt;
	}

	public void setPublishedAt(Date publishedAt) {
		this.publishedAt = publishedAt;
	}

	public String getDoi() {
		return doi;
	}

	public void setDoi(String doi) {
		this.doi = doi;
	}

	@Override
	public XContentBuilder toElasticEntity(XContentBuilder builder) throws IOException {
		builder.startObject();
		if (this.id != null) {
			builder.field(MapKey.ID.getName(), this.id.toString());
		}
		builder.field(MapKey.LABEL.getName(), this.label);
		builder.field(MapKey.DESCRIPTION.getName(), this.description);
		if (this.groupId != null) {
			builder.field(MapKey.GROUPID.getName(), this.groupId.toString());
		}
		builder.field(MapKey.STATUS.getName(), this.status);
		if (this.templates != null && !this.templates.isEmpty()) {
			builder.startArray(MapKey.TEMPLATES.getName());
			this.templates.forEach(template -> {
				try {
					template.toElasticEntity(builder);
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
			});
			builder.endArray();
		}
		if (this.collaborators != null && !this.collaborators.isEmpty()) {
			builder.startArray(MapKey.COLLABORATORS.getName());
			this.collaborators.forEach(collaborator -> {
				try {
					collaborator.toElasticEntity(builder);
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
			});
			builder.endArray();
		}
		if (this.organizations != null && !this.organizations.isEmpty()) {
			builder.startArray(MapKey.ORGANIZATIONS.getName());
			this.organizations.forEach(organization -> {
				try {
					organization.toElasticEntity(builder);
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
			});
			builder.endArray();
		}
		builder.field(MapKey.LASTVERSION.getName(), this.lastVersion);
		builder.field(MapKey.LASTPUBLICVERSION.getName(), this.lastPublicVersion);
		builder.field(MapKey.ISPUBLIC.getName(), this.isPublic);
		if (datasets != null && !this.datasets.isEmpty()) {
			builder.startArray(MapKey.DATASETS.getName());
			this.datasets.forEach(dataset -> {
				try {
					if (dataset != null) {
						dataset.toElasticEntity(builder);
					}
				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				}
			});
			builder.endArray();
		}
		if (this.grant != null) {
			builder.field(MapKey.GRANT.getName(), this.grant.toString());
		}
		builder.field(MapKey.GRANTSTATUS.getName(), this.grantStatus);
		builder.field(MapKey.CREATED.getName(), this.created);
		builder.field(MapKey.MODIFIED.getName(), this.modified);
		builder.field(MapKey.FINALIZEDAT.getName(), this.finalizedAt);
		builder.field(MapKey.PUBLISHEDAT.getName(), this.publishedAt);
		builder.field(MapKey.DOI.getName(), this.doi);
		builder.endObject();
		return builder;
	}

	@Override
	public Dmp fromElasticEntity(Map<String, Object> fields) {
		if (fields == null || fields.isEmpty()) {
			return null;
		}
		this.id = UUID.fromString((String) fields.get(MapKey.ID.getName()));
		if (fields.size() > 1) {
			this.label = (String) fields.get(MapKey.LABEL.getName());
			this.description = (String) fields.get(MapKey.DESCRIPTION.getName());
			if (fields.get(MapKey.GROUPID.getName()) != null) {
				this.groupId = UUID.fromString((String) fields.get(MapKey.GROUPID.getName()));
			}
			this.status = Short.valueOf(fields.get(MapKey.STATUS.getName()).toString());
			if (fields.get(MapKey.TEMPLATES.getName()) != null) {
				this.templates = ((List<HashMap<String, Object>>) fields.get(MapKey.TEMPLATES.getName())).stream().map(hashMap -> new DatasetTempalate().fromElasticEntity(hashMap)).collect(Collectors.toList());
			}
			if (fields.get(MapKey.COLLABORATORS.getName()) != null) {
				this.collaborators = ((List<HashMap<String, Object>>) fields.get(MapKey.COLLABORATORS.getName())).stream().map(map -> new Collaborator().fromElasticEntity(map)).collect(Collectors.toList());
			}
			if (fields.get(MapKey.ORGANIZATIONS.getName()) != null) {
				this.organizations = ((List<HashMap<String, Object>>) fields.get(MapKey.ORGANIZATIONS.getName())).stream().map(map -> new Organization().fromElasticEntity(map)).collect(Collectors.toList());
			}
			this.lastVersion = (Boolean) fields.get(MapKey.LASTVERSION.getName());
			this.lastPublicVersion = (Boolean) fields.get(MapKey.LASTPUBLICVERSION.getName());
			this.isPublic = (Boolean) fields.get(MapKey.ISPUBLIC.getName());
			if (fields.get(MapKey.DATASETS.getName()) != null) {
				this.datasets = ((List<HashMap<String, Object>>) fields.get(MapKey.DATASETS.getName())).stream().map(map -> new Dataset().fromElasticEntity(map)).collect(Collectors.toList());
			}
			this.grant = UUID.fromString((String) fields.get(MapKey.GRANT.getName()));
			if (fields.get(MapKey.GRANTSTATUS.getName()) != null) {
				this.grantStatus = Short.valueOf(fields.get(MapKey.GRANTSTATUS.getName()).toString());
			}
			if (fields.containsKey(MapKey.CREATED.getName())) {
				this.created = Date.from(Instant.parse(fields.get(MapKey.CREATED.getName()).toString()));
			}
			if (fields.containsKey(MapKey.MODIFIED.getName())) {
				this.modified = Date.from(Instant.parse(fields.get(MapKey.MODIFIED.getName()).toString()));
			}
			if (fields.get(MapKey.FINALIZEDAT.getName()) != null) {
				this.finalizedAt = Date.from(Instant.parse(fields.get(MapKey.FINALIZEDAT.getName()).toString()));
			}
			if (fields.get(MapKey.PUBLISHEDAT.getName()) != null) {
				this.publishedAt = Date.from(Instant.parse(fields.get(MapKey.PUBLISHEDAT.getName()).toString()));
			}
			if (fields.get(MapKey.DOI.getName()) != null) {
				this.doi = fields.get(MapKey.DOI.getName()).toString();
			}
		}
		return this;
	}

	public enum MapKey {
		ID ("id"),
		LABEL ("label"),
		DESCRIPTION ("description"),
		GROUPID ("groupId"),
		STATUS ("status"),
		TEMPLATES ("templates"),
		COLLABORATORS ("collaborators"),
		ORGANIZATIONS ("organizations"),
		LASTVERSION ("lastVersion"),
		LASTPUBLICVERSION ("lastPublicVersion"),
		ISPUBLIC ("isPublic"),
		DATASETS ("datasets"),
		GRANT ("grant"),
		GRANTSTATUS ("grantStatus"),
		CREATED ("created"),
		MODIFIED ("modified"),
		FINALIZEDAT ("finalizedAt"),
		PUBLISHEDAT ("publishedAt"),
		DOI ("doi");

		private final String name;

		private MapKey(String name) {
			this.name = name;
		}

		public String getName() {
			return name;
		}
	}
}
