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

import static com.iqser.red.service.persistence.management.v1.processor.service.FileAttributesManagementService.ASCII_ENCODING;
import static com.iqser.red.service.persistence.management.v1.processor.service.FileAttributesManagementService.UTF_ENCODING;
import static org.assertj.core.api.Assertions.assertThat;

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

import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mock.web.MockMultipartFile;

import com.google.common.collect.Sets;
import com.iqser.red.service.peristence.v1.server.integration.client.FileAttributeClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileAttributeConfigClient;
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.UploadClient;
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.service.v1.api.shared.model.FileAttributes;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttributesConfig;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileAttributeConfig;

import lombok.SneakyThrows;

public class FileAttributeTest extends AbstractPersistenceServerServiceTest {

    @Autowired
    private FileTesterAndProvider fileTesterAndProvider;

    @Autowired
    private DossierTesterAndProvider dossierTesterAndProvider;

    @Autowired
    private FileAttributeConfigClient fileAttributeConfigClient;

    @Autowired
    private FileAttributeClient fileAttributeClient;

    @Autowired
    private FileClient fileClient;
    @Autowired
    private UploadClient uploadClient;

    @Autowired
    private FileManagementClient fileManagementClient;


    @SneakyThrows
    @Test
    public void testFileAttributes() {

        var dossier = dossierTesterAndProvider.provideTestDossier();

        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var generalConfig = new FileAttributesConfig();
        generalConfig.setDelimiter(",");
        generalConfig.setEncoding("UTF-8");
        generalConfig.setFilenameMappingColumnHeaderName("Name");

        fileAttributeConfigClient.setFileAttributesConfig(dossier.getDossierTemplateId(), generalConfig);
        var loadedConfig = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId());
        assertThat(loadedConfig.getDelimiter()).isEqualTo(",");
        assertThat(loadedConfig.getEncoding()).isEqualTo(UTF_ENCODING);

        generalConfig.setEncoding(" ASCII ");
        fileAttributeConfigClient.setFileAttributesConfig(dossier.getDossierTemplateId(), generalConfig);
        loadedConfig = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId());
        assertThat(loadedConfig.getEncoding()).isEqualTo(ASCII_ENCODING);

        List<FileAttributeConfig> configs = new ArrayList<>();
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Name").primaryAttribute(true).label("Name").build());
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Attribute A").primaryAttribute(true).label("Attribute A").build());
        FileAttributeConfig attributeB = FileAttributeConfig.builder().csvColumnHeader("Attribute B").primaryAttribute(true).label("Attribute B").build();
        configs.add(attributeB);
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Attribute C").primaryAttribute(false).label("Attribute C").build());
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Attribute D").primaryAttribute(false).label("Attribute D").build());

        fileAttributeConfigClient.setFileAttributesConfig(dossier.getDossierTemplateId(), new FileAttributesConfig("Name", ",", UTF_ENCODING, configs));

        List<FileAttributeConfig> loadedConfigs = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId()).getFileAttributeConfigs();
        assertThat(loadedConfigs.size()).isEqualTo(5);
        FileAttributeConfig primaryAttribute = loadedConfigs.stream().filter(FileAttributeConfig::isPrimaryAttribute).findAny().get();
        assertThat(primaryAttribute.getLabel()).isEqualTo(attributeB.getLabel());

        fileAttributeConfigClient.deleteFileAttribute(dossier.getDossierTemplateId(),
                loadedConfigs.stream()
                        .filter(a -> a.getCsvColumnHeader().equalsIgnoreCase("Attribute C"))
                        .findAny()
                        .map(FileAttributeConfig::getId)
                        .orElseThrow(() -> new IllegalArgumentException("Should exists!")));

        fileAttributeConfigClient.deleteFileAttributes(dossier.getDossierTemplateId(),
                loadedConfigs.stream()
                        .filter(a -> a.getCsvColumnHeader().equalsIgnoreCase("Attribute D"))
                        .findAny()
                        .map(FileAttributeConfig::getId)
                        .stream()
                        .collect(Collectors.toList()));

        loadedConfigs = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId()).getFileAttributeConfigs();
        assertThat(loadedConfigs.size()).isEqualTo(3);

        FileAttributeConfig newConfig = new FileAttributeConfig();
        newConfig.setPrimaryAttribute(true);
        newConfig.setLabel("Test Attribute");
        var created = fileAttributeConfigClient.addOrUpdateFileAttribute(dossier.getDossierTemplateId(), newConfig);
        loadedConfigs = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId()).getFileAttributeConfigs();
        assertThat(loadedConfigs.size()).isEqualTo(4);

        newConfig.setId(created.getId());
        newConfig.setLabel("Test Attribute Update");
        var updated = fileAttributeConfigClient.addOrUpdateFileAttribute(dossier.getDossierTemplateId(), newConfig);
        loadedConfigs = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId()).getFileAttributeConfigs();
        assertThat(loadedConfigs.size()).isEqualTo(4);
        assertThat(updated.getLabel()).isEqualTo("Test Attribute Update");

        assertThat(fileClient.getFileStatus(dossier.getId(), file.getId()).getLastFileAttributeChange()).isNull();

        fileAttributeClient.setFileAttributes(dossier.getId(), file.getId(), new FileAttributes(Map.of(updated.getId(), "Lorem Ipsum")));

        assertThat(fileClient.getFileStatus(dossier.getId(), file.getId()).getLastFileAttributeChange()).isNotNull();

        Map<String, String> fileAttributes = fileClient.getFileStatus(dossier.getId(), file.getId()).getFileAttributes().getAttributeIdToValue();
        assertThat(fileAttributes.size()).isEqualTo(1);
        assertThat(fileAttributes.entrySet().iterator().next().getValue()).isEqualTo("Lorem Ipsum");

        fileAttributeClient.setFileAttributes(dossier.getId(), file.getId(), new FileAttributes(Map.of(updated.getId(), "   ")));

        fileAttributes = fileClient.getFileStatus(dossier.getId(), file.getId()).getFileAttributes().getAttributeIdToValue();
        assertThat(fileAttributes.size()).isEqualTo(1);
        assertThat(fileAttributes.entrySet().iterator().next().getValue()).isNull();

        Map<String, String> fileAttributesWithNullValue = new HashMap<>();
        fileAttributesWithNullValue.put(updated.getId(), null);
        fileAttributeClient.setFileAttributes(dossier.getId(), file.getId(), new FileAttributes(fileAttributesWithNullValue));

        fileAttributes = fileClient.getFileStatus(dossier.getId(), file.getId()).getFileAttributes().getAttributeIdToValue();
        assertThat(fileAttributes.size()).isEqualTo(1);
        assertThat(fileAttributes.entrySet().iterator().next().getValue()).isNull();

        var mock = new MockMultipartFile("test.csv","test.csv","application/csv", IOUtils.toByteArray(new ClassPathResource("files/test.csv").getInputStream()));
        uploadClient.upload(mock, dossier.getId(), false);

        fileAttributes = fileClient.getFileStatus(dossier.getId(), file.getId()).getFileAttributes().getAttributeIdToValue();
        assertThat(fileAttributes.size()).isEqualTo(2);

        fileAttributes.put(fileAttributes.keySet().iterator().next(), "changed");
        fileAttributeClient.setFileAttributes(dossier.getId(), file.getId(), new FileAttributes(fileAttributes));

        fileAttributes = fileClient.getFileStatus(dossier.getId(), file.getId()).getFileAttributes().getAttributeIdToValue();
        assertThat(fileAttributes.size()).isEqualTo(2);

        fileAttributeConfigClient.setFileAttributesConfig(dossier.getDossierTemplateId(), new FileAttributesConfig("Name",",","ASCII",new ArrayList<>()));

        loadedConfigs = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId()).getFileAttributeConfigs();
        assertThat(loadedConfigs.size()).isEqualTo(0);

        fileAttributes = fileClient.getFileStatus(dossier.getId(), file.getId()).getFileAttributes().getAttributeIdToValue();
        assertThat(fileAttributes.size()).isEqualTo(0);
    }


    @SneakyThrows
    @Test
    public void testDeleteFile() {
        // Create file with attributes
        var dossier = dossierTesterAndProvider.provideTestDossier();
        var file = fileTesterAndProvider.testAndProvideFile(dossier);

        var generalConfig = new FileAttributesConfig();
        generalConfig.setDelimiter(",");
        generalConfig.setEncoding("UTF-8");
        generalConfig.setFilenameMappingColumnHeaderName("Name");

        fileAttributeConfigClient.setFileAttributesConfig(dossier.getDossierTemplateId(), generalConfig);
        var loadedConfig = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId());
        assertThat(loadedConfig.getDelimiter()).isEqualTo(",");

        List<FileAttributeConfig> configs = new ArrayList<>();
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Name").primaryAttribute(true).label("Name").build());
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Attribute A").primaryAttribute(false).label("Attribute A").build());
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Attribute B").primaryAttribute(false).label("Attribute B").build());
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Attribute C").primaryAttribute(false).label("Attribute C").build());
        configs.add(FileAttributeConfig.builder().csvColumnHeader("Attribute D").primaryAttribute(false).label("Attribute D").build());

        generalConfig.setFileAttributeConfigs(configs);

        fileAttributeConfigClient.setFileAttributesConfig(dossier.getDossierTemplateId(), generalConfig);

        List<FileAttributeConfig> loadedConfigs = fileAttributeConfigClient.getFileAttributesConfiguration(dossier.getDossierTemplateId()).getFileAttributeConfigs();
        assertThat(loadedConfigs.size()).isEqualTo(5);

        fileAttributeClient.setFileAttributes(dossier.getId(), file.getId(), new FileAttributes(Map.of(loadedConfigs.get(0).getId(), "Lorem Ipsum")));

        Map<String, String> fileAttributes = fileClient.getFileStatus(dossier.getId(), file.getId()).getFileAttributes().getAttributeIdToValue();
        assertThat(fileAttributes.size()).isEqualTo(1);
        assertThat(fileAttributes.entrySet().iterator().next().getValue()).isEqualTo("Lorem Ipsum");

        // Delete file
        fileManagementClient.deleteFile(dossier.getId(), file.getId());
        fileManagementClient.hardDeleteFiles(dossier.getId(), Sets.newHashSet(file.getId()));
        var deletedFile = fileClient.getFileStatus(dossier.getId(), file.getId());
        assertThat(deletedFile.getFileAttributes().getAttributeIdToValue()).isEmpty();

        // Reupload file
        var newFile = fileTesterAndProvider.testAndProvideFile(dossier);
        assertThat(file.getId()).isEqualTo(newFile.getId());

        var loadedFile = fileClient.getFileStatus(dossier.getId(), newFile.getId());
        assertThat(loadedFile.getFileAttributes().getAttributeIdToValue()).isEmpty();
    }

}
