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

import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.projection.DossierStatsFileProjection;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.DossierChangeProjection;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FileChangeProjection;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FilePageCountsProjection;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FileProcessingStatusProjection;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.projection.FileWorkflowStatusProjection;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ErrorCode;
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 jakarta.persistence.Tuple;
import jakarta.transaction.Transactional;

public interface FileRepository extends JpaRepository<FileEntity, String> {

    boolean existsByDossierIdAndLastUpdatedIsAfter(String dossierId, OffsetDateTime since);


    List<FileEntity> findByDossierId(String dossierId);


    List<FileEntity> findByAddedBefore(OffsetDateTime end);


    @Modifying
    @Query("update FileEntity e set e.hasRedactions = :hasRedactions ,"
           + " e.hasHints = :hasHints, e.hasSuggestions = :hasSuggestions,"
           + " e.hasImages = :hasImages, e.hasUpdates = :hasUpdates, e.hasAnnotationComments = :hasComments, "
           + " e.lastUpdated = :lastUpdated "
           + " where e.id =:fileId")
    void updateFlags(@Param("fileId") String fileId,
                     @Param("lastUpdated") OffsetDateTime lastUpdated,
                     @Param("hasRedactions") boolean hasRedactions,
                     @Param("hasHints") boolean hasHints,
                     @Param("hasImages") boolean hasImages,
                     @Param("hasSuggestions") boolean hasSuggestions,
                     @Param("hasComments") boolean hasComments,
                     @Param("hasUpdates") boolean hasUpdates);


    @Query("select f from FileEntity f where f.assignee = :assignee")
    List<FileEntity> findFilesByAssignee(@Param("assignee") String assignee);


    @Modifying
    @Query("""
            update FileEntity f set f.numberOfPages = :numberOfPages, \
            f.processingStatus = :processingStatus, \
            f.dictionaryVersion = :dictionaryVersion, \
            f.rulesVersion = :rulesVersion, \
            f.componentRulesVersion = :componentRulesVersion, \
            f.dateFormatsVersion = :dateFormatsVersion, \
            f.legalBasisVersion = :legalBasisVersion, \
            f.analysisDuration = :analysisDuration, \
            f.dossierDictionaryVersion = :dossierDictionaryVersion, \
            f.analysisVersion = :analysisVersion, \
            f.numberOfAnalyses = :analysisNumber, \
            f.lastUpdated = :lastUpdated, \
            f.lastProcessed = :lastProcessed, \
            f.processingErrorCounter = :processingErrorCounter \
            where f.id = :fileId
            """)
    void updateProcessingStatus(@Param("fileId") String fileId,
                                @Param("numberOfPages") int numberOfPages,
                                @Param("processingStatus") ProcessingStatus processingStatus,
                                @Param("dictionaryVersion") long dictionaryVersion,
                                @Param("rulesVersion") long rulesVersion,
                                @Param("componentRulesVersion") long componentRulesVersion,
                                @Param("dateFormatsVersion") long dateFormatsVersion,
                                @Param("legalBasisVersion") long legalBasisVersion,
                                @Param("analysisDuration") long analysisDuration,
                                @Param("dossierDictionaryVersion") long dossierDictionaryVersion,
                                @Param("analysisVersion") int analysisVersion,
                                @Param("lastUpdated") OffsetDateTime lastUpdated,
                                @Param("lastProcessed") OffsetDateTime lastProcessed,
                                @Param("analysisNumber") int analysisNumber,
                                @Param("processingErrorCounter") int processingErrorCounter);


    @Modifying
    @Query("update FileEntity f set f.aiCreationVersion = :aiCreationVersion where f.id = :fileId")
    void setAiCreationVersion(@Param("fileId") String fileId, @Param("aiCreationVersion") long aiCreationVersion);


    @Modifying
    @Query("update FileEntity f set f.workflowStatus = :workflowStatus, f.lastUpdated = :lastUpdated, f.approvalDate = :approvalDate,"
           + " f.excludedFromAutomaticAnalysis = :excludedFromAutomaticAnalysis where f.id = :fileId")
    void updateWorkflowStatus(@Param("fileId") String fileId,
                              @Param("workflowStatus") WorkflowStatus workflowStatus,
                              @Param("lastUpdated") OffsetDateTime lastUpdated,
                              @Param("approvalDate") OffsetDateTime approvalDate,
                              @Param("excludedFromAutomaticAnalysis") boolean excludedFromAutomaticAnalysis);


    @Modifying(clearAutomatically = true, flushAutomatically = true)
    @Query("update FileEntity f set f.workflowStatus = :workflowStatus, f.lastUpdated = :lastUpdated, f.approvalDate = :approvalDate " + " where f.id = :fileId")
    void updateWorkflowStatus(@Param("fileId") String fileId,
                              @Param("workflowStatus") WorkflowStatus workflowStatus,
                              @Param("lastUpdated") OffsetDateTime lastUpdated,
                              @Param("approvalDate") OffsetDateTime approvalDate);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.fileSize = :fileSize  where f.id = :fileId")
    void updateFileSize(@Param("fileId") String fileId, @Param("fileSize") long fileSize);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :lastUpdated,"
           + " f.hasHighlights = :hasHighlights, f.processingErrorCounter = :processingErrorCounter "
           + " where f.id = :fileId")
    void updateProcessingStatus(@Param("fileId") String fileId,
                                @Param("processingStatus") ProcessingStatus processingStatus,
                                @Param("lastUpdated") OffsetDateTime lastUpdated,
                                @Param("hasHighlights") boolean hasHighlights,
                                @Param("processingErrorCounter") int processingErrorCounter);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :lastUpdated, f.processingErrorCounter = :processingErrorCounter "
           + "where f.id = :fileId")
    void updateProcessingStatus(@Param("fileId") String fileId,
                                @Param("processingStatus") ProcessingStatus processingStatus,
                                @Param("lastUpdated") OffsetDateTime lastUpdated,
                                @Param("processingErrorCounter") int processingErrorCounter);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set  f.processingErrorCounter = :processingErrorCounter "
           + "where f.dossierId in (select fe.dossierId from FileEntity fe inner join DossierEntity d on d.id = fe.dossierId where d.dossierTemplateId = :dossierTemplateId) and f.processingStatus = 'ERROR'")
    void updateErrorCounter(@Param("dossierTemplateId") String dossierTemplateId, @Param("processingErrorCounter") int processingErrorCounter);


    @Modifying
    @Query("update FileEntity f set f.errorCause = :cause, f.errorQueue = :queue, f.errorService = :service, f.errorTimestamp = :timestamp, f.errorCode = :errorCode where f.id = :fileId")
    void updateStatusErrorInfo(@Param("fileId") String fileId,
                               @Param("cause") String cause,
                               @Param("queue") String queue,
                               @Param("service") String service,
                               @Param("errorCode") ErrorCode errorCode,
                               @Param("timestamp") OffsetDateTime timestamp);


    @Modifying(clearAutomatically = true, flushAutomatically = true)
    @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :lastUpdated, f.lastProcessed = :lastProcessed, f.processingErrorCounter = :processingErrorCounter "
           + "where f.id = :fileId")
    void updateProcessingStatus(@Param("fileId") String fileId,
                                @Param("processingStatus") ProcessingStatus processingStatus,
                                @Param("lastUpdated") OffsetDateTime lastUpdated,
                                @Param("lastProcessed") OffsetDateTime lastProcessed,
                                @Param("processingErrorCounter") int processingErrorCounter);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :lastUpdated,"
           + "f.lastIndexed = :lastIndexed, f.processingErrorCounter = :processingErrorCounter where f.id = :fileId")
    void setUpdateStatusIndexingSuccessful(@Param("fileId") String fileId,
                                           @Param("processingStatus") ProcessingStatus processingStatus,
                                           @Param("lastUpdated") OffsetDateTime lastUpdated,
                                           @Param("lastIndexed") OffsetDateTime lastIndexed,
                                           @Param("processingErrorCounter") int processingErrorCounter);


    @Modifying
    @Query("update FileEntity f set f.lastUpdated = :lastUpdated, f.ocrStartTime = :ocrStartTime where f.id = :fileId")
    void updateOCRStartTime(@Param("fileId") String fileId, @Param("lastUpdated") OffsetDateTime lastUpdated, @Param("ocrStartTime") OffsetDateTime ocrStartTime);


    @Modifying
    @Query("update FileEntity f set f.fileManipulationDate = :fileManipulationDate where f.id = :fileId")
    void updateFileModificationDate(@Param("fileId") String fileId, @Param("fileManipulationDate") OffsetDateTime fileManipulationDate);


    @Modifying
    @Query("update FileEntity f set f.hasHighlights = :hasHighlights where f.id = :fileId")
    void updateHasHighlights(@Param("fileId") String fileId, @Param("hasHighlights") boolean hasHighlights);


    @Modifying
    @Query("update FileEntity f set f.lastUpdated = :lastUpdated, f.hasAnnotationComments = :hasAnnotationComments where f.id = :fileId")
    void updateHasComments(@Param("fileId") String fileId, @Param("lastUpdated") OffsetDateTime lastUpdated, @Param("hasAnnotationComments") boolean hasAnnotationComments);


    @Modifying
    @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :lastUpdated, " + "f.deleted = :softDeletedTime where f.id = :fileId")
    int setSoftDelete(@Param("fileId") String fileId,
                      @Param("processingStatus") ProcessingStatus processingStatus,
                      @Param("lastUpdated") OffsetDateTime lastUpdated,
                      @Param("softDeletedTime") OffsetDateTime softDeletedTime);


    @Modifying
    @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :lastUpdated, " + "f.deleted = :softDeletedTime where f.id in (:fileIds)")
    int softDeleteFiles(@Param("fileIds") List<String> fileIds,
                        @Param("processingStatus") ProcessingStatus processingStatus,
                        @Param("lastUpdated") OffsetDateTime lastUpdated,
                        @Param("softDeletedTime") OffsetDateTime softDeletedTime);


    @Modifying
    @Query("update FileEntity f set f.assignee = :assignee, f.lastReviewer = :lastReviewer, f.lastApprover = :lastApprover, " + "f.lastUpdated = :lastUpdated where f.id = :fileId")
    int setAssignee(@Param("fileId") String fileId,
                    @Param("assignee") String assignee,
                    @Param("lastReviewer") String lastReviewer,
                    @Param("lastApprover") String lastApprover,
                    @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.excluded = :excluded, f.lastUpdated = :lastUpdated where f.id = :fileId")
    int toggleExclusion(@Param("fileId") String fileId, @Param("excluded") boolean excluded, @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.excludedFromAutomaticAnalysis = :excludedFromAutomaticAnalysis, f.lastUpdated = :lastUpdated where f.id = :fileId")
    int toggleAutomaticAnalysis(@Param("fileId") String fileId,
                                @Param("excludedFromAutomaticAnalysis") boolean excludedFromAutomaticAnalysis,
                                @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.filename = :filename, f.uploader = :uploader, f.processingStatus = :processingStatus, "
           + "f.workflowStatus = :workflowStatus, f.lastUploaded = :lastUploaded, f.lastUpdated = :lastUpdated, "
           + "f.fileManipulationDate = :lastUploaded, f.excludedFromAutomaticAnalysis = :excludedFromAutomaticAnalysis, "
           + "f.ocrEndTime = null, f.ocrStartTime = null, f.numberOfPagesToOCR = null, f.numberOfOCRedPages = null, "
           + "f.excluded = false, f.lastProcessed = null, f.lastReviewer = null, f.lastApprover = null, "
           + "f.assignee = null, f.approvalDate = null, f.numberOfAnalyses = 0, f.lastManualChangeDate = null, "
           + "f.redactionModificationDate = null, "
           + "f.dictionaryVersion = 0, f.aiCreationVersion=0, f.dossierDictionaryVersion = 0, f.rulesVersion = 0, f.hasImages = false, "
           + "f.hasHints = false, f.hasRedactions = false, f.hasSuggestions = false, f.hasUpdates = false, "
           + "f.deleted = null, f.hardDeletedTime = null, f.hasHighlights = false, "
           + "f.processingErrorCounter = 0, f.errorCause = null, f.errorQueue = null, f.errorService = null, "
           + "f.overwriteFileCounter = coalesce(f.overwriteFileCounter, 0) + 1, "
           + "f.errorTimestamp = null where f.id = :fileId")
    int overwriteFile(@Param("fileId") String fileId,
                      @Param("filename") String filename,
                      @Param("uploader") String uploader,
                      @Param("processingStatus") ProcessingStatus processingStatus,
                      @Param("workflowStatus") WorkflowStatus workflowStatus,
                      @Param("lastUploaded") OffsetDateTime lastUploaded,
                      @Param("lastUpdated") OffsetDateTime lastUpdated,
                      @Param("excludedFromAutomaticAnalysis") boolean excludedFromAutomaticAnalysis);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.filename = :filename, f.uploader = :uploader, f.processingStatus = :processingStatus, "
           + "f.lastUploaded = :lastUploaded, f.lastUpdated = :lastUpdated, f.fileManipulationDate = :lastUploaded, "
           + " f.excludedFromAutomaticAnalysis = :excludedFromAutomaticAnalysis, f.lastProcessed = null,"
           + "f.approvalDate = null, f.numberOfAnalyses = 0, f.lastManualChangeDate = null, f.redactionModificationDate = null, "
           + "f.dictionaryVersion = 0, f.aiCreationVersion = 0, f.dossierDictionaryVersion = 0, f.rulesVersion = 0, f.hasImages = false, "
           + "f.hasHints = false, f.hasRedactions = false, f.hasSuggestions = false, f.hasUpdates = false, "
           + "f.deleted = null, f.hardDeletedTime = null, f.hasHighlights = false, f.processingErrorCounter = 0, "
           + "f.overwriteFileCounter = coalesce(f.overwriteFileCounter, 0) + 1, "
           + "f.ocrStartTime = null, f.ocrEndTime = null, f.numberOfPagesToOCR = null, f.numberOfOCRedPages = null, "
           + "f.errorCause = null, f.errorQueue = null, f.errorService = null, f.errorTimestamp = null "
           + "where f.id = :fileId")
    int overwriteFileAndKeepManualRedactions(@Param("fileId") String fileId,
                                             @Param("filename") String filename,
                                             @Param("uploader") String uploader,
                                             @Param("processingStatus") ProcessingStatus processingStatus,
                                             @Param("lastUploaded") OffsetDateTime lastUploaded,
                                             @Param("lastUpdated") OffsetDateTime lastUpdated,
                                             @Param("excludedFromAutomaticAnalysis") boolean excludedFromAutomaticAnalysis);


    @Query("select count(f) from FileEntity f inner join DossierEntity d on d.id = f.dossierId "
           + "where d.dossierTemplateId = :dossierTemplateId"
           + " and ((f.deleted is not null and f.hardDeletedTime is null) or "
           + " (d.softDeletedTime is not null and d.hardDeletedTime is null)) and d.archivedTime is null")
    int countSoftDeletedFilesPerDossierTemplateId(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select count(f) from FileEntity f inner join DossierEntity d on d.id = f.dossierId "
           + "where d.id = :dossierId"
           + " and ((f.deleted is not null and f.hardDeletedTime is null) or "
           + " (d.softDeletedTime is not null and d.hardDeletedTime is null))")
    int countSoftDeletedFilesPerDossierId(@Param("dossierId") String dossierId);


    @Query("select f from FileEntity f where f.lastUpdated > :since")
    List<FileChangeProjection> findFileChangeProjectionByLastUpdatedIsAfter(@Param("since") OffsetDateTime since);


    @Query("select distinct f.dossierId from FileEntity f where f.lastUpdated > :since")
    List<String> findDossierChangeByLastUpdatedIsAfter(@Param("since") OffsetDateTime since);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.redactionModificationDate = :redactionModificationDate, f.lastUpdated = :lastUpdated where f.id = :fileId")
    void setLastRedactionModificationDateForFile(@Param("fileId") String fileId,
                                                 @Param("redactionModificationDate") OffsetDateTime redactionModificationDate,
                                                 @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.lastManualChangeDate = :lastManualChangeDate, f.lastUpdated = :lastUpdated where f.id = :fileId")
    void setLastManualChangeDate(@Param("fileId") String fileId,
                                 @Param("lastManualChangeDate") OffsetDateTime lastManualChangeDate,
                                 @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.lastFlagCalculation = :lastFlagCalculation, f.lastUpdated = :lastUpdated where f.id = :fileId")
    void setLastFlagCalculationDate(@Param("fileId") String fileId,
                                    @Param("lastFlagCalculation") OffsetDateTime lastFlagCalculation,
                                    @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Query("select f from FileEntity f join DossierEntity d on d.id = f.dossierId "
           + "where f.excluded = false and f.workflowStatus <> 'APPROVED' and f.excludedFromAutomaticAnalysis = false "
           + " and ( f.processingStatus = 'PROCESSED' or f.processingStatus = 'ERROR' )"
           + " and d.softDeletedTime is null and d.hardDeletedTime is null and d.archivedTime is null "
           + " and f.deleted is null and f.hardDeletedTime is null and f.processingErrorCounter <= :maxRetries")
    List<FileEntity> getAllRelevantStatusesForReanalysisScheduler(@Param("maxRetries") int maxRetries);


    @Modifying
    @Query("update FileEntity f set f.lastFileAttributeChange = :date, f.lastUpdated = :date where f.id = :fileId")
    void updateLastAttributeChangeDate(@Param("fileId") String fileId, @Param("date") OffsetDateTime date);


    @Query("select f from FileEntity f where f.deleted is not null and f.hardDeletedTime is null and f.dossierId in :dossierIds")
    List<FileEntity> getSoftDeletedFiles(@Param("dossierIds") List<String> dossierIds);


    @Query("select f from FileEntity f where f.workflowStatus = 'APPROVED'")
    List<FileEntity> getApprovedFiles();


    @Query("select f from FileEntity f where f.hardDeletedTime is not null")
    List<FileEntity> getHardDeletedFiles();


    @Query("select f from FileEntity f "
           + "inner join DossierEntity d on d.id = f.dossierId "
           + "where f.processingStatus = 'ERROR' and f.deleted is null and f.hardDeletedTime is null "
           + "and d.softDeletedTime is null and d.hardDeletedTime is null and d.archivedTime is null ")
    List<FileEntity> getAllErrorFilesExcludeDeletedAndArchived();


    @Query("select f.processingStatus as processingStatus, count(f) as count from FileEntity f "
           + "inner join DossierEntity d on d.id = f.dossierId "
           + "where f.deleted is null and f.hardDeletedTime is null "
           + "and d.softDeletedTime is null and d.hardDeletedTime is null and d.archivedTime is null "
           + "and d.dossierTemplateId = :dossierTemplateId "
           + "group by f.processingStatus ")
    List<FileProcessingStatusProjection> countFilesByProcessingStatus(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select f.workflowStatus as workflowStatus, count(f) as count from FileEntity f "
           + "inner join DossierEntity d on d.id = f.dossierId "
           + "where f.deleted is null and f.hardDeletedTime is null "
           + "and d.softDeletedTime is null and d.hardDeletedTime is null and d.archivedTime is null "
           + "and d.dossierTemplateId = :dossierTemplateId "
           + "group by f.workflowStatus ")
    List<FileWorkflowStatusProjection> countFilesByWorkflowStatus(@Param("dossierTemplateId") String dossierTemplateId);


    @Query(value = "select COALESCE(sum(number_of_pages),0) as numberOfAnalyzedPages, "
                   + "COALESCE(sum(json_array_length(cast(excluded_pages AS json))),0) as numberOfExcludedPages "
                   + "from file join dossier on file.dossier_id = dossier.id "
                   + "where file.deleted is null and file.hard_deleted_time is null "
                   + "and dossier.archived_time is null and dossier.soft_deleted_time is null and dossier.hard_deleted_time is null"
                   + " and dossier.dossier_template_id = :dossierTemplateId", nativeQuery = true)
    FilePageCountsProjection countPages(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select count(f) from FileEntity f inner join DossierEntity d on d.id = f.dossierId where "
           + "f.hardDeletedTime is null and f.deleted is null and "
           + "d.dossierTemplateId = :dossierTemplateId and "
           + "d.softDeletedTime is null and d.hardDeletedTime is null and d.archivedTime is null")
    int countActiveFiles(@Param("dossierTemplateId") String dossierTemplateId);


    @Transactional
    @Modifying(clearAutomatically = true)
    @Query(value = "update FileEntity f set f.numberOfOCRedPages = :numberOfOCRedPages, "
                   + "f.numberOfPagesToOCR = :numberOfPagesToOCR, f.ocrEndTime = :ocrEndTime, "
                   + "f.lastUpdated = :lastUpdated where f.id = :fileId")
    void updateOCRStatus(@Param("fileId") String fileId,
                         @Param("numberOfPagesToOCR") int numberOfPagesToOCR,
                         @Param("numberOfOCRedPages") int numberOfOCRedPages,
                         @Param("ocrEndTime") OffsetDateTime ocrEndTime,
                         @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Transactional
    @Modifying(clearAutomatically = true)
    @Query(value = "update FileEntity f set f.lastLayoutProcessed = :offsetDateTime where f.id = :fileId")
    void updateLayoutProcessedTime(@Param("fileId") String fileId, @Param("offsetDateTime") OffsetDateTime offsetDateTime);


    @Query("select f.filename from FileEntity f where f.id = :fileId")
    Optional<String> getFilenameById(@Param("fileId") String fileId);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.ocrStartTime = NULL, f.ocrEndTime = NULL, f.numberOfPagesToOCR = NULL, f.numberOfOCRedPages = NULL where f.id = :fileId")
    void resetOcrStartAndEndDate(@Param("fileId") String fileId);


    /**
     * Analysis flags must be checked once after the initial analysis,
     * or whenever a manual change/analysis has happened since the last flag calculation,
     * or a page has been viewed by the assignee since the last flag calculation.
     *
     * @return a List of Tuples of fileIds and dossierIds, where a flag calculation is necessary
     */
    @Query("""
                select distinct f.id, f.dossierId
                from FileEntity f
                left join ViewedPageEntity v on f.id = v.file.id and v.id.userId = f.assignee
                where f.deleted is NULL and f.hardDeletedTime is NULL and f.processingStatus = 'PROCESSED'
                and ((f.lastFlagCalculation is NULL and f.lastProcessed is not NULL) or f.lastManualChangeDate > f.lastFlagCalculation or f.lastProcessed > f.lastFlagCalculation or f.lastFlagCalculation < v.viewedTime)
            """)
    List<Tuple> getFileIdentifiersWhereAnalysisFlagCalculationIsRequired();


    @Modifying
    @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :deletionTime,"
           + "f.hardDeletedTime = :deletionTime, "
           + "f.deleted = case "
           + "   when f.deleted is null then :deletionTime "
           + "   when f.deleted is not null then f.deleted "
           + "end "
           + "where f.id in (:fileIds)")
    int hardDeleteFiles(@Param("fileIds") List<String> fileIds, @Param("processingStatus") ProcessingStatus processingStatus, @Param("deletionTime") OffsetDateTime deletionTime);


    @Query("SELECT f FROM FileEntity f WHERE f.hardDeletedTime IS NOT NULL AND f.hardDeletedTime > :deletionThreshold")
    List<FileEntity> findFilesHardDeletedTimeAfter(@Param("deletionThreshold") OffsetDateTime deletionThreshold);


    @Query("SELECT f FROM FileEntity f WHERE f.hardDeletedTime IS NULL AND f.deleted IS NOT NULL AND f.deleted < :deletionThreshold")
    List<FileEntity> findFilesSoftDeletedBefore(@Param("deletionThreshold") OffsetDateTime deletionThreshold);


    @Query("SELECT f FROM FileEntity f WHERE f.protobufMigrationDone = false")
    List<FileEntity> findByProtobufMigrationDoneFalse(Pageable pageable);


    @Modifying
    @Query("update FileEntity f set f.protobufMigrationDone = true " + "where f.id in (:fileIds)")
    void setProtobufMigrationDone(@Param("fileIds") List<String> fileIds);


    @Query("SELECT f FROM FileEntity f WHERE f.dossierId = :dossierId AND f.hardDeletedTime IS NULL AND f.deleted IS NULL")
    List<DossierStatsFileProjection> findDossierStatsProjectionFileProjectionByDossierId(@Param("dossierId") String dossierId);


    @Query("SELECT f.id FROM FileEntity f WHERE f.dossierId = :dossierId AND f.hardDeletedTime IS NULL AND (f.deleted IS NULL or (f.deleted is not null and  :includeDeleted = true))")
    List<String> findAllByDossierId(@Param("dossierId") String dossierId, @Param("includeDeleted") boolean includeDeleted);


    @Modifying
    @Query("UPDATE FileEntity f SET f.lastDownload = :lastDownload WHERE f.id IN :fileIds AND f.workflowStatus = 'APPROVED'")
    void updateLastDownloadForApprovedFiles(@Param("fileIds") List<String> fileIds, @Param("lastDownload") DownloadStatusEntity lastDownload);

    @Modifying
    @Query("UPDATE FileEntity f SET f.lastDownload = :lastDownload WHERE f.id = :fileId")
    void updateLastDownloadForFile(@Param("fileId") String fileId, @Param("lastDownload") DownloadStatusEntity lastDownload);



    @Query("SELECT f FROM FileEntity f WHERE f.id in :fileIds AND f.dossierId = :dossierId")
    List<FileEntity> findAllDossierIdAndIds(@Param("dossierId") String dossierId, @Param("fileIds") Set<String> fileIds);

}


