package com.workfusion.odf.impl.converter.extractor.zip;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.workfusion.odf.impl.converter.extractor.DataWrapper;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class RecursiveZipExtractorTest {
    private Logger logger = LoggerFactory.getLogger(RecursiveZipExtractorTest.class);
	private final RecursiveZipExtractor recursiveZipExtractor = new RecursiveZipExtractor(logger);

    private static final String ZIP_EXTRACTOR_EXAMPLE_FOR_FLAT_MAP_ZIP = "/zipExtractor/exampleForFlatMap.zip";
    private static final String ZIP_FLAT_CLONE_EXAMPLE_FOR_FLAT_MAP_ZIP = "/zipExtractor/flatCloneOfExampleForFlatMap.zip";
    private static final String ZIP_EXTRACTOR_EMPTY_ZIP = "/zipExtractor/empty.zip";

    @Test
    void recursiveUnzipSuccessTest() throws IOException {
        final byte[] emptyZipBytes = IOUtils.resourceToByteArray(ZIP_EXTRACTOR_EMPTY_ZIP);
        final byte[] fatZipBytes = IOUtils.resourceToByteArray(ZIP_EXTRACTOR_EXAMPLE_FOR_FLAT_MAP_ZIP);

        final List<DataWrapper> emptyDataWrappers = recursiveZipExtractor.recursiveExtract(emptyZipBytes, FilenameUtils.getName(ZIP_EXTRACTOR_EMPTY_ZIP));
        assertEquals(0, emptyDataWrappers.size());

        final List<DataWrapper> fatDataWrappers = recursiveZipExtractor.recursiveExtract(fatZipBytes, FilenameUtils.getName(ZIP_EXTRACTOR_EXAMPLE_FOR_FLAT_MAP_ZIP));
        assertEquals(20, fatDataWrappers.size());
        fatDataWrappers.forEach(System.out::println);

        final Set<String> resultNames = fatDataWrappers.stream().map(DataWrapper::getName).collect(Collectors.toCollection(() -> new HashSet<>()));

        final Set<String> expected = getExpectedNames();

        Assertions.assertEquals(expected, resultNames);

        checkThatAllNamesAreUnique(fatDataWrappers);

        checkThatContentOfTestedAndFlatZipsAreEqual(fatDataWrappers);
    }

    // [AU]: "exampleForFlatMap.zip_" prefix added
    private Set<String> getExpectedNames() {
        final Set<String> expected = new HashSet<>();
        expected.add("exampleForFlatMap.zip_file in root of archive.rtf");
        expected.add("exampleForFlatMap.zip_folder in root of archive_1e0900ca-a20c-41c6-80c7-9f62a0f48f98.pdf");
        expected.add("exampleForFlatMap.zip_folder in root of archive_empty file 2.txt");
        expected.add("exampleForFlatMap.zip_folder in root of archive_emty file");
        expected.add("exampleForFlatMap.zip_folder in root of archive_excel file.xlsx");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 1_aerbret.pdf");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 1_New folder_New folder_tyukffk.pub");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 1_New folder_New OpenDocument Drawing.odg");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_New Rich Text Document.rtf");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_perf.xlsx");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_trace.txt");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_zipArchive.zip_zipArchive_zipArchive_folderInZipArchive_file.txt");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_zipArchive.zip_zipArchive_zipArchive_folderInZipArchive_file.zip_file.txt");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_zipArchive.zip_zipArchive_zipArchive_fZIP.docx");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_zipArchive.zip_zipArchive_zipArchive_gggggggggggggggggggggggggggggggggZIP.rtf");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_zipArchive.zip_zipArchive_zipArchive_perfZIP.xlsx");
        expected.add("exampleForFlatMap.zip_folder in root of archive_folder 2_zipArchive.zip_zipArchive_zipArchive_traceZIP.txt");
        expected.add("exampleForFlatMap.zip_folder in root of archive_New Rich Text Document.rtf");
        expected.add("exampleForFlatMap.zip_folder in root of archive_pdf file.pdf");
        expected.add("exampleForFlatMap.zip_folder in root of archive_text file.txt");
        return expected;
    }

    private void checkThatContentOfTestedAndFlatZipsAreEqual( List<DataWrapper> fatDataWrappers) throws IOException {
        final byte[] flatCloneOfFatZipBytes = IOUtils.resourceToByteArray(ZIP_FLAT_CLONE_EXAMPLE_FOR_FLAT_MAP_ZIP);

        final List<ByteBuffer> bytesFromTestedZip = fatDataWrappers.stream().map(e-> ByteBuffer.wrap(e.getData())).collect(Collectors.toCollection(ArrayList::new));

        final List<ByteBuffer> bytesFromFlatZip = new ArrayList<>();

        try  (ZipArchiveInputStream zais = new ZipArchiveInputStream(new ByteArrayInputStream(flatCloneOfFatZipBytes))) {
            ZipArchiveEntry entry;
            while ((entry = zais.getNextZipEntry()) != null) {
                bytesFromFlatZip.add(ByteBuffer.wrap(IOUtils.toByteArray(zais)));
            }
        }
        assertEquals(bytesFromFlatZip.size(), bytesFromTestedZip.size());
        bytesFromFlatZip.forEach(e-> assertTrue(bytesFromTestedZip.contains(e)));
    }

    private void checkThatAllNamesAreUnique(List<DataWrapper> fatDataWrappers) {
        final Set<String> setOfUniqueNamesFromZip = fatDataWrappers.stream().map(DataWrapper::getName).collect(Collectors.toCollection(HashSet::new));
        assertEquals(20, setOfUniqueNamesFromZip.size()); // Check that names are unique
    }
}
