package eu.eudat.logic.managers;

import eu.eudat.data.dao.entities.DatasetDao;
import eu.eudat.logic.proxy.config.configloaders.ConfigLoader;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.forms.VisibilityRuleService;
import eu.eudat.logic.utilities.documents.helpers.FileEnvelope;
import eu.eudat.logic.utilities.documents.word.WordBuilder;
import eu.eudat.logic.utilities.documents.xml.ExportXmlBuilder;
import eu.eudat.models.HintedModelFactory;
import eu.eudat.models.data.datasetwizard.DatasetWizardModel;
import eu.eudat.models.data.user.composite.PagedDatasetProfile;
import org.apache.commons.io.IOUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * Created by ikalyvas on 10/16/2018.
 */
@Service
public class DocumentManager {
    private static final Logger logger = LoggerFactory.getLogger(DocumentManager.class);

    private ApiContext context;
    private DatasetManager datasetManager;
    private ConfigLoader configLoader;
    private Environment environment;

    public DocumentManager(ApiContext context, DatasetManager datasetManager, ConfigLoader configLoader, Environment environment) {
        this.context = context;
        this.datasetManager = datasetManager;
        this.configLoader = configLoader;
        this.environment = environment;
    }

    public File getWordDocument(ConfigLoader configLoader, DatasetDao datatasetRepository, String id, VisibilityRuleService visibilityRuleService) throws InstantiationException, IllegalAccessException, IOException {
        WordBuilder wordBuilder = new WordBuilder();
        DatasetWizardModel dataset = new DatasetWizardModel();
        XWPFDocument document = configLoader.getDocument();
        eu.eudat.data.entities.Dataset datasetEntity = datatasetRepository.find(UUID.fromString(id), HintedModelFactory.getHint(DatasetWizardModel.class));
        Map<String, Object> properties = new HashMap<>();
        if (datasetEntity.getProperties() != null) {
            JSONObject jobject = new JSONObject(datasetEntity.getProperties());
            properties = jobject.toMap();
        }
        PagedDatasetProfile pagedDatasetProfile = datasetManager.getPagedProfile(dataset, datasetEntity);
        visibilityRuleService.setProperties(properties);
        visibilityRuleService.buildVisibilityContext(pagedDatasetProfile.getRules());
        wordBuilder.build(document, pagedDatasetProfile, visibilityRuleService);
        File exportFile = new File(dataset.getLabel() + ".docx");
        FileOutputStream out = new FileOutputStream(exportFile);
        document.write(out);
        out.close();
        return exportFile;
    }

    public FileEnvelope getXmlDocument(eu.eudat.data.entities.Dataset datasetEntity, String id, VisibilityRuleService visibilityRuleService) throws InstantiationException, IllegalAccessException, IOException {
        ExportXmlBuilder xmlBuilder = new ExportXmlBuilder();
        DatasetWizardModel dataset = new DatasetWizardModel();
        Map<String, Object> properties = new HashMap<>();
        if (datasetEntity.getProperties() != null) {
            JSONObject jobject = new JSONObject(datasetEntity.getProperties());
            properties = jobject.toMap();
        }
        PagedDatasetProfile pagedDatasetProfile = datasetManager.getPagedProfile(dataset, datasetEntity);
        visibilityRuleService.setProperties(properties);
        visibilityRuleService.buildVisibilityContext(pagedDatasetProfile.getRules());
        File file = xmlBuilder.build(pagedDatasetProfile, UUID.fromString(id), visibilityRuleService, environment);
        FileEnvelope fileEnvelope = new FileEnvelope();
        fileEnvelope.setFile(file);
        fileEnvelope.setFilename(datasetEntity.getLabel());
        return fileEnvelope;
    }

    public File convertToPDF(File file, Environment environment, String label) throws IOException, InterruptedException {
        LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("file", new FileSystemResource(file));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.add("Content-disposition", "attachment; filename=" + label + ".docx");

        headers.add("Content-type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");

        HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<LinkedMultiValueMap<String, Object>>(
                map, headers);

        Map queueResult = new RestTemplate().postForObject(
                environment.getProperty("pdf.converter.url") +
                        "api/v1/", requestEntity, Map.class);

        Map mediaResult = new RestTemplate().getForObject(environment.getProperty("pdf.converter.url") +
                "/api/v1/" + queueResult.get("id"), Map.class);
        logger.info("Status: " + mediaResult.get("status"));
        while (!mediaResult.get("status").equals("finished")) {
            Thread.sleep(500);
            mediaResult = new RestTemplate().getForObject(environment.getProperty("pdf.converter.url") +
                    "api/v1/" + queueResult.get("id"), Map.class);
            logger.info("Polling");
        }
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
        HttpHeaders headers2 = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
        HttpEntity<String> entity = new HttpEntity<String>(headers2);

        ResponseEntity<byte[]> response = restTemplate.exchange(environment.getProperty("pdf.converter.url") +
                mediaResult.get("result_url"), HttpMethod.GET, entity, byte[].class, "1");

        UUID uuid = UUID.randomUUID();
        File zip = new File(uuid + ".zip");
        if (response.getStatusCode().equals(HttpStatus.OK)) {
            FileOutputStream output = new FileOutputStream(zip);
            IOUtils.write(response.getBody(), output);
        }
        return extractFromZip(zip, label + ".pdf");
    }

    private File extractFromZip(File file, String filename) throws IOException {
        byte[] buffer = new byte[1024];
        File newFile = new File(filename);
        ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
        ZipEntry zipEntry = zis.getNextEntry();
        while (zipEntry != null) {
            String zippedFileName = zipEntry.getName();
            if (zippedFileName.equals("pdf")) {

                FileOutputStream fos = new FileOutputStream(newFile);
                int len;
                while ((len = zis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
                zipEntry = zis.getNextEntry();
            }
        }
        zis.closeEntry();
        zis.close();
        return newFile;
    }
}
