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

import static com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingQueueNames.LAYOUT_PARSING_REQUEST_QUEUE;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import com.google.common.collect.Sets;
import com.iqser.red.service.pdftron.redaction.v1.api.model.ProcessUntouchedDocumentRequest;
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.InternalServerErrorException;
import com.iqser.red.service.persistence.management.v1.processor.model.CvAnalysisServiceRequest;
import com.iqser.red.service.persistence.management.v1.processor.model.NerServiceRequest;
import com.iqser.red.service.persistence.management.v1.processor.model.OCRStatusUpdateResponse;
import com.iqser.red.service.persistence.management.v1.processor.model.image.ImageServiceRequest;
import com.iqser.red.service.persistence.management.v1.processor.service.layoutparsing.LayoutParsingRequestFactory;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionProviderService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ViewedPagesPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.AddRedactionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.CommentPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.ForceRedactionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.LegalBasisChangePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.RecategorizationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.RemoveRedactionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.annotations.ResizeRedactionPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileModelMapper;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeResult;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute;
import com.iqser.red.service.persistence.service.v1.api.shared.model.MessageType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileErrorInfo;
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.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class FileStatusService {

    private final FileStatusPersistenceService fileStatusPersistenceService;
    private final DossierPersistenceService dossierPersistenceService;
    private final RabbitTemplate rabbitTemplate;
    private final ManualRedactionProviderService manualRedactionProviderService;
    private final FileManagementStorageService fileManagementStorageService;
    private final LegalBasisChangePersistenceService legalBasisChangePersistenceService;
    private final RecategorizationPersistenceService recategorizationPersistenceService;
    private final CommentPersistenceService commentPersistenceService;
    private final ForceRedactionPersistenceService forceRedactionPersistenceService;
    private final RemoveRedactionPersistenceService removeRedactionPersistenceService;
    private final AddRedactionPersistenceService addRedactionPersistenceService;
    private final ResizeRedactionPersistenceService resizeRedactionPersistenceService;
    private final FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService;
    private final FileManagementServiceSettings settings;
    private final ReanalysisRequiredStatusService reanalysisRequiredStatusService;
    private final ViewedPagesPersistenceService viewedPagesPersistenceService;
    private final FileManagementServiceSettings fileManagementServiceSettings;
    private final LayoutParsingRequestFactory layoutParsingRequestFactory;


    @Transactional
    public List<FileModel> getAllRelevantStatusesForReanalysisScheduler() {

        var fileEntities = fileStatusPersistenceService.getAllRelevantStatusesForReanalysisScheduler(fileManagementServiceSettings.getMaxErrorRetries());
        var convertedList = MagicConverter.convert(fileEntities, FileModel.class, new FileModelMapper());
        log.debug("Initial list of files for scheduler contains: {} files.", fileEntities.size());
        return reanalysisRequiredStatusService.enhanceFileStatusWithAnalysisRequirements(convertedList).stream().filter(FileModel::isAnalysisRequired).collect(Collectors.toList());
    }


    @Transactional
    public List<FileModel> getActiveFiles(String dossierId) {

        var fileEntities = fileStatusPersistenceService.getActiveFiles(dossierId);
        var convertedList = MagicConverter.convert(fileEntities, FileModel.class, new FileModelMapper());
        return reanalysisRequiredStatusService.enhanceFileStatusWithAnalysisRequirements(convertedList);
    }


    @Transactional
    public List<FileModel> getDossierStatus(String dossierId) {

        var fileEntities = new ArrayList<>(fileStatusPersistenceService.getStatusesForDossier(dossierId));
        var convertedList = MagicConverter.convert(fileEntities, FileModel.class, new FileModelMapper());
        return reanalysisRequiredStatusService.enhanceFileStatusWithAnalysisRequirements(convertedList);
    }


    @Transactional
    public List<FileModel> getStatusesAddedBefore(OffsetDateTime end) {

        var fileEntities = fileStatusPersistenceService.getStatusesAddedBefore(end);
        return MagicConverter.convert(fileEntities, FileModel.class, new FileModelMapper());
    }


    public List<FileModel> getSoftDeletedForDossierList(List<String> dossierIds) {

        var fileEntities = fileStatusPersistenceService.getSoftDeletedFiles(dossierIds);
        var convertedList = MagicConverter.convert(fileEntities, FileModel.class, new FileModelMapper());
        return reanalysisRequiredStatusService.enhanceFileStatusWithAnalysisRequirements(convertedList);
    }


    @Transactional
    public FileModel getStatus(String fileId) {

        var fileEntity = fileStatusPersistenceService.getStatus(fileId);
        var converted = MagicConverter.convert(fileEntity, FileModel.class, new FileModelMapper());
        return reanalysisRequiredStatusService.enhanceFileStatusWithAnalysisRequirements(converted);
    }


    @Transactional
    public void updateProcessingStatusPreprocessed(String dossierId, String fileId, boolean hasHighlights, long fileSize) {

        fileStatusPersistenceService.updateProcessingStatusPreprocessed(fileId, hasHighlights, fileSize);
        addToAnalysisQueue(dossierId, fileId, false, Set.of(), false);

    }


    @Transactional
    protected void addToAnalysisQueue(String dossierId, String fileId, boolean priority, Set<Integer> sectionsToReanalyse, boolean manualRedactionReanalyse) {

        var dossier = dossierPersistenceService.getAndValidateDossier(dossierId);
        var fileEntity = fileStatusPersistenceService.getStatus(fileId);

        if (!fileManagementStorageService.objectExists(dossierId, fileId, FileType.ORIGIN)) {
            addToPreprocessingQueue(dossierId, fileId, fileEntity.getFilename());
            return;
        }

        if (fileEntity.isExcluded()) {
            log.debug("File {} is excluded", fileEntity.getId());
            setStatusProcessed(fileId);
            return;
        }

        if (settings.isFigureDetectionEnabled() && !fileManagementStorageService.objectExists(dossierId, fileId, FileType.FIGURE)) {
            log.debug("Add file: {} from dossier {} to Figure Detection queue", fileId, dossierId);
            addToFigureDetectionRequestQueue(dossierId, fileId);
            return;
        }

        if (settings.isCvTableParsingEnabled() && !fileManagementStorageService.objectExists(dossierId, fileId, FileType.TABLES)) {
            log.debug("Add file: {} from dossier {} to Cv Service queue", fileId, dossierId);
            addToTableParsingRequestQueue(dossierId, fileId);
            return;
        }

        if (settings.isImageServiceEnabled() && !fileManagementStorageService.objectExists(dossierId, fileId, FileType.IMAGE_INFO)) {
            log.info("Add file: {} from dossier {} to Image queue", fileId, dossierId);
            addToImageQueue(dossierId, fileId);
            return;
        }

        var fileModel = MagicConverter.convert(fileEntity, FileModel.class, new FileModelMapper());
        fileModel = reanalysisRequiredStatusService.enhanceFileStatusWithAnalysisRequirements(fileModel, true);

        var dossierTemplate = dossier.getDossierTemplate();
        if (dossierTemplate.isOcrByDefault() && fileModel.getOcrEndTime() == null) {
            log.debug("Add file: {} from dossier {} to OCR queue", fileId, dossierId);
            setStatusOcrQueued(dossierId, fileId);
            return;
        }

        if (!fileManagementStorageService.objectExists(dossierId, fileId, FileType.DOCUMENT_TEXT)) {
            var layoutParsingRequest = layoutParsingRequestFactory.build(dossierId, fileId, priority, dossier);
            setStatusFullProcessing(fileId);
            rabbitTemplate.convertAndSend(LAYOUT_PARSING_REQUEST_QUEUE, layoutParsingRequest);
            return;
        }

        if (settings.isNerServiceEnabled() && !fileManagementStorageService.objectExists(dossierId, fileId, FileType.NER_ENTITIES)) {
            log.debug("Add file: {} from dossier {} to NER queue", fileId, dossierId);
            addToNerQueue(dossierId, fileId);
            return;
        }

        boolean reanalyse = fileModel.isReanalysisRequired() || manualRedactionReanalyse;
        MessageType messageType = calculateMessageType(reanalyse, fileModel.getProcessingStatus(), fileModel);

        var analyseRequest = AnalyzeRequest.builder()
                .messageType(messageType)
                .dossierId(dossierId)
                .analysisNumber(fileModel.getNumberOfAnalyses() + 1)
                .sectionsToReanalyse(sectionsToReanalyse)
                .fileId(fileId)
                .manualRedactions(manualRedactionProviderService.getManualRedactions(fileId))
                .dossierTemplateId(dossier.getDossierTemplateId())
                .lastProcessed(fileModel.getLastProcessed())
                .fileAttributes(convertAttributes(fileEntity.getFileAttributes(), dossier.getDossierTemplateId()))
                .excludedPages(fileModel.getExcludedPages())
                .build();

        log.info("Add file: {} from dossier {} to Analysis queue with MessageType {}", fileId, dossierId, messageType);

        if (messageType.equals(MessageType.REANALYSE)) {
            setStatusProcessing(fileId);
        } else {
            setStatusFullProcessing(fileId);
        }

        if (priority) {
            rabbitTemplate.convertAndSend(MessagingConfiguration.REDACTION_PRIORITY_QUEUE, analyseRequest);
        } else {
            rabbitTemplate.convertAndSend(MessagingConfiguration.REDACTION_QUEUE, analyseRequest);
        }
    }


    @SneakyThrows
    public void addToPreprocessingQueue(String dossierId, String fileId, String filename) {

        var processUntouchedDocumentRequest = ProcessUntouchedDocumentRequest.builder().dossierId(dossierId).fileId(fileId).fileName(filename).build();

        setStatusPreProcessingQueued(fileId);

        rabbitTemplate.convertAndSend(MessagingConfiguration.PRE_PROCESSING_QUEUE, processUntouchedDocumentRequest);
    }


    public void setStatusProcessed(String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.PROCESSED);
    }


    public void addToFigureDetectionRequestQueue(String dossierId, String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.FIGURE_DETECTION_ANALYZING);

        rabbitTemplate.convertAndSend(MessagingConfiguration.CV_ANALYSIS_QUEUE,
                CvAnalysisServiceRequest.builder()
                        .dossierId(dossierId)
                        .fileId(fileId)
                        .operation(CvAnalysisServiceRequest.OPERATION_FIGURE_DETECTION)
                        .targetFileExtension(CvAnalysisServiceRequest.TARGET_FILE_EXTENSION)
                        .responseFileExtension(CvAnalysisServiceRequest.FIGURE_DETECTION_FILE_EXTENSION)
                        .build(),
                message -> {
                    message.getMessageProperties().setPriority(1);
                    return message;
                });
    }


    public void addToTableParsingRequestQueue(String dossierId, String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.TABLE_PARSING_ANALYZING);

        rabbitTemplate.convertAndSend(MessagingConfiguration.CV_ANALYSIS_QUEUE,
                CvAnalysisServiceRequest.builder()
                        .dossierId(dossierId)
                        .fileId(fileId)
                        .operation(CvAnalysisServiceRequest.OPERATION_TABLE_PARSING)
                        .targetFileExtension(CvAnalysisServiceRequest.TARGET_FILE_EXTENSION)
                        .responseFileExtension(CvAnalysisServiceRequest.TABLE_PARSING_FILE_EXTENSION)
                        .build(),
                message -> {
                    message.getMessageProperties().setPriority(1);
                    return message;
                });
    }


    public void addToImageQueue(String dossierId, String fileId) {

        setStatusImageAnalyzing(fileId);
        rabbitTemplate.convertAndSend(MessagingConfiguration.IMAGE_SERVICE_QUEUE,
                ImageServiceRequest.builder()
                        .dossierId(dossierId)
                        .fileId(fileId)
                        .targetFileExtension(ImageServiceRequest.TARGET_FILE_EXTENSION)
                        .responseFileExtension(ImageServiceRequest.RESPONSE_FILE_EXTENSION)
                        .build(),
                message -> {
                    message.getMessageProperties().setPriority(1);
                    return message;
                });
    }


    public void setStatusOcrQueued(String dossierId, String fileId) {

        FileEntity fileStatus = fileStatusPersistenceService.getStatus(fileId);

        if (fileStatus.isExcluded()) {
            log.debug("File {} is excluded", fileId);
            return;
        }

        updateOCRStartTime(fileId);
        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.OCR_PROCESSING_QUEUED);
        addToOcrQueue(dossierId, fileId, 2);
    }


    protected void addToNerQueue(String dossierId, String fileId) {

        setStatusNerAnalyzing(fileId);
        rabbitTemplate.convertAndSend(MessagingConfiguration.NER_SERVICE_QUEUE,
                NerServiceRequest.builder()
                        .dossierId(dossierId)
                        .fileId(fileId)
                        .targetFileExtension(NerServiceRequest.TARGET_FILE_EXTENSION)
                        .responseFileExtension(NerServiceRequest.RESPONSE_FILE_EXTENSION)
                        .build(),
                message -> {
                    message.getMessageProperties().setPriority(1);
                    return message;
                });
    }


    private MessageType calculateMessageType(boolean reanalyse, ProcessingStatus processingStatus, FileModel fileModel) {

        if (ProcessingStatus.NER_ANALYZING.equals(processingStatus)) {
            return MessageType.ANALYSE;
        }

        if (reanalyse) {
            if (fileModel.getLastFileAttributeChange() != null && fileModel.getLastProcessed().isBefore(fileModel.getLastFileAttributeChange())) {
                return MessageType.ANALYSE;
            } else {
                return MessageType.REANALYSE;
            }
        }

        return MessageType.ANALYSE;
    }


    private List<FileAttribute> convertAttributes(List<FileAttributeEntity> fileAttributes, String dossierTemplateId) {

        List<FileAttribute> fileAttributeList = new ArrayList<>();

        var fileAttributeConfigs = fileAttributeConfigPersistenceService.getFileAttributes(dossierTemplateId);

        for (FileAttributeEntity fileAttribute : fileAttributes) {

            var config = fileAttributeConfigs.stream()
                    .filter(fac -> fac.getId().equals(fileAttribute.getFileAttributeId().getFileAttributeConfigId()))
                    .findFirst()
                    .orElseThrow(() -> new InternalServerErrorException("File Attribute config not defined for attribute :" + fileAttribute.getFileAttributeId()));

            var attribute = FileAttribute.builder()
                    .id(fileAttribute.getFileAttributeId().getFileAttributeConfigId())
                    .label(config.getLabel())
                    .placeholder(config.getPlaceholder())
                    .value(fileAttribute.getValue())
                    .build();
            fileAttributeList.add(attribute);
        }

        return fileAttributeList;
    }


    public void setStatusProcessing(String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.PROCESSING);
    }


    public void setStatusFullProcessing(String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.FULL_PROCESSING);
    }


    public void setStatusPreProcessingQueued(String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.PRE_PROCESSING_QUEUED);
    }


    public void setStatusImageAnalyzing(String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.IMAGE_ANALYZING);
    }


    public void updateOCRStartTime(String fileId) {

        fileStatusPersistenceService.updateOCRStartTime(fileId, OffsetDateTime.now());
    }


    private void addToOcrQueue(String dossierId, String fileId, int priority) {

        rabbitTemplate.convertAndSend(MessagingConfiguration.OCR_QUEUE, new com.iqser.red.service.ocr.v1.api.model.DocumentRequest(dossierId, fileId), message -> {
            message.getMessageProperties().setPriority(priority);
            return message;
        });
    }


    public void setStatusNerAnalyzing(String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.NER_ANALYZING);
    }


    public void setExcludedPages(String fileId, Set<Integer> excludedPages) {

        fileStatusPersistenceService.setExcludedPages(fileId, excludedPages);
    }


    public void setStatusSuccessful(String fileId, AnalyzeResult analyzeResult) {

        fileStatusPersistenceService.updateProcessingStatus(fileId,
                analyzeResult.getNumberOfPages(),
                analyzeResult.getDictionaryVersion(),
                analyzeResult.getRulesVersion(),
                analyzeResult.getComponentRulesVersion(),
                analyzeResult.getLegalBasisVersion(),
                analyzeResult.getDuration(),
                analyzeResult.getDossierDictionaryVersion(),
                analyzeResult.getAnalysisVersion(),
                analyzeResult.getAnalysisNumber());
    }


    public void setStatusSuccessful(String fileId, WorkflowStatus workflowStatus) {

        fileStatusPersistenceService.updateWorkflowStatus(fileId, workflowStatus, false);
    }


    public void setApprovalStatusSuccessful(String dossierId, String fileId, WorkflowStatus workflowStatus) {

        fileStatusPersistenceService.updateWorkflowStatus(fileId, workflowStatus, true);
    }


    @Transactional
    public void setStatusReprocess(String dossierId, String fileId, boolean priority) {

        setStatusReprocess(dossierId, fileId, priority, Sets.newHashSet(), false, false);
    }


    @Transactional
    public void setStatusReprocess(String dossierId,
                                   String fileId,
                                   boolean priority,
                                   Set<Integer> sectionsToReanalyse,
                                   boolean triggeredManually,
                                   boolean manualRedactionReanalyse) {

        log.info("Reprocessing file: {} from dossier {}", fileId, dossierId);

        FileEntity fileStatus = fileStatusPersistenceService.getStatus(fileId);

        if (fileStatus.isExcluded()) {
            log.debug("File {} is excluded", fileStatus.getId());
            return;
        }

        if (fileStatus.isExcludedFromAutomaticAnalysis() && !triggeredManually) {
            log.debug("File {} is excluded from automatic analysis", fileStatus.getId());
            return;
        }

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.REPROCESS);
        addToAnalysisQueue(dossierId, fileId, priority, sectionsToReanalyse, manualRedactionReanalyse);
    }


    @Transactional
    public void setStatusReprocessForManual(String dossierId, String fileId, boolean priority) {

        setStatusReprocess(dossierId, fileId, priority, Sets.newHashSet(), false, true);
    }


    @Transactional
    public void setStatusReprocess(String dossierId, String fileId, boolean priority, boolean triggeredManually) {

        setStatusReprocess(dossierId, fileId, priority, Sets.newHashSet(), triggeredManually, priority);
    }


    @Transactional
    public void createStatus(String dossierId, String fileId, String uploader, String filename, long size) {

        fileStatusPersistenceService.createStatus(dossierId, fileId, filename, uploader, size);
        addToAnalysisQueue(dossierId, fileId, false, Set.of(), false);
    }


    public void setStatusIndexingSuccessful(String fileId) {

        fileStatusPersistenceService.setUpdateStatusIndexingSuccessful(fileId);
    }


    public void setStatusIndexing(String fileId) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.INDEXING);
    }


    public void setStatusError(String fileId, FileErrorInfo fileErrorInfo) {

        fileStatusPersistenceService.updateStatusErrorInfo(fileId, fileErrorInfo);
        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.ERROR);
    }


    public void setFileStatusDeleted(String fileId, OffsetDateTime softDeletedTime) {

        fileStatusPersistenceService.softDelete(fileId, softDeletedTime);
    }


    public void setFileStatusHardDeleted(String fileId) {

        fileStatusPersistenceService.hardDelete(fileId);
    }


    public void setFileStatusUndeleted(String fileId) {

        fileStatusPersistenceService.undelete(fileId);
    }


    public void updateFileModificationDate(String fileId, OffsetDateTime fileManipulationDate) {

        fileStatusPersistenceService.updateFileModificationDate(fileId, fileManipulationDate);
    }


    public void updateHasHighlights(String fileId, boolean hasHighlights) {

        fileStatusPersistenceService.updateHasHighlights(fileId, hasHighlights);
    }


    public void setAssignee(String fileId, String assignee) {

        FileEntity fileStatus = fileStatusPersistenceService.getStatus(fileId);
        String lastReviewer = fileStatus.getLastReviewer();
        String lastApprover = fileStatus.getLastApprover();
        if (WorkflowStatus.UNDER_REVIEW.equals(fileStatus.getWorkflowStatus())) {
            lastReviewer = StringUtils.isNotEmpty(assignee) ? fileStatus.getAssignee() : lastReviewer;
        }
        if (WorkflowStatus.UNDER_APPROVAL.equals(fileStatus.getWorkflowStatus())) {
            lastApprover = StringUtils.isNotEmpty(assignee) ? fileStatus.getAssignee() : lastApprover;
        }

        fileStatusPersistenceService.setAssignee(fileId, assignee, lastReviewer, lastApprover);
    }


    @Transactional
    public void toggleExclusion(String dossierId, String fileId, boolean excluded) {

        fileStatusPersistenceService.toggleExclusion(fileId, excluded);

        if (!excluded) {
            setStatusFullReprocess(dossierId, fileId, false, true);
        }
    }


    @Transactional
    public void setStatusFullReprocess(String dossierId, String fileId, boolean priority, boolean requiresStructureAnalysis) {

        FileEntity fileStatus = fileStatusPersistenceService.getStatus(fileId);

        if (fileStatus.isExcluded()) {
            log.debug("File {} is excluded", fileStatus.getId());
            return;
        }

        if (requiresStructureAnalysis) {
            log.info("Delete text and NER entities from file {} in dossier {}", fileId, dossierId);
            fileManagementStorageService.deleteObject(dossierId, fileId, FileType.DOCUMENT_POSITION);
            fileManagementStorageService.deleteObject(dossierId, fileId, FileType.DOCUMENT_PAGES);
            fileManagementStorageService.deleteObject(dossierId, fileId, FileType.DOCUMENT_TEXT);
            fileManagementStorageService.deleteObject(dossierId, fileId, FileType.DOCUMENT_STRUCTURE);
            fileManagementStorageService.deleteObject(dossierId, fileId, FileType.NER_ENTITIES);
        }

        addToAnalysisQueue(dossierId, fileId, priority, Sets.newHashSet(), false);
    }


    @Transactional
    public void toggleAutomaticAnalysis(String dossierId, String fileId, boolean excludedFromAutomaticAnalysis) {

        fileStatusPersistenceService.toggleAutomaticAnalysis(fileId, excludedFromAutomaticAnalysis);

        if (!excludedFromAutomaticAnalysis) {
            setStatusFullReprocess(dossierId, fileId, false, true);
        }
    }


    public void setStatusPreProcessing(String fileId, int processingErrorCount) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.PRE_PROCESSING, processingErrorCount);
    }


    public void setStatusOcrProcessing(String fileId, int processingErrorCount) {

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.OCR_PROCESSING, processingErrorCount);
    }


    @Transactional
    public void overwriteFile(String dossierId, String fileId, String uploader, String filename, boolean keepManualRedactions) {

        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.ORIGIN);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.REDACTION_LOG);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.SECTION_GRID);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.IMAGE_INFO);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.DOCUMENT_STRUCTURE);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.DOCUMENT_PAGES);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.DOCUMENT_POSITION);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.DOCUMENT_TEXT);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.NER_ENTITIES);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.FIGURE);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.TABLES);

        if (keepManualRedactions) {
            fileStatusPersistenceService.overwriteFile(fileId, uploader, filename, true);
        } else {
            fileStatusPersistenceService.overwriteFile(fileId, uploader, filename, false);
            deleteManualRedactions(dossierId, fileId);
        }

        viewedPagesPersistenceService.deleteForFile(fileId);

        addToAnalysisQueue(dossierId, fileId, false, Set.of(), false);
    }


    @Transactional
    public void deleteManualRedactions(String dossierId, String fileId) {

        OffsetDateTime now = OffsetDateTime.now();
        // remove everything related to redaction
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.REDACTION_LOG);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.SECTION_GRID);
        fileManagementStorageService.deleteObject(dossierId, fileId, FileType.IMAGE_INFO);

        // wipe comments
        var comments = commentPersistenceService.findCommentsByFileID(fileId, false);
        comments.forEach((key, value) -> value.forEach(comment -> commentPersistenceService.softDelete(comment.getId(), now)));

        // wipe force redactions
        var forceRedactions = forceRedactionPersistenceService.findForceRedactions(fileId, false);
        forceRedactions.forEach(f -> forceRedactionPersistenceService.softDelete(fileId, f.getId().getAnnotationId(), now));

        // wipe add manual redactions
        var addRedactions = addRedactionPersistenceService.findAddRedactions(fileId, false);
        addRedactions.forEach(f -> addRedactionPersistenceService.softDelete(fileId, f.getId().getAnnotationId(), now));

        // wipe removeRedactions
        var removeRedactions = removeRedactionPersistenceService.findRemoveRedactions(fileId, false);
        removeRedactions.forEach(f -> removeRedactionPersistenceService.softDelete(fileId, f.getId().getAnnotationId(), now));

        // wipe image recat
        var imageRecategorizations = recategorizationPersistenceService.findRecategorizations(fileId, false);
        imageRecategorizations.forEach(f -> recategorizationPersistenceService.softDelete(fileId, f.getId().getAnnotationId(), now));// wipe image recat

        // wipe resize redactions
        var resizeRedactions = resizeRedactionPersistenceService.findResizeRedactions(fileId, false);
        resizeRedactions.forEach(f -> resizeRedactionPersistenceService.softDelete(fileId, f.getId().getAnnotationId(), now));

        // wipe legal basis changes
        var legalBasisChanges = legalBasisChangePersistenceService.findLegalBasisChanges(fileId, false);
        legalBasisChanges.forEach(f -> legalBasisChangePersistenceService.softDelete(fileId, f.getId().getAnnotationId(), now));

        fileStatusPersistenceService.updateHasComments(fileId, false);
    }


    @Transactional
    public void setStatusAnalyse(String dossierId, String fileId, boolean priority) {

        FileEntity fileStatus = fileStatusPersistenceService.getStatus(fileId);

        if (fileStatus.isExcluded()) {
            log.debug("File {} is excluded", fileStatus.getId());
            return;
        }

        fileStatusPersistenceService.updateProcessingStatus(fileId, ProcessingStatus.ANALYSE);
        addToAnalysisQueue(dossierId, fileId, priority, Sets.newHashSet(), false);
    }


    public void updateLayoutProcessedTime(String fileId) {

        fileStatusPersistenceService.updateLayoutProcessedTime(fileId);
    }


    public int countSoftDeletedFiles(String dossierId) {

        return fileStatusPersistenceService.countSoftDeletedFilesPerDossierId(dossierId);
    }


    public boolean hasChangesSince(String dossierId, OffsetDateTime since) {

        return fileStatusPersistenceService.hasChangesSince(dossierId, since);
    }


    public void updateOCRStatus(OCRStatusUpdateResponse response) {

        fileStatusPersistenceService.updateOCRStatus(response);
    }

}
