package eu.dnetlib.openaire.dsm.dao.utils;

import java.sql.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import com.fasterxml.jackson.databind.ObjectMapper;

import eu.dnetlib.data.transform.xml.AbstractDNetXsltFunctions;
import eu.dnetlib.openaire.dsm.domain.db.ApiDbEntry;
import eu.dnetlib.openaire.dsm.domain.db.DatasourceDbEntry;
import eu.dnetlib.openaire.dsm.domain.db.OrganizationDbEntry;
import eu.dnetlib.openaire.exporter.model.dsm.ApiDetails;
import eu.dnetlib.openaire.exporter.model.dsm.DatasourceDetails;
import eu.dnetlib.openaire.exporter.model.dsm.DatasourceDetailsUpdate;
import eu.dnetlib.openaire.exporter.model.dsm.DatasourceSnippetExtended;
import eu.dnetlib.openaire.exporter.model.dsm.OrganizationDetails;

public class DsmMappingUtils {

	public static final String DATE_FORMAT = "yyyy-MM-dd";

	public static final String ID_SEPARATOR = "::";

	public static final String ID_PREFIX = "api_________" + ID_SEPARATOR;

	public static String createId(final ApiDetails api) {
		return ID_PREFIX + api.getDatasource() + ID_SEPARATOR + RandomStringUtils.randomAlphanumeric(8);
	}

	public static DatasourceDetails asDetails(final DatasourceDbEntry d) {
		final DatasourceDetails details = _convert(d, DatasourceDetails.class);
		return details.setOpenaireId(asOpenaireId(details.getId()));
	}

	public static DatasourceSnippetExtended asSnippetExtended(final DatasourceDbEntry d) {
		final DatasourceSnippetExtended ds = new DatasourceSnippetExtended();
		ds.setId(d.getId());
		ds.setOfficialname(d.getOfficialname());
		ds.setEnglishname(d.getEnglishname());
		ds.setRegisteredby(d.getRegisteredby());
		ds.setWebsiteurl(d.getWebsiteurl());
		ds.setEoscDatasourceType(d.getEoscDatasourceType());
		ds.setRegistrationdate(d.getRegistrationdate());
		ds.setLogoUrl(d.getLogourl());
		ds.setDescription(d.getDescription());
		ds.setConsentTermsOfUse(d.getConsentTermsOfUse());
		ds.setConsentTermsOfUseDate(d.getConsentTermsOfUseDate());
		ds.setLastConsentTermsOfUseDate(d.getLastConsentTermsOfUseDate());
		ds.setFullTextDownload(d.getFullTextDownload());
		if (d.getOrganizations() != null) {
			ds.setOrganizations(d.getOrganizations().stream().map(DsmMappingUtils::asOrganizationDetail).collect(Collectors.toSet()));
		}
		ds.setTypology(d.getTypology());
		return ds;
	}

	private static OrganizationDetails asOrganizationDetail(final OrganizationDbEntry o) {
		return new OrganizationDetails()
			.setCountry(o.getCountry())
			.setLegalname(o.getLegalname())
			.setLegalshortname(o.getLegalshortname())
			.setWebsiteurl(o.getWebsiteurl())
			.setLogourl(o.getLogourl());
	}

	public static ApiDetails asDetails(final ApiDbEntry d) {
		return _convert(d, ApiDetails.class);
	}

	public static ApiDbEntry asDbEntry(final ApiDetails d) {
		final ApiDbEntry apiDbEntry = new ApiDbEntry();

		apiDbEntry.setId(d.getId());
		apiDbEntry.setBaseurl(d.getBaseurl());
		apiDbEntry.setProtocol(d.getProtocol());
		apiDbEntry.setDatasource(d.getDatasource());
		apiDbEntry.setContentdescription(d.getContentdescription());
		apiDbEntry.setCompatibility(d.getCompatibility());
		apiDbEntry.setCompatibilityOverride(d.getCompatibilityOverride());
		apiDbEntry.setRemovable(d.getRemovable());
		apiDbEntry.setMetadataIdentifierPath(d.getMetadataIdentifierPath());

		// Need to complete the references among objects, because you know, referential integrity ...
		apiDbEntry.getApiParams().forEach(ap -> ap.getId().setApi(apiDbEntry));

		return apiDbEntry;
	}

	public static DatasourceDbEntry asDbEntry(final DatasourceDetails d) {
		final DatasourceDbEntry dbe = _convert(d, DatasourceDbEntry.class);
		if (dbe.getOrganizations() != null) {
			dbe.getOrganizations().forEach(o -> {
				final String prefix = StringUtils.isNotBlank(dbe.getNamespaceprefix()) ? dbe.getNamespaceprefix() : dbe.getId();
				o.setId(prefix + ID_SEPARATOR + o.getLegalname());
				if (o.getDateofcollection() == null) {
					o.setDateofcollection(new Date(System.currentTimeMillis()));
				}
				o.setCollectedfrom(dbe.getCollectedfrom());
			});
		}

		_fix_typology(dbe, d.getTypology(), d.getEoscDatasourceType());

		return dbe;
	}

	@Deprecated
	private static void _fix_typology(final DatasourceDbEntry dbe, final String oldTypology, final String eoscTypology) {

		if (StringUtils.isNotBlank(oldTypology) && StringUtils.isBlank(eoscTypology)) {
			// THE ORDER IS IMPORTANT: DO NOT CHANGE IT
			if (oldTypology.startsWith("crissystem")) {
				dbe.setEoscDatasourceType("CRIS system");
			} else if (oldTypology.startsWith("entityregistry")) {
				dbe.setEoscDatasourceType("Registry");
			} else if (oldTypology.startsWith("pubscatalogue") || oldTypology.equals("websource")) {
				dbe.setEoscDatasourceType("Catalogue");
			} else if (oldTypology.contains("journal")) {
				dbe.setEoscDatasourceType("Journal archive");
			} else if (oldTypology.startsWith("aggregator")) {
				dbe.setEoscDatasourceType("Aggregator");
			} else if (oldTypology.contains("repository")) {
				dbe.setEoscDatasourceType("Repository");
			} else {
				dbe.setEoscDatasourceType("Aggregator");
			}
		} else if (StringUtils.isBlank(oldTypology) && StringUtils.isNotBlank(eoscTypology)) {
			if (dbe.getEoscDatasourceType().equals("CRIS system")) {
				dbe.setTypology("crissystem");
			} else if (dbe.getEoscDatasourceType().equals("Registry")) {
				dbe.setTypology("entityregistry");
			} else if (dbe.getEoscDatasourceType().equals("Catalogue")) {
				dbe.setTypology("pubscatalogue::unknown");
			} else if (dbe.getEoscDatasourceType().equals("Journal archive")) {
				dbe.setTypology("pubsrepository::journal");
			} else if (dbe.getEoscDatasourceType().equals("Aggregator")) {
				dbe.setTypology("aggregator");
			} else if (dbe.getEoscDatasourceType().equals("Repository")) {
				dbe.setTypology("pubsrepository::unknown");
			} else {
				dbe.setTypology("aggregator");
			}
		}
	}

	public static DatasourceDbEntry asDbEntry(final DatasourceDetailsUpdate d) {
		final DatasourceDbEntry dbe = _convert(d, DatasourceDbEntry.class);
		_fix_typology(dbe, d.getTypology(), d.getEoscDatasourceType());
		return dbe;
	}

	// HELPERS

	private static <T> T _convert(final Object o, final Class<T> clazz) {
		final ObjectMapper mapper = new ObjectMapper();
		return mapper.convertValue(o, clazz);
	}

	private static String asOpenaireId(final String id) {
		final String prefix = StringUtils.substringBefore(id, ID_SEPARATOR);
		final String md5 = StringUtils.substringAfter(id, ID_SEPARATOR);

		return prefix + ID_SEPARATOR + AbstractDNetXsltFunctions.md5(md5);
	}

	public static void copyNonNullProperties(final Object src, final Object target) {
		BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
	}

	public static String[] getNullPropertyNames(final Object source) {
		final BeanWrapper src = new BeanWrapperImpl(source);
		final java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

		final Set<String> emptyNames = new HashSet<>();
		for (final java.beans.PropertyDescriptor pd : pds) {
			final Object srcValue = src.getPropertyValue(pd.getName());
			if (srcValue == null) {
				emptyNames.add(pd.getName());
			}
		}
		final String[] result = new String[emptyNames.size()];
		return emptyNames.toArray(result);
	}

}
