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

import static com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.FileExchangeArchivalService.SIZE_THRESHOLD;

import java.time.OffsetDateTime;
import java.util.Collections;
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.pdftron.redaction.v1.api.model.RedactionMessage;
import com.iqser.red.service.pdftron.redaction.v1.api.model.RedactionType;
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.DatasetExchangeImportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.TemplateImportInfo;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.zipreaders.DatasetExchangeArchiveReader;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.zipreaders.ZipEntryIterator;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.ColorsEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.service.ColorsService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierCreatorService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.UploadService;
import com.iqser.red.service.persistence.management.v1.processor.service.download.DownloadPreparationService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
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.ImportResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierRequest;
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.WorkflowStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatusValue;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantContext;

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 DatasetExchangeService {

    DossierTemplateManagementService dossierTemplateManagementService;
    DownloadStatusPersistenceService downloadStatusPersistenceService;
    FileStatusPersistenceService fileStatusPersistenceService;
    DossierPersistenceService dossierPersistenceService;
    ColorsService colorsService;
    RabbitTemplate rabbitTemplate;
    FileManagementServiceSettings settings;
    DossierTemplateImportService dossierTemplateImportService;
    DossierCreatorService dossierCreatorService;
    ReportTemplatePersistenceService reportTemplateService;
    UploadService uploadService;
    DownloadPreparationService downloadPreparationService;
    FileManagementStorageService storageService;


    public DownloadResponse prepareExport(String dossierTemplateId) {

        var template = dossierTemplateManagementService.getDossierTemplate(dossierTemplateId);
        String downloadFilename = template.getName() + "_PREVIEW_FILES.zip";
        String storageId = StorageIdUtils.getStorageId(KeycloakSecurity.getUserId(), dossierTemplateId + "_PREVIEW_FILES");
        List<FileEntity> files = fileStatusPersistenceService.getActiveFilesForDossierTemplate(dossierTemplateId);
        List<DossierEntity> dossiers = dossierPersistenceService.findAllActiveDossiersForDossierTemplateId(dossierTemplateId);
        if (dossiers.isEmpty()) {
            throw new BadRequestException("No dossiers in DossierTemplate with id" + dossierTemplateId);
        }
        ColorsEntity colors = colorsService.getColors(dossierTemplateId);

        downloadPreparationService.clearRedactionStatusEntries(storageId);
        if (storageService.objectExists(storageId)) {
            storageService.deleteObject(storageId);
        }

        DownloadStatusEntity downloadStatus = downloadStatusPersistenceService.createStatus(KeycloakSecurity.getUserId(),
                                                                                            storageId,
                                                                                            dossiers.get(0),
                                                                                            downloadFilename,
                                                                                            "application/zip",
                                                                                            files.stream()
                                                                                                    .map(FileEntity::getId)
                                                                                                    .toList(),
                                                                                            Set.of(DownloadFileType.DATASET_EXPORT),
                                                                                            Collections.emptyList(),
                                                                                            colors.getPreviewColor());

        RedactionMessage.RedactionMessageBuilder builder = RedactionMessage.builder()
                .appliedRedactionColor(colors.getRedactionColor())
                .redactionPreviewColor(colors.getPreviewColor())
                .keepHiddenText(true)
                .keepOverlappingObjects(true)
                .keepImageMetaData(true)
                .downloadId(storageId)
                .redactionTypes(List.of(RedactionType.PREVIEW));

        downloadStatus.getFiles()
                .forEach(fileEntity -> {
                    RedactionMessage message = builder.dossierId(fileEntity.getDossierId())
                            .fileId(fileEntity.getId())
                            .unapprovedFile(fileEntity.getWorkflowStatus() != WorkflowStatus.APPROVED)
                            .build();
                    log.info("Sending redaction request for downloadId:{} fileId:{} to pdftron-redaction-queue", storageId, fileEntity.getId());
                    rabbitTemplate.convertAndSend(MessagingConfiguration.PDFTRON_REQUEST_EXCHANGE, TenantContext.getTenantId(), message);
                });

        downloadStatusPersistenceService.updateStatus(downloadStatus.getStorageId(), DownloadStatusValue.GENERATING);

        return new DownloadResponse(storageId);
    }


    @SneakyThrows
    public ImportResponse importDataset(String userId, byte[] archive) {

        DatasetExchangeArchiveReader datasetExchangeArchiveReader = new DatasetExchangeArchiveReader(userId);
        try (ZipEntryIterator zipEntryIterator = new ZipEntryIterator(archive, settings.getCompressionThresholdRatio(), SIZE_THRESHOLD)) {
            zipEntryIterator.forEachRemaining(datasetExchangeArchiveReader::handleZipEntryData);
        }
        DatasetExchangeImportModel importModel = datasetExchangeArchiveReader.getDatasetExchangeImportModel();

        TemplateImportInfo templateImportInfo = dossierTemplateImportService.importDossierTemplate(importModel.getImportTemplateResult());
        for (DatasetExchangeImportModel.Dossier dossier : importModel.getDossiers()) {

            String dossierId = getDossierForImport(templateImportInfo, dossier, userId);
            for (DatasetExchangeImportModel.File file : dossier.getFiles()) {
                uploadService.processSingleFile(dossierId, file.getName(), file.getData(), false, false);
            }
        }
        return new ImportResponse(templateImportInfo.getDossierTemplateId());
    }


    private String getDossierForImport(TemplateImportInfo templateImportInfo, DatasetExchangeImportModel.Dossier dossier, String userId) {

        String dossierTemplateId = templateImportInfo.getDossierTemplateId();

        CreateOrUpdateDossierRequest request = CreateOrUpdateDossierRequest.builder()
                .dossierName(dossier.getName())
                .description(dossier.getName())
                .downloadFileTypes(Collections.emptySet())
                .dossierTemplateId(dossierTemplateId)
                .requestingUser(userId)
                .reportTemplateIds(reportTemplateService.findByDossierTemplateId(templateImportInfo.getDossierTemplateId())
                                           .stream()
                                           .map(ReportTemplateEntity::getTemplateId)
                                           .toList())
                .build();

        Dossier importedDossier = null;
        int retries = 0;
        while (importedDossier == null && retries < 100) {
            try {
                importedDossier = dossierCreatorService.addDossier(request, Set.of(userId), Set.of(userId), userId);
            } catch (ConflictException e) {
                retries++;
                request.setDossierName(String.format("%s (%d)", dossier.getName(), retries));
            }
        }
        if (importedDossier == null) {
            throw new BadRequestException(String.format("Could not create dossier with name %s in %d retries", dossier.getName(), retries));
        }
        return importedDossier.getId();
    }

}
