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.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.DossierEntity;
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.DossierIdAndStatusProjection;

public interface DossierRepository extends JpaRepository<DossierEntity, String> {

    @Query("select d from DossierEntity d where d.lastUpdated > :since")
    List<DossierChangeProjection> findDossierChangeProjectionByLastUpdatedIsAfter(@Param("since") OffsetDateTime since);

    @Query("select d.id from DossierEntity  d where d.lastUpdated > :since")
    List<String> findDossierChangeByLastUpdatedIsAfter(@Param("since") OffsetDateTime since);


    @Modifying
    @Query("update DossierEntity d set d.softDeletedTime = null, d.lastUpdated = :lastUpdated where d.id = :dossierId" + " and d.hardDeletedTime is null")
    int undelete(@Param("dossierId") String dossierId, @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Modifying
    @Query("update DossierEntity d set d.softDeletedTime = :softDeletedTime, d.lastUpdated = :lastUpdated where d.id = :dossierId")
    void markDossierAsDeleted(@Param("dossierId") String dossierId, @Param("softDeletedTime") OffsetDateTime softDeletedTime, @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Modifying
    @Query("update DossierEntity d set d.hardDeletedTime = :hardDeletedTime, d.lastUpdated = :lastUpdated, "
           + "d.softDeletedTime = "
           + "case "
           + "when d.softDeletedTime is null then :hardDeletedTime "
           + "else d.softDeletedTime "
           + "end "
           + "where d.id = :dossierId")
    void hardDelete(@Param("dossierId") String dossierId, @Param("hardDeletedTime") OffsetDateTime hardDeletedTime, @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Modifying
    @Query("update DossierEntity d set d.archivedTime = :archiveTime, d.lastUpdated = :archiveTime where d.id = :dossierId ")
    void archiveDossier(@Param("dossierId") String dossierId, @Param("archiveTime") OffsetDateTime archiveTime);


    @Modifying
    @Query("update DossierEntity d set d.lastUpdated = :lastUpdated, d.archivedTime = null where d.id = :dossierId")
    int unarchiveDossier(@Param("dossierId") String dossierId, @Param("lastUpdated") OffsetDateTime lastUpdated);


    @Query("select count(d) from DossierEntity d where d.archivedTime is not null and d.softDeletedTime is null and d.hardDeletedTime is null and d.dossierTemplateId = :dossierTemplateId")
    int countArchived(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select count(d) from DossierEntity d where d.hardDeletedTime is null and d.softDeletedTime is not null and d.dossierTemplateId = :dossierTemplateId")
    int countSofDeleted(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select count(d) from DossierEntity d where d.archivedTime is null and d.softDeletedTime is null and d.hardDeletedTime is null and d.dossierTemplateId = :dossierTemplateId")
    int countActive(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select d.id from DossierEntity d where d.archivedTime is not null and d.softDeletedTime is null and d.hardDeletedTime is null and d.dossierTemplateId = :dossierTemplateId")
    List<String> findArchivedIds(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select d.id from DossierEntity d where d.hardDeletedTime is null and d.softDeletedTime is not null and d.dossierTemplateId = :dossierTemplateId")
    List<String> findSoftDeletedIds(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select d.id from DossierEntity d where d.archivedTime is null and d.softDeletedTime is null and d.hardDeletedTime is null and d.dossierTemplateId = :dossierTemplateId")
    List<String> findActiveIds(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select distinct d.dossierStatusId as dossierStatusId, d.id as dossierId from DossierEntity d where d.archivedTime is null and d.softDeletedTime is null and d.hardDeletedTime is null and d.dossierTemplateId = :dossierTemplateId")
    List<DossierIdAndStatusProjection> findDossierIdAndStatusForTemplate(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select d.id from DossierEntity d where d.dossierTemplateId = :dossierTemplateId and d.archivedTime is null and d.softDeletedTime is null and d.hardDeletedTime is null")
    List<String> findActiveDossierIdsForTemplate(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select d from DossierEntity d where d.dossierTemplateId = :dossierTemplateId")
    List<DossierEntity> findByDossierTemplateId(@Param("dossierTemplateId") String dossierTemplateId);


    @Query("select d.id from DossierEntity d where d.dossierTemplateId = :dossierTemplateId and d.id in :dossierIds")
    List<String> findIdsByDossierTemplateId(@Param("dossierTemplateId") String dossierTemplateId, @Param("dossierIds") Set<String> dossierIds);


    @Query("select d.id from DossierEntity d "
           + "where d.dossierTemplateId = :dossierTemplateId "
           + "and (d.archivedTime is null or (d.archivedTime is not null and :includeArchived = true)) "
           + "and (d.softDeletedTime is null or (d.softDeletedTime is not null and :includeDeleted = true)) "
           + "and d.hardDeletedTime is null")
    List<String> findIdsByDossierTemplateId(@Param("dossierTemplateId") String dossierTemplateId,
                                            @Param("includeArchived") boolean includeArchived,
                                            @Param("includeDeleted") boolean includeDeleted);


    @Modifying
    @Query("update DossierEntity d set d.watermarkId = null where d.watermarkId = :watermarkId")
    int countDeleteWatermark(@Param("watermarkId") long watermarkId);


    @Modifying
    @Query("update DossierEntity d set d.previewWatermarkId = null where d.previewWatermarkId = :previewWatermarkId")
    int countDeletePreviewWatermark(@Param("previewWatermarkId") long previewWatermarkId);


    @Query("select count(d) from DossierEntity d where d.watermarkId = :watermarkId or d.previewWatermarkId = :watermarkId")
    int countDossiersWithWatermarkInUse(@Param("watermarkId") long watermarkId);


    @Query("select d.dossierTemplateId from DossierEntity d where d.id = :dossierId")
    String findDossierTemplateId(@Param("dossierId") String dossierId);


    @Query("SELECT d FROM DossierEntity d WHERE d.hardDeletedTime IS NULL AND d.softDeletedTime IS NOT NULL AND d.softDeletedTime < :deletionThreshold")
    List<DossierEntity> findDossiersSoftDeletedBefore(@Param("deletionThreshold") OffsetDateTime deletionThreshold);


    @Query("SELECT d.dossierName FROM DossierEntity d WHERE d.id = :dossierId and d.hardDeletedTime IS NULL")
    Optional<String> findDossierNameById(@Param("dossierId") String dossierId);


    @Query("select d from DossierEntity d where d.dossierTemplateId = :dossierTemplateId and d.archivedTime is null and d.softDeletedTime is null and d.hardDeletedTime is null")
    List<DossierEntity> findActiveByDossierTemplateId(@Param("dossierTemplateId") String dossierTemplateId);

}
