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

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

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
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.ProcessingStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;

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(String fileId,
                     OffsetDateTime lastUpdated,
                     boolean hasRedactions,
                     boolean hasHints,
                     boolean hasImages,
                     boolean hasSuggestions,
                     boolean hasComments,
                     boolean hasUpdates);


    @Modifying
    @Query("update FileEntity f set f.numberOfPages = :numberOfPages, f.processingStatus = :processingStatus, " + "f.dictionaryVersion = :dictionaryVersion, f.rulesVersion = :rulesVersion, f.componentRulesVersion = :componentRulesVersion, 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(String fileId,
                                int numberOfPages,
                                ProcessingStatus processingStatus,
                                long dictionaryVersion,
                                long rulesVersion,
                                long componentRulesVersion,
                                long legalBasisVersion,
                                long analysisDuration,
                                long dossierDictionaryVersion,
                                int analysisVersion,
                                OffsetDateTime lastUpdated,
                                OffsetDateTime lastProcessed,
                                int analysisNumber,
                                int processingErrorCounter);


    @Modifying
    @Query("update FileEntity f set f.workflowStatus = :workflowStatus, f.lastUpdated = :lastUpdated, f.approvalDate = :approvalDate," + " f.excludedFromAutomaticAnalysis = :excludedFromAutomaticAnalysis where f.id = :fileId")
    void updateWorkflowStatus(String fileId, WorkflowStatus workflowStatus, OffsetDateTime lastUpdated, OffsetDateTime approvalDate, 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(String fileId, WorkflowStatus workflowStatus, OffsetDateTime lastUpdated, OffsetDateTime approvalDate);


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.fileSize = :fileSize  where f.id = :fileId")
    void updateFileSize(String fileId, 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(String fileId, ProcessingStatus processingStatus, OffsetDateTime lastUpdated, boolean hasHighlights, 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(String fileId, ProcessingStatus processingStatus, OffsetDateTime lastUpdated, int processingErrorCounter);


    @Modifying
    @Query("update FileEntity f set f.errorCause = :cause, f.errorQueue = :queue, f.errorService = :service, f.errorTimestamp = :timestamp  where f.id = :fileId")
    void updateStatusErrorInfo(String fileId, String cause, String queue, String service, 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(String fileId, ProcessingStatus processingStatus, OffsetDateTime lastUpdated, OffsetDateTime lastProcessed, 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(String fileId, ProcessingStatus processingStatus, OffsetDateTime lastUpdated, OffsetDateTime lastIndexed, int processingErrorCounter);


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


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


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


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


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


    @Modifying
    @Query("update FileEntity f set f.processingStatus = :processingStatus, f.lastUpdated = :lastUpdated," + " f.hardDeletedTime = :hardDeletedTime, " + " f.deleted = case " + " when f.deleted is null then :deleted " + " when f.deleted is not null then f.deleted " + " end " + " where f.id = :fileId")
    int setHardDelete(String fileId, ProcessingStatus processingStatus, OffsetDateTime lastUpdated, OffsetDateTime hardDeletedTime, OffsetDateTime deleted);


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


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


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.excludedFromAutomaticAnalysis = :excludedFromAutomaticAnalysis, f.lastUpdated = :lastUpdated where f.id = :fileId")
    int toggleAutomaticAnalysis(String fileId, boolean excludedFromAutomaticAnalysis, 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.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.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.excludedFromAutomaticAnalysis = false, f.processingErrorCounter = 0, f.errorCause = null, f.errorQueue = null, f.errorService = null, f.errorTimestamp = null where f.id = :fileId")
    int overwriteFile(String fileId,
                      String filename,
                      String uploader,
                      ProcessingStatus processingStatus,
                      WorkflowStatus workflowStatus,
                      OffsetDateTime lastUploaded,
                      OffsetDateTime lastUpdated);


    @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.lastProcessed = null," + "f.approvalDate = null, f.numberOfAnalyses = 0, f.lastManualChangeDate = null, f.redactionModificationDate = null, " + "f.dictionaryVersion = 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.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(String fileId,
                                             String filename,
                                             String uploader,
                                             ProcessingStatus processingStatus,
                                             OffsetDateTime lastUploaded,
                                             OffsetDateTime lastUpdated);


    @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(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(String dossierId);


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


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


    @Modifying(clearAutomatically = true)
    @Query("update FileEntity f set f.lastManualChangeDate = :lastManualChangeDate, f.lastUpdated = :lastUpdated where f.id = :fileId")
    void setLastManualChangeDate(String fileId, OffsetDateTime lastManualChangeDate, 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(int maxRetries);


    @Modifying
    @Query("update FileEntity f set f.lastFileAttributeChange = :date, f.lastUpdated = :date where f.id = :fileId")
    void updateLastAttributeChangeDate(String fileId, 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(List<String> dossierIds);


    @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(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(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(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(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(String fileId, int numberOfPagesToOCR, int numberOfOCRedPages, OffsetDateTime ocrEndTime, OffsetDateTime lastUpdated);


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

}


