package eu.eudat.data.entities;


import eu.eudat.data.converters.DateToUTCConverter;
import eu.eudat.queryable.queryableentity.DataEntity;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;
import java.util.*;
import java.util.stream.Collectors;


@Entity
@Table(name = "\"DMP\"")
@NamedEntityGraphs({
        @NamedEntityGraph(
                name = "dataManagementPlanListingModel",
                attributeNodes = {
                        @NamedAttributeNode("grant"), @NamedAttributeNode("creator"), @NamedAttributeNode("profile")}/*,*/
                /*subgraphs = {
                        @NamedSubgraph(name = "users", attributeNodes = {@NamedAttributeNode("user")}),
                }*/
        ),
        @NamedEntityGraph(
                name = "fullyDetailed",
                attributeNodes = {
                        @NamedAttributeNode("grant"), @NamedAttributeNode("profile"),
                        @NamedAttributeNode("users"), @NamedAttributeNode("organisations"), @NamedAttributeNode("researchers")}),
        @NamedEntityGraph(
                name = "dmpRecentActivity",
                attributeNodes = {
                        @NamedAttributeNode("users"), @NamedAttributeNode("creator")}),
        @NamedEntityGraph(
                name = "recentDmpModel",
                attributeNodes = {@NamedAttributeNode("organisations"), @NamedAttributeNode("researchers"), @NamedAttributeNode("associatedDmps"),
                        @NamedAttributeNode("grant"), @NamedAttributeNode(value = "users", subgraph = "users"), @NamedAttributeNode("creator"), @NamedAttributeNode("profile"), @NamedAttributeNode(value = "dataset", subgraph = "dataset")},
                subgraphs = {
                        @NamedSubgraph(name = "users", attributeNodes = {@NamedAttributeNode("user")}),
                        @NamedSubgraph(name = "dataset", attributeNodes = {@NamedAttributeNode("id"), @NamedAttributeNode("label")})
                }
        ),
        @NamedEntityGraph(
                name = "versionListingModel",
                attributeNodes = {@NamedAttributeNode("id"), @NamedAttributeNode("groupId"), @NamedAttributeNode("version")}
        )

})
public class DMP implements DataEntity<DMP, UUID> {

    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");
            }
        }
    }

    public static Set<String> getHints() {
        return hints;
    }

    private static final Set<String> hints = new HashSet<>(Arrays.asList("dataManagementPlanListingModel", "fullyDetailed"));

    @Id
    @GeneratedValue
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @Column(name = "\"ID\"", updatable = false, nullable = false, columnDefinition = "BINARY(16)")
    private UUID id;

    @Column(name = "\"GroupId\"", columnDefinition = "BINARY(16)")
    private UUID groupId;

    @Column(name = "\"Label\"")
    private String label;

    @Column(name = "\"Version\"")
    private Integer version;

    @OneToMany(mappedBy = "dmp", fetch = FetchType.LAZY)
    private Set<Dataset> dataset;


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "\"Grant\"")
    private Grant grant;


    /*@Type(type = "eu.eudat.configurations.typedefinition.XMLType")
    @Column(name = "\"AssociatedDmps\"", columnDefinition = "xml", nullable = true)
    private String associatedDmps;*/
    @OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "\"DMPDatasetProfile\"",
            joinColumns = {@JoinColumn(name = "\"dmp\"", referencedColumnName = "\"ID\"")},
            inverseJoinColumns = {@JoinColumn(name = "\"datasetprofile\"", referencedColumnName = "\"ID\"")}
    )
    private Set<DatasetProfile> associatedDmps;


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "\"Profile\"")
    private DMPProfile profile;


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "\"Creator\"")
    private UserInfo creator;


    @OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "\"DMPOrganisation\"",
            joinColumns = {@JoinColumn(name = "\"DMP\"", referencedColumnName = "\"ID\"")},
            inverseJoinColumns = {@JoinColumn(name = "\"Organisation\"", referencedColumnName = "\"ID\"")}
    )
    private Set<Organisation> organisations;


    @OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "\"DMPResearcher\"",
            joinColumns = {@JoinColumn(name = "\"DMP\"", referencedColumnName = "\"ID\"")},
            inverseJoinColumns = {@JoinColumn(name = "\"Researcher\"", referencedColumnName = "\"ID\"")}
    )
    private Set<Researcher> researchers;


    @OneToMany(mappedBy = "dmp", fetch = FetchType.LAZY)
    /*@OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "\"UserDMP\"",
            joinColumns = {@JoinColumn(name = "dmp", referencedColumnName = "\"ID\"")},
            inverseJoinColumns = {@JoinColumn(name = "usr", referencedColumnName = "id")}
    )*/
    private Set<UserDMP> users;


    @Column(name = "\"Status\"", nullable = false)
    private Short status;

    @Column(name = "\"Properties\"")
    private String properties;

    @Column(name = "\"DmpProperties\"")
    private String dmpProperties;

    @Column(name = "\"Created\"")
    @Convert(converter = DateToUTCConverter.class)
    private Date created = null;

    @Column(name = "\"Modified\"")
    @Convert(converter = DateToUTCConverter.class)
    private Date modified = new Date();

    @Column(name = "\"Description\"")
    private String description;

    @Column(name= "\"FinalizedAt\"")
    @Convert(converter = DateToUTCConverter.class)
    private Date finalizedAt;

    @Column(name = "\"isPublic\"", nullable = false)
    private boolean isPublic;

    @Column(name= "\"PublishedAt\"")
    @Convert(converter = DateToUTCConverter.class)
    private Date publishedAt;

    @Column(name = "\"DOI\"")
    private String doi;

    @OneToMany(mappedBy = "entityId", fetch = FetchType.LAZY)
    private Set<EntityDoi> dois;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "\"Project\"")
    private Project project;

    @Column(name = "\"extraProperties\"")
    private String extraProperties;


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

    public UserInfo getCreator() {
        return creator;
    }
    public void setCreator(UserInfo creator) {
        this.creator = creator;
    }

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

    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 Set<UserDMP> getUsers() {
        return users;
    }
    public void setUsers(Set<UserDMP> users) {
        this.users = users;
    }

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

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

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

    public Integer getVersion() {
        return version;
    }
    public void setVersion(Integer version) {
        this.version = version;
    }

    public Grant getGrant() {
        return grant;
    }
    public void setGrant(Grant grant) {
        this.grant = grant;
    }

    public Set<DatasetProfile> getAssociatedDmps() {
        return associatedDmps;
    }
    public void setAssociatedDmps(Set<DatasetProfile> associatedDmps) {
        this.associatedDmps = associatedDmps;
    }

    public DMPProfile getProfile() {
        return profile;
    }
    public void setProfile(DMPProfile profile) {
        this.profile = profile;
    }

    public Set<Dataset> getDataset() {
        return dataset;
    }
    public void setDataset(Set<Dataset> dataset) {
        this.dataset = dataset;
    }

    public Set<Organisation> getOrganisations() {
        return organisations;
    }
    public void setOrganisations(Set<Organisation> organisations) {
        this.organisations = organisations;
    }

    public Set<Researcher> getResearchers() {
        return researchers;
    }
    public void setResearchers(Set<Researcher> researchers) {
        this.researchers = researchers;
    }

    public String getProperties() {
        return properties;
    }
    public void setProperties(String properties) {
        this.properties = properties;
    }

    public String getDmpProperties() {
        return dmpProperties;
    }
    public void setDmpProperties(String dmpProperties) {
        this.dmpProperties = dmpProperties;
    }

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

    public boolean isPublic() {
        return isPublic;
    }
    public void setPublic(boolean aPublic) {
        isPublic = aPublic;
    }

    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;
	}

    public Set<EntityDoi> getDois() {
        return dois;
    }
    public void setDois(Set<EntityDoi> dois) {
        this.dois = dois;
    }

    public Project getProject() {
        return project;
    }
    public void setProject(Project project) {
        this.project = project;
    }

    public String getExtraProperties() {
        return extraProperties;
    }

    public void setExtraProperties(String extraProperties) {
        this.extraProperties = extraProperties;
    }

    @Override
    public void update(DMP entity) {
        this.associatedDmps = entity.associatedDmps;
        this.label = entity.getLabel();
        this.profile = entity.getProfile();
        this.status = entity.getStatus();
        this.created = entity.created;
        this.properties = entity.getProperties();
        this.grant = entity.getGrant();
        this.description = entity.getDescription();
        this.researchers = entity.getResearchers();
        this.organisations = entity.getOrganisations();
        this.dmpProperties = entity.getDmpProperties();
        this.isPublic = entity.isPublic;
        this.project = entity.getProject();
        this.setModified(new Date());
        if (entity.getStatus().equals(DMPStatus.FINALISED.getValue())) this.setFinalizedAt(new Date());
        if (entity.isPublic) this.setPublishedAt(new Date());
        if (entity.getUsers() != null) this.users = entity.getUsers();
        if (entity.getDoi() != null && entity.getDoi().trim().isEmpty()) this.doi = entity.doi;
        this.dois = entity.getDois();
        this.extraProperties = entity.getExtraProperties();
    }

    @Override
    public UUID getKeys() {
        return this.id;
    }

    @Override
    public DMP buildFromTuple(List<Tuple> tuple, List<String> fields, String base) {
        this.id = tuple.get(0).get(base.isEmpty() ? "id" : base + "." + "id", UUID.class);
        this.dataset = tuple.stream().map(x -> new Dataset().buildFromTuple(tuple, fields ,base.isEmpty() ? "dataset" : base + "." + "dataset")).collect(Collectors.toSet());
        this.creator = tuple.stream().map(x -> new UserInfo().buildFromTuple(tuple, fields , base.isEmpty() ? "creator" : base + "." + "creator")).collect(Collectors.toList()).get(0);
        return this;
    }
}
