package eu.eudat.logic.managers;

import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import eu.eudat.data.dao.criteria.RequestItem;
import eu.eudat.data.dao.entities.DMPProfileDao;
import eu.eudat.data.entities.DMPProfile;
import eu.eudat.data.query.items.item.dmpprofile.DataManagementPlanProfileCriteriaRequest;
import eu.eudat.data.query.items.table.dmpprofile.DataManagementPlanProfileTableRequest;
import eu.eudat.logic.services.operations.DatabaseRepository;
import eu.eudat.logic.utilities.builders.XmlBuilder;
import eu.eudat.logic.utilities.documents.helpers.FileEnvelope;
import eu.eudat.logic.utilities.documents.xml.dmpXml.ExportXmlBuilderDmpProfile;
import eu.eudat.logic.utilities.documents.xml.dmpXml.ImportXmlBuilderDmpProfile;
import eu.eudat.models.data.entities.xmlmodels.dmpprofiledefinition.DmpProfileExternalAutoComplete;
import eu.eudat.models.data.entities.xmlmodels.dmpprofiledefinition.Field;
import eu.eudat.models.data.helpermodels.Tuple;
import eu.eudat.models.data.helpers.common.AutoCompleteLookupItem;
import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.listingmodels.DataManagementPlanProfileListingModel;
import eu.eudat.models.data.security.Principal;
import eu.eudat.queryable.QueryableList;
import eu.eudat.logic.services.ApiContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import javax.activation.MimetypesFileTypeMap;
import javax.xml.xpath.*;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Created by ikalyvas on 3/21/2018.
 */
@Component
public class DataManagementProfileManager {
	private static final Logger logger = LoggerFactory.getLogger(DataManagementProfileManager.class);

    private ApiContext apiContext;
    private DatabaseRepository databaseRepository;
    private Environment environment;

    @Autowired
    public DataManagementProfileManager(ApiContext apiContext, Environment environment) {
        this.apiContext = apiContext;
        this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository();
        this.environment = environment;
    }

    public DataTableData<DataManagementPlanProfileListingModel> getPaged(DataManagementPlanProfileTableRequest dataManagementPlanProfileTableRequest, Principal principal) throws Exception {

        QueryableList<DMPProfile> items = apiContext.getOperationsContext().getDatabaseRepository().getDmpProfileDao().getWithCriteria(dataManagementPlanProfileTableRequest.getCriteria());
        QueryableList<DMPProfile> pagedItems = PaginationManager.applyPaging(items, dataManagementPlanProfileTableRequest);

        DataTableData<DataManagementPlanProfileListingModel> dataTable = new DataTableData<DataManagementPlanProfileListingModel>();

        CompletableFuture itemsFuture = pagedItems
                .selectAsync(item -> new DataManagementPlanProfileListingModel().fromDataModel(item)).whenComplete((resultList, throwable) -> {
                    dataTable.setData(resultList);
                });

        CompletableFuture countFuture = items.countAsync().whenComplete((count, throwable) -> {
            dataTable.setTotalCount(count);
        });
        CompletableFuture.allOf(itemsFuture, countFuture).join();
        return dataTable;
    }

    public DataManagementPlanProfileListingModel getSingle(String id, Principal principal) throws InstantiationException, IllegalAccessException {
        DMPProfile dmpProfile = databaseRepository.getDmpProfileDao().find(UUID.fromString(id));
        DataManagementPlanProfileListingModel dataManagementPlanProfileListingModel = new DataManagementPlanProfileListingModel();
        dataManagementPlanProfileListingModel.fromDataModel(dmpProfile);
        return dataManagementPlanProfileListingModel;
    }

    public List<DataManagementPlanProfileListingModel> getWithCriteria(DataManagementPlanProfileCriteriaRequest dataManagementPlanProfileCriteriaRequest) throws IllegalAccessException, InstantiationException {
        QueryableList<DMPProfile> items = databaseRepository.getDmpProfileDao().getWithCriteria(dataManagementPlanProfileCriteriaRequest.getCriteria());
        List<DataManagementPlanProfileListingModel> datamanagementPlans = items.select(item -> new DataManagementPlanProfileListingModel().fromDataModel(item));
        return datamanagementPlans;
    }

    public void createOrUpdate(DataManagementPlanProfileListingModel dataManagementPlanProfileListingModel, Principal principal) throws Exception {
        DMPProfile dmpProfile = dataManagementPlanProfileListingModel.toDataModel();
        apiContext.getOperationsContext().getDatabaseRepository().getDmpProfileDao().createOrUpdate(dmpProfile);
    }

    public ResponseEntity<byte[]> getDocument(DataManagementPlanProfileListingModel dmpProfile, String label) throws IllegalAccessException, IOException, InstantiationException {
        FileEnvelope envelope = getXmlDocument(dmpProfile, label);
        InputStream resource = new FileInputStream(envelope.getFile());
        logger.info("Mime Type of " + envelope.getFilename() + " is " +
                new MimetypesFileTypeMap().getContentType(envelope.getFile()));
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.setContentLength(envelope.getFile().length());
        responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        String fileName = envelope.getFilename().replace(" ", "_").replace(",", "_");
        responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName + ".xml");
        responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
        responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");
        byte[] content = org.apache.poi.util.IOUtils.toByteArray(resource);
        resource.close();
        Files.deleteIfExists(envelope.getFile().toPath());
        return new ResponseEntity<>(content,
                responseHeaders,
                HttpStatus.OK);
    }

    public FileEnvelope getXmlDocument(DataManagementPlanProfileListingModel dmpProfile, String label) throws InstantiationException, IllegalAccessException, IOException {
        ExportXmlBuilderDmpProfile xmlBuilder = new ExportXmlBuilderDmpProfile();
        File file = xmlBuilder.build(dmpProfile, environment);
        FileEnvelope fileEnvelope = new FileEnvelope();
        fileEnvelope.setFile(file);
        fileEnvelope.setFilename(label);
        return fileEnvelope;
    }


    public eu.eudat.logic.utilities.documents.xml.dmpXml.dmpProfileModel.DmpProfile createDmpProfileFromXml(MultipartFile multiPartFile) {
        ImportXmlBuilderDmpProfile xmlBuilder = new ImportXmlBuilderDmpProfile();
        try {
            return xmlBuilder.build(convert(multiPartFile));
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }

    private File convert(MultipartFile file) throws IOException {
        File convFile = new File(this.environment.getProperty("temp.temp") + file.getOriginalFilename());
        convFile.createNewFile();
        FileOutputStream fos = new FileOutputStream(convFile);
        fos.write(file.getBytes());
        fos.close();
        return convFile;
    }

    public List<Tuple<String, String>> getExternalAutocomplete(RequestItem<AutoCompleteLookupItem> lookupItem) throws XPathExpressionException {
        DMPProfile dmpProfile = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpProfileDao().find(UUID.fromString(lookupItem.getCriteria().getProfileID()));
        Field field = this.queryForField(dmpProfile.getDefinition(), lookupItem.getCriteria().getFieldID());
		DmpProfileExternalAutoComplete data = field.getExternalAutocomplete();
		return this.externalAutocompleteRequest(data, lookupItem.getCriteria().getLike());
    }

    private Field queryForField(String xml, String fieldId) throws XPathExpressionException {
    	Field field = new Field();
		Document document = XmlBuilder.fromXml(xml);
		XPathFactory xpathFactory = XPathFactory.newInstance();
		XPath xpath = xpathFactory.newXPath();
		XPathExpression expr = xpath.compile("//field[@id='" + fieldId + "']");
		Element name = (Element) expr.evaluate(document, XPathConstants.NODE);
		field.fromXml(name);
		return field;
	}

	private List<Tuple<String, String>> externalAutocompleteRequest(DmpProfileExternalAutoComplete data, String like) {
		return externalAutocompleteRequest(data.getUrl(), data.getOptionsRoot(), data.getLabel(), data.getValue(), like);
	}

    public static List<Tuple<String, String>> externalAutocompleteRequest(String url, String optionsRoot, String label, String value, String like) {
        List<Tuple<String, String>> result = new LinkedList<>();
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.valueOf("application/vnd.api+json; charset=utf-8")));
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<>("parameters", headers);

        ResponseEntity<Object> response = restTemplate.exchange(url + "?search=" + like, HttpMethod.GET, entity, Object.class);
        DocumentContext jsonContext = JsonPath.parse(response.getBody());

        List<Map<String, String>> jsonItems = jsonContext.read(optionsRoot + "['" + label + "','" + value + "']");
        jsonItems.forEach(item -> result.add(new Tuple<>(item.get(value), item.get(label))));
        return result;

    }
}
