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

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

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

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

import com.google.common.collect.Sets;
import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierStatusClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateStatsClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileManagementClient;
import com.iqser.red.service.peristence.v1.server.integration.client.ReanalysisClient;
import com.iqser.red.service.peristence.v1.server.integration.client.UploadClient;
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.UserProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.CreateTypeValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierStatusRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageExclusionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageRange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateDictionaryStats;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
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.dossiertemplate.type.DictionarySummary;
import com.knecon.fforesight.tenantcommons.TenantContext;

public class DossierTemplateStatsTest extends AbstractPersistenceServerServiceTest {

    private static final String TYPE_ID_1 = "type1";
    private static final String TYPE_ID_2 = "type2";
    private static final String TYPE_ID_3 = "type3";

    @Autowired
    private DossierTesterAndProvider dossierTesterAndProvider;

    @Autowired
    private DossierTemplateTesterAndProvider dossierTemplateTesterAndProvider;

    @Autowired
    private DictionaryClient dictionaryClient;

    @Autowired
    private DossierTemplateClient dossierTemplateClient;

    @Autowired
    private DossierTemplateStatsClient dossierTemplateStatsClient;

    @Autowired
    private DossierClient dossierClient;

    @Autowired
    private FileClient fileClient;

    @Autowired
    private UploadClient uploadClient;

    @Autowired
    private FileTesterAndProvider fileTesterAndProvider;
    @Autowired
    private FileStatusPersistenceService fileStatusPersistenceService;

    @Autowired
    private DossierStatusClient dossierStatusClient;

    @Autowired
    private FileManagementClient fileManagementClient;

    @Autowired
    private ReanalysisClient reanalysisClient;

    @Autowired
    private UserProvider userProvider;


    @Test
    public void testDossierTemplateDictionaryStats() {

        var dossier = dossierTesterAndProvider.provideTestDossier();

        var addedType1 = dictionaryClient.addType(CreateTypeValue.builder()
                .type("type_dossier")
                .label("Dossier Redactions")
                .hexColor("#fcba03")
                .rank(100)
                .description("Something")
                .addToDictionaryAction(false)
                .dossierTemplateId(dossier.getDossierTemplateId())
                .build());

        assertThat(addedType1).isNotNull();

        var dossierTemplate2 = provideTestTemplate("dossierTemp2");

        var addedType = dictionaryClient.addType(CreateTypeValue.builder()
                .type(TYPE_ID_1)
                .label("Dossier Redactions")
                .hexColor("#fcba03")
                .rank(100)
                .description("Something")
                .addToDictionaryAction(false)
                .dossierTemplateId(dossierTemplate2.getId())
                .hasDictionary(true)
                .build());

        assertThat(addedType).isNotNull();
        var entries1 = new ArrayList<String>();
        entries1.add("entry1");
        entries1.add("entry2");
        dictionaryClient.addEntry(addedType.getType(), addedType.getDossierTemplateId(), entries1, false, null, DictionaryEntryType.ENTRY);

        var dictionary = dictionaryClient.getDictionaryForType(addedType.getType(), addedType.getDossierTemplateId(), null);
        assertThat(dictionary.getEntries().size()).isEqualTo(entries1.size());

        var addedType2 = dictionaryClient.addType(CreateTypeValue.builder()
                .type(TYPE_ID_2)
                .label("Dossier Redactions 2")
                .hexColor("#fcba03")
                .rank(102)
                .description("Something")
                .addToDictionaryAction(false)
                .dossierTemplateId(dossierTemplate2.getId())
                .hasDictionary(true)
                .build());
        var entries2 = new ArrayList<String>();
        entries2.add("entry1");
        entries2.add("entry2");
        entries2.add("entry3");
        dictionaryClient.addEntry(addedType2.getType(), addedType2.getDossierTemplateId(), entries2, false, null, DictionaryEntryType.ENTRY);

        dictionary = dictionaryClient.getDictionaryForType(addedType2.getType(), addedType.getDossierTemplateId(), null);
        assertThat(dictionary.getEntries().size()).isEqualTo(entries2.size());

        var dossierTemplate3 = provideTestTemplate("dossierTemp3");
        var addedType3 = dictionaryClient.addType(CreateTypeValue.builder()
                .type(TYPE_ID_3)
                .label("Dossier Redactions 3")
                .hexColor("#fcba03")
                .rank(100)
                .description("Something")
                .addToDictionaryAction(false)
                .dossierTemplateId(dossierTemplate3.getId())
                .hasDictionary(true)
                .build());

        assertThat(addedType3).isNotNull();
        var entries3 = new ArrayList<String>();
        entries3.add("entry1");
        entries3.add("entry2");
        dictionaryClient.addEntry(addedType3.getType(), addedType3.getDossierTemplateId(), entries3, false, null, DictionaryEntryType.ENTRY);

        dictionary = dictionaryClient.getDictionaryForType(addedType3.getType(), addedType3.getDossierTemplateId(), null);
        assertThat(dictionary.getEntries().size()).isEqualTo(entries3.size());

        var dossierTemplate4 = provideTestTemplate("dossierTemp4");

        var dossierTemplate5 = provideTestTemplate("dossierTemp5");

        var addedType5 = dictionaryClient.addType(CreateTypeValue.builder()
                .type("type_dossier5")
                .label("5")
                .hexColor("#fcba03")
                .rank(100)
                .description("Something")
                .addToDictionaryAction(false)
                .dossierTemplateId(dossierTemplate5.getId())
                .hasDictionary(true)
                .build());
        assertThat(addedType5).isNotNull();

        Set<String> dossierTemplateIds = new HashSet<>();
        dossierTemplateIds.add(dossier.getDossierTemplateId());
        dossierTemplateIds.add(dossierTemplate2.getId());
        dossierTemplateIds.add(dossierTemplate3.getId());
        dossierTemplateIds.add(dossierTemplate4.getId());
        dossierTemplateIds.add(dossierTemplate5.getId());

        List<DossierTemplateDictionaryStats> dossierTemplateStatsList = dossierTemplateStatsClient.getDossierTemplateStats(dossierTemplateIds);
        assertThat(dossierTemplateStatsList.size()).isEqualTo(dossierTemplateIds.size());
        DossierTemplateDictionaryStats dossierTemplateStats1 = dossierTemplateStatsList.stream()
                .filter(d -> d.getDossierTemplateId().equals(dossierTemplate2.getId()))
                .findAny()
                .get();

        assertThat(dossierTemplateStats1.getDossierTemplateId()).isEqualTo(dossierTemplate2.getId());
        assertThat(dossierTemplateStats1.getNumberOfDictionaries()).isEqualTo(2);
        List<DictionarySummary> dictionarySummaryList = dossierTemplateStats1.getDictionarySummaryList();
        assertThat(dictionarySummaryList.stream().filter(d -> d.getType().equals(TYPE_ID_1)).findAny().get().getEntriesCount()).isEqualTo(entries1.size());
        assertThat(dictionarySummaryList.stream().filter(d -> d.getType().equals(TYPE_ID_2)).findAny().get().getEntriesCount()).isEqualTo(entries2.size());

        // dossier template with no type
        DossierTemplateDictionaryStats dossierTemplateStats3 = dossierTemplateStatsList.stream()
                .filter(d -> d.getDossierTemplateId().equals(dossierTemplate4.getId()))
                .findAny()
                .get();
        assertThat(dossierTemplateStats3.getDossierTemplateId()).isEqualTo(dossierTemplate4.getId());
        assertThat(dossierTemplateStats3.getNumberOfDictionaries()).isZero();

        // delete some entries, stats should be updated
        var entries22 = new ArrayList<String>();
        entries22.add(entries2.get(2));

        dictionaryClient.deleteEntries(addedType2.getType(), addedType2.getDossierTemplateId(), entries22, null, DictionaryEntryType.ENTRY);
        dossierTemplateStatsList = dossierTemplateStatsClient.getDossierTemplateStats(dossierTemplateIds);

        assertThat(dossierTemplateStatsList.size()).isEqualTo(dossierTemplateIds.size());
        DossierTemplateDictionaryStats dossierTemplateStats2 = dossierTemplateStatsList.stream()
                .filter(d -> d.getDossierTemplateId().equals(dossierTemplate2.getId()))
                .findAny()
                .get();

        dictionarySummaryList = dossierTemplateStats2.getDictionarySummaryList();
        assertThat(dictionarySummaryList.stream().filter(d -> d.getType().equals(TYPE_ID_2)).findAny().get().getEntriesCount()).isEqualTo(entries2.size() - entries22.size());

        // delete all entries, stats should be updated
        var entries23 = new ArrayList<String>();
        entries23.add(entries2.get(0));
        entries23.add(entries2.get(1));

        dictionaryClient.deleteEntries(addedType2.getType(), addedType2.getDossierTemplateId(), entries23, null, DictionaryEntryType.ENTRY);

        dictionary = dictionaryClient.getDictionaryForType(addedType2.getType(), addedType.getDossierTemplateId(), null);
        var entries23loaded = dictionary.getEntries();
        assertThat(entries23loaded).isEmpty();

        dossierTemplateStatsList = dossierTemplateStatsClient.getDossierTemplateStats(dossierTemplateIds);

        assertThat(dossierTemplateStatsList.size()).isEqualTo(dossierTemplateIds.size());
        DossierTemplateDictionaryStats dossierTemplateStats23 = dossierTemplateStatsList.stream()
                .filter(d -> d.getDossierTemplateId().equals(dossierTemplate2.getId()))
                .findAny()
                .get();

        dictionarySummaryList = dossierTemplateStats23.getDictionarySummaryList();
        assertThat(dictionarySummaryList.stream().filter(d -> d.getType().equals(TYPE_ID_2)).findAny().isEmpty()).isTrue();

    }


    public DossierTemplateModel provideTestTemplate(String dossierTemplateName) {

        var cru = new DossierTemplateModel();
        cru.setDownloadFileTypes(Sets.newHashSet(DownloadFileType.ORIGINAL));
        cru.setName(dossierTemplateName);
        cru.setDescription("Template 1");
        cru.setValidFrom(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        cru.setValidTo(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));

        return dossierTemplateClient.createOrUpdateDossierTemplate(cru);
    }


    @Test
    public void testDossierTemplateStats() {

        TenantContext.setTenantId("redaction");
        var userId = userProvider.getUserId();

        for (int i = 0; i < 2; i++) {
            var template = dossierTemplateTesterAndProvider.provideTestTemplate("test template: " + i);

            var status = dossierStatusClient.createOrUpdateDossierStatus(DossierStatusRequest.builder()
                    .dossierTemplateId(template.getId())
                    .name("test")
                    .color("#cccccc")
                    .rank(100)
                    .build());
            for (int j = 0; j < 4; j++) {

                var dossier = dossierTesterAndProvider.provideTestDossierQuick(template, "test dossier: " + j + " - " + i, j % 2 == 0 ? status : null);

                for (int k = 0; k < 6; k++) {
                    var fileId = fileTesterAndProvider.testAndProvideFileQuick(dossier, "file: " + k);

                    if (k == 1) {
                        fileStatusPersistenceService.updateProcessingStatus(fileId, k, 0L, 0L, 0L, 0L, 0L, 0L, 1, 1);
                        reanalysisClient.excludePages(dossier.getId(), fileId, new PageExclusionRequest(List.of(new PageRange(k, k))));
                    }
                    if (k == 2) {
                        fileManagementClient.deleteFile(dossier.getId(), fileId);
                    }
                    if (k == 3) {
                        fileManagementClient.hardDeleteFiles(dossier.getId(), Set.of(fileId));
                    }
                    if (k == 4) {
                        fileClient.setStatusUnderReview(dossier.getId(), fileId, userId);
                    }
                    if (k == 5) {
                        fileClient.setStatusUnderApproval(dossier.getId(), fileId, userId);
                    }
                }

                if (j == 1) {
                    dossierClient.archiveDossiers(Set.of(dossier.getId()));
                }
                if (j == 2) {
                    dossierClient.deleteDossier(dossier.getId());
                }
                if (j == 3) {
                    dossierClient.hardDeleteDossiers(Set.of(dossier.getId()));
                }

            }
        }

        var template = dossierTemplateTesterAndProvider.provideTestTemplate("test template inactive: ");

        var status = dossierStatusClient.createOrUpdateDossierStatus(DossierStatusRequest.builder()
                .dossierTemplateId(template.getId())
                .name("test")
                .color("#cccccc")
                .rank(100)
                .build());
        var dossier = dossierTesterAndProvider.provideTestDossierQuick(template, "test dossier: ", status);
        dossierClient.deleteDossier(dossier.getId());

        var cru = new DossierTemplateModel();
        cru.setDossierTemplateId(template.getId());
        cru.setDownloadFileTypes(Sets.newHashSet(DownloadFileType.ORIGINAL));
        cru.setName(template.getName());
        cru.setValidTo(OffsetDateTime.now().minusHours(1).truncatedTo(ChronoUnit.MILLIS));

        var result = dossierTemplateClient.createOrUpdateDossierTemplate(cru);

        long t1 = System.currentTimeMillis();
        var stats = dossierTemplateStatsClient.getDossierTemplateStats(Set.of(template.getId()));
        assertThat(stats.size()).isEqualTo(1);
        long t2 = System.currentTimeMillis();
        System.out.println("Time for Stats: " + (t2 - t1) + "ms");
    }

}
