package com.iqser.red.service.persistence.management.v1.processor.dataexchange.zipreaders;

import static com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.FileExchangeExportService.buildFileName;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.commons.jackson.ObjectMapperFactory;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.FileExchangeImportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.FileExchangeNames;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.FileWithType;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.manualchanges.ManualChangesExportModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;

import lombok.AccessLevel;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;

@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class FileExchangeArchiveReader {

    ObjectMapper mapper;
    String userId;
    DossierTemplateArchiveReader dossierTemplateArchiveReader;
    Map<String, EntityTypeArchiveReader> entityTypeArchiveReaderMap;
    FileExchangeImportModel fileExchangeImportModel;
    Pattern UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}");


    public FileExchangeArchiveReader(String userId) {

        this.mapper = ObjectMapperFactory.create();
        this.userId = userId;
        this.dossierTemplateArchiveReader = new DossierTemplateArchiveReader(this.userId, null, false);
        entityTypeArchiveReaderMap = new HashMap<>();
        fileExchangeImportModel = FileExchangeImportModel.builder().build();
    }


    public FileExchangeImportModel buildResult() {

        fileExchangeImportModel.setImportTemplateResult(dossierTemplateArchiveReader.buildResult());
        entityTypeArchiveReaderMap.forEach((dossierId, entityTypeArchiveReader) -> //
                                                   fileExchangeImportModel.getDossierTypes().put(dossierId, entityTypeArchiveReader.getEntityTypeImportModel()));
        return fileExchangeImportModel;
    }


    @SneakyThrows
    public void handleZipEntryData(ZipEntryData zipEntryData) {

        if (zipEntryData.getBaseFolder().equals(FileExchangeNames.TEMPLATE_FOLDER)) {
            dossierTemplateArchiveReader.handleZipEntryData(zipEntryData);
        } else if (zipEntryData.getDepth() > 1 && UUID_PATTERN.matcher(zipEntryData.getBaseFolder()).matches()) { // in dossier folders

            String dossierId = zipEntryData.getBaseFolder();

            if (zipEntryData.isInFolder(FileExchangeNames.DOSSIER_ENTITY_FOLDER)) {
                getOrCreateEntityTypeArchiveReader(dossierId).handleZipEntryData(zipEntryData);
            } else if (zipEntryData.getFileName().equals(FileExchangeNames.DOSSIER)) {
                fileExchangeImportModel.getDossiers().add(mapper.readValue(zipEntryData.data(), Dossier.class));
            } else if (zipEntryData.getDepth() > 2 && zipEntryData.isFile()) { // in file folders

                String fileId = zipEntryData.getParentFolder();

                FileExchangeImportModel.FileImport file = fileExchangeImportModel.getOrCreateFile(dossierId, fileId);

                if (fileNameEquals(fileId, FileExchangeNames.FILE_STATUS, zipEntryData)) {
                    file.setFileModel(mapper.readValue(zipEntryData.data(), FileModel.class));
                } else if (fileNameEquals(fileId, FileExchangeNames.COMPONENT_LOG, zipEntryData)) {
                    file.setComponentLog(mapper.readValue(zipEntryData.data(), ComponentLog.class));
                } else if (fileNameEquals(fileId, FileExchangeNames.COMPONENT_OVERRIDES, zipEntryData)) {
                    file.setOverrides(mapper.readValue(zipEntryData.data(), new TypeReference<>() {
                    }));
                } else if (fileNameEquals(fileId, FileExchangeNames.ENTITY_LOG, zipEntryData)) {
                    file.setEntityLog(mapper.readValue(zipEntryData.data(), EntityLog.class));
                } else if (fileNameEquals(fileId, FileExchangeNames.FILE_ATTRIBUTES, zipEntryData)) {
                    file.getFileAttributes().add(mapper.readValue(zipEntryData.data(), FileAttribute.class));
                } else if (fileNameEquals(fileId, FileExchangeNames.MANUAL_REDACTIONS, zipEntryData)) {
                    file.setManualChanges(mapper.readValue(zipEntryData.data(), ManualChangesExportModel.class));
                } else {
                    Optional<FileType> optionalFileType = fileNameEqualsAny(fileId,
                                                                            zipEntryData,
                                                                            FileExchangeNames.STRUCTURE,
                                                                            FileExchangeNames.PAGES,
                                                                            FileExchangeNames.TEXT,
                                                                            FileExchangeNames.POSITIONS,
                                                                            FileExchangeNames.SIMPLIFIED_TEXT,
                                                                            FileExchangeNames.NER_ENTITIES,
                                                                            FileExchangeNames.AZURE_NER_ENTITIES,
                                                                            FileExchangeNames.LLM_NER_ENTITIES,
                                                                            FileExchangeNames.TABLES,
                                                                            FileExchangeNames.IMAGES,
                                                                            FileExchangeNames.VISUAL_LAYOUT,
                                                                            FileExchangeNames.IMPORTED,
                                                                            FileExchangeNames.HIGHLIGHTS,
                                                                            FileExchangeNames.FIGURE,
                                                                            FileExchangeNames.ORIGIN,
                                                                            FileExchangeNames.CHUNKS,
                                                                            FileExchangeNames.UNTOUCHED,
                                                                            FileExchangeNames.VIEWER);
                    if (optionalFileType.isPresent()) {
                        Path tmpFile = Files.createTempFile("tmp", ".tmp");
                        Files.write(tmpFile, zipEntryData.data());
                        file.getFilesToUpload().add(new FileWithType(optionalFileType.get(), tmpFile));
                    }
                }

            }
        }
    }


    private Optional<FileType> fileNameEqualsAny(String fileId, ZipEntryData zipEntryData, FileExchangeNames.Definition... definitions) {

        return Arrays.stream(definitions)
                .filter(def -> fileNameEquals(fileId, def, zipEntryData))
                .map(FileExchangeNames.Definition::fileType)
                .findAny();
    }


    private static boolean fileNameEquals(String fileId, FileExchangeNames.Definition definition, ZipEntryData zipEntryData) {

        return fileNameEquals(fileId, definition.getName(), zipEntryData);
    }


    private static boolean fileNameEquals(String fileId, String definition, ZipEntryData zipEntryData) {

        return buildFileName(fileId, definition).equals(zipEntryData.getFileName());
    }


    private EntityTypeArchiveReader getOrCreateEntityTypeArchiveReader(String dossierId) {

        if (entityTypeArchiveReaderMap.containsKey(dossierId)) {
            return entityTypeArchiveReaderMap.get(dossierId);
        }
        entityTypeArchiveReaderMap.put(dossierId, new EntityTypeArchiveReader());
        return entityTypeArchiveReaderMap.get(dossierId);
    }

}
