package eu.eudat.depositinterface.dataverserepository.interfaces;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.researchspace.dataverse.api.v1.DataverseAPI;
import com.researchspace.dataverse.api.v1.DataverseConfig;
import com.researchspace.dataverse.entities.*;
import com.researchspace.dataverse.entities.facade.DatasetAuthor;
import com.researchspace.dataverse.entities.facade.DatasetContact;
import com.researchspace.dataverse.entities.facade.DatasetDescription;
import com.researchspace.dataverse.entities.facade.DatasetFacade;
import com.researchspace.dataverse.http.DataverseAPIImpl;
import eu.eudat.depositinterface.dataverserepository.config.ConfigLoader;
import eu.eudat.depositinterface.models.DMPDepositModel;
import eu.eudat.depositinterface.repository.RepositoryDeposit;
import eu.eudat.depositinterface.repository.RepositoryDepositConfiguration;
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.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;

import static com.researchspace.dataverse.entities.Version.MAJOR;
import static com.researchspace.dataverse.entities.Version.MINOR;

@Component
public class DataverseDeposit implements RepositoryDeposit {
    private static final Logger logger = LoggerFactory.getLogger(DataverseDeposit.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    private static final String API_TOKEN = "";
    private static final String SYSTEM_PARENT_DATAVERSE_ALIAS = "";
    private static final String SERVER = "";
    private static final String SYSTEM_EMAIL = "";

    private DataverseAPI api;
    private boolean isApiSet;

    private ConfigLoader configLoader;
    private Environment environment;

    @Autowired
    public DataverseDeposit(ConfigLoader configLoader, Environment environment){
        this.configLoader = configLoader;
        this.environment = environment;
        this.isApiSet = false;
    }

    private void setDataverseApi() throws MalformedURLException {
        if(!this.isApiSet) {
            this.api = new DataverseAPIImpl();
            DataverseConfig config = new DataverseConfig(new URL(SERVER), API_TOKEN, SYSTEM_PARENT_DATAVERSE_ALIAS);
            api.configure(config);
            this.isApiSet = true;
        }
    }

    @Override
    public String deposit(DMPDepositModel dmpDepositModel, boolean update, String repositoryAccessToken) throws Exception {

        if(!this.isApiSet)
            this.setDataverseApi();

        String doi;
        DatasetFacade dataset = DatasetFacade.builder()
                .title(dmpDepositModel.getLabel())
                .authors(dmpDepositModel.getUsers().stream().map(x -> DatasetAuthor.builder().authorName(x.getUser().getName()).build()).collect(Collectors.toList()))
                .contacts(dmpDepositModel.getUsers().stream().map(x -> DatasetContact.builder().datasetContactEmail(x.getUser().getEmail()).build()).collect(Collectors.toList()))
                .subject("Other")
                .description(DatasetDescription.builder().description(dmpDepositModel.getDescription()).build())
                .languages(new ArrayList<>())
                .depositor("")
                .build();

        if(dmpDepositModel.getPreviousDOI() == null || dmpDepositModel.getPreviousDOI().isEmpty()){
            Identifier id = this.api.getDataverseOperations().createDataset(dataset, SYSTEM_PARENT_DATAVERSE_ALIAS);

            doi = this.api.getDatasetOperations().getDataset(id).getDoiId().orElse(null);

            this.uploadFiles(dmpDepositModel, doi);

            this.api.getDatasetOperations().publishDataset(id, MAJOR);
        }
        else{
            Map<String, Object> datasetJson = this.getDatasetIdentifier(dmpDepositModel.getPreviousDOI());
            Identifier id = new Identifier();
            id.setId(((Integer) datasetJson.get("id")).longValue());

            this.uploadFiles(dmpDepositModel, dmpDepositModel.getPreviousDOI());

            this.api.getDatasetOperations().updateDataset(dataset, id);
            DataverseResponse<PublishedDataset> publishedDataset = this.api.getDatasetOperations().publishDataset(id, MAJOR);
            doi = publishedDataset.getData().getAuthority() + "/" + publishedDataset.getData().getIdentifier();
        }


        return doi;

    }

    private void uploadFiles(DMPDepositModel dmpDepositModel, String doi) throws IOException {
        this.uploadFile(dmpDepositModel.getPdfFileName(), dmpDepositModel.getPdfFile(), doi);

        String contentDisposition = dmpDepositModel.getRdaJson().getHeaders().get("Content-Disposition").get(0);
        String jsonFileName = contentDisposition.substring(contentDisposition.lastIndexOf('=')  + 1);
        File rdaJson = new File(this.environment.getProperty("storage.temp") + jsonFileName);
        OutputStream output = new FileOutputStream(rdaJson);
        try {
            output.write(Objects.requireNonNull(dmpDepositModel.getRdaJson().getBody()));
            output.flush();
            output.close();
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
        this.uploadFile(jsonFileName, rdaJson, doi);
        Files.deleteIfExists(rdaJson.toPath());
    }

    private Map<String, Object> getDatasetIdentifier(String previousDOI) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("X-Dataverse-key", API_TOKEN);
        String serverUrl = SERVER + "/api/datasets/:persistentId?persistentId=doi:" + previousDOI;
        RestTemplate restTemplate = new RestTemplate();
        return (Map<String, Object>) restTemplate.exchange(serverUrl, HttpMethod.GET, new HttpEntity<>(headers), Map.class).getBody().get("data");
    }

    private void uploadFile(String filename, File file, String doi) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.set("X-Dataverse-key", API_TOKEN);
        MultiValueMap<String, String> fileMap = new LinkedMultiValueMap<>();
        ContentDisposition contentDisposition = ContentDisposition
                .builder("form-data")
                .name("file")
                .filename(filename)
                .build();
        fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
        HttpEntity<byte[]> fileEntity = new HttpEntity<>(Files.readAllBytes(file.toPath()), fileMap);
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("file", fileEntity);
        body.add("jsonData", "{\"restrict\":\"false\", \"tabIngest\":\"false\"}");
        HttpEntity<MultiValueMap<String, Object>> requestEntity
                = new HttpEntity<>(body, headers);

        String serverUrl = SERVER + "/api/datasets/:persistentId/add?persistentId=doi:" + doi;

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<Object> resp = restTemplate.postForEntity(serverUrl, requestEntity, Object.class);
    }

    @Override
    public RepositoryDepositConfiguration getConfiguration() {
        eu.eudat.depositinterface.dataverserepository.config.DataverseConfig dataverseConfig = this.configLoader.getDataverseConfig();
        return dataverseConfig.toRepoConfig();
    }

    @Override
    public String authenticate(String code) {
        return null;
    }
}
