package com.iqser.red.service.persistence.management.v1.processor.service.persistence;

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.ISO_ENCODING;
import static com.iqser.red.service.persistence.management.v1.processor.service.FileAttributesManagementService.UTF_ENCODING;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import jakarta.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.FileAttributesGeneralConfigurationEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributeConfigRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributesGeneralConfigurationRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributesRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class FileAttributeConfigPersistenceService {

    private final FileAttributeConfigRepository fileAttributeConfigRepository;
    private final FileAttributesGeneralConfigurationRepository fileAttributesGeneralConfigurationRepository;
    private final DossierTemplateRepository dossierTemplateRepository;
    private final FileAttributesRepository fileAttributesRepository;


    @Transactional
    public void updatePrimaryAttribute(String fileAttribute) {

        fileAttributeConfigRepository.updatePrimaryAttributeValueToTrue(fileAttribute);
    }


    public FileAttributesGeneralConfigurationEntity setFileAttributesGeneralConfig(String dossierTemplateId, FileAttributesGeneralConfigurationEntity fileAttributesConfig) {

        fileAttributesConfig.setDossierTemplateId(dossierTemplateId);
        fileAttributesConfig.setDossierTemplate(dossierTemplateRepository.getOne(dossierTemplateId));
        fileAttributesConfig.setEncoding(this.getValidatedEncoding(fileAttributesConfig.getEncoding()));
        return fileAttributesGeneralConfigurationRepository.save(fileAttributesConfig);
    }


    private String getValidatedEncoding(String encoding) {

        if (StringUtils.isBlank(encoding)) {
            return UTF_ENCODING;
        }
        if (UTF_ENCODING.equalsIgnoreCase(encoding.trim())) {
            return UTF_ENCODING;
        }
        if (ASCII_ENCODING.equalsIgnoreCase(encoding.trim())) {
            return ASCII_ENCODING;
        }
        if (ISO_ENCODING.equalsIgnoreCase(encoding.trim())) {
            return ISO_ENCODING;
        }
        return UTF_ENCODING;
    }


    public FileAttributesGeneralConfigurationEntity getFileAttributesGeneralConfiguration(String dossierTemplateId) {

        return fileAttributesGeneralConfigurationRepository.findById(dossierTemplateId).orElseThrow(() -> new NotFoundException("File Attribute Config not found"));
    }


    @Transactional
    public List<FileAttributeConfigEntity> setFileAttributesConfig(String dossierTemplateId, List<FileAttributeConfigEntity> fileAttributesConfig) {

        Set<String> toSetIds = fileAttributesConfig.stream().map(FileAttributeConfigEntity::getId).filter(Objects::nonNull).collect(Collectors.toSet());
        var currentConfigs = fileAttributeConfigRepository.findByDossierTemplateId(dossierTemplateId);
        Set<FileAttributeConfigEntity> configsToRemove = currentConfigs.stream().filter(c -> !toSetIds.contains(c.getId())).collect(Collectors.toSet());

        fileAttributesConfig.forEach(fac -> addOrUpdateFileAttribute(dossierTemplateId, fac));

        configsToRemove.forEach(ctr -> {
            fileAttributesRepository.deleteByFileAttributeConfigId(ctr.getId());
            fileAttributeConfigRepository.deleteById(ctr.getId());
        });

        return getFileAttributes(dossierTemplateId);
    }


    @Transactional
    public FileAttributeConfigEntity addOrUpdateFileAttribute(String dossierTemplateId, FileAttributeConfigEntity fileAttributeConfig) {

        var dossierTemplate = dossierTemplateRepository.getOne(dossierTemplateId);
        fileAttributeConfig.setDossierTemplate(dossierTemplate);
        if (fileAttributeConfig.isPrimaryAttribute()) {
            fileAttributeConfigRepository.updateAllPrimaryAttributeValuesToFalse(dossierTemplateId);
        }
        if (fileAttributeConfig.getId() == null) {
            fileAttributeConfig.setId(UUID.randomUUID().toString());
            setPlaceholder(fileAttributeConfig);
            uniqueLabelAndPlaceholder(fileAttributeConfig);
            return fileAttributeConfigRepository.save(fileAttributeConfig);
        } else {

            Optional<FileAttributeConfigEntity> optionalConfig = fileAttributeConfigRepository.findById(fileAttributeConfig.getId());

            if (optionalConfig.isPresent()) {
                var config = optionalConfig.get();
                config.setLabel(fileAttributeConfig.getLabel());
                config.setType(fileAttributeConfig.getType());
                config.setEditable(fileAttributeConfig.isEditable());
                config.setCsvColumnHeader(fileAttributeConfig.getCsvColumnHeader());
                config.setDisplayedInFileList(fileAttributeConfig.isDisplayedInFileList());
                config.setPrimaryAttribute(fileAttributeConfig.isPrimaryAttribute());
                config.setFilterable(fileAttributeConfig.isFilterable());
                setPlaceholder(config);
                uniqueLabelAndPlaceholder(fileAttributeConfig);
                return fileAttributeConfigRepository.save(config);
            } else {
                fileAttributeConfig.setId(UUID.randomUUID().toString());
                setPlaceholder(fileAttributeConfig);
                uniqueLabelAndPlaceholder(fileAttributeConfig);
                return fileAttributeConfigRepository.save(fileAttributeConfig);
            }

        }
    }


    public List<FileAttributeConfigEntity> getFileAttributes(String dossierTemplateId) {

        return fileAttributeConfigRepository.findByDossierTemplateId(dossierTemplateId);
    }


    private void setPlaceholder(FileAttributeConfigEntity fileAttributeConfig) {

        if (fileAttributeConfig.getPlaceholder() == null || fileAttributeConfig.getPlaceholder().isEmpty()) {
            String placeholder = "{{file.attribute." + StringUtils.remove(WordUtils.capitalizeFully(fileAttributeConfig.getLabel(), ' '), " ") + "}}";
            fileAttributeConfig.setPlaceholder(placeholder);
        }
    }


    private void uniqueLabelAndPlaceholder(FileAttributeConfigEntity fileAttributeConfig) {

        getFileAttributes(fileAttributeConfig.getDossierTemplate().getId()).stream().filter(d -> !d.getId().equals(fileAttributeConfig.getId())).forEach(other -> {
            if (other.getLabel().equalsIgnoreCase(fileAttributeConfig.getLabel())) {
                throw new ConflictException("Label already exists.");
            }
            if (other.getPlaceholder().equalsIgnoreCase(fileAttributeConfig.getPlaceholder())) {
                throw new ConflictException("Placeholder already exists.");
            }
        });
    }


    @Transactional
    public void deleteFileAttribute(String fileAttributeId) {

        fileAttributesRepository.deleteByFileAttributeConfigId(fileAttributeId);
        fileAttributeConfigRepository.deleteById(fileAttributeId);
    }


    @Transactional
    public void deleteFileAttributes(List<String> fileAttributeIds) {

        fileAttributeIds.forEach(fileAttributeId -> {
            fileAttributesRepository.deleteByFileAttributeConfigId(fileAttributeId);
            fileAttributeConfigRepository.deleteById(fileAttributeId);
        });
    }


    public String getFileAttributeByIdOrName(String dossierTemplateId, String fileAttributeId, String label) {

        Optional<FileAttributeConfigEntity> result = fileAttributeConfigRepository.findByIdOrDossierTemplateIdAndLabel(dossierTemplateId, fileAttributeId, label);
        return result.isPresent() ? result.get().getId() : null;
    }

}
