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

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.dossier.DossierAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierAttributeConfigRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierAttributeRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class DossierAttributeConfigPersistenceService {

    private final DossierAttributeConfigRepository dossierAttributeConfigRepository;
    private final DossierAttributeRepository dossierAttributeRepository;
    private final DossierTemplateRepository dossierTemplateRepository;


    @Transactional
    public List<DossierAttributeConfigEntity> setDossierAttributesConfig(String dossierTemplateId, List<DossierAttributeConfigEntity> dossierAttributesConfig) {

        Set<String> toSetIds = dossierAttributesConfig.stream().map(DossierAttributeConfigEntity::getId).filter(Objects::nonNull).collect(Collectors.toSet());
        var currentConfigs = dossierAttributeConfigRepository.findAllByDossierTemplateId(dossierTemplateId);
        Set<DossierAttributeConfigEntity> configsToRemove = currentConfigs.stream().filter(c -> !toSetIds.contains(c.getId())).collect(Collectors.toSet());

        dossierAttributesConfig.forEach(fac -> addOrUpdateDossierAttribute(dossierTemplateId, fac));

        configsToRemove.forEach(ctr -> {
            dossierAttributeRepository.deleteByDossierAttributeConfigId(ctr.getId());
            dossierAttributeConfigRepository.deleteById(ctr.getId());
        });

        return getDossierAttributes(dossierTemplateId);

    }


    public DossierAttributeConfigEntity addOrUpdateDossierAttribute(String dossierTemplateId, DossierAttributeConfigEntity dossierAttributeConfig) {

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

            Optional<DossierAttributeConfigEntity> optionalConfig = dossierAttributeConfigRepository.findById(dossierAttributeConfig.getId());

            if (optionalConfig.isPresent()) {
                var config = optionalConfig.get();
                config.setLabel(dossierAttributeConfig.getLabel());
                config.setType(dossierAttributeConfig.getType());
                config.setEditable(dossierAttributeConfig.isEditable());
                setPlaceholder(config);
                uniqueLabelAndPlaceholder(dossierAttributeConfig);
                return dossierAttributeConfigRepository.save(config);
            } else {
                dossierAttributeConfig.setId(UUID.randomUUID().toString());
                setPlaceholder(dossierAttributeConfig);
                uniqueLabelAndPlaceholder(dossierAttributeConfig);
                return dossierAttributeConfigRepository.save(dossierAttributeConfig);
            }

        }
    }


    public List<DossierAttributeConfigEntity> getDossierAttributes(String dossierTemplateId) {

        return dossierAttributeConfigRepository.findAllByDossierTemplateId(dossierTemplateId);
    }


    private void setPlaceholder(DossierAttributeConfigEntity dossierAttributeConfig) {

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


    private void uniqueLabelAndPlaceholder(DossierAttributeConfigEntity dossierAttributesConfig) {

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

    }


    @Transactional
    public void deleteDossierAttributes(List<String> dossierAttributeIds) {

        dossierAttributeIds.forEach(this::deleteDossierAttribute);
    }


    @Transactional
    public void deleteDossierAttribute(String dossierAttributeId) {

        dossierAttributeRepository.deleteByDossierAttributeConfigId(dossierAttributeId);
        dossierAttributeConfigRepository.deleteById(dossierAttributeId);
    }


    public String getDossierAttributeByIdOrName(String dossierTemplateId, String dossierAttributeId, String label) {

        Optional<DossierAttributeConfigEntity> result = dossierAttributeConfigRepository.findAttributeByIdOrDossierTemplateIdAndLabel(dossierTemplateId, dossierAttributeId, label);
        return result.isPresent() ? result.get().getId() : null;
    }

}
