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

import java.time.OffsetDateTime;
import java.util.List;
import java.util.Set;


import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
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.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierChange;

import jakarta.validation.ConstraintViolationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Provides the internal interface between dossier request and the actual persistence.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DossierService {

    private final DossierPersistenceService dossierPersistenceService;

    private final FileStatusPersistenceService fileStatusPersistenceService;

    private final DossierTemplatePersistenceService dossierTemplatePersistenceService;

    private final DictionaryPersistenceService dictionaryPersistenceService;


    public DossierEntity addDossier(CreateOrUpdateDossierRequest createOrUpdateDossierRequest) {

        if (dossierPersistenceService.findAllDossiers()
                .stream()
                .anyMatch(p -> p.getHardDeletedTime() == null && p.getDossierName().equals(createOrUpdateDossierRequest.getDossierName()))) {
            throw new ConflictException("Dossier with this name already exists");
        }

        var dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(createOrUpdateDossierRequest.getDossierTemplateId());
        if (!dossierTemplate.getDossierTemplateStatus().equals(DossierTemplateStatus.ACTIVE)) {
            throw new BadRequestException("Dossier template is not active.");
        }

        try {
            return dossierPersistenceService.insert(createOrUpdateDossierRequest);
        } catch (Exception e) {
            if (e.getCause() instanceof ConstraintViolationException) {
                throw new ConflictException("Dossier with this name already exists");
            } else {
                log.debug("Unknown error when creating a dossier: ", e);
                throw new BadRequestException("Failed to save dossier!");
            }
        }

    }


    public DossierEntity updateDossier(CreateOrUpdateDossierRequest dossierRequest, String dossierId) {

        var dossierTemplate = dossierTemplatePersistenceService.getDossierTemplate(dossierRequest.getDossierTemplateId());
        if (!dossierTemplate.getDossierTemplateStatus().equals(DossierTemplateStatus.ACTIVE)) {
            throw new BadRequestException("Dossier template is not active.");
        }

        DossierEntity dossier = dossierPersistenceService.findByDossierId(dossierId);

        if (dossier.getDossierName().equals(dossierRequest.getDossierName()) || dossierPersistenceService.findAllDossiers()
                .stream()
                .filter(p -> p.getHardDeletedTime() == null && p.getDossierName().equals(dossierRequest.getDossierName()))
                .findAny()
                .isEmpty()) {

            if (!dossier.getDossierTemplateId().equalsIgnoreCase(dossierRequest.getDossierTemplateId())) {
                if (fileStatusPersistenceService.getStatusesForDossier(dossierId).isEmpty()) {

                    String typeId = TypeIdUtils.toTypeId("dossier_redaction", dossier.getDossierTemplateId(), dossier.getId());
                    String newTypeId = TypeIdUtils.toTypeId("dossier_redaction", dossierRequest.getDossierTemplateId(), dossier.getId());
                    dictionaryPersistenceService.updateTypeIdAndIncrementVersionByOne(typeId, newTypeId, dossierRequest.getDossierTemplateId());

                } else {
                    throw new BadRequestException("Cannot update Dossier Template. Dossier already has files");
                }
            }

            dossierPersistenceService.update(dossierId, dossierRequest);
            return dossierPersistenceService.findByDossierId(dossierId);
        } else {
            throw new ConflictException("Dossier with this name already exists. Please choose another name.");
        }
    }


    public void softDeleteDossier(String dossierId, OffsetDateTime softDeleteTime) {

        dossierPersistenceService.markDossierAsDeleted(dossierId, softDeleteTime);
    }


    public void hardDeleteDossier(String dossierId) {

        dossierPersistenceService.hardDelete(dossierId);
    }


    public void undeleteDossier(String dossierId) {

        dossierPersistenceService.undelete(dossierId);
    }


    public DossierEntity getDossierById(String dossierId) {

        return dossierPersistenceService.findByDossierId(dossierId);
    }


    public List<DossierEntity> getAllDossiers() {

        return dossierPersistenceService.findAllDossiers();
    }


    public List<DossierEntity> getAllDossiers(List<String> dossierIds) {

        return dossierPersistenceService.findAllDossiers(dossierIds);
    }


    public List<DossierEntity> getAllDossiersForDossierTemplateId(String dossierTemplateId) {

        return dossierPersistenceService.findAllDossiersForDossierTemplateId(dossierTemplateId);
    }


    public Set<DossierChange> changesSince(OffsetDateTime since) {

        return dossierPersistenceService.hasChangesSince(since);
    }


    public void archiveDossier(String dossierId) {

        dossierPersistenceService.archiveDossier(dossierId);
    }


    public void unarchiveDossier(String dossierId) {

        dossierPersistenceService.unarchiveDossier(dossierId);
    }

}
