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

import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.AnnotationEntityId;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ManualRedactionEntryEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.RectangleEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.model.ManualChangesQueryOptions;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.annotationentity.ManualRedactionRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AddRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class AddRedactionPersistenceService {

    private final ManualRedactionRepository manualRedactionRepository;


    public ManualRedactionEntryEntity insert(String fileId, String annotationId, AddRedactionRequest addRedactionRequest) {

        ManualRedactionEntryEntity manualRedactionEntry = new ManualRedactionEntryEntity();
        manualRedactionEntry.setId(new AnnotationEntityId(annotationId, fileId));
        BeanUtils.copyProperties(addRedactionRequest, manualRedactionEntry);
        manualRedactionEntry.setRequestDate(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
        manualRedactionEntry.setPositions(convert(addRedactionRequest.getPositions()));
        manualRedactionEntry.setTypeId(addRedactionRequest.getDictionaryTypeId());
        manualRedactionEntry.setDictionaryEntryType(addRedactionRequest.getDictionaryEntryType());
        manualRedactionEntry.setSection(addRedactionRequest.getSection());
        manualRedactionEntry.setAddToDossierDictionary(manualRedactionEntry.isAddToDictionary() && !manualRedactionEntry.isAddToAllDossiers());

        return manualRedactionRepository.saveAndFlush(manualRedactionEntry);

    }


    @Transactional
    public List<ManualRedactionEntryEntity> insert(List<ManualRedactionEntryEntity> manualRedactionEntryEntities) {

        return manualRedactionRepository.saveAllAndFlush(manualRedactionEntryEntities);
    }


    public Optional<ManualRedactionEntryEntity> findById(String annotationId, String fileId) {

        return manualRedactionRepository.findById(new AnnotationEntityId(annotationId, fileId));
    }


    private List<RectangleEntity> convert(List<Rectangle> positions) {

        List<RectangleEntity> rectangleEntities = new ArrayList<>();
        positions.forEach(p -> {
            RectangleEntity re = new RectangleEntity();
            BeanUtils.copyProperties(p, re);
            rectangleEntities.add(re);
        });
        return rectangleEntities;
    }


    public ManualRedactionEntryEntity findAddRedaction(String fileId, String annotationId) {

        return manualRedactionRepository.findAddRedaction(new AnnotationEntityId(annotationId, fileId))
                .orElseThrow(() -> new NotFoundException("Unknown file/annotation combination: " + fileId + "/" + annotationId));
    }


    @Transactional
    public List<ManualRedactionEntryEntity> findEntriesByFileIdAndOptions(String fileId, ManualChangesQueryOptions options) {

        if (options.getAnnotationIds().isEmpty()) {
            return manualRedactionRepository.findByFileIdAndOptions(fileId, options.isIncludeDeletions(), options.isIncludeOnlyUnprocessed(), options.isIncludeDictChanges());
        }
        return manualRedactionRepository.findByFileIdAndOptionsAndAnnotationIds(fileId,
                                                                                options.isIncludeDeletions(),
                                                                                options.isIncludeOnlyUnprocessed(),
                                                                                options.isIncludeDictChanges(),
                                                                                options.getAnnotationIds());
    }


    public int softDeleteByFileIds(List<String> fileIds, OffsetDateTime softDeletionTime) {

        return manualRedactionRepository.softDeleteByFileIds(fileIds, softDeletionTime);
    }


    public int deleteByFileIds(List<String> fileIds) {

        return manualRedactionRepository.deleteByFileIds(fileIds);
    }


    @Transactional
    public void hardDelete(String fileId, String annotationId) {

        manualRedactionRepository.deleteByIdIs(new AnnotationEntityId(annotationId, fileId));
    }


    @Transactional
    public void softDelete(String fileId, String annotationId, OffsetDateTime softDeleteTime) {

        manualRedactionRepository.updateSoftDelete(new AnnotationEntityId(annotationId, fileId), softDeleteTime);
    }


    @Transactional
    public void undelete(String fileId, String annotationId) {

        manualRedactionRepository.updateSoftDelete(new AnnotationEntityId(annotationId, fileId), null);
    }


    @Transactional
    public void update(ManualRedactionEntryEntity manualRedactionEntry) {

        manualRedactionRepository.saveAndFlush(manualRedactionEntry);
    }


    @Transactional
    public void updateModifiedDictionaries(String fileId, String annotationId, boolean isAddOrRemoveFromDictionary, Set<String> typeIdsOfModifiedDictionaries) {

        ManualRedactionEntryEntity addRedaction = manualRedactionRepository.findById(new AnnotationEntityId(annotationId, fileId))
                .orElseThrow(() -> new NotFoundException("Unknown file/annotation combination: " + fileId + "/" + annotationId));

        addRedaction.setAddToDictionary(isAddOrRemoveFromDictionary);
        addRedaction.setTypeIdsOfModifiedDictionaries(typeIdsOfModifiedDictionaries);

        manualRedactionRepository.saveAndFlush(addRedaction);
    }


    @Transactional
    public void markAsProcessed(ManualRedactionEntry e) {

        manualRedactionRepository.markAsProcessed(new AnnotationEntityId(e.getAnnotationId(), e.getFileId()), OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
    }


    public void undeleteByFileId(String fileId, OffsetDateTime deletionTime) {

        manualRedactionRepository.undeleteByFileId(fileId, deletionTime);
    }


    @Transactional
    public int updateRequestDate(String fileId, String annotationId, OffsetDateTime requestDate) {

        return manualRedactionRepository.updateRequestDateById(new AnnotationEntityId(annotationId, fileId), requestDate);
    }

}
