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

import java.time.OffsetDateTime;
import java.util.Map;
import java.util.stream.Collectors;

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

import com.iqser.red.service.persistence.management.v1.processor.entity.annotations.ViewedPageEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ViewedPagesPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.ChangeType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.ManualRedactionType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLogEntry;

import io.micrometer.core.annotation.Timed;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class AnalysisFlagsCalculationService {

    private final FileStatusPersistenceService fileStatusPersistenceService;
    private final RedactionLogService redactionLogService;
    private final ViewedPagesPersistenceService viewedPagesPersistenceService;


    @Timed("redactmanager_calculateFlags")
    public void calculateFlags(String dossierId, String fileId) {

        long startTime = System.currentTimeMillis();

        var file = fileStatusPersistenceService.getStatus(fileId);
        var redactionLog = redactionLogService.getRedactionLog(dossierId, fileId);

        var viewedPagesForCurrentAssignee = viewedPagesPersistenceService.findViewedPages(fileId, file.getAssignee());

        Map<Integer, OffsetDateTime> viewedPages = viewedPagesForCurrentAssignee.stream().collect(Collectors.toMap(ViewedPageEntity::getPage, ViewedPageEntity::getViewedTime));

        boolean hasRedactions = false;
        boolean hasHints = false;
        boolean hasSuggestions = false;
        boolean hasImages = false;
        boolean hasUpdates = false;
        boolean hasComments = false;

        OffsetDateTime lastRedactionModification = null;
        OffsetDateTime lastManualChangeDate = null;

        for (RedactionLogEntry entry : redactionLog.getRedactionLogEntry()) {
            if (entry.isExcluded()) {
                continue;
            }

            String type = getType(entry.getType());

            var lastChange = entry.getChanges().isEmpty() ? null : entry.getChanges().get(entry.getChanges().size() - 1);

            if (entry.getManualChanges() != null && !entry.getManualChanges().isEmpty()) {

                for (var manualChange : entry.getManualChanges()) {
                    if (!entry.isHint() && !entry.isRecommendation() && StringUtils.isNotEmpty(entry.getReason()) && (manualChange.getManualRedactionType()
                            .equals(ManualRedactionType.ADD_LOCALLY) || manualChange.getManualRedactionType()
                            .equals(ManualRedactionType.RECATEGORIZE) || manualChange.getManualRedactionType()
                            .equals(ManualRedactionType.REMOVE_LOCALLY) || manualChange.getManualRedactionType()
                            .equals(ManualRedactionType.FORCE_REDACT) || manualChange.getManualRedactionType()
                            .equals(ManualRedactionType.FORCE_HINT) || manualChange.getManualRedactionType()
                            .equals(ManualRedactionType.LEGAL_BASIS_CHANGE) || manualChange.getManualRedactionType()
                            .equals(ManualRedactionType.RESIZE)) && manualChange.getProcessedDate() != null && (lastRedactionModification == null || manualChange.getProcessedDate()
                            .isAfter(lastRedactionModification))) {
                        lastRedactionModification = manualChange.getProcessedDate();
                    }

                    if (manualChange.getProcessedDate() != null && (lastManualChangeDate == null || manualChange.getProcessedDate().isAfter(lastManualChangeDate))) {
                        lastManualChangeDate = manualChange.getProcessedDate();
                    }

                    if (manualChange.getRequestedDate() != null && (lastManualChangeDate == null || manualChange.getRequestedDate().isAfter(lastManualChangeDate))) {
                        lastManualChangeDate = manualChange.getRequestedDate();
                    }
                }
            }

            if (lastChange != null && (lastRedactionModification == null || lastChange.getDateTime()
                    .isAfter(lastRedactionModification)) && !entry.isHint() && !entry.isRecommendation()) {
                lastRedactionModification = lastChange.getDateTime();
            }

            if (!hasRedactions && entry.isRedacted() && !entry.isRecommendation()) {
                hasRedactions = true;
            }

            if (!hasHints && entry.isHint()) {
                hasHints = true;
            }

            if (!hasImages && (type.equals("image") || entry.isImage())) {
                hasImages = true;
            }

            if (!hasSuggestions && (lastChange == null || lastChange.getType() != ChangeType.REMOVED) && entry.getManualChanges()
                    .stream()
                    .anyMatch(e -> e.getAnnotationStatus().name().equals(AnnotationStatus.REQUESTED.name()))) {
                hasSuggestions = true;
            }

            if (!hasComments && entry.getComments() != null && !entry.getComments().isEmpty()) {
                hasComments = true;
            }

            var viewedPage = entry.getPositions().isEmpty() ? null : viewedPages.get(entry.getPositions().get(0).getPage());

            if (file.getWorkflowStatus() != WorkflowStatus.APPROVED && lastChange != null && lastChange.getDateTime() != null && viewedPage != null && viewedPage.isBefore(
                    lastChange.getDateTime())) {
                hasUpdates = true;
            }

        }

        log.info("Flag Calculations for file: {} took: {}ms", fileId, System.currentTimeMillis() - startTime);
        if (file.isHasRedactions() == hasRedactions && file.isHasHints() == hasHints && file.isHasImages() == hasImages && file.isHasSuggestions() == hasSuggestions && file.isHasAnnotationComments() == hasComments && file.isHasUpdates() == hasUpdates) {
            log.info("Nothing Changed for file: {}", fileId);
        } else {
            fileStatusPersistenceService.updateFlags(fileId, hasRedactions, hasHints, hasImages, hasSuggestions, hasComments, hasUpdates);
        }

        if (lastRedactionModification != null && (file.getRedactionModificationDate() == null || file.getRedactionModificationDate().isBefore(lastRedactionModification))) {
            fileStatusPersistenceService.setLastRedactionModificationDateForFile(fileId, lastRedactionModification);
        }
        if (lastManualChangeDate != null && (file.getLastManualChangeDate() == null || file.getLastManualChangeDate().isBefore(lastManualChangeDate))) {
            fileStatusPersistenceService.setLastManualChangeDate(fileId, lastManualChangeDate);
        }

    }


    private String getType(String typeId) {

        return typeId.split(":")[0];
    }

}
