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

import java.nio.file.Path;

import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.FileExchangeNames;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileSystemBackedArchiver;
import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
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 com.knecon.fforesight.tenantcommons.TenantContext;

import io.micrometer.observation.annotation.Observed;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class FileExportService {

    ManualChangesExportService manualChangesExportService;
    ObjectMapper mapper;
    FileManagementStorageService storageService;


    @SneakyThrows
    @Observed(name = "FileExchangeExportService", contextualName = "export-file")
    public void addFileToArchive(FileSystemBackedArchiver archiver, Path folder, FileExchangeExportRequest request, FileModel file) {

        Path fileFolder = folder.resolve(file.getId());

        if (!request.excludeAnalysisLogs()) {
            if (storageService.entityLogExists(file.getDossierId(), file.getId())) {
                archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
                                                                            buildFileName(file, FileExchangeNames.ENTITY_LOG.getName()),
                                                                            mapper.writeValueAsBytes(storageService.getEntityLog(file.getDossierId(), file.getId()))));
            }
            if (storageService.componentLogExists(file.getDossierId(), file.getId())) {
                archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
                                                                            buildFileName(file, FileExchangeNames.COMPONENT_LOG.getName()),
                                                                            mapper.writeValueAsBytes(storageService.getComponentLog(file.getDossierId(), file.getId()))));
            }
        }

        archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder, buildFileName(file, FileExchangeNames.FILE_STATUS), mapper.writeValueAsBytes(file)));

        if (!request.excludeLayoutFiles()) {
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.STRUCTURE);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.TEXT);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.POSITIONS);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.PAGES);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.NER_ENTITIES);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.LLM_NER_ENTITIES);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.AZURE_NER_ENTITIES);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.SIMPLIFIED_TEXT);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.CHUNKS);
        }

        addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.FIGURE);
        addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.HIGHLIGHTS);
        addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.TABLES);
        addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.IMAGES);
        addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.VISUAL_LAYOUT);
        addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.IMPORTED);

        addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.ORIGIN);
        if (!request.originFileOnly()) {
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.UNTOUCHED);
            addArchiveModelForStorageFile(archiver, file, fileFolder, FileExchangeNames.VIEWER);
        }

        if (!request.excludeFileAttributes()) {
            archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
                                                                        buildFileName(file, FileExchangeNames.FILE_ATTRIBUTES),
                                                                        mapper.writeValueAsBytes(file.getFileAttributes())));
        }

        if (!request.excludeManualRedactions()) {
            archiver.addEntry(new FileSystemBackedArchiver.ArchiveModel(fileFolder,
                                                                        buildFileName(file, FileExchangeNames.MANUAL_REDACTIONS),
                                                                        mapper.writeValueAsBytes(manualChangesExportService.export(file.getId()))));
        }
    }


    private static String buildFileName(FileModel file, String name) {

        return buildFileName(file.getId(), name);
    }


    public static String buildFileName(String fileId, String name) {

        return fileId + "." + name;
    }


    @SneakyThrows
    private void addArchiveModelForStorageFile(FileSystemBackedArchiver archiver, FileModel file, Path fileFolder, FileExchangeNames.Definition definition) {

        String storageId = StorageIdUtils.getStorageId(file.getDossierId(), file.getId(), definition.fileType());
        if (!storageService.objectExists(storageId)) {
            log.debug("File {} not found in storage with id {}, skipping...", definition.fileType(), storageId);
            return;
        }
        log.debug("Adding file {} from storage with id {} to archive.", definition.fileType(), storageId);

        byte[] data = storageService.getObject(TenantContext.getTenantId(), storageId).readAllBytes();
        FileSystemBackedArchiver.ArchiveModel archiveModel = new FileSystemBackedArchiver.ArchiveModel(fileFolder, buildFileName(file, definition.getName()), data);
        archiver.addEntry(archiveModel);
    }

}
