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

import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
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.TypeRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.CreateOrUpdateDossierTemplateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStatus;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class DossierTemplatePersistenceService {

    @Value("${application.type}")
    private String applicationType;

    public static final String DOSSIER_TEMPLATE_NOT_FOUND_MESSAGE = "DossierTemplate with Id %s not found.";

    private final DossierTemplateRepository dossierTemplateRepository;
    private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
    private final RulesPersistenceService rulesPersistenceService;

    private final TypeRepository typeRepository;

    private final int MAX_NAME_LENGTH = 255;
    private final int MAX_DESCRIPTION_LENGTH = 4000;


    @Transactional
    public DossierTemplateEntity createOrUpdateDossierTemplate(CreateOrUpdateDossierTemplateRequest createOrUpdateDossierRequest) {

        if (createOrUpdateDossierRequest.getDossierTemplateId() != null) {
            validateDossierTemplate(createOrUpdateDossierRequest.getName(), createOrUpdateDossierRequest.getDescription());
            Optional<DossierTemplateEntity> dossierTemplate = dossierTemplateRepository.findById(createOrUpdateDossierRequest.getDossierTemplateId());
            if (dossierTemplate.isPresent()) {

                if (!dossierTemplate.get().getName().equals(createOrUpdateDossierRequest.getName())) {
                    validateDossierTemplateNameIsUnique(createOrUpdateDossierRequest.getName());
                }

                // order is important
                BeanUtils.copyProperties(createOrUpdateDossierRequest, dossierTemplate.get());
                dossierTemplate.get().setDateModified(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
                dossierTemplate.get().setModifiedBy(createOrUpdateDossierRequest.getRequestingUser());
                dossierTemplate.get().setDossierTemplateStatus(computeDossierTemplateStatus(dossierTemplate.get()));
                return dossierTemplate.get();
            } else {
                throw new NotFoundException(String.format(DOSSIER_TEMPLATE_NOT_FOUND_MESSAGE, createOrUpdateDossierRequest.getDossierTemplateId()));
            }
        } else {
            if (StringUtils.isEmpty(createOrUpdateDossierRequest.getName())) {
                throw new ConflictException("DossierTemplate name must be set");
            }
            validateDossierTemplateNameIsUnique(createOrUpdateDossierRequest.getName());
            validateDossierTemplate(createOrUpdateDossierRequest.getName(), createOrUpdateDossierRequest.getDescription());
            DossierTemplateEntity dossierTemplate = new DossierTemplateEntity();
            dossierTemplate.setId(UUID.randomUUID().toString());
            // order is important
            BeanUtils.copyProperties(createOrUpdateDossierRequest, dossierTemplate);
            dossierTemplate.setDateAdded(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
            dossierTemplate.setCreatedBy(createOrUpdateDossierRequest.getRequestingUser());
            rulesPersistenceService.setRules(RulesPersistenceService.DEFAULT_RULES, dossierTemplate.getId(), RuleFileType.ENTITY);
            var loadedDossierTemplate = dossierTemplateRepository.save(dossierTemplate);
            loadedDossierTemplate.setDossierTemplateStatus(computeDossierTemplateStatus(loadedDossierTemplate));

            return loadedDossierTemplate;
        }

    }


    @Transactional
    public void validateDossierTemplateNameIsUnique(String templateName) {

        if (isDossierTemplateNameNotUnique(templateName)) {
            throw new ConflictException("DossierTemplate name must be unique");
        }
    }

    private void validateDossierTemplate(String name, String description) {

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

        if (!StringUtils.isEmpty(description) && description.length() > MAX_DESCRIPTION_LENGTH) {
            throw new BadRequestException(String.format("The description is too long (%s), max length %s", description.length(), MAX_DESCRIPTION_LENGTH));
        }

    }


    @Transactional
    public boolean isDossierTemplateNameNotUnique(String templateName) {

        return dossierTemplateRepository.existsByName(templateName);
    }


    @Transactional
    public DossierTemplateStatus computeDossierTemplateStatus(DossierTemplateEntity dossierTemplate) {

        var legalBasis = legalBasisMappingPersistenceService.getLegalBasisMapping(dossierTemplate.getId());
        if (legalBasis == null || legalBasis.isEmpty()) {
            return DossierTemplateStatus.INCOMPLETE;
        }
        try {
            rulesPersistenceService.getRules(dossierTemplate.getId(), RuleFileType.ENTITY);
        } catch (NotFoundException e) {
            return DossierTemplateStatus.INCOMPLETE;
        }
        if (applicationType.equals("DocuMine")) {
            try {
                rulesPersistenceService.getRules(dossierTemplate.getId(), RuleFileType.COMPONENT);
            } catch (NotFoundException e) {
                return DossierTemplateStatus.INCOMPLETE;
            }
        }

        var now = OffsetDateTime.now();
        boolean isNotInRange = !now.isAfter(dossierTemplate.getValidFrom() != null ? dossierTemplate.getValidFrom() : OffsetDateTime.MIN) || !now.isBefore(dossierTemplate.getValidTo() != null ? dossierTemplate.getValidTo() : OffsetDateTime.MAX);
        if (isNotInRange) {
            return DossierTemplateStatus.INACTIVE;
        }

        return DossierTemplateStatus.ACTIVE;
    }


    @Transactional
    public List<DossierTemplateEntity> getAllDossierTemplates() {

        var allDossierTemplates = dossierTemplateRepository.findAllWhereDeletedIsFalse();
        allDossierTemplates.forEach(dossierTemplateEntity -> dossierTemplateEntity.setDossierTemplateStatus(computeDossierTemplateStatus(dossierTemplateEntity)));

        return allDossierTemplates;
    }


    @Transactional
    public DossierTemplateEntity getDossierTemplate(String dossierTemplateId) {

        var dossierTemplate = dossierTemplateRepository.findByIdAndNotDeleted(dossierTemplateId)
                .orElseThrow(() -> new NotFoundException(String.format(DOSSIER_TEMPLATE_NOT_FOUND_MESSAGE, dossierTemplateId)));
        dossierTemplate.setDossierTemplateStatus(computeDossierTemplateStatus(dossierTemplate));

        return dossierTemplate;
    }


    @Transactional
    public List<TypeEntity> getTypesForDossierTemplate(String dossierTemplateId) {

        return typeRepository.findAllByDossierTemplateIdAndDossierId(dossierTemplateId, null);
    }


    @Transactional
    public void deleteDossierTemplate(String dossierTemplateId, String deletingUserId) {

        dossierTemplateRepository.findById(dossierTemplateId).ifPresentOrElse((dossierTemplate) -> {

            var hasNonDeletedDossiers = dossierTemplate.getDossiers().stream().anyMatch(template -> template.getHardDeletedTime() == null);
            if (hasNonDeletedDossiers) {
                throw new BadRequestException("Cannot delete dossier template with active dossiers!");
            } else {
                dossierTemplate.setModifiedBy(deletingUserId);
                dossierTemplate.setDateModified(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
                dossierTemplate.setSoftDeleteTime(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
            }
        }, () -> {
            throw new NotFoundException(String.format(DOSSIER_TEMPLATE_NOT_FOUND_MESSAGE, dossierTemplateId));
        });

    }


    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public void insertDossierTemplate(DossierTemplateEntity dossierTemplate) {

        dossierTemplateRepository.saveAndFlush(dossierTemplate);
    }

}
