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

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

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.FileTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ComponentLogEntryDocument;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.mapper.ComponentLogDocumentMapper;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.ComponentLogEntryDocumentRepository;

import lombok.SneakyThrows;

public class ComponentLogUpdateTest extends AbstractPersistenceServerServiceTest {

    @Autowired
    private FileManagementStorageService fileManagementStorageService;
    @Autowired
    private ComponentLogEntryDocumentRepository componentLogEntryDocumentRepository;
    @Autowired
    private DossierTesterAndProvider dossierTesterAndProvider;
    @Autowired
    private FileTesterAndProvider fileTesterAndProvider;

    private final ComponentLogDocumentMapper mapper = ComponentLogDocumentMapper.INSTANCE;


    @Test
    @SneakyThrows
    public void testComponentLogUpdateWithOverrides() {

        var dossier = dossierTesterAndProvider.provideTestDossier();
        var file = fileTesterAndProvider.testAndProvideFile(dossier, "filename");

        String dossierId = dossier.getId();
        String fileId = file.getId();

        ComponentLog initialComponentLog = createInitialComponentLog();

        fileManagementStorageService.saveComponentLog(dossierId, fileId, initialComponentLog);

        addOverridesToComponentLogEntry(dossierId,
                                        fileId,
                                        "entry1",
                                        List.of(new ComponentLogEntryValue("overriddenValue1", "overriddenDescription1", "overriddenRuleId1", Collections.emptyList())));

        ComponentLog newComponentLog = createNewComponentLog();

        fileManagementStorageService.saveComponentLog(dossierId, fileId, newComponentLog);

        verifyComponentLogState(dossierId, fileId);

        testComponentLogModifications(dossierId, fileId);
    }


    private ComponentLog createInitialComponentLog() {

        ComponentLog componentLog = new ComponentLog();
        componentLog.setAnalysisNumber(1);
        componentLog.setComponentRulesVersion(1L);

        ComponentLogEntry entry1 = new ComponentLogEntry();
        entry1.setName("entry1");
        entry1.setOverridden(false);
        entry1.setOriginalValues(List.of(new ComponentLogEntryValue("originalValue1", "description1", "ruleId1", Collections.emptyList())));
        entry1.setValues(List.of(new ComponentLogEntryValue("value1", "description1", "ruleId1", Collections.emptyList())));

        ComponentLogEntry entry2 = new ComponentLogEntry();
        entry2.setName("entry2");
        entry2.setOverridden(false);
        entry2.setOriginalValues(List.of(new ComponentLogEntryValue("originalValue2", "description2", "ruleId2", Collections.emptyList())));
        entry2.setValues(List.of(new ComponentLogEntryValue("value2", "description2", "ruleId2", Collections.emptyList())));

        componentLog.setComponentLogEntries(Arrays.asList(entry1, entry2));
        return componentLog;
    }


    private void addOverridesToComponentLogEntry(String dossierId, String fileId, String entryName, List<ComponentLogEntryValue> overrideValues) {

        String componentLogId = mapper.getLogId(dossierId, fileId);

        Optional<ComponentLogEntryDocument> optionalComponentLogEntryDocument = componentLogEntryDocumentRepository.findById(mapper.getLogEntryId(componentLogId, entryName));
        if (optionalComponentLogEntryDocument.isPresent()) {
            ComponentLogEntryDocument entryDocument = optionalComponentLogEntryDocument.get();
            entryDocument.setOverridden(true);
            entryDocument.setValues(overrideValues);
            componentLogEntryDocumentRepository.save(entryDocument);
        }
    }


    private ComponentLog createNewComponentLog() {

        ComponentLog componentLog = new ComponentLog();
        componentLog.setAnalysisNumber(2);
        componentLog.setComponentRulesVersion(2L);

        ComponentLogEntry entry1Updated = new ComponentLogEntry();
        entry1Updated.setName("entry1");
        entry1Updated.setOverridden(false);
        entry1Updated.setOriginalValues(List.of(new ComponentLogEntryValue("updatedOriginalValue1", "updatedDescription1", "updatedRuleId1", Collections.emptyList())));
        entry1Updated.setValues(List.of(new ComponentLogEntryValue("newValue1", "newDescription1", "newRuleId1", Collections.emptyList())));

        ComponentLogEntry entry3 = new ComponentLogEntry();
        entry3.setName("entry3");
        entry3.setOverridden(false);
        entry3.setOriginalValues(List.of(new ComponentLogEntryValue("originalValue3", "description3", "ruleId3", Collections.emptyList())));
        entry3.setValues(List.of(new ComponentLogEntryValue("value3", "description3", "ruleId3", Collections.emptyList())));

        componentLog.setComponentLogEntries(Arrays.asList(entry1Updated, entry3));
        return componentLog;
    }


    private void verifyComponentLogState(String dossierId, String fileId) {

        ComponentLog finalComponentLog = fileManagementStorageService.getComponentLog(dossierId, fileId);

        ComponentLogEntry finalEntry1 = finalComponentLog.getComponentLogEntries()
                .stream()
                .filter(e -> e.getName().equals("entry1"))
                .findFirst()
                .orElse(null);

        assertThat(finalEntry1).isNotNull();
        assertThat(finalEntry1.isOverridden()).isTrue();
        assertThat(finalEntry1.getValues()).extracting("value").containsExactly("overriddenValue1");

        ComponentLogEntry finalEntry2 = finalComponentLog.getComponentLogEntries()
                .stream()
                .filter(e -> e.getName().equals("entry2"))
                .findFirst()
                .orElse(null);

        assertThat(finalEntry2).isNull();

        ComponentLogEntry finalEntry3 = finalComponentLog.getComponentLogEntries()
                .stream()
                .filter(e -> e.getName().equals("entry3"))
                .findFirst()
                .orElse(null);

        assertThat(finalEntry3).isNotNull();
        assertThat(finalEntry3.isOverridden()).isFalse();
        assertThat(finalEntry3.getValues()).extracting("value").containsExactly("value3");

        assertThat(finalEntry1.getOriginalValues()).extracting("value").containsExactly("updatedOriginalValue1");
    }


    private void testComponentLogModifications(String dossierId, String fileId) {

        ComponentLog currentComponentLog = fileManagementStorageService.getComponentLog(dossierId, fileId);

        ComponentLogEntry entry3Modified = currentComponentLog.getComponentLogEntries()
                .stream()
                .filter(e -> e.getName().equals("entry3"))
                .findFirst()
                .orElse(null);

        assertThat(entry3Modified).isNotNull();
        entry3Modified.setValues(List.of(new ComponentLogEntryValue("modifiedValue3", "modifiedDescription3", "modifiedRuleId3", Collections.emptyList())));

        currentComponentLog.setAnalysisNumber(currentComponentLog.getAnalysisNumber() + 1);
        fileManagementStorageService.saveComponentLog(dossierId, fileId, currentComponentLog);

        ComponentLog updatedComponentLog = fileManagementStorageService.getComponentLog(dossierId, fileId);
        ComponentLogEntry updatedEntry3 = updatedComponentLog.getComponentLogEntries()
                .stream()
                .filter(e -> e.getName().equals("entry3"))
                .findFirst()
                .orElse(null);

        assertThat(updatedEntry3).isNotNull();
        assertThat(updatedEntry3.getValues()).extracting("value").containsExactly("modifiedValue3");
    }

}
