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

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
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.redactionlog.RedactionLogMergeService;
import com.iqser.red.service.persistence.management.v1.processor.service.redactionlog.RedactionRequest;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.FilteredRedactionLogRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionGrid;

import feign.FeignException;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class RedactionLogService {

    private final FileManagementStorageService fileManagementStorageService;
    private final ManualRedactionProviderService manualRedactionService;
    private final DossierPersistenceService dossierPersistenceService;
    private final FileStatusService fileStatusService;
    private final ColorsService colorsService;
    private final DictionaryPersistenceService dictionaryPersistenceService;
    private final RedactionLogMergeService redactionLogMergeService;


    public RedactionLog getRedactionLog(String dossierId, String fileId, boolean withManualRedactions, boolean includeFalsePositives) {

        return getRedactionLog(dossierId, fileId, null, withManualRedactions, includeFalsePositives);
    }


    public RedactionLog getRedactionLog(String dossierId, String fileId, List<String> excludedTypes, boolean withManualRedactions, boolean includeFalsePositives) {

        var fileStatus = fileStatusService.getStatus(fileId);

        RedactionLog redactionLog;

        if (withManualRedactions) {
            var dossier = dossierPersistenceService.findByDossierId(dossierId);
            var manualRedactions = manualRedactionService.getManualRedactions(fileId);
            var colors = MagicConverter.convert(colorsService.getColors(dossier.getDossierTemplateId()), Colors.class);
            var types = MagicConverter.convert(dictionaryPersistenceService.getAllTypesForDossierTemplate(dossier.getDossierTemplateId(), true), Type.class);
            var dossierTypes = MagicConverter.convert(dictionaryPersistenceService.getAllTypesForDossier(dossierId, true), Type.class);
            types.addAll(dossierTypes);

            try {
                redactionLog = redactionLogMergeService.provideRedactionLog(RedactionRequest.builder()
                        .dossierId(dossierId)
                        .fileId(fileId)
                        .manualRedactions(manualRedactions)
                        .dossierTemplateId(dossier.getDossierTemplateId())
                        .excludedPages(fileStatus.getExcludedPages())
                        .includeFalsePositives(includeFalsePositives)
                        .colors(colors)
                        .types(types)
                        .build());
            } catch (FeignException e) {
                if (e.status() == HttpStatus.NOT_FOUND.value()) {
                    throw new NotFoundException(e.getMessage());
                }
                throw e;
            }
        } else {
            redactionLog = fileManagementStorageService.getRedactionLog(dossierId, fileId);
        }

        if (fileStatus.isExcluded()) {
            redactionLog.setRedactionLogEntry(new ArrayList<>());
        }

        if (excludedTypes != null) {
            redactionLog.getRedactionLogEntry().removeIf(nextEntry -> excludedTypes.contains(nextEntry.getType()));
        }

        return redactionLog;
    }


    public SectionGrid getSectionGrid(String dossierId, String fileId) {

        return fileManagementStorageService.getSectionGrid(dossierId, fileId);
    }


    public RedactionLog getFilteredRedactionLog(String dossierId, String fileId, FilteredRedactionLogRequest filteredRedactionLogRequest) {

        if (filteredRedactionLogRequest.getSpecifiedDate() == null) {
            filteredRedactionLogRequest.setSpecifiedDate(OffsetDateTime.MIN);
        }

        var redactionLog = getRedactionLog(dossierId,
                fileId,
                filteredRedactionLogRequest.getExcludedTypes(),
                filteredRedactionLogRequest.isWithManualRedactions(),
                filteredRedactionLogRequest.isIncludeFalsePositives());
        var redactionLogEntries = redactionLog.getRedactionLogEntry();

        Iterator<RedactionLogEntry> it = redactionLogEntries.iterator();
        while (it.hasNext()) {
            var redactionLogEntry = it.next();
            boolean isAfterSpecifiedDate = false;
            for (var change : redactionLogEntry.getChanges()) {
                if (change.getDateTime() != null && change.getDateTime().isAfter(filteredRedactionLogRequest.getSpecifiedDate())) {
                    isAfterSpecifiedDate = true;
                }
            }
            for (var manualChange : redactionLogEntry.getManualChanges()) {
                if (manualChange.getProcessedDate() != null && manualChange.getProcessedDate()
                        .isAfter(filteredRedactionLogRequest.getSpecifiedDate()) || manualChange.getRequestedDate() != null && manualChange.getRequestedDate()
                        .isAfter(filteredRedactionLogRequest.getSpecifiedDate())) {
                    isAfterSpecifiedDate = true;
                }
            }
            var comments = redactionLogEntry.getComments();
            if (comments != null) {
                for (var comment : comments) {
                    if (comment.getSoftDeletedTime() != null && comment.getSoftDeletedTime()
                            .isAfter(filteredRedactionLogRequest.getSpecifiedDate()) || comment.getDate() != null && comment.getDate()
                            .isAfter(filteredRedactionLogRequest.getSpecifiedDate())) {
                        isAfterSpecifiedDate = true;
                    }
                }
            }

            if (!isAfterSpecifiedDate) {
                it.remove();
            }

        }

        return redactionLog;
    }

}
