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

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.ExportDownloadMessage;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.FileExchangeNames;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierIdFileIdRequestValidator;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
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.DownloadResponse;
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.Dossier;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantContext;

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

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

    DossierTemplateExportService dossierTemplateExportService;
    DossierTemplatePersistenceService dossierTemplatePersistenceService;
    DossierManagementService dossierManagementService;
    FileManagementStorageService storageService;
    DownloadStatusPersistenceService downloadStatusPersistenceService;

    RabbitTemplate rabbitTemplate;
    DossierIdFileIdRequestValidator requestValidator;
    DossierExportService dossierExportService;


    @Observed(name = "FileExchangeExportService", contextualName = "prepare-export")
    public DownloadResponse prepareExportDownload(String dossierTemplateId, FileExchangeExportRequest request) {

        var mimeType = "application/zip";
        DossierTemplateEntity dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(dossierTemplateId);
        String dossierTemplateName = dossierTemplate.getName();

        requestValidator.validateRequestOrThrow404(dossierTemplateId, request.dossierIds(), request.fileIds());

        String downloadFilename = dossierTemplateName + "_file-exchange.zip";
        String storageId = StorageIdUtils.getStorageId(KeycloakSecurity.getUserId(), dossierTemplateId + "_file-exchange");

        downloadStatusPersistenceService.createStatus(KeycloakSecurity.getUserId(), storageId, downloadFilename, mimeType);

        addToExportDownloadQueue(ExportDownloadMessage.builder()
                                         .storageId(storageId)
                                         .userId(KeycloakSecurity.getUserId())
                                         .type(ExportDownloadMessage.Type.FILE_EXCHANGE)
                                         .fileExchangeExportRequest(request)
                                         .dossierTemplateId(dossierTemplateId)
                                         .build());

        return new DownloadResponse(storageId);
    }


    private void addToExportDownloadQueue(ExportDownloadMessage downloadJob) {

        rabbitTemplate.convertAndSend(MessagingConfiguration.EXPORT_DOWNLOAD_REQUEST_EXCHANGE, TenantContext.getTenantId(), downloadJob, message -> {
            message.getMessageProperties().setPriority(1);
            return message;
        });
    }


    @Observed(name = "FileExchangeExportService", contextualName = "export-files")
    public long createDownloadArchive(ExportDownloadMessage downloadJob) throws IOException {

        FileExchangeExportRequest request = downloadJob.getFileExchangeExportRequest();
        DossierTemplateEntity dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(downloadJob.getDossierTemplateId());

        try (FileSystemBackedArchiver archiver = new FileSystemBackedArchiver()) {

            dossierTemplateExportService.addDossierTemplateToArchive(archiver, FileExchangeNames.TEMPLATE_FOLDER, dossierTemplate);

            Iterable<String> dossierIds;
            if (request.dossierIds().isEmpty()) {
                dossierIds = dossierManagementService.getAllDossierIdsForDossierTemplateId(dossierTemplate.getId(), true, false);
            } else {
                dossierIds = request.dossierIds();
            }

            for (String dossierId : dossierIds) {

                Dossier dossier = dossierManagementService.getDossierById(dossierId, true, false);

                dossierExportService.addDossierToArchive(archiver, Path.of(""), request, dossier);
            }

            storeZipFile(downloadJob.getStorageId(), archiver);

            return archiver.getContentLength();
        }
    }


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

        return fileId + "." + name;
    }


    @Observed(name = "FileExchangeExportService", contextualName = "store-archive")
    private void storeZipFile(String storageId, FileSystemBackedArchiver fileSystemBackedArchiver) {

        long start = System.currentTimeMillis();
        try (InputStream data = fileSystemBackedArchiver.toInputStream()) {
            storageService.storeObject(storageId, data);
        } catch (IOException ex) {
            log.warn("IO error when sending file to storage", ex);
        }
        log.info("Successfully stored zip for downloadId {}, took {}", storageId, System.currentTimeMillis() - start);
    }

}
