package eu.dnetlib.is.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.xml.sax.SAXException;

import com.fasterxml.jackson.databind.ObjectMapper;

import eu.dnetlib.data.is.resource.model.ResourceType;
import eu.dnetlib.data.is.resource.repository.ResourceTypeRepository;
import eu.dnetlib.is.errors.InformationServiceException;

@Component
public class ResourceValidator {

	private final Log log = LogFactory.getLog(ResourceValidator.class);

	@Autowired
	private ResourceTypeRepository resourceTypeRepository;

	public void validate(final String type, final String content) throws InformationServiceException {

		final ResourceType rtype = resourceTypeRepository.findById(type).orElseThrow(() -> new InformationServiceException("Invalid type"));

		if (rtype.getContentType().equals(MediaType.APPLICATION_XML_VALUE)) {
			validateXml(type, content);
		} else if (rtype.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
			validateJSON(content);
		}
	}

	private void validateXml(final String type, final String content) throws InformationServiceException {
		final Schema schema = getXmlSchema(type);
		if (schema != null) {
			try {
				schema.newValidator().validate(new StreamSource(new StringReader(content)));
			} catch (final Exception e) {
				throw new InformationServiceException(e.getMessage(), e);
			}
		} else {
			try {
				DocumentHelper.parseText(content);
			} catch (final DocumentException e) {
				throw new InformationServiceException(e.getMessage(), e);
			}
		}
	}

	private Schema getXmlSchema(final String type) {
		try {
			final InputStream is = getClass().getResourceAsStream("/schemas/" + type + ".xsd");
			if (is == null) { return null; }
			final String schemaSource = IOUtils.toString(is, StandardCharsets.UTF_8.name());
			if (StringUtils.isBlank(schemaSource)) { return null; }
			return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new StreamSource(new StringReader(schemaSource)));
		} catch (final SAXException | IOException e) {
			log.fatal("cannot parse resource type schema", e);
			return null;
		}
	}

	private void validateJSON(final String content) throws InformationServiceException {
		final ObjectMapper mapper = new ObjectMapper();
		try {
			mapper.readValue(content, Object.class);
		} catch (final Exception e) {
			throw new InformationServiceException(e.getMessage(), e);
		}
	}

}
