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

import static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;

import com.iqser.red.service.dictionarymerge.commons.DictionaryEntry;
import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileProcessingClient;
import com.iqser.red.service.peristence.v1.server.integration.client.InternalDictionaryClient;
import com.iqser.red.service.peristence.v1.server.integration.client.ManualRedactionClient;
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.service.TypeProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.UserProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.CommentEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRecategorizationEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.model.ManualChangesQueryOptions;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierService;
import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogMergeService;
import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionProviderService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
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.RecategorizationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.queue.SearchTermOccurrencesResponseReceiver;
import com.iqser.red.service.persistence.management.v1.processor.service.redactionlog.RedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeResult;
import com.iqser.red.service.persistence.service.v1.api.shared.model.BulkLocalResponse;
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.FoundTerm;
import com.iqser.red.service.persistence.service.v1.api.shared.model.MessageType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Change;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ChangeType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Engine;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogLegalBasis;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualRedactionType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAnnotationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactionResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.Dictionary;
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.dossier.file.FileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddCommentRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.LegalBasisChangeRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RecategorizationBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RecategorizationRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ResizeRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Point;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;

import feign.FeignException;

public class ManualRedactionTest extends AbstractPersistenceServerServiceTest {

    @Autowired
    private FileTesterAndProvider fileTesterAndProvider;

    @Autowired
    private DossierTesterAndProvider dossierTesterAndProvider;

    @Autowired
    private DossierTemplateTesterAndProvider dossierTemplateTesterAndProvider;

    @Autowired
    private TypeProvider typeProvider;

    @Autowired
    private ManualRedactionClient manualRedactionClient;

    @Autowired
    private DictionaryClient dictionaryClient;

    @Autowired
    private FileManagementStorageService fileManagementStorageService;

    @Autowired
    private FileStatusPersistenceService fileStatusPersistenceService;

    @Autowired
    private FileStatusService fileStatusService;

    @Autowired
    private FileClient fileClient;

    @Autowired
    private UserProvider userProvider;

    @Autowired
    private InternalDictionaryClient internalDictionaryClient;

    @Autowired
    private EntityLogService entityLogService;

    @Autowired
    private DictionaryManagementService dictionaryManagementService;

    @Autowired
    private FileProcessingClient fileProcessingClient;

    @Autowired
    private ManualRedactionProviderService manualRedactionProviderService;

    @Autowired
    private EntityLogMergeService entityLogMergeService;

    @Autowired
    private DossierService dossierService;

    @Autowired
    private SearchTermOccurrencesResponseReceiver searchTermOccurrencesResponseReceiver;

    @Autowired
    private RecategorizationPersistenceService recategorizationPersistenceService;

    @Autowired
    private AddRedactionPersistenceService addRedactionPersistenceService;


    @Test
    public void testRemoveToDossierTemplateWithDossierDictionaryOnlyTrue() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", true);
        assertThat(type.isDossierDictionaryOnly()).isTrue();

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type(type.getType())
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        assertThrows(FeignException.Forbidden.class,
                     () -> manualRedactionClient.removeRedactionBulk(dossier.getId(),
                                                                     file.getId(),
                                                                     Set.of(RemoveRedactionRequestModel.builder()
                                                                                    .annotationId("AnnotationId")
                                                                                    .removeFromDictionary(true)
                                                                                    .removeFromAllDossiers(true)
                                                                                    .build())));//.get(0);

        var dossierTemplateDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId()), null);
        assertThat(dossierTemplateDictionary.getEntries().size()).isZero();

    }


    @Test
    public void testAddToDossierTemplateWithDossierDictionaryOnlyTrue() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", true);
        assertThat(type.isDossierDictionaryOnly()).isTrue();
        assertThrows(FeignException.Forbidden.class, () ->

                manualRedactionClient.addRedactionBulk(dossier.getId(),
                                                       file.getId(),
                                                       Set.of(AddRedactionRequestModel.builder()
                                                                      .positions(List.of(Rectangle.builder().topLeftY(1).topLeftX(1).height(1).width(1).build()))
                                                                      .section("section test")
                                                                      .addToDictionary(true)
                                                                      .addToAllDossiers(true)
                                                                      .type(type.getType())
                                                                      .reason("1")
                                                                      .value("Luke Skywalker")
                                                                      .legalBasis("1")
                                                                      .sourceId("SourceId")
                                                                      .build())));

        var dossierTemplateDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId()), null);
        assertThat(dossierTemplateDictionary.getEntries().size()).isZero();

    }


    @Test
    public void testAddToAllDossiersIfDeletedInOneDossier() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate);

        dictionaryClient.deleteEntries(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), dossier.getId(), DictionaryEntryType.ENTRY);

        var dossierDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
        assertThat(dossierDictionary.getEntries().size()).isEqualTo(1);
        assertThat(dossierDictionary.getEntries()
                           .get(0).isDeleted()).isTrue();

        manualRedactionClient.addRedactionBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(AddRedactionRequestModel.builder()
                                                              .positions(List.of(Rectangle.builder().topLeftY(1).topLeftX(1).height(1).width(1).build()))
                                                              .section("section test")
                                                              .addToDictionary(true)
                                                              .addToAllDossiers(true)
                                                              .dictionaryEntryType(DictionaryEntryType.ENTRY)
                                                              .type(type.getType())
                                                              .reason("1")
                                                              .value("Luke Skywalker")
                                                              .legalBasis("1")
                                                              .sourceId("SourceId")
                                                              .build()));

        dossierDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
        assertThat(dossierDictionary.getEntries().size()).isEqualTo(1);
        assertThat(dossierDictionary.getEntries()
                           .get(0).isDeleted()).isFalse();

        var dossierTemplateDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId()), null);
        assertThat(dossierTemplateDictionary.getEntries().size()).isEqualTo(1);
        assertThat(dossierTemplateDictionary.getEntries()
                           .get(0).isDeleted()).isFalse();

    }


    @Test
    public void testAddToDossierDictionary() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate);

        manualRedactionClient.addRedactionBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(AddRedactionRequestModel.builder()
                                                              .positions(List.of(Rectangle.builder().topLeftY(1).topLeftX(1).height(1).width(1).build()))
                                                              .section("section test")
                                                              .addToDictionary(true)
                                                              .addToAllDossiers(false)
                                                              .type(type.getType())
                                                              .reason("1")
                                                              .value("Luke Skywalker")
                                                              .legalBasis("1")
                                                              .sourceId("SourceId")
                                                              .build()));

        var dossierDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
        assertThat(dossierDictionary.getEntries().size()).isEqualTo(1);
        assertThat(dossierDictionary.getEntries()
                           .get(0).isDeleted()).isFalse();

        var dossierTemplateDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId()), null);
        assertThat(dossierTemplateDictionary.getEntries().isEmpty()).isTrue();
    }


    @Test
    public void testAddToDictionaryEntryWithCarriageReturn() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);
        var type = typeProvider.testAndProvideType(dossierTemplate);

        manualRedactionClient.addRedactionBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(AddRedactionRequestModel.builder()
                                                              .positions(List.of(Rectangle.builder().topLeftY(1).topLeftX(1).height(1).width(1).build()))
                                                              .section("section test")
                                                              .addToDictionary(true)
                                                              .addToAllDossiers(false)
                                                              .type(type.getType())
                                                              .reason("1")
                                                              .value("Luke Skywalker\r")
                                                              .legalBasis("1")
                                                              .sourceId("SourceId")
                                                              .build()));

        var dossierDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
        assertThat(dossierDictionary.getEntries().size()).isEqualTo(1);
        assertEquals(dossierDictionary.getEntries()
                             .get(0).getValue(), "Luke Skywalker ");
    }


    @Test
    public void testManualRemoveFromAllDossiersAndUndo() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate);

        var entries = new ArrayList<String>();
        entries.add("Luke Skywalker");
        entries.add("Darth Vader");
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), entries, false, null, DictionaryEntryType.ENTRY);

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);

        Dictionary dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder("Luke Skywalker", "Darth Vader");

        Dictionary dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrder("Luke Skywalker");

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.removeRedactionBulk(dossier.getId(),
                                                  file.getId(),
                                                  Set.of(RemoveRedactionRequestModel.builder()
                                                                 .annotationId("AnnotationId")
                                                                 .removeFromDictionary(true)
                                                                 .removeFromAllDossiers(true)
                                                                 .build()));

        dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder("Darth Vader");
        dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).isEmpty();

        manualRedactionClient.undo(dossier.getId(), file.getFileId(), Set.of("AnnotationId"), false, false);

        dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder("Luke Skywalker", "Darth Vader");
        dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrder("Luke Skywalker");
    }


    @Test
    public void testRemoveFromDossierIfOnlyDossierTemplateEntryExists() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate);

        var entries = new ArrayList<String>();
        entries.add("Luke Skywalker");
        entries.add("Darth Vader");
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), entries, false, null, DictionaryEntryType.ENTRY);

        //FIXME should be created on the fly.
//        CreateTypeValue dossierDictionaryType = MagicConverter.convert(type, CreateTypeValue.class);
//        dictionaryClient.addType(dossierDictionaryType, dossier.getId());

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.removeRedactionBulk(dossier.getId(),
                                                  file.getId(),
                                                  Set.of(RemoveRedactionRequestModel.builder().annotationId("AnnotationId").removeFromDictionary(true).build()))
                .getManualAnnotationResponses()
                .get(0);

        var dossierDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);

        var dossierTemplateDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId()), null);
        assertThat(dossierTemplateDictionary.getEntries().size()).isEqualTo(2);
        assertThat(dossierTemplateDictionary.getEntries()
                           .stream()
                           .filter(e -> e.getValue().equals("Luke Skywalker"))
                           .findFirst()
                           .get().isDeleted()).isFalse();

        assertThat(dossierDictionary.getEntries().size()).isEqualTo(1);
        assertThat(dossierDictionary.getEntries()
                           .get(0).isDeleted()).isTrue();
    }


    @Test
    public void testRemoveFromAllDossiersIfOnlyDossierEntryExists() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate);

        //FIXME should be created on the fly.
//        CreateTypeValue dossierDictionaryType = MagicConverter.convert(type, CreateTypeValue.class);
//        dictionaryClient.addType(dossierDictionaryType, dossier.getId());
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.removeRedactionBulk(dossier.getId(),
                                                  file.getId(),
                                                  Set.of(RemoveRedactionRequestModel.builder()
                                                                 .annotationId("AnnotationId")
                                                                 .removeFromDictionary(true)
                                                                 .removeFromAllDossiers(true)
                                                                 .build())).getManualAnnotationResponses()
                .get(0);

        var dossierDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
        assertThat(dossierDictionary.getEntries().size()).isEqualTo(1);
        assertThat(dossierDictionary.getEntries()
                           .get(0).isDeleted()).isTrue();

        var dossierTemplateDictionary = internalDictionaryClient.getDictionaryForType(toTypeId(type.getType(), dossierTemplate.getId()), null);
        assertThat(dossierTemplateDictionary.getEntries().size()).isEqualTo(0);
    }


    @Test
    public void testEnlargeResizeRedactionInDossierDictionaryWithAddToAllDossiers() {

        // preparíng prerequisites
        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();

        var dossier1 = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "Dossier 1");
        var dossier2 = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "Dossier 2");

        var file1 = fileTesterAndProvider.testAndProvideFile(dossier1);
        var file2 = fileTesterAndProvider.testAndProvideFile(dossier2);

        var typeDosDict = typeProvider.testAndProvideType(dossierTemplate, null, "typeDosDict", false, 99);
        var typeDosTempDict = typeProvider.testAndProvideType(dossierTemplate, null, "typeDosTempDict", false, 101);

        var userId = userProvider.getUserId();

        var redactionDos = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().page(1).topLeftY(1).topLeftX(1).height(1).width(1).build()))
                .section("section test")
                .addToDictionary(true)
                .addToAllDossiers(false)
                .type(typeDosDict.getType())
                .dictionaryEntryType(DictionaryEntryType.ENTRY)
                .reason("1")
                .value("test redaction in dossier")
                .legalBasis("1")
                .rectangle(false)
                .sourceId("SourceId")
                .build();

        var redactionDosTempDict = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().page(1).topLeftY(1).topLeftX(1).height(1).width(1).build()))
                .section("section test")
                .addToDictionary(true)
                .addToAllDossiers(true)
                .type(typeDosTempDict.getType())
                .dictionaryEntryType(DictionaryEntryType.ENTRY)
                .reason("1")
                .value("test redaction in dossier template")
                .legalBasis("1")
                .rectangle(false)
                .sourceId("SourceId")
                .build();

        var addRedactions = manualRedactionClient.addRedactionBulk(dossier1.getId(), file1.getId(), Set.of(redactionDos, redactionDosTempDict));
        var loadedRedactionsFile1 = manualRedactionClient.getManualRedactions(file1.getDossierId(), file1.getFileId(), false, true);

        var entityLog1 = new EntityLog(1,
                                       1,
                                       List.of(EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(0).getAnnotationId())
                                                       .type(typeDosDict.getType())
                                                       .value("test redaction in dossier")
                                                       .dossierDictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build(),
                                               EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(1).getAnnotationId())
                                                       .type(typeDosTempDict.getType())
                                                       .value("test redaction in dossier template")
                                                       .dictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build()),
                                       null,
                                       0,
                                       0,
                                       0,
                                       0);
        fileManagementStorageService.saveEntityLog(dossier1.getId(), file1.getId(), entityLog1);
        var redactionRequest1 = RedactionRequest.builder().dossierId(file1.getDossierId()).fileId(file1.getFileId()).dossierTemplateId(file1.getDossierTemplateId()).build();
        when(entityLogService.getEntityLog(eq(file1.getDossierId()), eq(file1.getFileId()), anyBoolean())).thenReturn(entityLog1);

        var entityLog2 = new EntityLog(1,
                                       1,
                                       List.of(EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(1).getAnnotationId())
                                                       .type(typeDosTempDict.getType())
                                                       .value("test redaction in dossier template")
                                                       .dictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build()),
                                       null,
                                       0,
                                       0,
                                       0,
                                       0);
        fileManagementStorageService.saveEntityLog(dossier2.getId(), file2.getId(), entityLog2);
        var redactionRequest2 = RedactionRequest.builder().dossierId(file2.getDossierId()).fileId(file2.getFileId()).dossierTemplateId(file2.getDossierTemplateId()).build();
        when(entityLogService.getEntityLog(eq(file2.getDossierId()), eq(file2.getFileId()), anyBoolean())).thenReturn(entityLog2);

        // resize redaction in dossier 1
        var resizeRedactionDosAndAddToAllDos = ResizeRedactionRequestModel.builder()
                .annotationId(addRedactions.getManualAnnotationResponses()
                                      .get(0).getAnnotationId())
                .comment("resized dossier redaction")
                .value("test redaction in dossier dictionary")
                .updateDictionary(true)
                .addToAllDossiers(true)
                .build();

        manualRedactionClient.resizeRedactionBulk(dossier1.getId(), file1.getFileId(), Set.of(resizeRedactionDosAndAddToAllDos));

        loadedRedactionsFile1 = manualRedactionClient.getManualRedactions(file1.getDossierId(), file1.getFileId(), false, true);

        assertThat(loadedRedactionsFile1.getResizeRedactions()).hasSize(1);
        assertThat(loadedRedactionsFile1.getResizeRedactions()
                           .stream()
                           .toList()
                           .get(0).getValue()).isEqualTo("test redaction in dossier dictionary");

        var dictEntries = dictionaryManagementService.getAllEntriesInDossierTemplate(dossierTemplate.getId(), "test redaction in dossier dictionary", DictionaryEntryType.ENTRY);
        assertThat(dictEntries.stream()
                           .filter(dictionaryEntry -> dictionaryEntry.getValue().equals("test redaction in dossier dictionary"))).isNotEmpty();

        var dictionaryOfTypeDosDictInDossier1 = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(dictionaryOfTypeDosDictInDossier1.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier");

        var dictionaryOfTypeDosDictInDossier2 = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(dictionaryOfTypeDosDictInDossier2.getEntries()).isEmpty();

        var dictionaryOfTypeDosDictInDossierTemplate = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossierTemplate.getId(), null);
        assertThat(dictionaryOfTypeDosDictInDossierTemplate.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosDictInDossierTemplate.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier dictionary");

        var dictionaryOfTypeDosTempDictInDossier1 = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(dictionaryOfTypeDosTempDictInDossier1.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossier2 = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(dictionaryOfTypeDosTempDictInDossier2.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossierTemplate = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossierTemplate.getId(), null);
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");

        var mergedDictForTypeDosDictInDossier1 = dictionaryClient.getMergedDictionaries(typeDosDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()).hasSize(2);
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier");
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()
                           .get(1)).isEqualTo("test redaction in dossier dictionary");

        var mergedDictForTypeDosTempDictInDossier1 = dictionaryClient.getMergedDictionaries(typeDosTempDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");

        var mergedDictForTypeDosDictInDossier2 = dictionaryClient.getMergedDictionaries(typeDosDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(mergedDictForTypeDosDictInDossier2.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosDictInDossier2.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier dictionary");

        var mergedDictForTypeDosTempDictInDossier2 = dictionaryClient.getMergedDictionaries(typeDosTempDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");
    }


    @Test
    public void testShrinkResizeRedactionInDossierDictionaryWithAddToAllDossiers() {

        // preparíng prerequisites
        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();

        var dossier1 = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "Dossier 1");
        var dossier2 = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "Dossier 2");

        var file1 = fileTesterAndProvider.testAndProvideFile(dossier1);
        var file2 = fileTesterAndProvider.testAndProvideFile(dossier2);

        var typeDosDict = typeProvider.testAndProvideType(dossierTemplate, null, "typeDosDict", false, 99);
        var typeDosTempDict = typeProvider.testAndProvideType(dossierTemplate, null, "typeDosTempDict", false, 101);

        var userId = userProvider.getUserId();

        var redactionDos = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().page(1).topLeftY(1).topLeftX(1).height(1).width(1).build()))
                .section("section test")
                .addToDictionary(true)
                .addToAllDossiers(false)
                .type(typeDosDict.getType())
                .dictionaryEntryType(DictionaryEntryType.ENTRY)
                .reason("1")
                .value("test redaction in dossier yayy")
                .legalBasis("1")
                .rectangle(false)
                .sourceId("SourceId")
                .build();

        var redactionDosTempDict = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().page(1).topLeftY(1).topLeftX(1).height(1).width(1).build()))
                .section("section test")
                .addToDictionary(true)
                .addToAllDossiers(true)
                .type(typeDosTempDict.getType())
                .dictionaryEntryType(DictionaryEntryType.ENTRY)
                .reason("1")
                .value("test redaction in dossier template yayy")
                .legalBasis("1")
                .rectangle(false)
                .sourceId("SourceId")
                .build();

        var addRedactions = manualRedactionClient.addRedactionBulk(dossier1.getId(), file1.getId(), Set.of(redactionDos, redactionDosTempDict));
        var loadedRedactionsFile1 = manualRedactionClient.getManualRedactions(file1.getDossierId(), file1.getFileId(), false, true);

        var entityLog1 = new EntityLog(1,
                                       1,
                                       List.of(EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(0).getAnnotationId())
                                                       .type(typeDosDict.getType())
                                                       .value("test redaction in dossier yayy")
                                                       .dossierDictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build(),
                                               EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(1).getAnnotationId())
                                                       .type(typeDosTempDict.getType())
                                                       .value("test redaction in dossier template yayy")
                                                       .dictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build()),
                                       null,
                                       0,
                                       0,
                                       0,
                                       0);
        fileManagementStorageService.saveEntityLog(dossier1.getId(), file1.getId(), entityLog1);
        var redactionRequest1 = RedactionRequest.builder().dossierId(file1.getDossierId()).fileId(file1.getFileId()).dossierTemplateId(file1.getDossierTemplateId()).build();
        when(entityLogService.getEntityLog(eq(file1.getDossierId()), eq(file1.getFileId()), anyBoolean())).thenReturn(entityLog1);

        var entityLog2 = new EntityLog(1,
                                       1,
                                       List.of(EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(1).getAnnotationId())
                                                       .type(typeDosTempDict.getType())
                                                       .value("test redaction in dossier template yayy")
                                                       .dictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build()),
                                       null,
                                       0,
                                       0,
                                       0,
                                       0);
        fileManagementStorageService.saveEntityLog(dossier2.getId(), file2.getId(), entityLog2);
        var redactionRequest2 = RedactionRequest.builder().dossierId(file2.getDossierId()).fileId(file2.getFileId()).dossierTemplateId(file2.getDossierTemplateId()).build();
        when(entityLogService.getEntityLog(eq(file2.getDossierId()), eq(file2.getFileId()), anyBoolean())).thenReturn(entityLog2);

        // resize redaction in dossier 1
        var resizeRedactionDosAndAddToAllDos = ResizeRedactionRequestModel.builder()
                .annotationId(addRedactions.getManualAnnotationResponses()
                                      .get(0).getAnnotationId())
                .comment("resized dossier redaction")
                .value("test redaction in dossier")
                .updateDictionary(true)
                .addToAllDossiers(true)
                .build();

        manualRedactionClient.resizeRedactionBulk(dossier1.getId(), file1.getFileId(), Set.of(resizeRedactionDosAndAddToAllDos));

        loadedRedactionsFile1 = manualRedactionClient.getManualRedactions(file1.getDossierId(), file1.getFileId(), false, true);

        assertThat(loadedRedactionsFile1.getResizeRedactions()).hasSize(1);
        assertThat(loadedRedactionsFile1.getResizeRedactions()
                           .stream()
                           .toList()
                           .get(0).getValue()).isEqualTo("test redaction in dossier");

        var dictEntriesOldValue = dictionaryManagementService.getAllEntriesInDossierTemplate(dossierTemplate.getId(), "test redaction in dossier yayy", DictionaryEntryType.ENTRY);
        assertThat(dictEntriesOldValue.stream()
                           .filter(dictionaryEntry -> dictionaryEntry.getValue().equals("test redaction in dossier yayy") && dictionaryEntry.isDeleted())).hasSize(1);
        assertThat(dictEntriesOldValue.stream()
                           .filter(dictionaryEntry -> dictionaryEntry.getValue().equals("test redaction in dossier yayy") && !dictionaryEntry.isDeleted())).hasSize(1);
        var dictEntriesNewValue = dictionaryManagementService.getAllEntriesInDossierTemplate(dossierTemplate.getId(), "test redaction in dossier", DictionaryEntryType.ENTRY);
        assertThat(dictEntriesNewValue.stream()
                           .filter(dictionaryEntry -> dictionaryEntry.getValue().equals("test redaction in dossier"))).isNotEmpty();

        var dictionaryOfTypeDosDictInDossier1 = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(dictionaryOfTypeDosDictInDossier1.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier yayy");

        var dictionaryOfTypeDosDictInDossier2 = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(dictionaryOfTypeDosDictInDossier2.getEntries()).isEmpty();

        var dictionaryOfTypeDosDictInDossierTemplate = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossierTemplate.getId(), null);
        assertThat(dictionaryOfTypeDosDictInDossierTemplate.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosDictInDossierTemplate.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier");

        var dictionaryOfTypeDosTempDictInDossier1 = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(dictionaryOfTypeDosTempDictInDossier1.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossier2 = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(dictionaryOfTypeDosTempDictInDossier2.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossierTemplate = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossierTemplate.getId(), null);
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template yayy");

        var mergedDictForTypeDosDictInDossier1 = dictionaryClient.getMergedDictionaries(typeDosDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()).hasSize(2);
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier");
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()
                           .get(1)).isEqualTo("test redaction in dossier yayy");

        var mergedDictForTypeDosTempDictInDossier1 = dictionaryClient.getMergedDictionaries(typeDosTempDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template yayy");

        var mergedDictForTypeDosDictInDossier2 = dictionaryClient.getMergedDictionaries(typeDosDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(mergedDictForTypeDosDictInDossier2.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosDictInDossier2.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier");

        var mergedDictForTypeDosTempDictInDossier2 = dictionaryClient.getMergedDictionaries(typeDosTempDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template yayy");
    }


    @Test
    public void testEnlargeResizeRedactionInDossierTemplateDictionaryWithAddToAllDossiers() {

        // preparíng prerequisites
        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();

        var dossier1 = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "Dossier 1");
        var dossier2 = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "Dossier 2");

        var file1 = fileTesterAndProvider.testAndProvideFile(dossier1);
        var file2 = fileTesterAndProvider.testAndProvideFile(dossier2);

        var typeDosDict = typeProvider.testAndProvideType(dossierTemplate, null, "typeDosDict", false, 99);
        var typeDosTempDict = typeProvider.testAndProvideType(dossierTemplate, null, "typeDosTempDict", false, 101);

        var userId = userProvider.getUserId();

        var redactionDos = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().page(1).topLeftY(1).topLeftX(1).height(1).width(1).build()))
                .section("section test")
                .addToDictionary(true)
                .addToAllDossiers(false)
                .type(typeDosDict.getType())
                .dictionaryEntryType(DictionaryEntryType.ENTRY)
                .reason("1")
                .value("test redaction in dossier")
                .legalBasis("1")
                .rectangle(false)
                .sourceId("SourceId")
                .build();

        var redactionDosTempDict = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().page(1).topLeftY(1).topLeftX(1).height(1).width(1).build()))
                .section("section test")
                .addToDictionary(true)
                .addToAllDossiers(true)
                .type(typeDosTempDict.getType())
                .dictionaryEntryType(DictionaryEntryType.ENTRY)
                .reason("1")
                .value("test redaction in dossier template")
                .legalBasis("1")
                .rectangle(false)
                .sourceId("SourceId")
                .build();

        var addRedactions = manualRedactionClient.addRedactionBulk(dossier1.getId(), file1.getId(), Set.of(redactionDos, redactionDosTempDict));

        var entityLog1 = new EntityLog(1,
                                       1,
                                       List.of(EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(0).getAnnotationId())
                                                       .type(typeDosDict.getType())
                                                       .value("test redaction in dossier")
                                                       .dossierDictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build(),
                                               EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(1).getAnnotationId())
                                                       .type(typeDosTempDict.getType())
                                                       .value("test redaction in dossier template")
                                                       .dictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build()),
                                       null,
                                       0,
                                       0,
                                       0,
                                       0);
        fileManagementStorageService.saveEntityLog(dossier1.getId(), file1.getId(), entityLog1);
        var redactionRequest1 = RedactionRequest.builder().dossierId(file1.getDossierId()).fileId(file1.getFileId()).dossierTemplateId(file1.getDossierTemplateId()).build();
        when(entityLogService.getEntityLog(file1.getDossierId(), file1.getFileId())).thenReturn(entityLog1);

        var entityLog2 = new EntityLog(1,
                                       1,
                                       List.of(EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(1).getAnnotationId())
                                                       .type(typeDosTempDict.getType())
                                                       .value("test redaction in dossier template")
                                                       .dictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build()),
                                       null,
                                       0,
                                       0,
                                       0,
                                       0);
        fileManagementStorageService.saveEntityLog(dossier2.getId(), file2.getId(), entityLog2);
        var redactionRequest2 = RedactionRequest.builder().dossierId(file2.getDossierId()).fileId(file2.getFileId()).dossierTemplateId(file2.getDossierTemplateId()).build();
        when(entityLogService.getEntityLog(eq(file2.getDossierId()), eq(file2.getFileId()), anyBoolean())).thenReturn(entityLog2);

        // resize redaction in dossier dict
        var resizeRedactionDosTemp = ResizeRedactionRequestModel.builder()
                .annotationId(addRedactions.getManualAnnotationResponses()
                                      .get(1).getAnnotationId())
                .comment("resized dossier template redaction")
                .value("test redaction in dossier template dictionary")
                .updateDictionary(true)
                .addToAllDossiers(true)
                .build();

        var resizeRedactions = manualRedactionClient.resizeRedactionBulk(dossier2.getId(), file2.getFileId(), Set.of(resizeRedactionDosTemp));

        var loadedRedactionsFile2 = manualRedactionClient.getManualRedactions(file2.getDossierId(), file2.getFileId(), false, true);

        assertThat(loadedRedactionsFile2.getResizeRedactions()).hasSize(1);
        assertThat(loadedRedactionsFile2.getResizeRedactions()
                           .stream()
                           .toList()
                           .get(0).getValue()).isEqualTo("test redaction in dossier template dictionary");

        var dictEntries = dictionaryManagementService.getAllEntriesInDossierTemplate(dossierTemplate.getId(),
                                                                                     "test redaction in dossier template dictionary",
                                                                                     DictionaryEntryType.ENTRY);
        assertThat(dictEntries.stream()
                           .filter(dictionaryEntry -> dictionaryEntry.getValue().equals("test redaction in dossier template dictionary"))).hasSize(1);

        var dictionaryOfTypeDosDictInDossier1 = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(dictionaryOfTypeDosDictInDossier1.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier");

        var dictionaryOfTypeDosDictInDossier2 = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(dictionaryOfTypeDosDictInDossier2.getEntries()).isEmpty();

        var dictionaryOfTypeDosDictInDossierTemplate = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossierTemplate.getId(), null);
        assertThat(dictionaryOfTypeDosDictInDossierTemplate.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossier1 = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(dictionaryOfTypeDosTempDictInDossier1.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossier2 = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(dictionaryOfTypeDosTempDictInDossier2.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossierTemplate = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossierTemplate.getId(), null);
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()).hasSize(2);
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()
                           .get(1)).isEqualTo("test redaction in dossier template dictionary");

        var mergedDictForTypeDosDictInDossier1 = dictionaryClient.getMergedDictionaries(typeDosDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier");

        var mergedDictForTypeDosTempDictInDossier1 = dictionaryClient.getMergedDictionaries(typeDosTempDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()).hasSize(2);
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()
                           .get(1)).isEqualTo("test redaction in dossier template dictionary");

        var mergedDictForTypeDosDictInDossier2 = dictionaryClient.getMergedDictionaries(typeDosDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(mergedDictForTypeDosDictInDossier2.getEntries()).isEmpty();

        var mergedDictForTypeDosTempDictInDossier2 = dictionaryClient.getMergedDictionaries(typeDosTempDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()).hasSize(2);
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()
                           .get(1)).isEqualTo("test redaction in dossier template dictionary");
    }


    @Test
    public void testShrinkResizeRedactionInDossierTemplateDictionaryWithAddToAllDossiers() {

        // preparíng prerequisites
        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();

        var dossier1 = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "Dossier 1");
        var dossier2 = dossierTesterAndProvider.provideTestDossier(dossierTemplate, "Dossier 2");

        var file1 = fileTesterAndProvider.testAndProvideFile(dossier1);
        var file2 = fileTesterAndProvider.testAndProvideFile(dossier2);

        var typeDosDict = typeProvider.testAndProvideType(dossierTemplate, null, "typeDosDict", false, 99);
        var typeDosTempDict = typeProvider.testAndProvideType(dossierTemplate, null, "typeDosTempDict", false, 101);

        var userId = userProvider.getUserId();

        var redactionDos = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().page(1).topLeftY(1).topLeftX(1).height(1).width(1).build()))
                .section("section test")
                .addToDictionary(true)
                .addToAllDossiers(false)
                .type(typeDosDict.getType())
                .dictionaryEntryType(DictionaryEntryType.ENTRY)
                .reason("1")
                .value("test redaction in dossier yayy")
                .legalBasis("1")
                .rectangle(false)
                .sourceId("SourceId")
                .build();

        var redactionDosTempDict = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().page(1).topLeftY(1).topLeftX(1).height(1).width(1).build()))
                .section("section test")
                .addToDictionary(true)
                .addToAllDossiers(true)
                .type(typeDosTempDict.getType())
                .dictionaryEntryType(DictionaryEntryType.ENTRY)
                .reason("1")
                .value("test redaction in dossier template yayy")
                .legalBasis("1")
                .rectangle(false)
                .sourceId("SourceId")
                .build();

        var addRedactions = manualRedactionClient.addRedactionBulk(dossier1.getId(), file1.getId(), Set.of(redactionDos, redactionDosTempDict));

        var entityLog1 = new EntityLog(1,
                                       1,
                                       List.of(EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(0).getAnnotationId())
                                                       .type(typeDosDict.getType())
                                                       .value("test redaction in dossier yayy")
                                                       .dossierDictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build(),
                                               EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(1).getAnnotationId())
                                                       .type(typeDosTempDict.getType())
                                                       .value("test redaction in dossier template yayy")
                                                       .dictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build()),
                                       null,
                                       0,
                                       0,
                                       0,
                                       0);
        fileManagementStorageService.saveEntityLog(dossier1.getId(), file1.getId(), entityLog1);
        var redactionRequest1 = RedactionRequest.builder().dossierId(file1.getDossierId()).fileId(file1.getFileId()).dossierTemplateId(file1.getDossierTemplateId()).build();
        when(entityLogService.getEntityLog(file1.getDossierId(), file1.getFileId())).thenReturn(entityLog1);

        var entityLog2 = new EntityLog(1,
                                       1,
                                       List.of(EntityLogEntry.builder()
                                                       .id(addRedactions.getManualAnnotationResponses()
                                                                   .get(1).getAnnotationId())
                                                       .type(typeDosTempDict.getType())
                                                       .value("test redaction in dossier template yayy")
                                                       .dictionaryEntry(true)
                                                       .entryType(EntryType.ENTITY)
                                                       .state(EntryState.APPLIED)
                                                       .build()),
                                       null,
                                       0,
                                       0,
                                       0,
                                       0);
        fileManagementStorageService.saveEntityLog(dossier2.getId(), file2.getId(), entityLog2);
        var redactionRequest2 = RedactionRequest.builder().dossierId(file2.getDossierId()).fileId(file2.getFileId()).dossierTemplateId(file2.getDossierTemplateId()).build();
        when(entityLogService.getEntityLog(eq(file2.getDossierId()), eq(file2.getFileId()), anyBoolean())).thenReturn(entityLog2);

        // resize redaction in dossier dict
        var resizeRedactionDosTemp = ResizeRedactionRequestModel.builder()
                .annotationId(addRedactions.getManualAnnotationResponses()
                                      .get(1).getAnnotationId())
                .comment("resized dossier template redaction")
                .value("test redaction in dossier template")
                .updateDictionary(true)
                .addToAllDossiers(true)
                .build();

        var resizeRedactions = manualRedactionClient.resizeRedactionBulk(dossier2.getId(), file2.getFileId(), Set.of(resizeRedactionDosTemp));

        var loadedRedactionsFile2 = manualRedactionClient.getManualRedactions(file2.getDossierId(), file2.getFileId(), false, true);

        assertThat(loadedRedactionsFile2.getResizeRedactions()).hasSize(1);
        assertThat(loadedRedactionsFile2.getResizeRedactions()
                           .stream()
                           .toList()
                           .get(0).getValue()).isEqualTo("test redaction in dossier template");

        var dictEntriesOldValue = dictionaryManagementService.getAllEntriesInDossierTemplate(dossierTemplate.getId(),
                                                                                             "test redaction in dossier template yayy",
                                                                                             DictionaryEntryType.ENTRY);
        assertThat(dictEntriesOldValue.stream()
                           .filter(dictionaryEntry -> dictionaryEntry.getValue().equals("test redaction in dossier template yayy") && dictionaryEntry.isDeleted())).hasSize(3);
        assertThat(dictEntriesOldValue.stream()
                           .filter(dictionaryEntry -> dictionaryEntry.getValue().equals("test redaction in dossier template yayy") && !dictionaryEntry.isDeleted())).isEmpty();
        var dictEntriesNewValue = dictionaryManagementService.getAllEntriesInDossierTemplate(dossierTemplate.getId(),
                                                                                             "test redaction in dossier template",
                                                                                             DictionaryEntryType.ENTRY);
        assertThat(dictEntriesNewValue.stream()
                           .filter(dictionaryEntry -> dictionaryEntry.getValue().equals("test redaction in dossier template"))).isNotEmpty();

        var dictionaryOfTypeDosDictInDossier1 = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(dictionaryOfTypeDosDictInDossier1.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier yayy");

        var dictionaryOfTypeDosDictInDossier2 = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(dictionaryOfTypeDosDictInDossier2.getEntries()).isEmpty();

        var dictionaryOfTypeDosDictInDossierTemplate = dictionaryClient.getDictionaryForType(typeDosDict.getType(), dossierTemplate.getId(), null);
        assertThat(dictionaryOfTypeDosDictInDossierTemplate.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossier1 = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(dictionaryOfTypeDosTempDictInDossier1.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossier2 = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(dictionaryOfTypeDosTempDictInDossier2.getEntries()).isEmpty();

        var dictionaryOfTypeDosTempDictInDossierTemplate = dictionaryClient.getDictionaryForType(typeDosTempDict.getType(), dossierTemplate.getId(), null);
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()).hasSize(1);
        assertThat(dictionaryOfTypeDosTempDictInDossierTemplate.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");

        var mergedDictForTypeDosDictInDossier1 = dictionaryClient.getMergedDictionaries(typeDosDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier yayy");

        var mergedDictForTypeDosTempDictInDossier1 = dictionaryClient.getMergedDictionaries(typeDosTempDict.getType(), dossier1.getDossierTemplateId(), dossier1.getId());
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosTempDictInDossier1.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");

        var mergedDictForTypeDosDictInDossier2 = dictionaryClient.getMergedDictionaries(typeDosDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(mergedDictForTypeDosDictInDossier2.getEntries()).isEmpty();

        var mergedDictForTypeDosTempDictInDossier2 = dictionaryClient.getMergedDictionaries(typeDosTempDict.getType(), dossier2.getDossierTemplateId(), dossier2.getId());
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()).hasSize(1);
        assertThat(mergedDictForTypeDosTempDictInDossier2.getEntries()
                           .get(0)).isEqualTo("test redaction in dossier template");
    }


    @Test
    public void testManualRecategorizeAndUndo() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate);
        var type2 = typeProvider.testAndProvideType(dossierTemplate, dossier, "test2", false, 66);

        var entries = new ArrayList<String>();

        var lukeSkywalker = "Luke Skywalker";
        var darthVader = "Darth Vader";
        entries.add(lukeSkywalker);
        entries.add(darthVader);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), entries, false, null, DictionaryEntryType.ENTRY);

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of(lukeSkywalker), false, dossier.getId(), DictionaryEntryType.ENTRY);

        Dictionary dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder(lukeSkywalker, darthVader);
        Dictionary dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrder(lukeSkywalker);

        Dictionary dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).isEmpty();
        Dictionary dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).isEmpty();

        var annotationId = "AnnotationId";
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id(annotationId)
                                                      .type(type.getType())
                                                      .value(lukeSkywalker)
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.recategorizeBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(RecategorizationRequestModel.builder()
                                                              .type(type2.getType())
                                                              .annotationId(annotationId)
                                                              .addToDictionary(true)
                                                              .addToAllDossiers(true)
                                                              .legalBasis("")
                                                              .section("section")
                                                              .value(lukeSkywalker)
                                                              .build()));

        dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder(darthVader);
        dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).isEmpty();

        dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).containsExactlyInAnyOrder(lukeSkywalker);
        dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).isEmpty();

        manualRedactionClient.undo(dossier.getId(), file.getFileId(), Set.of(annotationId), false, false);

        dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder(lukeSkywalker, darthVader);
        dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrder(lukeSkywalker);

        dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).isEmpty();
        dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).isEmpty();
    }


    @Test
    public void testManualRecategorizeAndUndoOnlyManualChanges() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate);
        var type2 = typeProvider.testAndProvideType(dossierTemplate, dossier, "test2", false, 66);

        var entries = new ArrayList<String>();

        var lukeSkywalker = "Luke Skywalker";
        var darthVader = "Darth Vader";
        entries.add(lukeSkywalker);
        entries.add(darthVader);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), entries, false, null, DictionaryEntryType.ENTRY);

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of(lukeSkywalker), false, dossier.getId(), DictionaryEntryType.ENTRY);

        Dictionary dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder(lukeSkywalker, darthVader);
        Dictionary dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrder(lukeSkywalker);

        Dictionary dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).isEmpty();
        Dictionary dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).isEmpty();

        var annotationId = "AnnotationId";
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id(annotationId)
                                                      .type(type.getType())
                                                      .value(lukeSkywalker)
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.recategorizeBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(RecategorizationRequestModel.builder()
                                                              .type(type2.getType())
                                                              .annotationId(annotationId)
                                                              .addToDictionary(true)
                                                              .addToAllDossiers(true)
                                                              .legalBasis("")
                                                              .section("section")
                                                              .value(lukeSkywalker)
                                                              .build()));

        dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder(darthVader);
        dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).isEmpty();

        dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).containsExactlyInAnyOrder(lukeSkywalker);
        dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).isEmpty();

        // try to undo only local changes, but there are none for the requested annotationId
        String errorMessage = assertThrows(FeignException.NotFound.class,
                                           () -> manualRedactionClient.undo(dossier.getId(), file.getFileId(), Set.of(annotationId), false, true)).getMessage();
        assertTrue(errorMessage.contains("ManualRedaction with annotationIds [AnnotationId] could not be found"));

        dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).containsExactlyInAnyOrder(darthVader);
        dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).isEmpty();

        dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).containsExactlyInAnyOrder(lukeSkywalker);
        dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).isEmpty();

        var resizeRedactionRequestModel = ResizeRedactionRequestModel.builder()
                .annotationId(annotationId)
                .value(lukeSkywalker + " resized")
                .updateDictionary(false)
                .addToAllDossiers(false)
                .positions(List.of(Rectangle.builder().page(1).height(1).width(2).topLeftX(1).topLeftY(1).build()))
                .build();

        ManualRedactionResponse response = manualRedactionClient.resizeRedactionBulk(dossier.getId(), file.getId(), Set.of(resizeRedactionRequestModel));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);

        assertEquals(allManualRedactions.getResizeRedactions().size(), 1);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 1);
        assertEquals(allManualRedactions.getRecategorizations().size(), 1);
        var entryToResize = allManualRedactions.getResizeRedactions()
                .stream()
                .findFirst()
                .get();
        var entryToAdd = allManualRedactions.getEntriesToAdd()
                .stream()
                .findFirst()
                .get();
        assertFalse(entryToAdd.isAddToDictionary());
        assertFalse(entryToAdd.isAddToDossierDictionary());
        assertEquals(entryToAdd.getAnnotationId(), entryToResize.getAnnotationId());

        manualRedactionClient.undo(dossier.getId(), file.getFileId(), Set.of(annotationId, entryToResize.getAnnotationId()), false, true);

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);

        assertEquals(allManualRedactions.getResizeRedactions().size(), 0);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 0);
        assertEquals(allManualRedactions.getRecategorizations().size(), 1);
    }


    @Test
    public void testManualRecategorizeAndUndoDossierLevelOnly() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, dossier, "test", true, 66);
        var type2 = typeProvider.testAndProvideType(dossierTemplate, dossier, "test2", true, 100);

        var entries = new ArrayList<String>();

        var lukeSkywalker = "Luke Skywalker";
        var darthVader = "Darth Vader";
        entries.add(lukeSkywalker);
        entries.add(darthVader);

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), entries, false, dossier.getId(), DictionaryEntryType.ENTRY);

        Dictionary dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).isEmpty();
        Dictionary dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrder(lukeSkywalker, darthVader);

        Dictionary dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).isEmpty();
        Dictionary dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).isEmpty();

        var annotationId = "AnnotationId";
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id(annotationId)
                                                      .type(type.getType())
                                                      .value(lukeSkywalker)
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.recategorizeBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(RecategorizationRequestModel.builder()
                                                              .type(type2.getType())
                                                              .annotationId(annotationId)
                                                              .addToDictionary(true)
                                                              .addToAllDossiers(false)
                                                              .legalBasis("")
                                                              .section("section")
                                                              .value(lukeSkywalker)
                                                              .build()));

        dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).isEmpty();
        dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrder(darthVader);

        dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).isEmpty();
        dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).containsExactlyInAnyOrder(lukeSkywalker);

        manualRedactionClient.undo(dossier.getId(), file.getFileId(), Set.of(annotationId), false, false);

        dossierTemplateDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary.getEntries()).isEmpty();
        dossierDictionary = dictionaryClient.getDictionaryForType(type.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary.getEntries()).containsExactlyInAnyOrder(lukeSkywalker, darthVader);

        dossierTemplateDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), null);
        assertThat(dossierTemplateDictionary2.getEntries()).isEmpty();
        dossierDictionary2 = dictionaryClient.getDictionaryForType(type2.getType(), type.getDossierTemplateId(), dossier.getId());
        assertThat(dossierDictionary2.getEntries()).isEmpty();
    }


    @Test
    public void testUnprocessedManualRedactionsAddRedaction() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);
        var type = typeProvider.testAndProvideType(dossierTemplate);

        dictionaryClient.deleteEntries(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), dossier.getId(), DictionaryEntryType.ENTRY);

        manualRedactionClient.addRedactionBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(AddRedactionRequestModel.builder()
                                                              .positions(List.of(Rectangle.builder().topLeftY(1).topLeftX(1).height(1).width(1).build()))
                                                              .section("section test")
                                                              .addToDictionary(false)
                                                              .addToAllDossiers(false)
                                                              .dictionaryEntryType(DictionaryEntryType.ENTRY)
                                                              .type(type.getType())
                                                              .reason("1")
                                                              .value("Luke Skywalker")
                                                              .legalBasis("1")
                                                              .sourceId("SourceId")
                                                              .build()));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 1);
        assertTrue(allManualRedactions.getEntriesToAdd()
                           .stream()
                           .anyMatch(entry -> entry.getValue().equals("Luke Skywalker")));

        var unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getEntriesToAdd().size(), 1);
        assertTrue(unprocessedManualRedactions.getEntriesToAdd()
                           .stream()
                           .anyMatch(entry -> entry.getValue().equals("Luke Skywalker")));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(0)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        manualRedactionClient.addRedactionBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(AddRedactionRequestModel.builder()
                                                              .positions(List.of(Rectangle.builder().topLeftY(2).topLeftX(2).height(2).width(2).build()))
                                                              .section("section test")
                                                              .addToDictionary(false)
                                                              .addToAllDossiers(false)
                                                              .dictionaryEntryType(DictionaryEntryType.ENTRY)
                                                              .type(type.getType())
                                                              .reason("1")
                                                              .value("Skywalker Luke")
                                                              .legalBasis("1")
                                                              .sourceId("SourceId")
                                                              .build()));

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 2);
        assertTrue(allManualRedactions.getEntriesToAdd()
                           .stream()
                           .anyMatch(entry -> entry.getValue().equals("Skywalker Luke")));
        assertTrue(allManualRedactions.getEntriesToAdd()
                           .stream()
                           .anyMatch(entry -> entry.getValue().equals("Luke Skywalker")));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getEntriesToAdd().size(), 1);
        assertTrue(unprocessedManualRedactions.getEntriesToAdd()
                           .stream()
                           .anyMatch(entry -> entry.getValue().equals("Skywalker Luke")));
        assertTrue(unprocessedManualRedactions.getEntriesToAdd()
                           .stream()
                           .noneMatch(entry -> entry.getValue().equals("Luke Skywalker")));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(1)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 2);
        assertTrue(allManualRedactions.getEntriesToAdd()
                           .stream()
                           .anyMatch(entry -> entry.getValue().equals("Skywalker Luke")));
        assertTrue(allManualRedactions.getEntriesToAdd()
                           .stream()
                           .anyMatch(entry -> entry.getValue().equals("Luke Skywalker")));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertTrue(unprocessedManualRedactions.getEntriesToAdd().isEmpty());

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);
    }


    @Test
    public void testUnprocessedManualRedactionsRemoveRedaction() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier, "new_file");

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Anakin"), false, dossier.getId(), DictionaryEntryType.ENTRY);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type(type.getType())
                                                      .value("Anakin")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(false)
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("AnnotationId2")
                                                      .type(type.getType())
                                                      .value("Anakin2")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(false)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.removeRedactionBulk(dossier.getId(),
                                                  file.getId(),
                                                  Set.of(RemoveRedactionRequestModel.builder()
                                                                 .annotationId("AnnotationId")
                                                                 .removeFromDictionary(true)
                                                                 .removeFromAllDossiers(true)
                                                                 .build()));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getIdsToRemove().size(), 1);
        assertTrue(allManualRedactions.getIdsToRemove()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals("AnnotationId")));

        var unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getIdsToRemove().size(), 1);
        assertTrue(unprocessedManualRedactions.getIdsToRemove()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals("AnnotationId")));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(0)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        manualRedactionClient.removeRedactionBulk(dossier.getId(),
                                                  file.getId(),
                                                  Set.of(RemoveRedactionRequestModel.builder()
                                                                 .annotationId("AnnotationId2")
                                                                 .removeFromDictionary(true)
                                                                 .removeFromAllDossiers(true)
                                                                 .build()));

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getIdsToRemove().size(), 2);
        assertTrue(allManualRedactions.getIdsToRemove()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals("AnnotationId")));
        assertTrue(allManualRedactions.getIdsToRemove()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals("AnnotationId2")));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getIdsToRemove().size(), 1);
        assertTrue(unprocessedManualRedactions.getIdsToRemove()
                           .stream()
                           .noneMatch(entry -> entry.getAnnotationId().equals("AnnotationId")));
        assertTrue(unprocessedManualRedactions.getIdsToRemove()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals("AnnotationId2")));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(1)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getIdsToRemove().size(), 2);
        assertTrue(allManualRedactions.getIdsToRemove()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals("AnnotationId")));
        assertTrue(allManualRedactions.getIdsToRemove()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals("AnnotationId2")));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertTrue(unprocessedManualRedactions.getIdsToRemove().isEmpty());
    }


    @Test
    public void testUnprocessedManualRedactionsForceRedaction() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("forceRedactionAnnotation")
                                                      .type(type.getType())
                                                      .value("lukeSkywalker")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .legalBasis("legal basis")
                                                      .positions(List.of(new Position(1, 1, 1, 1, 1)))
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("forceRedactionAnnotation2")
                                                      .type(type.getType())
                                                      .value("lukeSkywalker")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .legalBasis("legal basis")
                                                      .positions(List.of(new Position(1, 1, 1, 1, 1)))
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.storeJSONObject(dossier.getId(), file.getId(), FileType.ENTITY_LOG, entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);
        entityLogMongoService.upsertEntityLog(file.getDossierId(), file.getFileId(), entityLog);

        ManualRedactionResponse forceResponse = manualRedactionClient.forceRedactionBulk(dossier.getId(),
                                                                                         file.getId(),
                                                                                         Set.of(ForceRedactionRequestModel.builder()
                                                                                                        .annotationId("forceRedactionAnnotation")
                                                                                                        .comment("comment")
                                                                                                        .legalBasis("1")
                                                                                                        .build()));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getForceRedactions().size(), 1);
        assertFalse(allManualRedactions.getForceRedactions()
                            .stream()
                            .anyMatch(entry -> entry.getAnnotationId().equals("forceRedactionAnnotation")));
        assertTrue(allManualRedactions.getForceRedactions()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(forceResponse.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        var unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getForceRedactions().size(), 1);
        assertFalse(unprocessedManualRedactions.getForceRedactions()
                            .stream()
                            .anyMatch(entry -> entry.getAnnotationId().equals("forceRedactionAnnotation")));
        assertTrue(allManualRedactions.getForceRedactions()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(forceResponse.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(0)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        ManualRedactionResponse forceResponse2 = manualRedactionClient.forceRedactionBulk(dossier.getId(),
                                                                                          file.getId(),
                                                                                          Set.of(ForceRedactionRequestModel.builder()
                                                                                                         .annotationId("forceRedactionAnnotation2")
                                                                                                         .comment("comment")
                                                                                                         .legalBasis("1")
                                                                                                         .build()));

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getForceRedactions().size(), 2);
        assertFalse(allManualRedactions.getForceRedactions()
                            .stream()
                            .anyMatch(entry -> entry.getAnnotationId().equals("forceRedactionAnnotation")));
        assertTrue(allManualRedactions.getForceRedactions()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(forceResponse.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));
        assertFalse(allManualRedactions.getForceRedactions()
                            .stream()
                            .anyMatch(entry -> entry.getAnnotationId().equals("forceRedactionAnnotation2")));
        assertTrue(allManualRedactions.getForceRedactions()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(forceResponse2.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getForceRedactions().size(), 1);
        assertFalse(unprocessedManualRedactions.getForceRedactions()
                            .stream()
                            .anyMatch(entry -> entry.getAnnotationId().equals("forceRedactionAnnotation2")));
        assertTrue(unprocessedManualRedactions.getForceRedactions()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(forceResponse2.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(1)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getForceRedactions().size(), 2);
        assertFalse(allManualRedactions.getForceRedactions()
                            .stream()
                            .anyMatch(entry -> entry.getAnnotationId().equals("forceRedactionAnnotation")));
        assertTrue(allManualRedactions.getForceRedactions()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(forceResponse.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));
        assertFalse(allManualRedactions.getForceRedactions()
                            .stream()
                            .anyMatch(entry -> entry.getAnnotationId().equals("forceRedactionAnnotation2")));
        assertTrue(allManualRedactions.getForceRedactions()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(forceResponse2.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertTrue(unprocessedManualRedactions.getForceRedactions().isEmpty());
    }


    @Test
    public void testUnprocessedManualRedactionsRecategorizations() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Darth Vader"), false, dossier.getId(), DictionaryEntryType.ENTRY);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("dv")
                                                      .type(type.getType())
                                                      .value("Darth Vader")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(false)
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("dv2")
                                                      .type(type.getType())
                                                      .value("Vader Darth")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(false)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualRedactionResponse response = manualRedactionClient.recategorizeBulk(dossier.getId(),
                                                                                  file.getId(),
                                                                                  Set.of(RecategorizationRequestModel.builder()
                                                                                                 .annotationId("dv")
                                                                                                 .legalBasis("")
                                                                                                 .section("section")
                                                                                                 .type(type.getType())
                                                                                                 .build()));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getRecategorizations().size(), 1);
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(response.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        var unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getRecategorizations().size(), 1);
        assertTrue(unprocessedManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(response.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(0)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        ManualRedactionResponse response2 = manualRedactionClient.recategorizeBulk(dossier.getId(),
                                                                                   file.getId(),
                                                                                   Set.of(RecategorizationRequestModel.builder()
                                                                                                  .annotationId("dv2")
                                                                                                  .legalBasis("")
                                                                                                  .section("section")
                                                                                                  .type(type.getType())
                                                                                                  .build()));

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getRecategorizations().size(), 2);
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(response.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(response2.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getRecategorizations().size(), 1);
        assertTrue(unprocessedManualRedactions.getRecategorizations()
                           .stream()
                           .noneMatch(entry -> entry.getAnnotationId()
                                   .equals(response.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));
        assertTrue(unprocessedManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(response2.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(1)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getRecategorizations().size(), 2);
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(response.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(response2.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertTrue(unprocessedManualRedactions.getRecategorizations().isEmpty());
    }


    @Test
    public void testUnprocessedManualRedactionsLegalBasisChanges() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type(type.getType())
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(false)
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("AnnotationId2")
                                                      .type(type.getType())
                                                      .value("Skywalker Luke")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(false)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.legalBasisChangeBulk(dossier.getId(),
                                                   file.getId(),
                                                   Set.of(LegalBasisChangeRequestModel.builder().legalBasis("legalBasis").annotationId("AnnotationId").build()));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getLegalBasisChanges().size(), 1);
        assertTrue(allManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis")));

        var unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getLegalBasisChanges().size(), 1);
        assertTrue(unprocessedManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis")));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(0)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        manualRedactionClient.legalBasisChangeBulk(dossier.getId(),
                                                   file.getId(),
                                                   Set.of(LegalBasisChangeRequestModel.builder().legalBasis("legalBasis2").annotationId("AnnotationId2").build()));

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getLegalBasisChanges().size(), 2);
        assertTrue(allManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis")));
        assertTrue(allManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis2")));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactions.getLegalBasisChanges().size(), 1);
        assertTrue(unprocessedManualRedactions.getLegalBasisChanges()
                           .stream()
                           .noneMatch(entry -> entry.getLegalBasis().equals("legalBasis")));
        assertTrue(unprocessedManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis2")));

        fileProcessingClient.analysisSuccessful(dossier.getId(),
                                                file.getId(),
                                                AnalyzeResult.builder()
                                                        .manualRedactions(allManualRedactions)
                                                        .messageType(MessageType.ANALYSE)
                                                        .analysisVersion(1)
                                                        .fileId(file.getId())
                                                        .dossierId(dossier.getId())
                                                        .build());

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getLegalBasisChanges().size(), 2);
        assertTrue(allManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis")));
        assertTrue(allManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis2")));

        unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertTrue(unprocessedManualRedactions.getResizeRedactions().isEmpty());
    }


    @Test
    public void testQueryOptions() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type(type.getType())
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(false)
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("AnnotationId2")
                                                      .type(type.getType())
                                                      .value("Skywalker Luke")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(false)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        manualRedactionClient.legalBasisChangeBulk(dossier.getId(),
                                                   file.getId(),
                                                   Set.of(LegalBasisChangeRequestModel.builder().legalBasis("legalBasis").annotationId("AnnotationId").build()));

        manualRedactionClient.addRedactionBulk(dossier.getId(),
                                               file.getId(),
                                               Set.of(AddRedactionRequestModel.builder()
                                                              .value("Peter")
                                                              .type("type")
                                                              .legalBasis("legalBasis")
                                                              .reason("reason")
                                                              .positions(List.of(new Rectangle(new Point(0, 0), 10, 10, 1)))
                                                              .dictionaryEntryType(DictionaryEntryType.ENTRY)
                                                              .addToDictionary(true)
                                                              .build()));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, false);
        assertEquals(allManualRedactions.getLegalBasisChanges().size(), 1);
        assertTrue(allManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis")));

        var unprocessedManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, false);
        assertEquals(unprocessedManualRedactions.buildAll().size(), 2);
        assertTrue(unprocessedManualRedactions.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis")));

        var unprocessedManualRedactionsWithDict = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);
        assertEquals(unprocessedManualRedactionsWithDict.buildAll().size(), 3);
        assertTrue(unprocessedManualRedactionsWithDict.getLegalBasisChanges()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("legalBasis")));
        assertTrue(unprocessedManualRedactionsWithDict.getEntriesToAdd()
                           .stream()
                           .anyMatch(entry -> entry.getValue().equals("Peter")));

    }


    @Test
    public void testLegalBasisInManualRecategorization() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("annotationId")
                                                      .type(type.getType())
                                                      .value("lukeSkywalker")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("annotationId2")
                                                      .type(type.getType())
                                                      .value("Johannisbrotkernmehl")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("annotationId3")
                                                      .type(type.getType())
                                                      .value("Baustelle")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var recatModel = RecategorizationRequestModel.builder()
                .type(type.getType())
                .annotationId("annotationId")
                .addToDictionary(true)
                .addToAllDossiers(true)
                .legalBasis("legalBasis")
                .section("section")
                .value("other value")
                .build();
        var recatModelLongLegalBasis = RecategorizationRequestModel.builder()
                .type(type.getType())
                .annotationId("annotationId2")
                .addToDictionary(true)
                .addToAllDossiers(true)
                .legalBasis(RandomStringUtils.randomAlphanumeric(4001))
                .build();
        var recatModelNoLegalBasis = RecategorizationRequestModel.builder()
                .type(type.getType())
                .annotationId("annotationId3")
                .addToDictionary(true)
                .section("section")
                .value("some value")
                .addToAllDossiers(true)
                .build();

        manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), Set.of(recatModel, recatModelNoLegalBasis));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(2, allManualRedactions.getRecategorizations().size());
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals("annotationId")));

        assertThatThrownBy(() -> manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), Set.of(recatModelLongLegalBasis)).getManualAnnotationResponses()
                .get(0)).isInstanceOf(FeignException.class).hasMessageContaining("The legal basis is too long");

        assertNull(allManualRedactions.getRecategorizations()
                           .stream()
                           .filter(manualRecategorization -> manualRecategorization.getAnnotationId().equals("annotationId3"))
                           .findFirst()
                           .get().getLegalBasis());

    }


    @Test
    public void testPropertyChangesForLegalBasisInManualRecategorization() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("annotationId")
                                                      .type(type.getType())
                                                      .value("lukeSkywalker")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .legalBasis("lb1")
                                                      .section("section")
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("annotationId2")
                                                      .type(type.getType())
                                                      .value("Johannisbrotkernmehl")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("annotationId3")
                                                      .type(type.getType())
                                                      .value("Baustelle")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var recatModel = RecategorizationRequestModel.builder()
                .type(type.getType())
                .annotationId("annotationId")
                .addToDictionary(false)
                .addToAllDossiers(false)
                .legalBasis("lb2")
                .section("overriddenSection")
                .build();

        ManualRedactionResponse response = manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), Set.of(recatModel));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(1, allManualRedactions.getRecategorizations().size());
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId()
                                   .equals(response.getManualAnnotationResponses()
                                                   .get(0).getAnnotationId())));
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getLegalBasis().equals("lb2")));
        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(entry -> entry.getSection().equals("overriddenSection")));
    }


    @Test
    public void testManualRecategorizationOnDictionaryRedaction() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false, 100);
        var type2 = typeProvider.testAndProvideType(dossierTemplate, null, "type2", false, 110);

        var entityLogEntry = EntityLogEntry.builder()
                .id("annotationId")
                .type(type.getType())
                .value("lukeSkywalker")
                .dictionaryEntry(true)
                .entryType(EntryType.ENTITY)
                .state(EntryState.APPLIED)
                .legalBasis("legal basis")
                .positions(List.of(new Position(1, 1, 1, 1, 1)))
                .build();
        var entityLog = new EntityLog(1, 1, List.of(entityLogEntry), null, 0, 0, 0, 0);
        fileManagementStorageService.storeJSONObject(dossier.getId(), file.getId(), FileType.ENTITY_LOG, entityLog);
        entityLogMongoService.upsertEntityLog(file.getDossierId(), file.getFileId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var recategorizationRequestModel = RecategorizationRequestModel.builder()
                .type(type2.getType())
                .annotationId("annotationId")
                .addToDictionary(false)
                .addToAllDossiers(false)
                .legalBasis("")
                .build();

        manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), Set.of(recategorizationRequestModel));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);

        assertEquals(allManualRedactions.getRecategorizations().size(), 1);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 1);
        var entryToAdd = allManualRedactions.getEntriesToAdd()
                .stream()
                .findFirst()
                .get();
        assertFalse(entryToAdd.isAddToDictionary());
        assertFalse(entryToAdd.isAddToDossierDictionary());
        assertEquals(entryToAdd.getValue(), entityLogEntry.getValue());
        assertEquals(entryToAdd.getType(), entityLogEntry.getType());
    }


    @Test
    public void testManualLegalBasisChangeOnDictionaryRedaction() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        var entityLogEntry = EntityLogEntry.builder()
                .id("annotationId")
                .type(type.getType())
                .value("lukeSkywalker")
                .dictionaryEntry(true)
                .entryType(EntryType.ENTITY)
                .state(EntryState.APPLIED)
                .legalBasis("legal basis")
                .positions(List.of(new Position(1, 1, 1, 1, 1)))
                .build();
        var entityLog = new EntityLog(1, 1, List.of(entityLogEntry), null, 0, 0, 0, 0);
        fileManagementStorageService.storeJSONObject(dossier.getId(), file.getId(), FileType.ENTITY_LOG, entityLog);
        entityLogMongoService.upsertEntityLog(file.getDossierId(), file.getFileId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var legalBasisChangeRequestModel = LegalBasisChangeRequestModel.builder()
                .annotationId("annotationId")
                .legalBasis("new legal basis")
                .value(entityLogEntry.getValue())
                .build();

        manualRedactionClient.legalBasisChangeBulk(dossier.getId(), file.getId(), Set.of(legalBasisChangeRequestModel));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);

        assertEquals(allManualRedactions.getLegalBasisChanges().size(), 1);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 1);
        var entryToAdd = allManualRedactions.getEntriesToAdd()
                .stream()
                .findFirst()
                .get();
        assertFalse(entryToAdd.isAddToDictionary());
        assertFalse(entryToAdd.isAddToDossierDictionary());
        assertEquals(entryToAdd.getValue(), entityLogEntry.getValue());
        assertEquals(entryToAdd.getLegalBasis(), entityLogEntry.getLegalBasis());
    }


    @Test
    public void testManualResizeOnDictionaryRedaction() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        var entityLogEntry = EntityLogEntry.builder()
                .id("annotationId")
                .type(type.getType())
                .value("lukeSkywalker")
                .dictionaryEntry(true)
                .entryType(EntryType.ENTITY)
                .state(EntryState.APPLIED)
                .legalBasis("legal basis")
                .positions(List.of(new Position(1, 1, 1, 1, 1)))
                .build();
        var entityLog = new EntityLog(1, 1, List.of(entityLogEntry), null, 0, 0, 0, 0);
        fileManagementStorageService.storeJSONObject(dossier.getId(), file.getId(), FileType.ENTITY_LOG, entityLog);
        entityLogMongoService.upsertEntityLog(file.getDossierId(), file.getFileId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var resizeRedactionRequestModel = ResizeRedactionRequestModel.builder()
                .annotationId("annotationId")
                .value("lukeSkywalker resized")
                .updateDictionary(false)
                .addToAllDossiers(false)
                .positions(List.of(Rectangle.builder().page(1).height(1).width(2).topLeftX(1).topLeftY(1).build()))
                .build();

        manualRedactionClient.resizeRedactionBulk(dossier.getId(), file.getId(), Set.of(resizeRedactionRequestModel));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);

        assertEquals(allManualRedactions.getResizeRedactions().size(), 1);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 1);
        var entryToAdd = allManualRedactions.getEntriesToAdd()
                .stream()
                .findFirst()
                .get();
        assertFalse(entryToAdd.isAddToDictionary());
        assertFalse(entryToAdd.isAddToDossierDictionary());
        assertEquals(entryToAdd.getValue(), entityLogEntry.getValue());
    }


    @Test
    public void testManualForceRedactionOnDictionaryRedaction() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        var entityLogEntry = EntityLogEntry.builder()
                .id("annotationId")
                .type(type.getType())
                .value("lukeSkywalker")
                .dictionaryEntry(true)
                .entryType(EntryType.ENTITY)
                .state(EntryState.APPLIED)
                .legalBasis("legal basis")
                .positions(List.of(new Position(1, 1, 1, 1, 1)))
                .build();
        var entityLog = new EntityLog(1, 1, List.of(entityLogEntry), null, 0, 0, 0, 0);
        fileManagementStorageService.storeJSONObject(dossier.getId(), file.getId(), FileType.ENTITY_LOG, entityLog);
        entityLogMongoService.upsertEntityLog(file.getDossierId(), file.getFileId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var forceRedactionRequestModel = ForceRedactionRequestModel.builder().annotationId("annotationId").build();

        manualRedactionClient.forceRedactionBulk(dossier.getId(), file.getId(), Set.of(forceRedactionRequestModel));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);

        assertEquals(allManualRedactions.getForceRedactions().size(), 1);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 1);
        var entryToAdd = allManualRedactions.getEntriesToAdd()
                .stream()
                .findFirst()
                .get();
        assertFalse(entryToAdd.isAddToDictionary());
        assertFalse(entryToAdd.isAddToDossierDictionary());
        assertEquals(entryToAdd.getValue(), entityLogEntry.getValue());
    }


    @Test
    public void testAddRedactionWithOver4000Characters() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", false);

        var entityLogEntry = EntityLogEntry.builder()
                .id("annotationId")
                .type(type.getType())
                .value("lukeSkywalker")
                .dictionaryEntry(true)
                .entryType(EntryType.ENTITY)
                .state(EntryState.APPLIED)
                .legalBasis("legal basis")
                .positions(List.of(new Position(1, 1, 1, 1, 1)))
                .build();
        var entityLog = new EntityLog(1, 1, List.of(entityLogEntry), null, 0, 0, 0, 0);
        fileManagementStorageService.storeJSONObject(dossier.getId(), file.getId(), FileType.ENTITY_LOG, entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var addRedactionRequest = AddRedactionRequestModel.builder()
                .value(StringUtils.repeat("a", 4001))
                .addToAllDossiers(false)
                .addToDictionary(false)
                .type("type")
                .reason("reason")
                .sourceId("sourceId")
                .build();

        String errorMessage = assertThrows(FeignException.BadRequest.class,
                                           () -> manualRedactionClient.addRedactionBulk(dossier.getId(), file.getId(), Set.of(addRedactionRequest))).getMessage();
        assertTrue(errorMessage.contains("Redaction value can not exceed 4000 characters"));
    }


    @Test
    public void testRecategorizeImageToLogo() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var typeImage = "image";
        var typeLogo = "logo";

        var entityLogEntry = EntityLogEntry.builder()
                .id("annotationId")
                .type(typeImage)
                .entryType(EntryType.IMAGE_HINT)
                .state(EntryState.SKIPPED)
                .value("Image:Other")
                .reason("")
                .matchedRule("")
                .legalBasis("")
                .imported(false)
                .containingNodeId(List.of(31, 12))
                .closestHeadline("Images: Regular ")
                .section("[31]: Section: Images: Regular Images: Formular")
                .color(null)
                .positions(List.of(new Position(1, 1, 1, 1, 1)))
                .textBefore(null)
                .textAfter(null)
                .startOffset(0)
                .endOffset(0)
                .imageHasTransparency(false)
                .dictionaryEntry(false)
                .dossierDictionaryEntry(false)
                .excluded(false)
                .changes(List.of(new Change(1, ChangeType.ADDED, OffsetDateTime.now(), Collections.emptyMap())))
                .build();
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(entityLogEntry),
                                      List.of(new EntityLogLegalBasis("1.1 personal data (incl. geolocation); Article 39(e)(3)",
                                                                      "desc",
                                                                      "Article 39(e)(3) of Regulation (EC) No 178/2002",
                                                                      ""),
                                              new EntityLogLegalBasis("4. commercial information",
                                                                      "desc 2",
                                                                      "Article 63(2)(a) of Regulation (EC) No 1107/2009  (making reference to Article 39 of Regulation EC No 178/2002)",
                                                                      "")),
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.storeJSONObject(dossier.getId(), file.getId(), FileType.ENTITY_LOG, entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var recategorizationRequestModel = RecategorizationRequestModel.builder()
                .annotationId("annotationId")
                .legalBasis("")
                .section("[31]: Section: Images: Regular Images: Formular")
                .type(typeLogo)
                .value("Image:Other")
                .build();

        manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), Set.of(recategorizationRequestModel));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);

        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(manualRecategorization -> manualRecategorization.getType().equals(typeLogo)));

    }


    @Test
    public void testLegalBasisChangeCbiAuthor() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "CBI_author", false);

        var entityLogEntry = EntityLogEntry.builder()
                .id("annotationId")
                .type(type.getType())
                .entryType(EntryType.ENTITY)
                .state(EntryState.APPLIED)
                .value("Ranya Eikenboom")
                .reason("Author found")
                .matchedRule("CBI.0.0")
                .legalBasis("Article 39(e)(3) of Regulation (EC) No 178/2002")
                .imported(false)
                .containingNodeId(List.of(1, 1, 1))
                .closestHeadline("CBI.0.0/1: Redact CBI Authors ")
                .section("[1, 1, 1]: Paragraph: David Ksenia Max Mustermann")
                .color(null)
                .positions(List.of(new Position(1, 1, 1, 1, 1)))
                .textBefore("Ksenia Max Mustermann ")
                .textAfter(" Charalampos Schenk")
                .startOffset(197)
                .endOffset(212)
                .imageHasTransparency(false)
                .dictionaryEntry(true)
                .dossierDictionaryEntry(false)
                .excluded(false)
                .changes(List.of(new Change(1, ChangeType.ADDED, OffsetDateTime.now(), Collections.emptyMap())))
                .engines(Set.of(Engine.DICTIONARY))
                .build();
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(entityLogEntry),
                                      List.of(new EntityLogLegalBasis("1.1 personal data (incl. geolocation); Article 39(e)(3)",
                                                                      "desc",
                                                                      "Article 39(e)(3) of Regulation (EC) No 178/2002",
                                                                      ""),
                                              new EntityLogLegalBasis("4. commercial information",
                                                                      "desc 2",
                                                                      "Article 63(2)(a) of Regulation (EC) No 1107/2009  (making reference to Article 39 of Regulation EC No 178/2002)",
                                                                      "")),
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.storeJSONObject(dossier.getId(), file.getId(), FileType.ENTITY_LOG, entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var recategorizationRequestModel = RecategorizationRequestModel.builder()
                .annotationId("annotationId")
                .legalBasis("Article 63(2)(a) of Regulation (EC) No 1107/2009  (making reference to Article 39 of Regulation EC No 178/2002)")
                .section("[1, 1, 1]: Paragraph: David Ksenia Max Mustermann")
                .type(type.getType())
                .value("Ranya Eikenboom")
                .build();

        manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), Set.of(recategorizationRequestModel));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);

        assertTrue(allManualRedactions.getRecategorizations()
                           .stream()
                           .anyMatch(manualRecategorization -> manualRecategorization.getLegalBasis().equals(recategorizationRequestModel.getLegalBasis())));

    }


    @Test
    public void testLocalAdd() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, "test", 100);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .positions(List.of(new Position(10f, 10f, 10f, 10f, 1)))
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualAnnotationResponse response = manualRedactionClient.addRedactionBulk(dossier.getId(),
                                                                                   file.getId(),
                                                                                   Set.of(AddRedactionRequestModel.builder()
                                                                                                  .sourceId("")
                                                                                                  .reason("Reason")
                                                                                                  .type(type.getType())
                                                                                                  .addToAllDossiers(false)
                                                                                                  .addToDictionary(false)
                                                                                                  .dictionaryEntryType(DictionaryEntryType.ENTRY)
                                                                                                  .forceAddToDictionary(false)
                                                                                                  .legalBasis("legalBasis")
                                                                                                  .positions(List.of(new Rectangle(10f, 10f, 10f, 10f, 1)))
                                                                                                  .value("MyValue")
                                                                                                  .build())).getManualAnnotationResponses()
                .get(0);

        assertEquals(response.getEntityLogEntry().getId(), response.getAnnotationId());
        assertEquals(response.getEntityLogEntry().getValue(), "MyValue");
        assertEquals(response.getEntityLogEntry().getLegalBasis(), "legalBasis");
        assertEquals(response.getEntityLogEntry().getReason(), "Reason");
        assertEquals(response.getEntityLogEntry().getType(), type.getType());
        assertEquals(response.getEntityLogEntry().getEntryType(), EntryType.ENTITY);
        assertEquals(response.getEntityLogEntry().getManualChanges()
                             .get(0).getManualRedactionType(), ManualRedactionType.ADD);
        assertNull(response.getEntityLogEntry().getManualChanges()
                           .get(0).getProcessedDate());
    }


    @Test
    public void testLocalForce() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, "test", 100);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.SKIPPED)
                                                      .dictionaryEntry(true)
                                                      .positions(List.of(new Position(10f, 10f, 10f, 10f, 1)))
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualAnnotationResponse response = manualRedactionClient.forceRedactionBulk(dossier.getId(),
                                                                                     file.getId(),
                                                                                     Set.of(ForceRedactionRequestModel.builder()
                                                                                                    .annotationId("AnnotationId")
                                                                                                    .legalBasis("legalBasis")
                                                                                                    .build())).getManualAnnotationResponses()
                .get(0);

        assertEquals(response.getEntityLogEntry().getId(), response.getAnnotationId());
        assertEquals(response.getEntityLogEntry().getLegalBasis(), "legalBasis");
        assertEquals(response.getEntityLogEntry().getState(), EntryState.APPLIED);
        assertEquals(response.getEntityLogEntry().getManualChanges()
                             .get(0).getManualRedactionType(), ManualRedactionType.FORCE);
        assertNull(response.getEntityLogEntry().getManualChanges()
                           .get(0).getProcessedDate());

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getForceRedactions().size(), 1);
        assertTrue(allManualRedactions.getForceRedactions()
                           .stream()
                           .anyMatch(entry -> entry.getAnnotationId().equals(response.getAnnotationId())));
    }


    @Test
    public void testLocalLegalBasisChangeOnDictEntry() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, "test", 100);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualAnnotationResponse response = manualRedactionClient.legalBasisChangeBulk(dossier.getId(),
                                                                                       file.getId(),
                                                                                       Set.of(LegalBasisChangeRequestModel.builder()
                                                                                                      .annotationId("AnnotationId")
                                                                                                      .legalBasis("new legal basis")
                                                                                                      .value("Luke Skywalker")
                                                                                                      .section("")
                                                                                                      .build())).getManualAnnotationResponses()
                .get(0);

        assertNotEquals(response.getEntityLogEntry().getId(), "AnnotationId");
        assertEquals(response.getEntityLogEntry().getLegalBasis(), "new legal basis");
        assertEquals(response.getEntityLogEntry().getManualChanges()
                             .get(0).getManualRedactionType(), ManualRedactionType.LEGAL_BASIS_CHANGE);
        assertNull(response.getEntityLogEntry().getManualChanges()
                           .get(0).getProcessedDate());
    }


    @Test
    public void testLocalResizeOnDictEntry() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, "test", 100);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .positions(List.of(new Position(10f, 10f, 10f, 10f, 1)))
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualAnnotationResponse response = manualRedactionClient.resizeRedactionBulk(dossier.getId(),
                                                                                      file.getId(),
                                                                                      Set.of(ResizeRedactionRequestModel.builder()
                                                                                                     .annotationId("AnnotationId")
                                                                                                     .updateDictionary(false)
                                                                                                     .addToAllDossiers(false)
                                                                                                     .value("Luke")
                                                                                                     .positions(List.of(new Rectangle(5f, 5f, 5f, 5f, 1)))
                                                                                                     .build())).getManualAnnotationResponses()
                .get(0);

        assertNotEquals(response.getEntityLogEntry().getId(), "AnnotationId");
        assertEquals(response.getEntityLogEntry().getValue(), "Luke");
        assertEquals(response.getEntityLogEntry().getPositions()
                             .get(0).getRectangle()[0], 5f);
        assertEquals(response.getEntityLogEntry().getPositions()
                             .get(0).getRectangle()[1], 5f);
        assertEquals(response.getEntityLogEntry().getPositions()
                             .get(0).getRectangle()[2], 5f);
        assertEquals(response.getEntityLogEntry().getPositions()
                             .get(0).getRectangle()[3], 5f);
        assertEquals(response.getEntityLogEntry().getManualChanges()
                             .get(0).getManualRedactionType(), ManualRedactionType.RESIZE);
        assertNull(response.getEntityLogEntry().getManualChanges()
                           .get(0).getProcessedDate());
    }


    @Test
    public void testLocalRecategorizeOnDictEntry() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, "test", 100);
        var type2 = typeProvider.testAndProvideType(dossierTemplate, "test2", 101);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualAnnotationResponse response = manualRedactionClient.recategorizeBulk(dossier.getId(),
                                                                                   file.getId(),
                                                                                   Set.of(RecategorizationRequestModel.builder()
                                                                                                  .type(type2.getType())
                                                                                                  .annotationId("AnnotationId")
                                                                                                  .addToDictionary(false)
                                                                                                  .addToAllDossiers(false)
                                                                                                  .legalBasis("")
                                                                                                  .section("section")
                                                                                                  .value("Luke Skywalker")
                                                                                                  .build())).getManualAnnotationResponses()
                .get(0);

        assertNotEquals(response.getEntityLogEntry().getId(), "AnnotationId");
        assertEquals(response.getEntityLogEntry().getType(), type2.getType());
        assertEquals(response.getEntityLogEntry().getManualChanges()
                             .get(0).getManualRedactionType(), ManualRedactionType.RECATEGORIZE);
        assertNull(response.getEntityLogEntry().getManualChanges()
                           .get(0).getProcessedDate());
    }


    @Test
    public void testLocalRemove() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate);
        String dictValue = "Crandu Seku Laku Meku";
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of(dictValue), false, dossier.getId(), DictionaryEntryType.ENTRY);
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId1")
                                                      .type("CBI_author")
                                                      .value(dictValue)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .dossierDictionaryEntry(true)
                                                      .engines(Set.of(Engine.DOSSIER_DICTIONARY))
                                                      .positions(List.of(new Position(new float[]{57f, 446f, 121f, 13f}, 1)))
                                                      .build(),
                                              EntityLogEntry.builder()
                                                      .id("AnnotationId2")
                                                      .type("CBI_author")
                                                      .value(dictValue)
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .dossierDictionaryEntry(true)
                                                      .engines(Set.of(Engine.DOSSIER_DICTIONARY))
                                                      .positions(List.of(new Position(new float[]{57f, 473f, 121f, 13f}, 1)))
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualAnnotationResponse response = manualRedactionClient.removeRedactionBulk(dossier.getId(),
                                                                                      file.getId(),
                                                                                      Set.of(RemoveRedactionRequestModel.builder()
                                                                                                     .annotationId("AnnotationId1")
                                                                                                     .removeFromDictionary(false)
                                                                                                     .removeFromAllDossiers(false)
                                                                                                     .build())).getManualAnnotationResponses()
                .get(0);

        assertEquals(response.getEntityLogEntry().getId(), "AnnotationId1");
        assertEquals(response.getEntityLogEntry().getState(), EntryState.IGNORED);
        assertEquals(response.getEntityLogEntry().getManualChanges()
                             .get(0).getManualRedactionType(), ManualRedactionType.REMOVE);
        assertNull(response.getEntityLogEntry().getManualChanges()
                           .get(0).getProcessedDate());
        assertFalse(response.getEntityLogEntry().getEngines().contains(Engine.MANUAL));
    }


    @Test
    public void testLocalRecategorizeADictBasedRedactionWithAnalysisStopped() { //RED-9094

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, "test", 100);
        var type2 = typeProvider.testAndProvideType(dossierTemplate, "test2", 101);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), List.of("Luke Skywalker"), false, dossier.getId(), DictionaryEntryType.ENTRY);
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id("AnnotationId")
                                                      .type("test")
                                                      .value("Luke Skywalker")
                                                      .entryType(EntryType.ENTITY)
                                                      .state(EntryState.APPLIED)
                                                      .dictionaryEntry(true)
                                                      .section("section")
                                                      .legalBasis("Article legal basis")
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getFileId(), true);
        var loadedFile = fileClient.getFileStatus(dossier.getId(), file.getId());
        assertThat(loadedFile.isExcludedFromAutomaticAnalysis()).isEqualTo(true);

        ManualAnnotationResponse response = manualRedactionClient.recategorizeBulk(dossier.getId(),
                                                                                   file.getId(),
                                                                                   Set.of(RecategorizationRequestModel.builder()
                                                                                                  .type(type2.getType())
                                                                                                  .annotationId("AnnotationId")
                                                                                                  .addToDictionary(false)
                                                                                                  .addToAllDossiers(false)
                                                                                                  .legalBasis("Article legal basis")
                                                                                                  .section("section")
                                                                                                  .value("Luke Skywalker")
                                                                                                  .build())).getManualAnnotationResponses()
                .get(0);

        assertNotEquals(response.getEntityLogEntry().getId(), "AnnotationId");
        assertEquals(response.getEntityLogEntry().getType(), type2.getType());
        assertEquals(response.getEntityLogEntry().getManualChanges()
                             .get(0).getManualRedactionType(), ManualRedactionType.RECATEGORIZE);
        assertEquals(response.getEntityLogEntry().getManualChanges()
                             .get(0).getPropertyChanges()
                             .get("type"), type2.getType());
        assertNull(response.getEntityLogEntry().getManualChanges()
                           .get(0).getProcessedDate());
    }


    @Test
    public void testRemoveMoreThan500Annotations() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();
        Set<RemoveRedactionRequestModel> removeRedactionRequestModels = new HashSet<>();
        for (int i = 0; i < 600; i++) {
            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationId" + i)
                                         .type("test")
                                         .value("Luke Skywalker" + i)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.APPLIED)
                                         .dictionaryEntry(false)
                                         .section("section")
                                         .legalBasis("Article legal basis")
                                         .build());
            removeRedactionRequestModels.add(RemoveRedactionRequestModel.builder()
                                                     .annotationId("AnnotationId" + i)
                                                     .removeFromDictionary(false)
                                                     .removeFromAllDossiers(false)
                                                     .build());
        }
        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualRedactionResponse manualRedactionResponse = manualRedactionClient.removeRedactionBulk(dossier.getId(), file.getId(), removeRedactionRequestModels);
        assertEquals(manualRedactionResponse.getManualAnnotationResponses().size(), 600);
    }


    @Test
    public void testForceMoreThan500Annotations() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();
        Set<ForceRedactionRequestModel> forceRedactionRequestModels = new HashSet<>();
        for (int i = 0; i < 600; i++) {
            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationId" + i)
                                         .type("test")
                                         .value("Luke Skywalker" + i)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.SKIPPED)
                                         .dictionaryEntry(false)
                                         .section("section")
                                         .legalBasis("Article legal basis")
                                         .build());
            forceRedactionRequestModels.add(ForceRedactionRequestModel.builder().annotationId("AnnotationId" + i).legalBasis("lb1").comment("").build());
        }
        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualRedactionResponse manualRedactionResponse = manualRedactionClient.forceRedactionBulk(dossier.getId(), file.getId(), forceRedactionRequestModels);
        assertEquals(manualRedactionResponse.getManualAnnotationResponses().size(), 600);
    }


    @Test
    public void testRecategorizeMoreThan500Annotations() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "type", true);
        assertThat(type.isDossierDictionaryOnly()).isTrue();

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();
        Set<RecategorizationRequestModel> recategorizationRequestModels = new HashSet<>();
        for (int i = 0; i < 600; i++) {
            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationId" + i)
                                         .type("test")
                                         .value("Luke Skywalker" + i)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.SKIPPED)
                                         .dictionaryEntry(false)
                                         .section("section")
                                         .legalBasis("Article legal basis")
                                         .build());
            recategorizationRequestModels.add(RecategorizationRequestModel.builder()
                                                      .annotationId("AnnotationId" + i)
                                                      .legalBasis("lb1")
                                                      .type("type")
                                                      .addToAllDossiers(false)
                                                      .addToDictionary(false)
                                                      .comment("")
                                                      .build());
        }
        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        ManualRedactionResponse manualRedactionResponse = manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), recategorizationRequestModels);
        assertEquals(manualRedactionResponse.getManualAnnotationResponses().size(), 600);
    }


    @Test
    public void testRemoveFromDictMoreThan100Annotations() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();
        Set<RemoveRedactionRequestModel> removeRedactionRequestModels = new HashSet<>();
        for (int i = 0; i < 101; i++) {
            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationId" + i)
                                         .type("test")
                                         .value("Luke Skywalker" + i)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.APPLIED)
                                         .dictionaryEntry(true)
                                         .section("section")
                                         .legalBasis("Article legal basis")
                                         .build());
            removeRedactionRequestModels.add(RemoveRedactionRequestModel.builder().annotationId("AnnotationId" + i).removeFromDictionary(true).removeFromAllDossiers(true).build());
        }
        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        var result = assertThrows(FeignException.class, () -> manualRedactionClient.removeRedactionBulk(dossier.getId(), file.getId(), removeRedactionRequestModels));
        assertTrue(result.getMessage().contains("Maximum number of remove from dictionary requests is 100."));
    }


    @Test
    public void testBulkLocalAutomaticAnalysisOff() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getId(), true);
        testBulkLocal(dossierTemplate, dossier, file);
    }


    @Test
    public void testBulkLocalAutomaticAnalysisOn() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getId(), false);
        testBulkLocal(dossierTemplate, dossier, file);
    }


    @Test
    public void testRectangleRedactionsBulkLocalAutomaticAnalysisOff() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getId(), true);
        testRectangleRedactionsBulkLocal(dossierTemplate, dossier, file);
    }


    @Test
    public void testRectangleRedactionsBulkLocalAutomaticAnalysisOn() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getId(), false);
        testRectangleRedactionsBulkLocal(dossierTemplate, dossier, file);
    }


    private void testBulkLocal(DossierTemplateModel dossierTemplate, Dossier dossier, FileStatus file) {

        whenGetEntityLogInvocation();

        var type1 = typeProvider.testAndProvideType(dossierTemplate, dossier, "test1", false, 70);
        var type2 = typeProvider.testAndProvideType(dossierTemplate, dossier, "test2", false, 71);
        var type3 = typeProvider.testAndProvideType(dossierTemplate, dossier, "test3", false, 72);
        var newType = typeProvider.testAndProvideType(dossierTemplate, dossier, "newType", false, 73);

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();
        String legal3 = "Legal 3";
        String darthVader = "Darth Vader";
        for (int i = 0; i < 101; i++) {
            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationId1" + i)
                                         .type(type1.getType())
                                         .value("Luke Skywalker" + i)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.APPLIED)
                                         .section("section")
                                         .legalBasis("Legal 1")
                                         .engines(Set.of(Engine.MANUAL))
                                         .build());
            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationId2" + i)
                                         .type(type2.getType())
                                         .value(darthVader)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.APPLIED)
                                         .section("section")
                                         .legalBasis("Legal 2")
                                         .engines(Set.of(Engine.MANUAL))
                                         .build());
            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationId3" + i)
                                         .type(type3.getType())
                                         .value(darthVader)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.APPLIED)
                                         .section("section")
                                         .legalBasis(legal3)
                                         .engines(Set.of(Engine.MANUAL))
                                         .build());
        }
        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        String newLegal = "new Legal";
        String otherSection = "other section";
        String comment1 = "Recategorizing Luke Skywalker37";
        ManualRedactionResponse manualRedactionResponse = manualRedactionClient.recategorizeBulkLocal(dossier.getId(),
                                                                                                      file.getId(),
                                                                                                      RecategorizationBulkLocalRequestModel.builder()
                                                                                                              .rectangle(false)
                                                                                                              .type(newType.getType())
                                                                                                              .legalBasis(newLegal)
                                                                                                              .section(otherSection)
                                                                                                              .value("Luke Skywalker37")
                                                                                                              .comment(comment1)
                                                                                                              .build());
        assertEquals(1, manualRedactionResponse.getManualAnnotationResponses().size());
        assertEquals(newType.getType(),
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(0).getEntityLogEntry().getType());
        assertEquals(newLegal,
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(0).getEntityLogEntry().getLegalBasis());
        assertEquals(otherSection,
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(0).getEntityLogEntry().getSection());
        Long commentId1 = manualRedactionResponse.getManualAnnotationResponses()
                .get(0).getCommentId();
        assertNotNull(commentId1);
        Optional<CommentEntity> commentEntity1 = commentRepository.findById(commentId1);
        assertTrue(commentEntity1.isPresent());
        assertEquals(comment1, commentEntity1.get().getText());

        String comment2 = "Recategorizing Darth Vader with Legal 3";
        manualRedactionResponse = manualRedactionClient.recategorizeBulkLocal(dossier.getId(),
                                                                              file.getId(),
                                                                              RecategorizationBulkLocalRequestModel.builder()
                                                                                      .rectangle(false)
                                                                                      .type(newType.getType())
                                                                                      .legalBasis(newLegal)
                                                                                      .section(otherSection)
                                                                                      .value(darthVader)
                                                                                      .originLegalBases(Set.of(legal3))
                                                                                      .comment(comment2)
                                                                                      .build());
        assertEquals(101, manualRedactionResponse.getManualAnnotationResponses().size());
        assertEquals(newType.getType(),
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(1).getEntityLogEntry().getType());
        assertEquals(newLegal,
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(29).getEntityLogEntry().getLegalBasis());
        assertEquals(otherSection,
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(79).getEntityLogEntry().getSection());
        Long commentId2 = manualRedactionResponse.getManualAnnotationResponses()
                .get(0).getCommentId();
        assertNotNull(commentId2);
        Optional<CommentEntity> commentEntity2 = commentRepository.findById(commentId2);
        assertTrue(commentEntity2.isPresent());
        assertEquals(comment2, commentEntity2.get().getText());

        String comment3 = "Recategorizing Darth Vader with type2";
        manualRedactionResponse = manualRedactionClient.recategorizeBulkLocal(dossier.getId(),
                                                                              file.getId(),
                                                                              RecategorizationBulkLocalRequestModel.builder()
                                                                                      .rectangle(false)
                                                                                      .type(newType.getType())
                                                                                      .legalBasis(newLegal)
                                                                                      .section(otherSection)
                                                                                      .value(darthVader)
                                                                                      .originTypes(Set.of(type2.getType()))
                                                                                      .comment(comment3)
                                                                                      .build());
        assertEquals(101, manualRedactionResponse.getManualAnnotationResponses().size());
        assertEquals(newType.getType(),
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(0).getEntityLogEntry().getType());
        assertEquals(newLegal,
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(36).getEntityLogEntry().getLegalBasis());
        assertEquals(otherSection,
                     manualRedactionResponse.getManualAnnotationResponses()
                             .get(98).getEntityLogEntry().getSection());
        Long commentId3 = manualRedactionResponse.getManualAnnotationResponses()
                .get(0).getCommentId();
        assertNotNull(commentId3);
        Optional<CommentEntity> commentEntity3 = commentRepository.findById(commentId3);
        assertTrue(commentEntity3.isPresent());
        assertEquals(comment3, commentEntity3.get().getText());

        String comment4 = "Removing all Darth Vader annotations";
        manualRedactionResponse = manualRedactionClient.removeRedactionBulkLocal(dossier.getId(),
                                                                                 file.getId(),
                                                                                 RemoveRedactionBulkLocalRequestModel.builder()
                                                                                         .rectangle(false)
                                                                                         .value(darthVader)
                                                                                         .comment(comment4)
                                                                                         .build());
        assertEquals(202, manualRedactionResponse.getManualAnnotationResponses().size());
        Long commentId4 = manualRedactionResponse.getManualAnnotationResponses()
                .get(0).getCommentId();
        assertNotNull(commentId4);
        Optional<CommentEntity> commentEntity4 = commentRepository.findById(commentId4);
        assertTrue(commentEntity4.isPresent());
        assertEquals(comment4, commentEntity4.get().getText());
    }


    private void testRectangleRedactionsBulkLocal(DossierTemplateModel dossierTemplate, Dossier dossier, FileStatus file) {

        whenGetEntityLogInvocation();

        var typeManual = "manual";
        var newType = typeProvider.testAndProvideType(dossierTemplate, dossier, "newType", false, 71);

        String value = "non-readable content";
        String legalBasis = "Article 63(2)(a) of Regulation (EC) No 1107/2009 (making reference to Article 39 of Regulation EC No 178/2002)";

        float x = 88.34f;
        float y = 701.95f;
        float height = 77.57000000000002f;
        float width = 420.15f;

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();

        for (int page = 1; page <= 3; page++) {
            Position position = new Position(new float[]{x, y, width, height}, page);

            EntityLogEntry entityLogEntry = EntityLogEntry.builder()
                    .id("RectangleAnnotation" + page)
                    .type(typeManual)
                    .value(value)
                    .entryType(EntryType.AREA)
                    .state(EntryState.APPLIED)
                    .legalBasis(legalBasis)
                    .positions(List.of(position))
                    .engines(Set.of(Engine.MANUAL))
                    .build();

            entityLogEntries.add(entityLogEntry);
        }

        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        Position filterPosition = new Position(new float[]{x, y, width, height}, 0);

        RecategorizationBulkLocalRequestModel recategorizationRequest = RecategorizationBulkLocalRequestModel.builder()
                .rectangle(true)
                .type(newType.getType())
                .legalBasis("new Legal Basis")
                .section("new Section")
                .position(filterPosition)
                .value("new value")
                .build();

        ManualRedactionResponse manualRedactionResponse = manualRedactionClient.recategorizeBulkLocal(dossier.getId(), file.getId(), recategorizationRequest);

        assertNotNull(manualRedactionResponse);
        assertEquals(3, manualRedactionResponse.getManualAnnotationResponses().size());
        for (ManualAnnotationResponse response : manualRedactionResponse.getManualAnnotationResponses()) {
            assertEquals(typeManual, response.getEntityLogEntry().getType());
            assertEquals("new Legal Basis", response.getEntityLogEntry().getLegalBasis());
            assertEquals("new Section", response.getEntityLogEntry().getSection());
            assertEquals("new value", response.getEntityLogEntry().getValue());
        }

        recategorizationRequest = RecategorizationBulkLocalRequestModel.builder()
                .rectangle(false)
                .type(newType.getType())
                .legalBasis("another Legal Basis")
                .section("another Section")
                .value(value)
                .build();

        RecategorizationBulkLocalRequestModel finalRecategorizationRequest = recategorizationRequest;
        FeignException feignException = assertThrows(FeignException.class,
                                                     () -> manualRedactionClient.recategorizeBulkLocal(dossier.getId(), file.getId(), finalRecategorizationRequest));

        assertEquals(feignException.status(), 400);

        recategorizationRequest = RecategorizationBulkLocalRequestModel.builder()
                .rectangle(false)
                .type(newType.getType())
                .legalBasis("and another Legal Basis")
                .section("and another Section")
                .position(filterPosition)
                .build();

        feignException = assertThrows(FeignException.class, () -> manualRedactionClient.recategorizeBulkLocal(dossier.getId(), file.getId(), finalRecategorizationRequest));
        assertEquals(feignException.status(), 400);

        RemoveRedactionBulkLocalRequestModel removeRequest = RemoveRedactionBulkLocalRequestModel.builder().rectangle(true).position(filterPosition).build();

        manualRedactionResponse = manualRedactionClient.removeRedactionBulkLocal(dossier.getId(), file.getId(), removeRequest);

        assertNotNull(manualRedactionResponse);
        assertEquals(3, manualRedactionResponse.getManualAnnotationResponses().size());
        List<EntityLogEntry> removedEntries = entityLogService.getEntityLog(dossier.getId(), file.getId()).getEntityLogEntry()
                .stream()
                .filter(ele -> ele.getState().equals(EntryState.REMOVED))
                .toList();
        assertEquals(3, removedEntries.size());

        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        removeRequest = RemoveRedactionBulkLocalRequestModel.builder().rectangle(false).position(filterPosition).build();

        RemoveRedactionBulkLocalRequestModel finalRemoveRequest = removeRequest;
        assertThrows(FeignException.class, () -> manualRedactionClient.removeRedactionBulkLocal(dossier.getId(), file.getId(), finalRemoveRequest));
        assertEquals(feignException.status(), 400);
    }


    @Test
    public void testCaseSensitivityAutomaticAnalysisOff() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getId(), true);
        testCaseSensitivity(dossierTemplate, dossier, file);
    }


    @Test
    public void testCaseSensitivityAutomaticAnalysisOn() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getId(), false);
        testCaseSensitivity(dossierTemplate, dossier, file);
    }


    private void testCaseSensitivity(DossierTemplateModel dossierTemplate, Dossier dossier, FileStatus file) {

        whenGetEntityLogInvocation();

        var originalType = typeProvider.testAndProvideType(dossierTemplate, dossier, "originalType", false, 70);
        var newType = typeProvider.testAndProvideType(dossierTemplate, dossier, "newType", false, 71);

        String valueLowerCase = "luke skywalker";
        String valueMixedCase = "Luke Skywalker";
        String valueUpperCase = "LUKE SKYWALKER";

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationIdLowerCase" + i)
                                         .type(originalType.getType())
                                         .value(valueLowerCase)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.APPLIED)
                                         .section("Original Section")
                                         .legalBasis("Original Legal Basis")
                                         .engines(Set.of(Engine.MANUAL))
                                         .build());

            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationIdMixedCase" + i)
                                         .type(originalType.getType())
                                         .value(valueMixedCase)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.APPLIED)
                                         .section("Original Section")
                                         .legalBasis("Original Legal Basis")
                                         .engines(Set.of(Engine.MANUAL))
                                         .build());

            entityLogEntries.add(EntityLogEntry.builder()
                                         .id("AnnotationIdUpperCase" + i)
                                         .type(originalType.getType())
                                         .value(valueUpperCase)
                                         .entryType(EntryType.ENTITY)
                                         .state(EntryState.APPLIED)
                                         .section("Original Section")
                                         .legalBasis("Original Legal Basis")
                                         .engines(Set.of(Engine.MANUAL))
                                         .build());
        }

        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        String newLegalBasis = "New Legal Basis";
        String newSection = "New Section";

        ManualRedactionResponse responseCaseSensitive = manualRedactionClient.recategorizeBulkLocal(dossier.getId(),
                                                                                                    file.getId(),
                                                                                                    RecategorizationBulkLocalRequestModel.builder()
                                                                                                            .rectangle(false)
                                                                                                            .type(newType.getType())
                                                                                                            .legalBasis(newLegalBasis)
                                                                                                            .section(newSection)
                                                                                                            .value(valueMixedCase)
                                                                                                            .caseSensitive(true)
                                                                                                            .build());

        assertEquals(10, responseCaseSensitive.getManualAnnotationResponses().size());
        for (ManualAnnotationResponse response : responseCaseSensitive.getManualAnnotationResponses()) {
            assertEquals(newType.getType(), response.getEntityLogEntry().getType());
            assertEquals(valueMixedCase, response.getEntityLogEntry().getValue());
            assertEquals(newLegalBasis, response.getEntityLogEntry().getLegalBasis());
            assertEquals(newSection, response.getEntityLogEntry().getSection());
        }

        ManualRedactionResponse responseCaseInsensitive = manualRedactionClient.recategorizeBulkLocal(dossier.getId(),
                                                                                                      file.getId(),
                                                                                                      RecategorizationBulkLocalRequestModel.builder()
                                                                                                              .rectangle(false)
                                                                                                              .type(newType.getType())
                                                                                                              .legalBasis(newLegalBasis)
                                                                                                              .section(newSection)
                                                                                                              .value(valueMixedCase)
                                                                                                              .caseSensitive(false)
                                                                                                              .build());

        assertEquals(30, responseCaseInsensitive.getManualAnnotationResponses().size());
        for (ManualAnnotationResponse response : responseCaseInsensitive.getManualAnnotationResponses()) {
            assertEquals(newType.getType(), response.getEntityLogEntry().getType());
            assertTrue(response.getEntityLogEntry().getValue().equalsIgnoreCase(valueMixedCase));
            assertEquals(newLegalBasis, response.getEntityLogEntry().getLegalBasis());
            assertEquals(newSection, response.getEntityLogEntry().getSection());
        }

        ManualRedactionResponse removalResponseCaseSensitive = manualRedactionClient.removeRedactionBulkLocal(dossier.getId(),
                                                                                                              file.getId(),
                                                                                                              RemoveRedactionBulkLocalRequestModel.builder()
                                                                                                                      .rectangle(false)
                                                                                                                      .value(valueMixedCase)
                                                                                                                      .caseSensitive(true)
                                                                                                                      .build());

        assertEquals(10, removalResponseCaseSensitive.getManualAnnotationResponses().size());

        ManualRedactionResponse removalResponseCaseInsensitive = manualRedactionClient.removeRedactionBulkLocal(dossier.getId(),
                                                                                                                file.getId(),
                                                                                                                RemoveRedactionBulkLocalRequestModel.builder()
                                                                                                                        .rectangle(false)
                                                                                                                        .value(valueMixedCase)
                                                                                                                        .caseSensitive(false)
                                                                                                                        .build());

        assertEquals(20, removalResponseCaseInsensitive.getManualAnnotationResponses().size()); // 10 have already been removed by the previous bulk remove
    }


    @Test
    public void testAddRemoveAddSameValueAndPositionAutomaticAnalysisOff() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getId(), true);
        testAddRemoveAddSameValueAndPosition(dossierTemplate, dossier, file);
    }


    private void testAddRemoveAddSameValueAndPosition(DossierTemplateModel dossierTemplate, Dossier dossier, FileStatus file) {

        whenGetEntityLogInvocation();

        var typeOriginal = typeProvider.testAndProvideType(dossierTemplate, dossier, "originalType", false, 70);

        String value = "Test Value";
        String legalBasis = "Original Legal Basis";
        String section = "Original Section";

        Position position = new Position(new float[]{100f, 200f, 50f, 50f}, 1);

        EntityLogEntry valueAnnotation = EntityLogEntry.builder()
                .id("ValueAnnotation1")
                .type(typeOriginal.getType())
                .value(value)
                .entryType(EntryType.ENTITY)
                .state(EntryState.APPLIED)
                .section(section)
                .legalBasis(legalBasis)
                .engines(Set.of(Engine.MANUAL))
                .build();

        EntityLogEntry rectangleAnnotation = EntityLogEntry.builder()
                .id("RectangleAnnotation1")
                .type(typeOriginal.getType())
                .value("non-readable content")
                .entryType(EntryType.AREA)
                .state(EntryState.APPLIED)
                .positions(List.of(position))
                .legalBasis(legalBasis)
                .section(section)
                .engines(Set.of(Engine.MANUAL))
                .build();

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();
        entityLogEntries.add(valueAnnotation);
        entityLogEntries.add(rectangleAnnotation);

        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        RemoveRedactionBulkLocalRequestModel removeValueRequest = RemoveRedactionBulkLocalRequestModel.builder().rectangle(false).value(value).build();

        ManualRedactionResponse removeValueResponse = manualRedactionClient.removeRedactionBulkLocal(dossier.getId(), file.getId(), removeValueRequest);

        assertEquals(1, removeValueResponse.getManualAnnotationResponses().size());
        EntityLog currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertEquals(2, currentEntityLog.getEntityLogEntry().size());
        EntityLogEntry removedValueAnnotation = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> "ValueAnnotation1".equals(entry.getId()))
                .findFirst()
                .orElse(null);
        assertNotNull(removedValueAnnotation);
        assertEquals(EntryState.REMOVED, removedValueAnnotation.getState());
        EntityLogEntry rectangleAnnotationEntry = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> "RectangleAnnotation1".equals(entry.getId()))
                .findFirst()
                .orElse(null);
        assertNotNull(rectangleAnnotationEntry);
        assertEquals(EntryState.APPLIED, rectangleAnnotationEntry.getState());

        String awesomeComment = "awesome comment";
        String myUser = "MyUser";
        BulkLocalResponse addValueBackResponse = BulkLocalResponse.builder()
                .fileId(file.getId())
                .dossierId(dossier.getId())
                .type(typeOriginal.getType())
                .legalBasis(legalBasis)
                .reason("Adding value annotation back")
                .section(section)
                .foundTerms(List.of(new FoundTerm(List.of(position), value)))
                .comment(awesomeComment)
                .userId(myUser)
                .build();
        searchTermOccurrencesResponseReceiver.receive(addValueBackResponse);

        currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertEquals(3, currentEntityLog.getEntityLogEntry().size());
        removedValueAnnotation = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> "ValueAnnotation1".equals(entry.getId()))
                .findFirst()
                .orElse(null);
        assertNotNull(removedValueAnnotation);
        assertEquals(EntryState.REMOVED, removedValueAnnotation.getState());
        EntityLogEntry newValueAnnotation = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> !entry.getId().equals("ValueAnnotation1") && !entry.getId().equals("RectangleAnnotation1"))
                .filter(entry -> entry.getEntryType() == EntryType.ENTITY)
                .findFirst()
                .orElse(null);
        assertNotNull(newValueAnnotation);
        assertEquals(EntryState.APPLIED, newValueAnnotation.getState());
        assertEquals(value, newValueAnnotation.getValue());
        List<CommentEntity> commentEntities = commentRepository.findByFileIdAndAnnotationId(file.getId(), newValueAnnotation.getId(), false);
        assertEquals(commentEntities.size(), 1);
        assertEquals(commentEntities.get(0).getText(), awesomeComment);
        assertEquals(commentEntities.get(0).getUser(), myUser);

        removeValueResponse = manualRedactionClient.removeRedactionBulkLocal(dossier.getId(), file.getId(), removeValueRequest);

        assertEquals(1, removeValueResponse.getManualAnnotationResponses().size());
        currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertEquals(3, currentEntityLog.getEntityLogEntry().size());
        removedValueAnnotation = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> "ValueAnnotation1".equals(entry.getId()))
                .findFirst()
                .orElse(null);
        assertNotNull(removedValueAnnotation);
        assertEquals(EntryState.REMOVED, removedValueAnnotation.getState());
        EntityLogEntry removedNewValueAnnotation = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> !entry.getId().equals("ValueAnnotation1") && !entry.getId().equals("RectangleAnnotation1"))
                .filter(entry -> entry.getEntryType() == EntryType.ENTITY)
                .findFirst()
                .orElse(null);
        assertNotNull(removedNewValueAnnotation);
        assertEquals(EntryState.REMOVED, removedNewValueAnnotation.getState());

        rectangleAnnotationEntry = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> "RectangleAnnotation1".equals(entry.getId()))
                .findFirst()
                .orElse(null);
        assertNotNull(rectangleAnnotationEntry);
        assertEquals(EntryState.APPLIED, rectangleAnnotationEntry.getState());

        searchTermOccurrencesResponseReceiver.receive(addValueBackResponse);

        currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertEquals(4, currentEntityLog.getEntityLogEntry().size());
        List<EntityLogEntry> appliedValueAnnotations = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> entry.getValue().equals(value) && entry.getState() == EntryState.APPLIED)
                .toList();
        assertEquals(1, appliedValueAnnotations.size());
        commentEntities = commentRepository.findByFileIdAndAnnotationId(file.getId(), appliedValueAnnotations.get(0).getId(), false);
        assertEquals(commentEntities.size(), 1);
        assertEquals(commentEntities.get(0).getText(), awesomeComment);
        assertEquals(commentEntities.get(0).getUser(), myUser);

        String totallyDifferentComment = "totally different comment";
        RemoveRedactionBulkLocalRequestModel removeRectangleRequest = RemoveRedactionBulkLocalRequestModel.builder()
                .rectangle(true)
                .position(position)
                .comment(totallyDifferentComment)
                .build();

        ManualRedactionResponse removeRectangleResponse = manualRedactionClient.removeRedactionBulkLocal(dossier.getId(), file.getId(), removeRectangleRequest);

        assertEquals(1, removeRectangleResponse.getManualAnnotationResponses().size());
        Long commentId = removeRectangleResponse.getManualAnnotationResponses()
                .get(0).getCommentId();
        assertNotNull(commentId);
        Optional<CommentEntity> comment = commentRepository.findById(commentId);
        assertTrue(comment.isPresent());
        assertEquals(comment.get().getText(), totallyDifferentComment);

        currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertEquals(4, currentEntityLog.getEntityLogEntry().size());
        rectangleAnnotationEntry = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> "RectangleAnnotation1".equals(entry.getId()))
                .findFirst()
                .orElse(null);
        assertNotNull(rectangleAnnotationEntry);
        assertEquals(EntryState.REMOVED, rectangleAnnotationEntry.getState());

        AddRedactionRequestModel addRectangleRequest = AddRedactionRequestModel.builder()
                .type(typeOriginal.getType())
                .positions(List.of(toRectangle(position)))
                .legalBasis(legalBasis)
                .section(section)
                .reason("Adding rectangle annotation back")
                .value("non-readable content")
                .rectangle(true)
                .build();
        ManualRedactionResponse addRectangleResponse = manualRedactionClient.addRedactionBulk(dossier.getId(), file.getId(), Set.of(addRectangleRequest));

        assertEquals(1, addRectangleResponse.getManualAnnotationResponses().size());
        currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertEquals(5, currentEntityLog.getEntityLogEntry().size());
        rectangleAnnotationEntry = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> "RectangleAnnotation1".equals(entry.getId()))
                .findFirst()
                .orElse(null);
        assertNotNull(rectangleAnnotationEntry);
        assertEquals(EntryState.REMOVED, rectangleAnnotationEntry.getState());
        EntityLogEntry newRectangleAnnotation = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> entry.getEntryType() == EntryType.AREA && entry.getState() == EntryState.APPLIED && !entry.getId().equals("RectangleAnnotation1"))
                .findFirst()
                .orElse(null);
        assertNotNull(newRectangleAnnotation);

        removeRectangleResponse = manualRedactionClient.removeRedactionBulkLocal(dossier.getId(), file.getId(), removeRectangleRequest);

        assertEquals(1, removeRectangleResponse.getManualAnnotationResponses().size());
        currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertEquals(5, currentEntityLog.getEntityLogEntry().size());
        EntityLogEntry removedNewRectangleAnnotation = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> entry.getEntryType() == EntryType.AREA && entry.getState() == EntryState.REMOVED && !entry.getId().equals("RectangleAnnotation1"))
                .findFirst()
                .orElse(null);
        assertNotNull(removedNewRectangleAnnotation);

        addRectangleResponse = manualRedactionClient.addRedactionBulk(dossier.getId(), file.getId(), Set.of(addRectangleRequest));

        assertEquals(1, addRectangleResponse.getManualAnnotationResponses().size());
        currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertEquals(6, currentEntityLog.getEntityLogEntry().size());
        long appliedRectangleAnnotationsCount = currentEntityLog.getEntityLogEntry()
                .stream()
                .filter(entry -> entry.getEntryType() == EntryType.AREA && entry.getState() == EntryState.APPLIED)
                .count();
        assertEquals(1, appliedRectangleAnnotationsCount);
    }


    @Test
    public void testLocalAddWithRectangle() {

        whenGetEntityLogInvocation();

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, dossier, "test", false, 100);

        var entityLog = new EntityLog(1, 1, new ArrayList<>(), null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        AddRedactionBulkLocalRequestModel addRedactionRequest = AddRedactionBulkLocalRequestModel.builder()
                .type(type.getType())
                .value("non-readable content")
                .legalBasis("legal basis")
                .reason("reason")
                .section("section")
                .comment("test comment")
                .positions(List.of(new Rectangle(100f, 200f, 50f, 50f, 0)))
                .pageNumbers(Set.of(1, 2, 3))
                .rectangle(true)
                .build();

        ManualRedactionResponse response = manualRedactionClient.addRedactionBulkLocal(dossier.getId(), file.getId(), addRedactionRequest);

        assertNotNull(response);
        assertEquals(3, response.getManualAnnotationResponses().size());

        EntityLog currentEntityLog = entityLogService.getEntityLog(dossier.getId(), file.getId());
        assertNotNull(currentEntityLog);
        assertEquals(3, currentEntityLog.getEntityLogEntry().size());

        for (int i = 1; i <= 3; i++) {
            int page = i;
            EntityLogEntry entry = currentEntityLog.getEntityLogEntry()
                    .stream()
                    .filter(e -> e.getPositions() != null
                                 && !e.getPositions().isEmpty()
                                 && e.getPositions()
                                            .get(0).getPageNumber() == page)
                    .findFirst()
                    .orElse(null);
            assertNotNull(entry);
            assertEquals("non-readable content", entry.getValue());
            assertEquals("legal basis", entry.getLegalBasis());
            assertEquals("reason", entry.getReason());
            assertEquals("section", entry.getSection());
            assertEquals(type.getType(), entry.getType());
            assertEquals(EntryType.AREA, entry.getEntryType());
            assertEquals(EntryState.APPLIED, entry.getState());
            assertEquals(1, entry.getPositions().size());
            Position position = entry.getPositions()
                    .get(0);
            assertEquals(page, position.getPageNumber());
            assertEquals(100f, position.x());
            assertEquals(200f, position.y());
            assertEquals(50f, position.w());
            assertEquals(50f, position.h());
        }

        for (EntityLogEntry entry : currentEntityLog.getEntityLogEntry()) {
            List<CommentEntity> comments = commentRepository.findByFileIdAndAnnotationId(file.getId(), entry.getId(), false);
            assertEquals(1, comments.size());
            CommentEntity comment = comments.get(0);
            assertEquals("test comment", comment.getText());
        }
    }


    private Rectangle toRectangle(Position position) {

        return new Rectangle(position.x(), position.y(), position.w(), position.h(), 0);
    }


    private void whenGetEntityLogInvocation() {

        Answer<Object> answer = invocation -> {

            String dossierId = invocation.getArgument(0);
            String fileId = invocation.getArgument(1);

            DossierEntity dossier = dossierService.getDossierById(dossierId);
            EntityLog entityLog = fileManagementStorageService.getEntityLog(dossierId, fileId);
            ManualRedactions unprocessedManualRedactions = manualRedactionProviderService.getManualRedactions(fileId, ManualChangesQueryOptions.unprocessedOnly());
            return entityLogMergeService.mergeEntityLog(unprocessedManualRedactions, entityLog, dossier);
        };

        when(entityLogService.getEntityLog(any(), any(), anyList(), anyBoolean())).thenAnswer(answer);
        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenAnswer(answer);
        when(entityLogService.getEntityLog(any(), any())).thenAnswer(answer);
    }


    @Test
    public void testReaddSameImageRecatsAfterFullFileOverwrite() {

        // Step 1: Prepare file
        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        // Step 2: Do recategorizations
        var type1 = typeProvider.testAndProvideType(dossierTemplate, dossier, "Type1", false, 66);
        var type2 = typeProvider.testAndProvideType(dossierTemplate, dossier, "Type2", false, 100);

        List<String> entries = List.of("Luke Skywalker");
        dictionaryClient.addEntry(type1.getType(), type1.getDossierTemplateId(), entries, false, null, DictionaryEntryType.ENTRY);

        // Create an annotation
        var annotationId = "AnnotationId";
        var entityLog = new EntityLog(1,
                                      1,
                                      List.of(EntityLogEntry.builder()
                                                      .id(annotationId)
                                                      .type(type1.getType())
                                                      .value("Luke Skywalker")
                                                      .dictionaryEntry(true)
                                                      .entryType(EntryType.IMAGE)
                                                      .state(EntryState.APPLIED)
                                                      .build()),
                                      null,
                                      0,
                                      0,
                                      0,
                                      0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        when(entityLogService.getEntityLog(any(), any(), anyBoolean())).thenReturn(entityLog);

        Set<RecategorizationRequestModel> recategorizationRequests = Set.of(RecategorizationRequestModel.builder()
                                                                                    .type(type2.getType())
                                                                                    .annotationId(annotationId)
                                                                                    .addToDictionary(false)
                                                                                    .addToAllDossiers(false)
                                                                                    .legalBasis("")
                                                                                    .section("section")
                                                                                    .value("Luke Skywalker")
                                                                                    .build());
        manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), recategorizationRequests);

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getRecategorizations().size(), 1);

        List<ManualRecategorizationEntity> recats = recategorizationPersistenceService.findEntriesByFileIdAndOptions(file.getId(),
                                                                                                                     ManualChangesQueryOptions.builder()
                                                                                                                             .includeDeletions(true)
                                                                                                                             .build());
        assertEquals(recats.size(), 1);
        assertEquals(annotationId, recats.get(0).getId().getAnnotationId());
        assertNull(recats.get(0).getSoftDeletedTime());

        // Step 3: file overwrite
        fileStatusService.overwriteFile(file.getDossierId(), file.getId(), "testUploader", file.getFilename(), false, false);

        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        assertEquals(allManualRedactions.getRecategorizations().size(), 0);
        recats = recategorizationPersistenceService.findEntriesByFileIdAndOptions(file.getId(), ManualChangesQueryOptions.builder().includeDeletions(true).build());
        assertEquals(recats.size(), 1);
        assertEquals(annotationId, recats.get(0).getId().getAnnotationId());
        assertNotNull(recats.get(0).getSoftDeletedTime());

        // Step 4: Do the same recategorizations again
        manualRedactionClient.recategorizeBulk(dossier.getId(), file.getId(), recategorizationRequests);

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), false, true);
        recats = recategorizationPersistenceService.findEntriesByFileIdAndOptions(file.getId(), ManualChangesQueryOptions.builder().includeDeletions(true).build());
        assertEquals(recats.size(), 1);
        assertEquals(annotationId, recats.get(0).getId().getAnnotationId());
        assertNull(recats.get(0).getSoftDeletedTime());
        assertEquals(allManualRedactions.getRecategorizations().size(), 1);
    }

    @Test
    public void testPendingEntryFactoryEngineBasedOnAddToDossierDictionary() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var type = typeProvider.testAndProvideType(dossierTemplate, null, "typeEngineTest", false);
        assertThat(type.isDossierDictionaryOnly()).isFalse();

        AddRedactionRequestModel addRedactionAllDossiers = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().topLeftX(10).topLeftY(10).width(50).height(50).page(1).build()))
                .section("Test Section")
                .addToDictionary(true)
                .addToAllDossiers(true)
                .type(type.getType())
                .reason("Adding to all dossiers")
                .value("TestValueAllDossiers")
                .legalBasis("Legal Basis")
                .sourceId("SourceId1")
                .build();

        manualRedactionClient.addRedactionBulk(dossier.getId(), file.getId(), Set.of(addRedactionAllDossiers));

        AddRedactionRequestModel addRedactionSingleDossier = AddRedactionRequestModel.builder()
                .positions(List.of(Rectangle.builder().topLeftX(20).topLeftY(20).width(30).height(30).page(2).build()))
                .section("Test Section Single Dossier")
                .addToDictionary(true)
                .addToAllDossiers(false)
                .type(type.getType())
                .reason("Adding to single dossier")
                .value("TestValueSingleDossier")
                .legalBasis("Legal Basis")
                .sourceId("SourceId2")
                .build();

        manualRedactionClient.addRedactionBulk(dossier.getId(), file.getId(), Set.of(addRedactionSingleDossier));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, true);

        DossierEntity dossierEntity = MagicConverter.convert(dossier, DossierEntity.class);
        EntityLog updatedEntityLog = entityLogMergeService.mergeEntityLog(
                allManualRedactions,
                new EntityLog(),
                dossierEntity
        );

        var entityLogEntries = updatedEntityLog.getEntityLogEntry();
        assertThat(entityLogEntries).hasSize(2);

        for (EntityLogEntry entry : entityLogEntries) {
            if (entry.getValue().equals("TestValueAllDossiers")) {
                // Should have Engine.DICTIONARY
                assertThat(entry.getEngines()).containsExactlyInAnyOrder(Engine.DICTIONARY);
            } else if (entry.getValue().equals("TestValueSingleDossier")) {
                // Should have Engine.DOSSIER_DICTIONARY
                assertThat(entry.getEngines()).containsExactlyInAnyOrder(Engine.DOSSIER_DICTIONARY);
            } else {
                throw new AssertionError("Unexpected EntityLogEntry value: " + entry.getValue());
            }
        }
    }

    @Test
    public void testAddRedactionWithCommentToDossierAndTemplateDictionary() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        // Create a type that allows adding to dictionary and not to template dictionary
        var type = typeProvider.testAndProvideType(dossierTemplate, dossier, "typeWithComment", true, 50);
        assertThat(type.isDossierDictionaryOnly()).isTrue();

        // Verify initial state of dictionaries
        var initialDossierDictionary = internalDictionaryClient.getDictionaryForType(
                toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
        assertThat(initialDossierDictionary.getEntries()).isEmpty();

        var initialTemplateDictionary = internalDictionaryClient.getDictionaryForType(
                toTypeId(type.getType(), dossierTemplate.getId()), null);
        assertThat(initialTemplateDictionary.getEntries()).isEmpty();

        // Add a manual redaction with a comment to the dossier dictionary
        String comment = "This is a test comment for the redaction.";
        String redactionValue = "Leia Organa";

        ManualRedactionResponse addRedactionResponse = manualRedactionClient.addRedactionBulk(
                dossier.getId(),
                file.getId(),
                Set.of(AddRedactionRequestModel.builder()
                               .positions(List.of(Rectangle.builder()
                                                          .topLeftX(100)
                                                          .topLeftY(200)
                                                          .width(50)
                                                          .height(20)
                                                          .page(1)
                                                          .build()))
                               .section("Confidential Section")
                               .addToDictionary(true)
                               .addToAllDossiers(false)
                               .type(type.getType())
                               .reason("Confidential Information")
                               .value(redactionValue)
                               .legalBasis("GDPR")
                               .comment(new AddCommentRequestModel(comment))
                               .build())
        );

        // Extract the annotation ID from the response for verification
        String annotationId = addRedactionResponse.getManualAnnotationResponses()
                .stream()
                .findFirst()
                .map(ManualAnnotationResponse::getAnnotationId)
                .orElseThrow(() -> new AssertionError("No annotation ID returned"));

        // Verify the redaction is present
        var manualRedactions = manualRedactionClient.getManualRedactions(
                dossier.getId(), file.getId(), false, true);

        assertThat(manualRedactions.getEntriesToAdd()).hasSize(1);
        ManualRedactionEntry addedRedaction = manualRedactions.getEntriesToAdd()
                .stream()
                .filter(entry -> entry.getAnnotationId().equals(annotationId))
                .findFirst()
                .orElseThrow(() -> new AssertionError("Redaction not found in manual redactions"));

        assertThat(addedRedaction.getValue()).isEqualTo(redactionValue);

        // Verify the comment was created
        List<CommentEntity> comments = commentRepository.findAll();
        assertFalse(comments.isEmpty());
        assertEquals(comments.get(0).getText(), comment);
        assertEquals(comments.get(0).getAnnotationId(), annotationId);

        // Verify the redaction is added to the dossier dictionary
        var updatedDossierDictionary = internalDictionaryClient.getDictionaryForType(
                toTypeId(type.getType(), dossierTemplate.getId(), dossier.getId()), null);
        assertThat(updatedDossierDictionary.getEntries().stream().map(DictionaryEntry::getValue)).containsExactly(redactionValue);

        // Verify the redaction was not added to the template dictionary
        var updatedTemplateDictionary = internalDictionaryClient.getDictionaryForType(
                toTypeId(type.getType(), dossierTemplate.getId()), null);
        assertThat(updatedTemplateDictionary.getEntries()).isEmpty();
    }


    @Test
    public void testSingleForceAndBulkForceOnDictEntryAutomaticAnalysisOff() {

        var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
        var dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        fileStatusPersistenceService.toggleAutomaticAnalysis(file.getId(), true);
        whenGetEntityLogInvocation();

        var type1 = typeProvider.testAndProvideType(dossierTemplate, dossier, "test1", false, 70);

        List<EntityLogEntry> entityLogEntries = new ArrayList<>();
        String legal2 = "Legal 2";
        String legal3 = "Legal 3";
        String darthVader = "Darth Vader";
        List<Position> position1 = List.of(new Position(56.8f, 528.9f, 120.96f, 12.64f, 1));
        List<Position> position2 = List.of(new Position(56.8f, 501.3f, 120.96f, 12.64f, 1));
        List<Position> position3 = List.of(new Position(56.8f, 473.7f, 120.96f, 12.64f, 1));
        List<Position> position4 = List.of(new Position(56.8f, 446.1f, 120.96f, 12.64f, 1));
        entityLogEntries.add(EntityLogEntry.builder()
                                     .id("AnnotationId1")
                                     .type(type1.getType())
                                     .value(darthVader)
                                     .entryType(EntryType.ENTITY)
                                     .state(EntryState.SKIPPED)
                                     .section("section")
                                     .legalBasis("Legal 1")
                                     .engines(Set.of(Engine.DOSSIER_DICTIONARY))
                                     .dictionaryEntry(true)
                                     .dossierDictionaryEntry(true)
                                     .positions(position1)
                                     .build());
        entityLogEntries.add(EntityLogEntry.builder()
                                     .id("AnnotationId2")
                                     .type(type1.getType())
                                     .value(darthVader)
                                     .entryType(EntryType.ENTITY)
                                     .state(EntryState.SKIPPED)
                                     .section("section")
                                     .legalBasis("Legal 1")
                                     .engines(Set.of(Engine.DOSSIER_DICTIONARY))
                                     .dictionaryEntry(true)
                                     .dossierDictionaryEntry(true)
                                     .positions(position2)
                                     .build());
        entityLogEntries.add(EntityLogEntry.builder()
                                     .id("AnnotationId3")
                                     .type(type1.getType())
                                     .value(darthVader)
                                     .entryType(EntryType.ENTITY)
                                     .state(EntryState.SKIPPED)
                                     .section("section")
                                     .legalBasis("Legal 1")
                                     .engines(Set.of(Engine.DOSSIER_DICTIONARY))
                                     .dictionaryEntry(true)
                                     .dossierDictionaryEntry(true)
                                     .positions(position3)
                                     .build());
        entityLogEntries.add(EntityLogEntry.builder()
                                     .id("AnnotationId4")
                                     .type(type1.getType())
                                     .value(darthVader)
                                     .entryType(EntryType.ENTITY)
                                     .state(EntryState.SKIPPED)
                                     .section("section")
                                     .legalBasis("Legal 1")
                                     .engines(Set.of(Engine.DOSSIER_DICTIONARY))
                                     .dictionaryEntry(true)
                                     .dossierDictionaryEntry(true)
                                     .positions(position4)
                                     .build());
        var entityLog = new EntityLog(1, 1, entityLogEntries, null, 0, 0, 0, 0);
        fileManagementStorageService.saveEntityLog(dossier.getId(), file.getId(), entityLog);

        // single force
        manualRedactionClient.forceRedactionBulk(dossier.getId(), file.getId(), Set.of(ForceRedactionRequestModel.builder()
                                                                                               .annotationId("AnnotationId1")
                                                                                               .legalBasis(legal2)
                                                                                               .build()));

        var allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, false);

        assertEquals(allManualRedactions.getForceRedactions().size(), 1);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 1);
        var entryToAdd = allManualRedactions.getEntriesToAdd()
                .stream()
                .findFirst()
                .get();
        assertFalse(entryToAdd.isAddToDictionary());
        assertFalse(entryToAdd.isAddToDossierDictionary());
        assertEquals(entryToAdd.getValue(), darthVader);

        //bulk force
        manualRedactionClient.addRedactionBulkLocal(dossier.getId(),
                                                    file.getId(),
                                                    AddRedactionBulkLocalRequestModel.builder()
                                                            .type(type1.getType())
                                                            .value(darthVader)
                                                            .legalBasis(legal3)
                                                            .reason("reason")
                                                            .section("section")
                                                            .positions(List.of(new Rectangle(56.8f, 528.9f, 120.96f, 12.64f, 0)))
                                                            .build());
        List<FoundTerm> foundTerms = new ArrayList<>();
        foundTerms.add(new FoundTerm(position1, darthVader));
        foundTerms.add(new FoundTerm(position2, darthVader));
        foundTerms.add(new FoundTerm(position3, darthVader));
        foundTerms.add(new FoundTerm(position4, darthVader));

        BulkLocalResponse addValueBackResponse = BulkLocalResponse.builder()
                .fileId(file.getId())
                .dossierId(dossier.getId())
                .type(type1.getType())
                .legalBasis(legal3)
                .reason("reason")
                .section("section")
                .foundTerms(foundTerms)
                .userId("user")
                .build();
        searchTermOccurrencesResponseReceiver.receive(addValueBackResponse);

        allManualRedactions = manualRedactionClient.getManualRedactions(dossier.getId(), file.getId(), true, false);
        // check that there is a link to the dictionary entries. the merge of entity log will take case of them based on the sourceId
        assertEquals(allManualRedactions.getForceRedactions().size(), 1);
        assertEquals(allManualRedactions.getEntriesToAdd().size(), 5);
        var addLocalBulk1 = allManualRedactions.getEntriesToAdd().stream().filter(e -> convertPositions(position1).equals(e.getPositions())).findFirst();
        assertThat(addLocalBulk1.isPresent()).isTrue();
        assertThat(addLocalBulk1.get().getSourceId()).isEqualTo("AnnotationId1");
        var addLocalBulk2 = allManualRedactions.getEntriesToAdd().stream().filter(e -> convertPositions(position2).equals(e.getPositions())).findFirst();
        assertThat(addLocalBulk2.isPresent()).isTrue();
        assertThat(addLocalBulk2.get().getSourceId()).isEqualTo("AnnotationId2");
        var addLocalBulk3 = allManualRedactions.getEntriesToAdd().stream().filter(e -> convertPositions(position3).equals(e.getPositions())).findFirst();
        assertThat(addLocalBulk3.isPresent()).isTrue();
        assertThat(addLocalBulk3.get().getSourceId()).isEqualTo("AnnotationId3");
        var addLocalBulk4 = allManualRedactions.getEntriesToAdd().stream().filter(e -> convertPositions(position4).equals(e.getPositions())).findFirst();
        assertThat(addLocalBulk4.isPresent()).isTrue();
        assertThat(addLocalBulk4.get().getSourceId()).isEqualTo("AnnotationId4");
    }

    private List<Rectangle> convertPositions(List<Position> positions) {

        return positions.stream()
                .map(position -> Rectangle.builder().page(position.getPageNumber()).height(position.h()).width(position.w()).topLeftX(position.x()).topLeftY(position.y()).build())
                .toList();
    }
}
