package com.iqser.red.service.peristence.v1.server.integration.tests;

import static org.assertj.core.api.Assertions.assertThat;

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

import org.springframework.beans.factory.annotation.Autowired;

import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.FileTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.AnnotationEntityId;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.CommentEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.IdRemovalEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualForceRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualLegalBasisChangeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRecategorizationEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRedactionEntryEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualResizeRedactionEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.RectangleEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
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.type.DictionaryEntryType;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;

@Slf4j
public class SoftDeleteTest extends AbstractPersistenceServerServiceTest {

    @Autowired
    private DossierTemplateTesterAndProvider dossierTemplateTesterAndProvider;

    @Autowired
    private DossierTesterAndProvider dossierTesterAndProvider;

    @Autowired
    private FileTesterAndProvider fileTesterAndProvider;

    @Autowired
    private DossierManagementService dossierManagementService;


    @Test
    public void testSoftDeleteDossier() {

        var template = dossierTemplateTesterAndProvider.provideTestTemplate("template1");
        var dossier = prepareDossier(template, "dossier1");
        long startSoftDelete = System.currentTimeMillis();
        dossierManagementService.softDeleteDossier(dossier.getDossierId());
        log.warn("Soft delete time: {}ms", System.currentTimeMillis() - startSoftDelete);

        var deletedDossier = dossierRepository.findById(dossier.getId());
        var allComments = commentRepository.findAll();
        var allManualRedactions = manualRedactionRepository.findAll();
        var allIdRemovals = removeRedactionRepository.findAll();
        var allRecategorizations = recategorizationRepository.findAll();
        var allLegalBasis = legalBasisChangeRepository.findAll();
        var allForceRedactions = forceRedactionRepository.findAll();
        var allResizes = resizeRedactionRepository.findAll();

        assertThat(allComments.stream().map(CommentEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsExactly(deletedDossier.get().getSoftDeletedTime());
        assertThat(allManualRedactions.stream().map(ManualRedactionEntryEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsExactly(deletedDossier.get()
                .getSoftDeletedTime());
        assertThat(allIdRemovals.stream().map(IdRemovalEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsExactly(deletedDossier.get().getSoftDeletedTime());
        assertThat(allRecategorizations.stream().map(ManualRecategorizationEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsExactly(deletedDossier.get()
                .getSoftDeletedTime());
        assertThat(allLegalBasis.stream().map(ManualLegalBasisChangeEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsExactly(deletedDossier.get()
                .getSoftDeletedTime());
        assertThat(allForceRedactions.stream().map(ManualForceRedactionEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsExactly(deletedDossier.get()
                .getSoftDeletedTime());
        assertThat(allResizes.stream().map(ManualResizeRedactionEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsExactly(deletedDossier.get().getSoftDeletedTime());

        long startUndeleteTime = System.currentTimeMillis();
        dossierManagementService.undeleteDossiers(Set.of(dossier.getDossierId()));
        log.warn("Undo soft delete time: {}ms", System.currentTimeMillis() - startUndeleteTime);

        allComments = commentRepository.findAll();
        allManualRedactions = manualRedactionRepository.findAll();
        allIdRemovals = removeRedactionRepository.findAll();
        allRecategorizations = recategorizationRepository.findAll();
        allLegalBasis = legalBasisChangeRepository.findAll();
        allForceRedactions = forceRedactionRepository.findAll();
        allResizes = resizeRedactionRepository.findAll();

        assertThat(allComments.stream().map(CommentEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsOnlyNulls();
        assertThat(allManualRedactions.stream().map(ManualRedactionEntryEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsOnlyNulls();
        assertThat(allIdRemovals.stream().map(IdRemovalEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsOnlyNulls();
        assertThat(allRecategorizations.stream().map(ManualRecategorizationEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsOnlyNulls();
        assertThat(allLegalBasis.stream().map(ManualLegalBasisChangeEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsOnlyNulls();
        assertThat(allForceRedactions.stream().map(ManualForceRedactionEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsOnlyNulls();
        assertThat(allResizes.stream().map(ManualResizeRedactionEntity::getSoftDeletedTime).collect(Collectors.toSet())).containsOnlyNulls();

        long startHardDeleteTime = System.currentTimeMillis();
        dossierManagementService.hardDeleteDossiers(Set.of(dossier.getDossierId()));
        log.warn("Hard Delete Time: {}ms", System.currentTimeMillis() - startHardDeleteTime);

        allComments = commentRepository.findAll();
        allManualRedactions = manualRedactionRepository.findAll();
        allIdRemovals = removeRedactionRepository.findAll();
        allRecategorizations = recategorizationRepository.findAll();
        allLegalBasis = legalBasisChangeRepository.findAll();
        allForceRedactions = forceRedactionRepository.findAll();
        allResizes = resizeRedactionRepository.findAll();

        assertThat(allComments).isEmpty();
        assertThat(allManualRedactions).isEmpty();
        assertThat(allIdRemovals).isEmpty();
        assertThat(allRecategorizations).isEmpty();
        assertThat(allLegalBasis).isEmpty();
        assertThat(allForceRedactions).isEmpty();
        assertThat(allResizes).isEmpty();
    }


    public Dossier prepareDossier(DossierTemplateModel template, String name) {

        var dossier = dossierTesterAndProvider.provideTestDossier(template, name);

        var manualRedactions = new ArrayList<ManualRedactionEntryEntity>();
        var idRemovals = new ArrayList<IdRemovalEntity>();
        var forceRedactions = new ArrayList<ManualForceRedactionEntity>();
        var legalBasisChanges = new ArrayList<ManualLegalBasisChangeEntity>();
        var recategorizations = new ArrayList<ManualRecategorizationEntity>();
        var resizes = new ArrayList<ManualResizeRedactionEntity>();
        var comments = new ArrayList<CommentEntity>();
        for (int i = 0; i < 20; i++) {
            var file = fileTesterAndProvider.testAndProvideFile(dossier, "file" + i);

            for (int j = 0; j < 10; j++) {

                var manualRedaction = getManualRedactionEntryEntity(i, j, file);
                var idRemoval = getIdRemovalEntity(i, j, file);
                var forceRedaction = getManualForceRedactionEntity(i, j, file);
                var legalBasisChange = getManualLegalBasisChangeEntity(i, j, file);
                var recategorization = getManualRecategorizationEntity(i, j, file);
                var resize = getManualResizeRedactionEntity(i, j, file);
                manualRedactions.add(manualRedaction);
                idRemovals.add(idRemoval);
                forceRedactions.add(forceRedaction);
                legalBasisChanges.add(legalBasisChange);
                recategorizations.add(recategorization);
                resizes.add(resize);

                for (int k = 0; k < 10; k++) {
                    var comment = new CommentEntity();
                    comment.setAnnotationId(manualRedaction.getId().getAnnotationId());
                    comment.setFileId(file.getFileId());
                    comment.setText("test");
                    comment.setUser("test");
                    comments.add(comment);
                }

            }

        }

        commentRepository.saveAll(comments);
        manualRedactionRepository.saveAll(manualRedactions);
        removeRedactionRepository.saveAll(idRemovals);
        forceRedactionRepository.saveAll(forceRedactions);
        legalBasisChangeRepository.saveAll(legalBasisChanges);
        recategorizationRepository.saveAll(recategorizations);
        resizeRedactionRepository.saveAll(resizes);

        return dossier;
    }


    private static @NotNull ManualResizeRedactionEntity getManualResizeRedactionEntity(int i, int j, FileStatus file) {

        var entity = new ManualResizeRedactionEntity();
        entity.setId(new AnnotationEntityId("ManualResizeRedactionEntity:" + i + ":" + j, file.getFileId()));
        entity.setPositions(List.of(new RectangleEntity(1, 1, 1, 1, 1)));
        entity.setUser("test");
        entity.setRequestDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setProcessedDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setUpdateDictionary(false);
        entity.setTextAfter("test");
        entity.setTextBefore("test");
        entity.setValue("test");
        return entity;
    }


    private static @NotNull ManualRecategorizationEntity getManualRecategorizationEntity(int i, int j, FileStatus file) {

        var entity = new ManualRecategorizationEntity();
        entity.setId(new AnnotationEntityId("ManualRecategorizationEntity:" + i + ":" + j, file.getFileId()));
        entity.setUser("test");
        entity.setLegalBasis("test");
        entity.setTypeId("123");
        entity.setRequestDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setProcessedDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setSection("test");
        entity.setValue("test");
        return entity;
    }


    private static @NotNull ManualLegalBasisChangeEntity getManualLegalBasisChangeEntity(int i, int j, FileStatus file) {

        var entity = new ManualLegalBasisChangeEntity();
        entity.setId(new AnnotationEntityId("ManualLegalBasisChangeEntity:" + i + ":" + j, file.getFileId()));
        entity.setUser("test");
        entity.setLegalBasis("test");
        entity.setRequestDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setProcessedDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setSection("test");
        entity.setValue("test");
        return entity;
    }


    private static @NotNull IdRemovalEntity getIdRemovalEntity(int i, int j, FileStatus file) {

        var entity = new IdRemovalEntity();
        entity.setId(new AnnotationEntityId("IdRemovalEntity:" + i + ":" + j, file.getFileId()));
        entity.setRequestDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setUser("test");
        entity.setProcessedDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        return entity;
    }


    private static @NotNull ManualForceRedactionEntity getManualForceRedactionEntity(int i, int j, FileStatus file) {

        var entity = new ManualForceRedactionEntity();
        entity.setId(new AnnotationEntityId("IdRemovalEntity:" + i + ":" + j, file.getFileId()));
        entity.setRequestDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setUser("test");
        entity.setProcessedDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setPage(1);
        return entity;
    }


    private static @NotNull ManualRedactionEntryEntity getManualRedactionEntryEntity(int i, int j, FileStatus file) {

        var entity = new ManualRedactionEntryEntity();
        entity.setId(new AnnotationEntityId("ManualRedactionEntryEntity:" + i + ":" + j, file.getFileId()));
        entity.setPositions(List.of(new RectangleEntity(1, 1, 1, 1, 1)));
        entity.setUser("test");
        entity.setLegalBasis("test");
        entity.setDictionaryEntryType(DictionaryEntryType.ENTRY);
        entity.setReason("test");
        entity.setRequestDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setProcessedDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        entity.setRectangle(true);
        entity.setSection("test");
        entity.setSourceId("test");
        entity.setTextAfter("test");
        entity.setTextBefore("test");
        entity.setValue("test");
        return entity;
    }

}
