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

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.migration.migrations.V29TechnicalNameEntityLogMigration;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.MigrationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository;
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.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.ManualChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.ManualRedactionType;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.EntityLogMongoService;

public class TechnicalNameEntityLogMigrationTest {

    @Mock
    private DossierRepository dossierRepository;

    @Mock
    private FileRepository fileRepository;

    @Mock
    private EntityLogMongoService entityLogMongoService;

    @Mock
    private LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;

    @Mock
    private MigrationPersistenceService migrationPersistenceService;

    private V29TechnicalNameEntityLogMigration migration;

    private final HashFunction hashFunction = Hashing.murmur3_128();


    @BeforeEach
    public void setup() {

        MockitoAnnotations.openMocks(this);

        when(migrationPersistenceService.getLatestProcessedVersion()).thenReturn(0L);
        when(migrationPersistenceService.isProcessed(anyLong(), anyLong())).thenReturn(false);

        LegalBasisMappingService legalBasisMappingService = new LegalBasisMappingService(legalBasisMappingPersistenceService);

        migration = new V29TechnicalNameEntityLogMigration(dossierRepository, fileRepository, entityLogMongoService, legalBasisMappingService);

        migration.setMigrationPersistenceService(migrationPersistenceService);
    }


    @Test
    public void testMigrationUpdatesMappedLegalBasisTechnicalNames() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));

        LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_TechName").reason("LB1_Reason").build();
        LegalBasisEntity lb2 = LegalBasisEntity.builder().technicalName("LB2_TechName").reason("LB2_Reason").build();
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Arrays.asList(lb1, lb2));

        FileEntity file = createFile("file1", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));

        EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
        EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("LB2_Reason").build();
        EntityLog entityLog = createEntityLog(Arrays.asList(entry1, entry2));

        entityLog.setLegalBasis(List.of(EntityLogLegalBasis.builder().technicalName("LB1_TechName").reason("LB1_Reason").build(),
                                        EntityLogLegalBasis.builder().technicalName("LB2_TechName").reason("LB2_Reason").build()));

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        assertAll(() -> assertEquals("LB1_TechName", entry1.getLegalBasis()), () -> assertEquals("LB2_TechName", entry2.getLegalBasis()));
        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
    }


    @Test
    public void testMigrationHashesUnmappedLegalBasis() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());

        FileEntity file = createFile("file1", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));

        EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("Unmapped_LB1").build();
        EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("Unmapped_LB2").build();
        EntityLog entityLog = createEntityLog(Arrays.asList(entry1, entry2));

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        String hashedLB1 = hashFunction.hashString("Unmapped_LB1", StandardCharsets.UTF_8).toString();
        String hashedLB2 = hashFunction.hashString("Unmapped_LB2", StandardCharsets.UTF_8).toString();
        assertAll(() -> assertEquals(hashedLB1, entry1.getLegalBasis()), () -> assertEquals(hashedLB2, entry2.getLegalBasis()));
        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
    }


    @Test
    public void testMigrationSkipsEntriesWithoutLegalBasis() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());

        FileEntity file = createFile("file1", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));

        EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis(null).build();
        EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("").build();
        EntityLog entityLog = createEntityLog(Arrays.asList(entry1, entry2));

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        assertAll(() -> assertNull(entry1.getLegalBasis()), () -> assertEquals("", entry2.getLegalBasis()));
        verify(entityLogMongoService, never()).saveEntityLog(anyString(), anyString(), any(EntityLog.class));
    }


    @Test
    public void testMigrationSkipsFilesWithoutEntityLog() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));

        FileEntity file = createFile("file1", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.empty());

        migration.run();

        verify(entityLogMongoService, never()).saveEntityLog(anyString(), anyString(), any(EntityLog.class));
    }


    @Test
    public void testMigrationWithNoDossiers() {

        when(dossierRepository.findAll()).thenReturn(Collections.emptyList());

        migration.run();

        verify(fileRepository, never()).findByDossierId(anyString());
        verify(entityLogMongoService, never()).findEntityLogByDossierIdAndFileId(anyString(), anyString());
    }


    @Test
    public void testMigrationWithMixedEntries() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));

        LegalBasisEntity mappedLB = LegalBasisEntity.builder().technicalName("Mapped_TechName").reason("Mapped_Reason").build();
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(mappedLB));

        FileEntity file = createFile("file1", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));

        EntityLogEntry mappedEntry = EntityLogEntry.builder().legalBasis("Mapped_Reason").build();
        EntityLogEntry unmappedEntry = EntityLogEntry.builder().legalBasis("Unmapped_Reason").build();
        EntityLogEntry nullEntry = EntityLogEntry.builder().legalBasis(null).build();
        EntityLog entityLog = createEntityLog(Arrays.asList(mappedEntry, unmappedEntry, nullEntry));

        entityLog.setLegalBasis(new ArrayList<>());

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        String hashedUnmapped = hashFunction.hashString("Unmapped_Reason", StandardCharsets.UTF_8).toString();

        assertAll(() -> assertEquals("Mapped_TechName", mappedEntry.getLegalBasis()),
                  () -> assertEquals(hashedUnmapped, unmappedEntry.getLegalBasis()),
                  () -> assertNull(nullEntry.getLegalBasis()));

        assertEquals(2, entityLog.getLegalBasis().size());
        assertTrue(entityLog.getLegalBasis()
                           .stream()
                           .anyMatch(lb -> lb.getTechnicalName().equals("Mapped_TechName") && lb.getReason().equals("Mapped_Reason")));
        assertTrue(entityLog.getLegalBasis()
                           .stream()
                           .anyMatch(lb -> lb.getTechnicalName().equals(hashedUnmapped) && lb.getReason().equals("Unmapped_Reason")));

        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
    }


    @Test
    public void testMigrationDoesNotUpdateWhenNoChanges() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.singletonList(LegalBasisEntity.builder()
                                                                                                                                 .technicalName("LB1_Tech")
                                                                                                                                 .reason("LB1_Reason")
                                                                                                                                 .build()));

        FileEntity file = createFile("file1", "dossier1");

        EntityLogEntry entry = EntityLogEntry.builder().legalBasis("LB1_Tech").build();
        EntityLog entityLog = createEntityLog(List.of(entry));

        entityLog.setLegalBasis(new ArrayList<>(List.of(EntityLogLegalBasis.builder().technicalName("LB1_Tech").reason("LB1_Reason").build())));

        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        verify(entityLogMongoService, never()).saveEntityLog(anyString(), anyString(), any(EntityLog.class));
    }


    @Test
    public void testMigrationProcessesMultipleDossiers() {

        DossierEntity dossier1 = createDossier("dossier1", "template1");
        DossierEntity dossier2 = createDossier("dossier2", "template2");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier1, dossier2));

        LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
        LegalBasisEntity lb2 = LegalBasisEntity.builder().technicalName("LB2_Tech").reason("LB2_Reason").build();
        LegalBasisEntity lb3 = LegalBasisEntity.builder().technicalName("LB3_Tech").reason("LB3_Reason").build();
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1, lb2));
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template2")).thenReturn(List.of(lb3));

        FileEntity file1 = createFile("file1", "dossier1");
        FileEntity file2 = createFile("file2", "dossier2");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file1));
        when(fileRepository.findByDossierId("dossier2")).thenReturn(List.of(file2));

        EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
        EntityLog entityLog1 = createEntityLog(List.of(entry1));
        entityLog1.setLegalBasis(List.of(EntityLogLegalBasis.builder().technicalName("LB1_Tech").reason("LB1_Reason").build(),
                                         EntityLogLegalBasis.builder().technicalName("LB2_Tech").reason("LB2_Reason").build()));

        EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("LB3_Reason").build();
        EntityLog entityLog2 = createEntityLog(List.of(entry2));
        entityLog2.setLegalBasis(List.of(EntityLogLegalBasis.builder().technicalName("LB3_Tech").reason("LB3_Reason").build()));

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog1));
        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier2", "file2")).thenReturn(Optional.of(entityLog2));

        migration.run();

        assertAll(() -> assertEquals("LB1_Tech", entry1.getLegalBasis()), () -> assertEquals("LB3_Tech", entry2.getLegalBasis()));
        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog1);
        verify(entityLogMongoService).saveEntityLog("dossier2", "file2", entityLog2);
    }


    @Test
    public void testMigrationProcessesMultipleFilesPerDossier() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));

        LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1));

        FileEntity file1 = createFile("file1", "dossier1");
        FileEntity file2 = createFile("file2", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file1, file2));

        EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
        EntityLog entityLog1 = createEntityLog(List.of(entry1));
        entityLog1.setLegalBasis(List.of(EntityLogLegalBasis.builder().technicalName("LB1_Tech").reason("LB1_Reason").build()));

        EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
        EntityLog entityLog2 = createEntityLog(List.of(entry2));
        entityLog2.setLegalBasis(new ArrayList<>());

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog1));
        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file2")).thenReturn(Optional.of(entityLog2));

        migration.run();

        String expectedHash = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();

        assertAll(() -> assertEquals("LB1_Tech", entry1.getLegalBasis()), () -> assertEquals(expectedHash, entry2.getLegalBasis()));
        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog1);
        verify(entityLogMongoService).saveEntityLog("dossier1", "file2", entityLog2);
    }


    @Test
    public void testMigrationDoesNotAddDuplicateLegalBasis() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());

        FileEntity file = createFile("file1", "dossier1");

        String hashedLB = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
        EntityLogLegalBasis existingLB = createEntityLogLegalBasisForUnmappedLegalBasis(hashedLB);

        EntityLogEntry entry = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
        EntityLog entityLog = createEntityLog(List.of(entry));
        entityLog.setLegalBasis(new ArrayList<>(List.of(existingLB)));

        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        assertEquals(hashedLB, entry.getLegalBasis());
        assertEquals(1, entityLog.getLegalBasis().size());
        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
    }


    @Test
    public void testMigrationInitializesLegalBasisListWhenNull() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());

        FileEntity file = createFile("file1", "dossier1");

        EntityLogEntry entry = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
        EntityLog entityLog = createEntityLog(List.of(entry));
        entityLog.setLegalBasis(null);

        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        assertNotNull(entityLog.getLegalBasis());
        assertEquals(1, entityLog.getLegalBasis().size());

        String expectedHash = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
        assertEquals(expectedHash, entry.getLegalBasis());

        EntityLogLegalBasis newLB = entityLog.getLegalBasis()
                .get(0);
        assertEquals(expectedHash, newLB.getTechnicalName());
        assertEquals("Unmapped_LB", newLB.getReason());
        assertEquals("Generated Legal Basis", newLB.getName());
        assertEquals("", newLB.getDescription());

        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
    }


    @Test
    public void testMigrationUpdates() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));

        LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1));

        FileEntity file = createFile("file1", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));

        EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
        EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
        EntityLogEntry entry3 = EntityLogEntry.builder().legalBasis("Another_Unmapped_LB").build();
        EntityLog entityLog = createEntityLog(Arrays.asList(entry1, entry2, entry3));

        entityLog.setLegalBasis(new ArrayList<>());

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        String hashedUnmappedLB = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
        String hashedAnotherUnmappedLB = hashFunction.hashString("Another_Unmapped_LB", StandardCharsets.UTF_8).toString();

        assertAll(() -> assertEquals("LB1_Tech", entry1.getLegalBasis()),
                  () -> assertEquals(hashedUnmappedLB, entry2.getLegalBasis()),
                  () -> assertEquals(hashedAnotherUnmappedLB, entry3.getLegalBasis()));

        assertEquals(3, entityLog.getLegalBasis().size());
        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
    }


    @Test
    public void testMigrationHandlesDuplicateEntityLogEntries() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));

        LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1));

        FileEntity file = createFile("file1", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));

        EntityLogEntry entry1 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
        EntityLogEntry entry2 = EntityLogEntry.builder().legalBasis("LB1_Reason").build();
        EntityLog entityLog = createEntityLog(List.of(entry1, entry2));

        entityLog.setLegalBasis(new ArrayList<>());

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        assertAll(() -> assertEquals("LB1_Tech", entry1.getLegalBasis()),
                  () -> assertEquals("LB1_Tech", entry2.getLegalBasis()),
                  () -> assertEquals(1, entityLog.getLegalBasis().size()));
        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
    }


    @Test
    public void testMigrationHandlesExistingHashedTechnicalName() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(Collections.emptyList());

        FileEntity file = createFile("file1", "dossier1");

        String hashedLB = hashFunction.hashString("Unmapped_LB", StandardCharsets.UTF_8).toString();
        EntityLogLegalBasis existingLB = createEntityLogLegalBasisForUnmappedLegalBasis(hashedLB);

        EntityLogEntry entry = EntityLogEntry.builder().legalBasis("Unmapped_LB").build();
        EntityLog entityLog = createEntityLog(List.of(entry));
        entityLog.setLegalBasis(new ArrayList<>(List.of(existingLB)));

        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        assertEquals(hashedLB, entry.getLegalBasis());
        assertEquals(1, entityLog.getLegalBasis().size());
        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);
    }


    @Test
    public void testMigrationSkipsSavingWhenNoEntriesUpdated() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));

        LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("LB1_Tech").reason("LB1_Reason").build();
        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1));

        FileEntity file = createFile("file1", "dossier1");

        EntityLogEntry entry = EntityLogEntry.builder().legalBasis("LB1_Tech").build();
        EntityLogLegalBasis lb = EntityLogLegalBasis.builder()
                .technicalName("LB1_Tech")
                .reason("LB1_Reason")
                .name("Generated Legal Basis")
                .description("")
                .build();
        EntityLog entityLog = createEntityLog(List.of(entry));
        entityLog.setLegalBasis(new ArrayList<>(List.of(lb)));

        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));
        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        verify(entityLogMongoService, never()).saveEntityLog(anyString(), anyString(), any(EntityLog.class));
    }


    @Test
    public void testMigrationUpdatesPropertyChangesLegalBasis() {

        DossierEntity dossier = createDossier("dossier1", "template1");
        when(dossierRepository.findAll()).thenReturn(List.of(dossier));

        LegalBasisEntity lb1 = LegalBasisEntity.builder().technicalName("personal_data_geolocation_article_39e3").reason("Article 39(e)(3) of Regulation (EC) No 178/2002").build();

        LegalBasisEntity lb2 = LegalBasisEntity.builder()
                .technicalName("article_63_2_a_regulation_1107_2009")
                .reason("Article 63(2)(a) of Regulation (EC) No 1107/2009​‌‌​ (making reference to Article 39 of Regulation EC No 178/2002)")
                .build();

        when(legalBasisMappingPersistenceService.getLegalBasisMapping("template1")).thenReturn(List.of(lb1, lb2));

        FileEntity file = createFile("file1", "dossier1");
        when(fileRepository.findByDossierId("dossier1")).thenReturn(List.of(file));

        Change change1 = new Change();
        change1.setAnalysisNumber(18);
        change1.setType(ChangeType.CHANGED);
        change1.setDateTime(OffsetDateTime.now());

        Map<String, String> propertyChanges1 = new HashMap<>();
        propertyChanges1.put("legalBasis", "personal_data_geolocation_article_39e3 -> Article 39(e)(3) of Regulation (EC) No 178/2002");
        change1.setPropertyChanges(propertyChanges1);

        Change change2 = new Change();
        change2.setAnalysisNumber(19);
        change2.setType(ChangeType.CHANGED);
        change2.setDateTime(OffsetDateTime.now());

        Map<String, String> propertyChanges2 = new HashMap<>();
        propertyChanges2.put("legalBasis", "Article 39(e)(3) of Regulation (EC) No 178/2002 -> personal_data_geolocation_article_39e3");
        change2.setPropertyChanges(propertyChanges2);

        ManualChange manualChange = new ManualChange();
        manualChange.setManualRedactionType(ManualRedactionType.RECATEGORIZE);
        manualChange.setProcessedDate(OffsetDateTime.now());
        manualChange.setRequestedDate(OffsetDateTime.now());
        manualChange.setUserId("user123");

        Map<String, String> manualPropertyChanges = new HashMap<>();
        manualPropertyChanges.put("legalBasis", "Article 63(2)(a) of Regulation (EC) No 1107/2009​‌‌​ (making reference to Article 39 of Regulation EC No 178/2002)");
        manualChange.setPropertyChanges(manualPropertyChanges);

        EntityLogEntry entry = EntityLogEntry.builder()
                .legalBasis("personal_data_geolocation_article_39e3")
                .changes(List.of(change1, change2))
                .manualChanges(List.of(manualChange))
                .build();

        EntityLog entityLog = createEntityLog(List.of(entry));
        entityLog.setLegalBasis(new ArrayList<>());

        when(entityLogMongoService.findEntityLogByDossierIdAndFileId("dossier1", "file1")).thenReturn(Optional.of(entityLog));

        migration.run();

        String expectedLegalBasis1 = "personal_data_geolocation_article_39e3 -> personal_data_geolocation_article_39e3";
        String expectedLegalBasis2 = "personal_data_geolocation_article_39e3 -> personal_data_geolocation_article_39e3";
        String expectedManualLegalBasis = "article_63_2_a_regulation_1107_2009";

        assertEquals(expectedLegalBasis1,
                     change1.getPropertyChanges()
                             .get("legalBasis"));
        assertEquals(expectedLegalBasis2,
                     change2.getPropertyChanges()
                             .get("legalBasis"));
        assertEquals(expectedManualLegalBasis,
                     manualChange.getPropertyChanges()
                             .get("legalBasis"));

        verify(entityLogMongoService).saveEntityLog("dossier1", "file1", entityLog);

        assertEquals(2, entityLog.getLegalBasis().size());
        assertTrue(entityLog.getLegalBasis()
                           .stream()
                           .anyMatch(lb -> lb.getTechnicalName().equals("personal_data_geolocation_article_39e3")));
        assertTrue(entityLog.getLegalBasis()
                           .stream()
                           .anyMatch(lb -> lb.getTechnicalName().equals("article_63_2_a_regulation_1107_2009")));
    }


    private DossierEntity createDossier(String id, String templateId) {

        DossierEntity dossier = new DossierEntity();
        dossier.setId(id);
        dossier.setDossierTemplateId(templateId);
        return dossier;
    }


    private FileEntity createFile(String id, String dossierId) {

        FileEntity file = new FileEntity();
        file.setId(id);
        file.setDossierId(dossierId);
        return file;
    }


    private EntityLog createEntityLog(List<EntityLogEntry> entries) {

        EntityLog entityLog = new EntityLog();
        entityLog.setEntityLogEntry(entries);
        return entityLog;
    }


    private EntityLogLegalBasis createEntityLogLegalBasisForUnmappedLegalBasis(String technicalName) {

        return EntityLogLegalBasis.builder()
                .technicalName(technicalName)
                .reason("Unmapped_LB")
                .name("Generated Legal Basis")
                .description("")
                .build();
    }

}
