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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.util.List;
import java.util.Set;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.persistence.service.v2.external.api.impl.controller.ComponentControllerV2;
import com.iqser.red.service.peristence.v1.server.integration.client.ComponentClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateClient;
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.FileService;
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.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue;
import com.iqser.red.service.persistence.service.v2.api.external.model.EntityReference;
import com.knecon.fforesight.tenantcommons.TenantApplicationType;

import feign.FeignException;
import lombok.SneakyThrows;

public class ComponentOverrideTest extends AbstractPersistenceServerServiceTest {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private DossierTesterAndProvider dossierTesterAndProvider;

    @Autowired
    private FileTesterAndProvider fileTesterAndProvider;

    @Autowired
    private ComponentClient componentClient;

    @Autowired
    private DossierTemplateClient dossierTemplateClient;

    @Autowired
    private FileService fileService;

    @Autowired
    private ComponentControllerV2 componentControllerV2;


    @Override
    protected TenantApplicationType getTenantApplicationType() {

        return TenantApplicationType.DocuMine;
    }



    @Test
    public void testOverrides() throws IOException {

        var dossier = dossierTesterAndProvider.provideTestDossier();

        var dossierTemplate = dossierTemplateClient.getDossierTemplate(dossier.getDossierTemplateId());

        var file = fileTesterAndProvider.testAndProvideFile(dossier, "filename1");

        System.out.println("DOSSIER TEMPLATE ID: " + dossierTemplate.getId());
        System.out.println("DOSSIER ID: " + dossier.getId());
        System.out.println("FILE ID: " + file.getId());

        Component componentOverrideModel = Component.builder()
                .name("Study_Title")
                .componentValues(List.of(ComponentValue.builder()
                                                 .value("AAAA Strange Chemical Name And the rest of a title – With a dash and some more text")
                                                 ////.originalValue("Strange Chemical Name And the rest of a title – With a dash and some more text")
                                                 .valueDescription("First found value of type title or else ''")
                                                 .componentRuleId("StudyTitle.0.0")
                                                 .entityReferences(List.of(EntityReference.builder()
                                                                                   .id("cf7f0d0c4c07918ce7d67b204f5fdb7d")
                                                                                   .type("title")
                                                                                   .entityRuleId("DOC.6.1")
                                                                                   .page(1)
                                                                                   .build()))
                                                 .build()))
                .build();

        var componentLogJson = new ClassPathResource("files/componentlog/exampleComponentLog.json");

        ComponentLog componentLog = objectMapper.readValue(componentLogJson.getInputStream(), ComponentLog.class);
        fileManagementStorageService.saveComponentLog(dossier.getId(), file.getId(), componentLog);

        componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel);

        var overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());

        assertEquals(dossierTemplate.getId(), overrides.getDossierTemplateId());
        assertEquals(dossier.getId(), overrides.getDossierId());
        assertEquals(file.getId(), overrides.getFileId());

        assertFalse(overrides.getComponentOverrides().isEmpty());
        assertTrue(overrides.getComponentOverrides()
                           .get(0).isOverridden());

        // override same entry a second time
        Component componentOverrideModel2 = Component.builder()
                .name("Study_Title")
                .componentValues(List.of(ComponentValue.builder()
                                                 .value("BBBB Strange Chemical Name And the rest of a title – With a dash and some more text")
                                                 //.originalValue("Strange Chemical Name And the rest of a title – With a dash and some more text")
                                                 .valueDescription("First found value of type title or else ''")
                                                 .componentRuleId("StudyTitle.0.0")
                                                 .entityReferences(List.of(EntityReference.builder()
                                                                                   .id("cf7f0d0c4c07918ce7d67b204f5fdb7d")
                                                                                   .type("title")
                                                                                   .entityRuleId("DOC.6.1")
                                                                                   .page(1)
                                                                                   .build()))
                                                 .build()))
                .build();
        componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel2);

        overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());

        assertEquals(dossierTemplate.getId(), overrides.getDossierTemplateId());
        assertEquals(dossier.getId(), overrides.getDossierId());
        assertEquals(file.getId(), overrides.getFileId());

        assertFalse(overrides.getComponentOverrides().isEmpty());
        assertTrue(overrides.getComponentOverrides()
                           .get(0).isOverridden());

        // add and revert override
        Component componentOverrideModel3 = Component.builder().name("Report_Number").componentValues(List.of(ComponentValue.builder()
                                                                                                                      .value("WOHOO 11/111-111A")
                                                                                                                      //.originalValue("11/111-111A")
                                                                                                                      .valueDescription(
                                                                                                                              "First found value of type report_number or else ''")
                                                                                                                      .componentRuleId("ReportNumber.0.0")
                                                                                                                      .entityReferences(List.of(EntityReference.builder()
                                                                                                                                                        .id("e2a93bcc72e9740bbfc19bd9cd982e01")
                                                                                                                                                        .type("report_number")
                                                                                                                                                        .entityRuleId("DOC.2.0")
                                                                                                                                                        .page(1)
                                                                                                                                                        .build()))
                                                                                                                      .build())).build();
        componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel3);

        overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());

        assertEquals(dossierTemplate.getId(), overrides.getDossierTemplateId());
        assertEquals(dossier.getId(), overrides.getDossierId());
        assertEquals(file.getId(), overrides.getFileId());

        assertFalse(overrides.getComponentOverrides().isEmpty());
        assertTrue(overrides.getComponentOverrides()
                           .stream()
                           .filter(component -> component.getName().equals("Report_Number"))
                           .findAny()
                           .get().isOverridden());

        RevertOverrideRequest revertOverrideRequest = RevertOverrideRequest.builder().components(Set.of("Report_Number")).build();
        componentClient.revertOverrides(dossierTemplate.getId(), dossier.getId(), file.getId(), revertOverrideRequest);

        overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());

        assertEquals(dossierTemplate.getId(), overrides.getDossierTemplateId());
        assertEquals(dossier.getId(), overrides.getDossierId());
        assertEquals(file.getId(), overrides.getFileId());

        assertFalse(overrides.getComponentOverrides().isEmpty());
        assertFalse(overrides.getComponentOverrides()
                            .stream()
                            .anyMatch(component -> component.getName().equals("Report_Number")));

    }


    @Test
    @SneakyThrows
    public void testDeletedFileOverrides() throws IOException {

        var dossier = dossierTesterAndProvider.provideTestDossier();

        var dossierTemplate = dossierTemplateClient.getDossierTemplate(dossier.getDossierTemplateId());

        var file = fileTesterAndProvider.testAndProvideFile(dossier, "filename2");

        System.out.println("DOSSIER TEMPLATE ID: " + dossierTemplate.getId());
        System.out.println("DOSSIER ID: " + dossier.getId());
        System.out.println("FILE ID: " + file.getId());

        Component componentOverrideModel = Component.builder()
                .name("Study_Title")
                .componentValues(List.of(ComponentValue.builder()
                                                 .value("AAAA Strange Chemical Name And the rest of a title – With a dash and some more text")
                                                 //.originalValue("Strange Chemical Name And the rest of a title – With a dash and some more text")
                                                 .valueDescription("First found value of type title or else ''")
                                                 .componentRuleId("StudyTitle.0.0")
                                                 .entityReferences(List.of(EntityReference.builder()
                                                                                   .id("cf7f0d0c4c07918ce7d67b204f5fdb7d")
                                                                                   .type("title")
                                                                                   .entityRuleId("DOC.6.1")
                                                                                   .page(1)
                                                                                   .build()))
                                                 .build()))
                .build();

        Component componentOverrideModel2 = Component.builder()
                .name("Performing_Laboratory")
                .componentValues(List.of(ComponentValue.builder()
                                                 .value("Test Labor Deutschland AG, Switzerland")
                                                 //.originalValue("Test Labor Deutschland AG, Switzerland BBBB")
                                                 .valueDescription("Laboratory name and country found!")
                                                 .componentRuleId("PerformingLaboratory.1.0")
                                                 .entityReferences(List.of(EntityReference.builder()
                                                                                   .id("cdfa1386cc0b1c665c7dfb1b8bd2a134")
                                                                                   .type("laboratory_name")
                                                                                   .entityRuleId("DOC.7.0")
                                                                                   .page(1)
                                                                                   .build()))
                                                 .build()))
                .build();

        var componentLogJson = new ClassPathResource("files/componentlog/exampleComponentLog.json");

        ComponentLog componentLog = objectMapper.readValue(componentLogJson.getInputStream(), ComponentLog.class);
        fileManagementStorageService.saveComponentLog(dossier.getId(), file.getId(), componentLog);

        componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel);
        componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel2);

        var overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());

        assertFalse(overrides.getComponentOverrides().isEmpty());
        assertTrue(overrides.getComponentOverrides()
                           .get(0).isOverridden());
        assertTrue(overrides.getComponentOverrides()
                           .get(1).isOverridden());

        // case 1: delete file, overrides should not be returned anymore
        fileService.deleteFile(dossier.getId(), file.getFileId());

        overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());

        assertTrue(overrides.getComponentOverrides().isEmpty());

        // case 2: re-upload file that was deleted before overrides should also not be returned anymore
        fileTesterAndProvider.testAndProvideFile(dossier, "filename2");

        var e = assertThrows(FeignException.class, () -> componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId()));
        assertEquals(e.status(), 404);
        fileManagementStorageService.saveComponentLog(dossier.getId(), file.getId(), componentLog);

        assertTrue(overrides.getComponentOverrides().isEmpty());

        componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel);
        componentClient.addOverride(dossierTemplate.getId(), dossier.getId(), file.getId(), componentOverrideModel2);

        overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());

        assertFalse(overrides.getComponentOverrides().isEmpty());

        // delete file again
        fileService.deleteFile(dossier.getId(), file.getFileId());

        Thread.sleep(1000);

        // case 3: when undeleting, the overrides should be there again (restored)
        fileService.undeleteFiles(dossier.getId(), Set.of(file.getFileId()));

        overrides = componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId());

        assertFalse(overrides.getComponentOverrides().isEmpty());
        assertTrue(overrides.getComponentOverrides()
                           .get(0).isOverridden());
        assertTrue(overrides.getComponentOverrides()
                           .get(1).isOverridden());

        // case 4: on hard delete overrides should also be gone of course
        fileService.hardDeleteFiles(dossier.getId(), List.of(file.getFileId()));

        e = assertThrows(FeignException.class, () -> componentClient.getOverrides(dossierTemplate.getId(), dossier.getId(), file.getId()));
        assertEquals(e.status(), 404);

    }

}
