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

import static com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter.convert;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.LegalBasisMappingEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.LegalBasisMappingRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.legalbasis.LegalBasis;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class LegalBasisMappingPersistenceService {

    private final LegalBasisMappingRepository legalBasisMappingRepository;

    public static final int MAX_NAME_LENGTH = 255;
    public static final int MAX_LEGAL_BASIS_FIELD_LENGTH = 4000;


    @Transactional
    public void deleteLegalBasis(String dossierTemplateId, List<String> legalBasisTechnicalNames) {

        var mapping = getLegalBasisMappingOrCreate(dossierTemplateId);
        var filteredLegalBasis = mapping.getLegalBasis()
                .stream()
                .filter(l -> !legalBasisTechnicalNames.contains(l.getTechnicalName()))
                .collect(Collectors.toList());

        mapping.setLegalBasis(filteredLegalBasis);
        mapping.setVersion(mapping.getVersion() + 1);
        legalBasisMappingRepository.save(mapping);

    }


    @Transactional
    public void deleteLegalBasis(String dossierTemplateId) {

        var mapping = getLegalBasisMappingOrCreate(dossierTemplateId);
        mapping.setLegalBasis(Collections.emptyList());
        mapping.setVersion(mapping.getVersion() + 1);
        legalBasisMappingRepository.save(mapping);
    }


    private LegalBasisMappingEntity getLegalBasisMappingOrCreate(String dossierTemplateId) {

        return legalBasisMappingRepository.findById(dossierTemplateId)
                .orElseGet(() -> {
                    var lbm = new LegalBasisMappingEntity();
                    lbm.setDossierTemplateId(dossierTemplateId);
                    lbm.setLegalBasis(new ArrayList<>());
                    lbm.setVersion(1);
                    return legalBasisMappingRepository.save(lbm);

                });
    }


    @Transactional
    public void addOrUpdateLegalBasis(String dossierTemplateId, LegalBasis legalBasis) {

        validateLegalBasis(legalBasis);

        var mapping = getLegalBasisMappingOrCreate(dossierTemplateId);

        Optional<LegalBasisEntity> existingBasisByTechnicalName = mapping.getLegalBasis()
                .stream()
                .filter(l -> Objects.equals(l.getTechnicalName(), legalBasis.getTechnicalName()))
                .findAny();

        Optional<LegalBasisEntity> existingBasisByName = mapping.getLegalBasis()
                .stream()
                .filter(l -> Objects.equals(l.getName(), legalBasis.getName()))
                .findAny();

        if (existingBasisByTechnicalName.isPresent() && existingBasisByName.isPresent()) {
            LegalBasisEntity existingBasis = existingBasisByTechnicalName.get();
            existingBasis.setDescription(legalBasis.getDescription());
            existingBasis.setReason(legalBasis.getReason());
        } else if (existingBasisByTechnicalName.isPresent()) {
            LegalBasisEntity existingBasis = existingBasisByTechnicalName.get();
            existingBasis.setName(legalBasis.getName());
            existingBasis.setDescription(legalBasis.getDescription());
            existingBasis.setReason(legalBasis.getReason());
        } else if (existingBasisByName.isPresent()) {
            LegalBasisEntity existingBasis = existingBasisByName.get();
            existingBasis.setTechnicalName(legalBasis.getTechnicalName());
            existingBasis.setDescription(legalBasis.getDescription());
            existingBasis.setReason(legalBasis.getReason());
        } else {
            mapping.getLegalBasis()
                    .add(LegalBasisEntity.builder()
                                 .name(legalBasis.getName())
                                 .description(legalBasis.getDescription())
                                 .reason(legalBasis.getReason())
                                 .technicalName(legalBasis.getTechnicalName())
                                 .build());
        }
        mapping.setVersion(mapping.getVersion() + 1);

    }


    private void validateLegalBasis(LegalBasis legalBasis) {

        if (legalBasis.getTechnicalName() == null || legalBasis.getTechnicalName().isEmpty()) {
            throw new BadRequestException("The technical name cannot be empty!");
        }

        if (legalBasis.getName() == null || legalBasis.getName().isEmpty()) {
            throw new BadRequestException("The name cannot be empty!");
        }

        if (legalBasis.getTechnicalName().length() > MAX_LEGAL_BASIS_FIELD_LENGTH) {
            throw new BadRequestException(String.format("The legal basis is too long (%s), max length %s", legalBasis.getTechnicalName().length(), MAX_LEGAL_BASIS_FIELD_LENGTH));
        }

        if (legalBasis.getName().length() > MAX_NAME_LENGTH) {
            throw new BadRequestException(String.format("The name is too long (%s), max length %s", legalBasis.getName().length(), MAX_NAME_LENGTH));
        }

        if (legalBasis.getDescription() != null && legalBasis.getDescription().length() > MAX_LEGAL_BASIS_FIELD_LENGTH) {
            throw new BadRequestException(String.format("The description is too long (%s), max length %s", legalBasis.getDescription().length(), MAX_LEGAL_BASIS_FIELD_LENGTH));
        }

        if (legalBasis.getReason() != null && legalBasis.getReason().length() > MAX_LEGAL_BASIS_FIELD_LENGTH) {
            throw new BadRequestException(String.format("The legal basis is too long (%s), max length %s", legalBasis.getReason().length(), MAX_LEGAL_BASIS_FIELD_LENGTH));
        }
    }


    @Transactional
    public void setLegalBasisMapping(String dossierTemplateId, List<LegalBasis> legalBasisMapping) {

        legalBasisMapping.forEach(this::validateLegalBasis);

        legalBasisMappingRepository.findById(dossierTemplateId).ifPresentOrElse((lbm) -> {
            lbm.setVersion(lbm.getVersion() + 1);
            lbm.setLegalBasis(convert(legalBasisMapping, LegalBasisEntity.class));
            legalBasisMappingRepository.save(lbm);
        }, () -> {
            var lbm = new LegalBasisMappingEntity();
            lbm.setDossierTemplateId(dossierTemplateId);
            lbm.setLegalBasis(convert(legalBasisMapping, LegalBasisEntity.class));
            lbm.setVersion(1);
            legalBasisMappingRepository.save(lbm);
        });

    }


    @Transactional
    public List<LegalBasisEntity> getLegalBasisMapping(String dossierTemplateId) {

        return getLegalBasisMappingOrCreate(dossierTemplateId).getLegalBasis();
    }


    public long getVersion(String dossierTemplateId) {

        return legalBasisMappingRepository.findById(dossierTemplateId)
                .map(LegalBasisMappingEntity::getVersion)
                .orElse(0L);
    }

}
