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

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

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

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

import com.iqser.red.service.peristence.v1.server.integration.client.DictionaryClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierTemplateClient;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.TypeProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DictionaryEntryEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.dictionaryentry.EntryRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.CloneDossierTemplateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;
import com.knecon.fforesight.databasetenantcommons.providers.utils.JDBCWriteUtils;
import com.knecon.fforesight.tenantcommons.TenantContext;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class EntityPerformanceTest extends AbstractPersistenceServerServiceTest {

    @Autowired
    private DictionaryClient dictionaryClient;

    @Autowired
    private DossierTemplateClient dossierTemplateClient;

    @Autowired
    private DossierTemplateTesterAndProvider dossierTemplateTesterAndProvider;

    @Autowired
    private TypeProvider typeProvider;

    @Autowired
    private EntryRepository entryRepository;
    @Autowired
    private JDBCWriteUtils jdbcWriteUtils;


    @Test
    public void testAddToDictionary() {

        TenantContext.setTenantId("redaction");
        var template = dossierTemplateTesterAndProvider.provideTestTemplate("test");
        var type = typeProvider.testAndProvideType(template);

        var fiveKEntries = generateEntries(5000);
        var tenKEntries = generateEntries(10000);

        long t1 = System.currentTimeMillis();
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), fiveKEntries, true, null, DictionaryEntryType.ENTRY);
        long t2 = System.currentTimeMillis();
        log.info("Add Time: {}ms counting: {} entries", (t2 - t1), entryRepository.findByTypeIdAndVersionGreaterThan(type.getTypeId(), 0).size());

        t1 = System.currentTimeMillis();
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), tenKEntries, true, null, DictionaryEntryType.ENTRY);
        t2 = System.currentTimeMillis();
        log.info("Add Time: {}ms counting: {} entries", (t2 - t1), entryRepository.findByTypeIdAndVersionGreaterThan(type.getTypeId(), 0).size());

        t1 = System.currentTimeMillis();
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), fiveKEntries, true, null, DictionaryEntryType.ENTRY);
        t2 = System.currentTimeMillis();
        log.info("Update Time: {}ms counting: {} entries", (t2 - t1), entryRepository.findByTypeIdAndVersionGreaterThan(type.getTypeId(), 0).size());

        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), fiveKEntries, true, null, DictionaryEntryType.FALSE_RECOMMENDATION);
        dictionaryClient.addEntry(type.getType(), type.getDossierTemplateId(), fiveKEntries, true, null, DictionaryEntryType.FALSE_POSITIVE);

        t1 = System.currentTimeMillis();
        var cloned = dossierTemplateClient.cloneDossierTemplate(template.getId(), new CloneDossierTemplateRequest());
        t2 = System.currentTimeMillis();
        log.info("Clone Time: {}", (t2 - t1));

        var types = dictionaryClient.getAllTypes(cloned.getId(), null, false).getTypes();

        assertThat(types.size()).isEqualTo(1);
        for (var clonedType : types) {
            var found = entryRepository.findByTypeIdAndVersionGreaterThan(clonedType.getTypeId(), 0);
            assertThat(found.size()).isEqualTo(5000);
        }

    }


    private List<String> generateEntries(int count) {

        List<String> entries = new ArrayList<>();
        for (var i = 1; i <= count; i++) {
            entries.add("Dictionary Entry: " + i);
        }
        return entries;
    }


    @Test
    public void testWritePerformance() {

        final int numberOfEntries = 10_000;
        var entries = generateEntries(numberOfEntries);

        var template = dossierTemplateTesterAndProvider.provideTestTemplate("test");
        var type1 = typeProvider.testAndProvideType(template, null, "t1");
        var type2 = typeProvider.testAndProvideType(template, null, "t2");

        List<DictionaryEntryEntity> type1Entries = entries.stream().map(s -> new DictionaryEntryEntity(0, s, 1, false, type1.getTypeId())).collect(Collectors.toList());

        List<DictionaryEntryEntity> type2Entries = entries.stream().map(s -> new DictionaryEntryEntity(0, s, 1, false, type2.getTypeId())).collect(Collectors.toList());

        assertThat(entryRepository.count()).isEqualTo(0);
        long t1 = System.currentTimeMillis();
        entryRepository.saveAll(type1Entries);
        long jpaTime = System.currentTimeMillis() - t1;
        assertThat(entryRepository.count()).isEqualTo(numberOfEntries);

        t1 = System.currentTimeMillis();
        jdbcWriteUtils.saveBatch(type2Entries);
        long jdbcTime = System.currentTimeMillis() - t1;

        assertThat(entryRepository.count()).isEqualTo(2 * numberOfEntries);

//        assertThat(jpaTime).isGreaterThan(jdbcTime);

        log.info("JPA Time: {} ms for {} entries", jpaTime, numberOfEntries);
        log.info("JDBC Time: {} ms for {} entries", jdbcTime, numberOfEntries);
    }

}
