package com.iqser.red.service.peristence.v1.server.integration.tests;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mock.web.MockMultipartFile;

import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateClient;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateManagementService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ImportDossierTemplateRequest;
import com.knecon.fforesight.tenantcommons.TenantContext;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DossierTemplateImportTest extends AbstractPersistenceServerServiceTest {

    public static final String DOSSIERTEMPLATES_PATH = "files/dossiertemplates/";
    @Autowired
    private DossierTemplateManagementService dossierTemplateManagementService;

    @Autowired
    private DossierTemplateClient dossierTemplateClient;


    @SneakyThrows
    @Test
    // For local testing, so disabled for CI
    @Disabled
    public void testLocalDossierTemplateImport() {

        var importDir = new File("/Users/timobejan/work/dossier-templates-v2/dev");
        assertThat(importDir).isNotNull();
        assertThat(importDir.exists()).isTrue();

        TenantContext.setTenantId("redaction");
        for (var file : importDir.listFiles()) {
            if (file.isDirectory()) {
                var archive = pack(file.getAbsolutePath());
                log.info("Importing file: " + file.getName() + " " + " with size: " + archive.length);
                testDossierTemplateImport(archive);
            }
        }
    }


    public byte[] pack(String sourceDirPath) throws IOException {

        var bos = new ByteArrayOutputStream();
        var p = Paths.get(sourceDirPath);
        try (ZipOutputStream zs = new ZipOutputStream(bos)) {
            try (Stream<Path> paths = Files.walk(p)) {
                {
                    paths.filter(path -> !Files.isDirectory(path)).forEach(path -> {
                        ZipEntry zipEntry = new ZipEntry(p.relativize(path).toString());
                        try {
                            zs.putNextEntry(zipEntry);
                            Files.copy(path, zs);
                            zs.closeEntry();
                        } catch (IOException e) {
                            log.error("Failed to read a file", e);
                        }
                    });
                }
            }
        }

        return bos.toByteArray();

    }


    @SneakyThrows
    @Test
    public void testDossierTemplateImport() {

        TenantContext.setTenantId("redaction");

        var archive = loadFileFromClasspath("EFSA_sanitisation_GFL_v1.zip");
        testDossierTemplateImport(archive);
    }


    @SneakyThrows
    private byte[] loadFileFromClasspath(String filename) {

        ClassPathResource classPathResource = new ClassPathResource(DOSSIERTEMPLATES_PATH + filename);
        assertThat(classPathResource.exists()).isTrue();
        try (InputStream inputStream = classPathResource.getInputStream()) {
            return inputStream.readAllBytes();
        }
    }


    private DossierTemplate testDossierTemplateImport(byte[] archive) {

        var request = ImportDossierTemplateRequest.builder().archive(archive).updateExistingDossierTemplate(false).userId("system").build();
        DossierTemplate dossierTemplate = dossierTemplateManagementService.importDossierTemplate(request);
        assertThat(dossierTemplate).isNotNull();
        assertThat(dossierTemplate.getId()).isNotBlank();
        return dossierTemplate;
    }


    @SneakyThrows
    @Test
    public void testDossierTemplateImportOverwrite() {

        TenantContext.setTenantId("redaction");

        var archive = loadFileFromClasspath("EFSA_sanitisation_GFL_v1.zip");
        var dossierTemplate = testDossierTemplateImport(archive);

        var request = ImportDossierTemplateRequest.builder()
                .archive(archive)
                .updateExistingDossierTemplate(true)
                .userId("system")
                .dossierTemplateId(dossierTemplate.getId())
                .build();
        DossierTemplate reimportedTemplate = dossierTemplateManagementService.importDossierTemplate(request);
        assertThat(reimportedTemplate).isNotNull();
        assertThat(reimportedTemplate.getId()).isNotBlank();
    }


    @Test
    @SneakyThrows
    // This is currently not working, since there seems to be some missing Feign decoder config for tests
    @Disabled
    public void dossierImportClientTest() {

        var multipartFile = loadMultiPartFileFromClasspath("EFSA_sanitisation_GFL_v1.zip");
        DossierTemplateModel dossierTemplateModel = dossierTemplateClient.importDossierTemplate(multipartFile, null, false);
        assertThat(dossierTemplateModel).isNotNull();
        assertThat(dossierTemplateModel.getId()).isNotBlank();

    }


    @SneakyThrows
    private MockMultipartFile loadMultiPartFileFromClasspath(String filename) {

        ClassPathResource classPathResource = new ClassPathResource(DOSSIERTEMPLATES_PATH + filename);
        assertThat(classPathResource.exists()).isTrue();
        try (InputStream inputStream = classPathResource.getInputStream()) {
            return new MockMultipartFile(filename, filename, "", inputStream);
        }
    }


    // This can be used to test with local files that are not part of the project.
    @SuppressWarnings("unused")
    @SneakyThrows
    private MockMultipartFile loadDossierTemplateFromFile(String filename) {

        File file = new File(filename);
        assertThat(file.exists()).isTrue();

        return new MockMultipartFile(filename, filename, "", Files.readAllBytes(file.toPath()));
    }

}


