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

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import jakarta.transaction.Transactional;

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

import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierStatusEntity;
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.DossierStatusRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierStatusRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierStatusInfo;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class DossierStatusPersistenceService {

    public static final String DOSSIER_STATUS_NOT_FOUND_MESSAGE = "Dossier Status with Id %s not found.";

    private final DossierStatusRepository dossierStatusRepository;
    private final DossierTemplatePersistenceService dossierTemplatePersistenceService;


    @Transactional
    public DossierStatusEntity createOrUpdateDossierStatus(CreateOrUpdateDossierStatusRequest createOrUpdateDossierStatusRequest) {

        if (StringUtils.isBlank(createOrUpdateDossierStatusRequest.getName())) {
            throw new BadRequestException("Dossier Status Name cannot be empty");
        }

        if (createOrUpdateDossierStatusRequest.getDossierStatusId() != null) { //update
            Optional<DossierStatusEntity> dossierStatus = dossierStatusRepository.findById(createOrUpdateDossierStatusRequest.getDossierStatusId());
            if (dossierStatus.isPresent()) {
                validateDossierTemplateNameIsUnique(createOrUpdateDossierStatusRequest.getName(),
                        createOrUpdateDossierStatusRequest.getDossierTemplateId(),
                        createOrUpdateDossierStatusRequest.getDossierStatusId());
                if (createOrUpdateDossierStatusRequest.getRank() != dossierStatus.get().getRank() && dossierStatusRepository.getDuplicateRanks(dossierStatus.get()
                        .getDossierTemplateId(), createOrUpdateDossierStatusRequest.getRank()) > 0) {
                    // in case of duplicate ranks, increase the ranks
                    dossierStatusRepository.adjustRanksForDossierTemplate(dossierStatus.get().getDossierTemplateId(), createOrUpdateDossierStatusRequest.getRank());
                }
                BeanUtils.copyProperties(createOrUpdateDossierStatusRequest, dossierStatus.get());
                return dossierStatus.get();
            } else {
                throw new NotFoundException(String.format(DOSSIER_STATUS_NOT_FOUND_MESSAGE, createOrUpdateDossierStatusRequest.getDossierStatusId()));
            }
        } else {
            validateDossierTemplateNameIsUnique(createOrUpdateDossierStatusRequest.getName(), createOrUpdateDossierStatusRequest.getDossierTemplateId(), null);
            DossierStatusEntity dossierStatus = new DossierStatusEntity();
            dossierStatus.setId(UUID.randomUUID().toString());
            BeanUtils.copyProperties(createOrUpdateDossierStatusRequest, dossierStatus);
            var dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(createOrUpdateDossierStatusRequest.getDossierTemplateId());
            // in case of duplicate ranks, increase the ranks
            if (dossierStatusRepository.getDuplicateRanks(createOrUpdateDossierStatusRequest.getDossierTemplateId(), createOrUpdateDossierStatusRequest.getRank()) > 0) {
                dossierStatusRepository.adjustRanksForDossierTemplate(createOrUpdateDossierStatusRequest.getDossierTemplateId(), createOrUpdateDossierStatusRequest.getRank());
            }
            var loadedDossierStatus = dossierStatusRepository.save(dossierStatus);
            loadedDossierStatus.setDossierTemplate(dossierTemplate);

            return loadedDossierStatus;
        }
    }


    private void validateDossierTemplateNameIsUnique(String dossierStatusName, String dossierTemplateId, String dossierStatusId) {

        getAllDossierStatusForTemplate(dossierTemplateId).forEach(existing -> {
            if (existing.getName().equals(dossierStatusName) && !existing.getId().equals(dossierStatusId)) {
                throw new ConflictException("DossierStatus name must be unique within the same dossier template");
            }
        });
    }


    public List<DossierStatusInfo> getAllDossierStatusForTemplate(String dossierTemplateId) {

        return dossierStatusRepository.getAllDossierStatusForDossierTemplate(Collections.singletonList(dossierTemplateId));
    }


    public List<DossierStatusInfo> getAllDossierStatuses(List<String> dossierTemplateIds) {

        return dossierStatusRepository.getAllDossierStatusForDossierTemplate(dossierTemplateIds);
    }


    public DossierStatusInfo getDossierStatusInfo(String dossierStatusId) {

        return dossierStatusRepository.findProjectionById(dossierStatusId)
                .orElseThrow(() -> new NotFoundException(String.format(DOSSIER_STATUS_NOT_FOUND_MESSAGE, dossierStatusId)));
    }


    public DossierStatusEntity getDossierStatus(String dossierStatusId) {

        return dossierStatusRepository.findById(dossierStatusId).orElseThrow(() -> new NotFoundException(String.format(DOSSIER_STATUS_NOT_FOUND_MESSAGE, dossierStatusId)));
    }


    @Transactional
    public void deleteDossierStatus(String dossierStatusId, String replaceDossierStatusId) {

        if (dossierStatusId.equalsIgnoreCase(replaceDossierStatusId)) {
            throw new BadRequestException("Cannot replace with self");
        }

        dossierStatusRepository.findById(dossierStatusId).ifPresentOrElse(toDeleteDossierStatus -> {

            DossierStatusEntity replaceDossierStatusEntity = null;
            if (replaceDossierStatusId != null) {
                replaceDossierStatusEntity = dossierStatusRepository.findById(replaceDossierStatusId)
                        .orElseThrow(() -> new NotFoundException(String.format(DOSSIER_STATUS_NOT_FOUND_MESSAGE, replaceDossierStatusId)));
            }

            if (replaceDossierStatusEntity != null && !replaceDossierStatusEntity.getDossierTemplateId().equals(toDeleteDossierStatus.getDossierTemplateId())) {
                throw new BadRequestException("Cannot replace with dossier status from different template");
            }

            for (var dossier : toDeleteDossierStatus.getDossiers()) {
                dossier.setDossierStatus(replaceDossierStatusEntity);
            }

            dossierStatusRepository.deleteById(dossierStatusId);
        }, () -> {
            throw new NotFoundException("Dossier Status not found");
        });

    }

}
