package eu.dnetlib.openaire.project.domain.db;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.hibernate.annotations.Type;
import org.supercsv.cellprocessor.Optional;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.io.CsvBeanReader;
import org.supercsv.io.CsvBeanWriter;
import org.supercsv.io.ICsvBeanReader;
import org.supercsv.io.ICsvBeanWriter;
import org.supercsv.prefs.CsvPreference;
import org.supercsv.util.CsvContext;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.gson.Gson;

import io.swagger.v3.oas.annotations.media.Schema;

/**
 * Created by claudio on 04/07/2017.
 */
@Entity
@Table(name = "project_details")
@Schema(name = "Project details model", description = "provides project details")
public class ProjectDetails {

	@Transient
	@JsonIgnore
	private transient static final String[] FIELDS = Arrays.stream(ProjectDetails.class.getDeclaredFields())
		.map(Field::getName)
		.filter(s -> !s.equals("FIELDS"))
		.filter(s -> !s.startsWith("optional"))
		.toArray(String[]::new);

	@Id
	@Column(name = "projectid")
	private String projectId;
	private String acronym;
	private String code;

	private String jsonextrainfo;

	@Type(type = "eu.dnetlib.openaire.common.GenericArrayUserType")
	@Column(name = "fundingpath", columnDefinition = "text[]")
	private String[] fundingPath;

	public ProjectDetails() {}

	public String getProjectId() {
		return projectId;
	}

	public String getAcronym() {
		return acronym;
	}

	public String getCode() {
		return code;
	}

	public String getJsonextrainfo() {
		return jsonextrainfo;
	}

	public String[] getFundingPath() {
		return fundingPath;
	}

	public void setFundingPath(final List<String> fundingPath) {
		if (fundingPath != null && !fundingPath.isEmpty()) {
			this.fundingPath = fundingPath.toArray(new String[fundingPath.size()]);
		}
	}

	public static ProjectDetails fromJson(final String json) {
		return new Gson().fromJson(json, ProjectDetails.class);
	}

	public static ProjectDetails fromCSV(final String csv) throws IOException {
		try (ICsvBeanReader beanReader = new CsvBeanReader(new StringReader(csv), CsvPreference.STANDARD_PREFERENCE)) {
			final CellProcessor cp = new CellProcessor() {

				@SuppressWarnings("unchecked")
				@Override
				public <T> T execute(final Object value, final CsvContext context) {
					return (T) new Gson().fromJson(value.toString(), List.class);
				}
			};

			return beanReader.read(ProjectDetails.class, FIELDS, getProcessors(cp));
		}
	}

	/**
	 * Sets up the processors used for the examples. There are 10 CSV columns, so 10 processors are defined. Empty columns are read as null
	 * (hence the NotNull() for mandatory columns).
	 *
	 * @return the cell processors
	 */
	private static CellProcessor[] getProcessors(final CellProcessor fundingPathProcessor) {
		return new CellProcessor[] {
			new Optional(), // projectId
			new Optional(), // acronym
			new Optional(), // code
			new Optional(), // jsonextrainfo
			fundingPathProcessor
		};
	}

	public String asJson() {
		return new Gson().toJson(this) + '\n';
	}

	public String asCSV() throws IOException {
		final StringWriter sb = new StringWriter();
		try (ICsvBeanWriter beanWriter = new CsvBeanWriter(sb, CsvPreference.STANDARD_PREFERENCE)) {
			final CellProcessor cp = new CellProcessor() {

				@SuppressWarnings("unchecked")
				@Override
				public <T> T execute(final Object value, final CsvContext context) {
					return (T) new Gson().toJson(value);
				}
			};
			beanWriter.write(this, FIELDS, getProcessors(cp));
			beanWriter.flush();
		}

		return sb.toString();
	}

	public ProjectDetails setProjectId(final String projectId) {
		this.projectId = projectId;
		return this;
	}

	public ProjectDetails setAcronym(final String acronym) {
		this.acronym = acronym;
		return this;
	}

	public ProjectDetails setCode(final String code) {
		this.code = code;
		return this;
	}

	public ProjectDetails setJsonextrainfo(final String jsonextrainfo) {
		this.jsonextrainfo = jsonextrainfo;
		return this;
	}

	public ProjectDetails setFundingPath(final String[] fundingPath) {
		this.fundingPath = fundingPath;
		return this;
	}

}
